/* ===========================================================================
   Track DZ — responsive app shell.
   Loaded AFTER trackdz.base.css; overrides the mockup's fixed "phone" frame so
   the validated design becomes a real, responsive web app:
     - phone-only on mobile (fills the viewport)
     - centered max-480 column on tablet/desktop
   Everything else (components, duotone status colors, panels) is inherited
   verbatim from the mockup base.
   =========================================================================== */

*{ -webkit-tap-highlight-color:transparent; }
html,body{ height:100%; margin:0; padding:0; }

/* The hidden attribute only carries `display:none` from the UA stylesheet, so
   ANY class that sets an explicit display beats it. .login-form is display:flex,
   which made the password-recovery panel render next to the sign-in fields even
   though its markup said hidden. This restores hidden as the app-wide "not
   present" switch for every element that uses it (#recoverForm, #loginError,
   #asDstRange, #asMapRow, the eye icons), rather than patching one selector and
   leaving the same trap for the next one. */
[hidden]{ display:none !important; }
body{
  font-family:'Inter',system-ui,-apple-system,'Segoe UI',Roboto,Arial,sans-serif;
  background:#0c0d10;              /* neutral backdrop framing the app on desktop */
  color:var(--ink);
  overflow:hidden;
  -webkit-font-smoothing:antialiased;
}

/* App root: full viewport on phones, centered column on larger screens. */
.screen{
  width:100%;
  max-width:480px;
  height:100dvh;
  margin:0 auto;
  border-radius:0;
  box-shadow:none;
}
@media (min-width:481px){
  .screen{ box-shadow:0 0 60px rgba(0,0,0,.45); }
}

/* A real browser / OS provides its own status bar + nav — drop the mockup's fakes. */
.statusbar{ display:none !important; }

/* The real Leaflet map replaces the mockup's decorative CSS "map". */
.map-wrap{ position:absolute; inset:0; }
#tdzMap{ position:absolute; inset:0; z-index:1; }
.map-wrap .legend-fab,
.map-wrap .legend-pop,
.map-wrap .places-fab,
.map-wrap .callout{ z-index:600; }
/* Places layer toggle (below the legend fab) */
.places-fab{
  position:absolute; right:12px; top:56px; z-index:600;
  width:36px; height:36px; border-radius:50%; background:#fff; border:none;
  box-shadow:0 6px 16px -4px rgba(20,30,50,.35);
  display:flex; align-items:center; justify-content:center; cursor:pointer; color:#3d4451;
}
.places-fab svg{ width:18px; height:18px; }
.places-fab.on{ background:var(--brand,#0f6e8c); color:#fff; }
[dir="rtl"] .places-fab{ right:auto; left:12px; }

/* Base-map chooser (third in the fab stack, below places).
   The same choice also lives in Preferences; this is the shortcut for changing
   it while looking at the map, and both write through switchMapLayer(). */
.map-wrap .layers-fab,
.map-wrap .layers-pop{ z-index:600; }
.layers-fab{
  position:absolute; right:12px; top:100px; z-index:600;
  width:36px; height:36px; border-radius:50%; background:#fff; border:none;
  box-shadow:0 6px 16px -4px rgba(20,30,50,.35);
  display:flex; align-items:center; justify-content:center; cursor:pointer; color:#3d4451;
}
.layers-fab svg{ width:18px; height:18px; }
.layers-fab.on{ background:var(--brand,#0f6e8c); color:#fff; }
[dir="rtl"] .layers-fab{ right:auto; left:12px; }

.layers-pop{
  position:absolute; right:12px; top:142px; z-index:600;
  background:#26282f; color:#f2f3f6; border-radius:12px; padding:12px 8px 8px;
  width:200px; box-shadow:0 12px 30px -10px rgba(0,0,0,.5);
  opacity:0; transform:translateY(-6px) scale(.97); pointer-events:none;
  transition:opacity .16s ease, transform .16s ease;
  font-size:12px;
  /* Providers can grow; never let the list run off a phone screen. */
  max-height:min(58vh, 340px); overflow-y:auto; -webkit-overflow-scrolling:touch;
}
.layers-pop.show{ opacity:1; transform:translateY(0) scale(1); pointer-events:auto; }
[dir="rtl"] .layers-pop{ right:auto; left:12px; }
.layers-pop .ly-title{
  font-size:10px; text-transform:uppercase; letter-spacing:.05em;
  color:#9aa0ab; font-weight:700; margin:0 8px 8px;
}
.layers-pop .ly-row{
  display:flex; align-items:center; gap:9px; width:100%;
  padding:8px; border:none; background:none; color:inherit;
  font:inherit; text-align:start; cursor:pointer; border-radius:8px;
}
.layers-pop .ly-row:hover{ background:rgba(255,255,255,.07); }
.layers-pop .ly-row[aria-selected="true"]{ background:rgba(255,255,255,.12); font-weight:600; }
/* Reserve the tick column on every row so labels line up whether ticked or not. */
.layers-pop .ly-check{ width:14px; height:14px; flex-shrink:0; opacity:0; }
.layers-pop .ly-row[aria-selected="true"] .ly-check{ opacity:1; }
.layers-pop .ly-name{ flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* Place picker (full-screen map + crosshair) */
.pick-map-wrap{ position:relative; flex:1; min-height:0; }
#placePickMap{ position:absolute; inset:0; z-index:1; }
.pick-crosshair{ position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); z-index:600; pointer-events:none; }
.pick-crosshair svg{ width:34px; height:34px; filter:drop-shadow(0 1px 2px rgba(0,0,0,.4)); }
.pick-bar{ padding:10px 16px 14px; background:#fff; box-shadow:0 -4px 14px -6px rgba(20,30,50,.25); }
.pick-bar .rep-field{ margin:4px 0; }
.poi-icon-row{ display:flex; flex-wrap:wrap; gap:6px; }
.poi-ic{ width:38px; height:38px; border-radius:9px; border:1px solid #e6e8ee; background:#fff; cursor:pointer; display:flex; align-items:center; justify-content:center; padding:0; }
.poi-ic.sel{ border-color:var(--brand,#0f6e8c); box-shadow:0 0 0 2px rgba(15,110,140,.25); }

/* Recolored + heading-rotated vehicle marker (L.divIcon content).
   Size comes from --tdz-icon, which tdz.app.js sets from the user's map_is
   multiplier. The box and the image must scale together, and the image keeps its
   original 38/44 ratio so the shadow and rotation still sit right at any size.
   The fallbacks preserve the original look if the script has not run. */
.tdz-marker{ position:relative; width:var(--tdz-icon,44px); height:var(--tdz-icon,44px); }
.tdz-marker .tdz-veh{
  position:absolute; left:50%; top:50%;
  width:calc(var(--tdz-icon,44px) * 0.8636); height:calc(var(--tdz-icon,44px) * 0.8636);
  margin:calc(var(--tdz-icon,44px) * -0.4318) 0 0 calc(var(--tdz-icon,44px) * -0.4318);
  object-fit:contain;
  transform-origin:50% 50%;
  transition:transform .5s linear;
  filter:drop-shadow(0 2px 3px rgba(0,0,0,.35));
}

/* Historique controls (vehicle + period). */
.hist-select{
  flex:1; min-width:0; padding:8px 10px; border-radius:9px;
  border:1px solid #e6e8ee; background:#fff; font-family:inherit;
  font-size:13px; color:var(--ink,#1c2230);
}
.hist-period-btn{ cursor:pointer; text-align:left; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
[dir="rtl"] .hist-period-btn{ text-align:right; }
/* period modal */
.per-opt{ display:flex; align-items:center; gap:10px; padding:9px 4px; font-size:14px; color:var(--ink,#1c2230); cursor:pointer; }
.per-opt input{ width:18px; height:18px; accent-color:var(--brand,#0f6e8c); }
.per-range{ margin:6px 0 4px; display:flex; flex-direction:column; gap:6px; }
.per-range label{ font-size:11px; color:#9aa0ab; }
.per-range input{ width:100%; box-sizing:border-box; }
/* trip leg addresses */
.tr-addr .addr-k{ color:#9aa0ab; font-weight:600; }
#histMap .leaflet-container{ background:#e7ede3; }

/* Historique — playback bar */
.hist-playbar{ display:flex; align-items:center; gap:10px; margin:10px 16px 0;
  background:#fff; border-radius:10px; box-shadow:0 1px 0 #e9ebf0; padding:8px 12px; }
.hist-play{ width:34px; height:34px; flex-shrink:0; border:none; border-radius:50%;
  background:var(--brand,#0f6e8c); color:#fff; display:flex; align-items:center; justify-content:center; cursor:pointer; }
.hist-play svg{ width:16px; height:16px; }
.hist-slider{ flex:1; min-width:0; accent-color:var(--brand,#0f6e8c); }
.hist-time{ font-size:10.5px; color:#5b6472; font-variant-numeric:tabular-nums; white-space:nowrap; }
.hist-speed{ border:1px solid #e6e8ee; border-radius:8px; padding:4px 6px; font-size:12px;
  font-family:inherit; background:#fff; color:var(--ink,#1c2230); }

/* Historique — Voyage/Vitesse tabs */
.hist-tabs{ display:flex; gap:6px; margin:14px 16px 0; }
.hist-tab{ flex:1; padding:8px 10px; border:1px solid #e6e8ee; background:#fff; border-radius:9px;
  font-family:inherit; font-size:12.5px; font-weight:650; color:#5b6472; cursor:pointer; }
.hist-tab.active{ background:var(--brand,#0f6e8c); border-color:var(--brand,#0f6e8c); color:#fff; }

/* Historique — trip summary + list */
.trip-summary{ background:#fff; border-radius:12px; box-shadow:0 1px 0 #e9ebf0; margin:12px 16px 0; overflow:hidden; }
.trip-summary .ts-row{ display:flex; justify-content:space-between; padding:9px 14px; font-size:12.5px;
  color:#5b6472; border-bottom:1px solid #f0f1f5; }
.trip-summary .ts-row:last-child{ border-bottom:none; }
.trip-summary .ts-row b{ color:var(--ink,#1c2230); font-variant-numeric:tabular-nums; }
.trip-list{ margin:12px 16px 16px; }
.trip-row{ display:flex; align-items:center; gap:10px; background:#fff; border-radius:10px;
  box-shadow:0 1px 0 #e9ebf0; padding:10px 12px; margin-bottom:8px; }
.trip-row .tr-idx{ font-size:11px; font-weight:700; color:#b0b5bf; width:16px; flex-shrink:0; text-align:center; }
.trip-row .tr-dot{ width:9px; height:9px; border-radius:50%; flex-shrink:0; }
.trip-row .tr-main{ flex:1; min-width:0; }
.trip-row .tr-title{ font-size:13px; font-weight:700; color:var(--ink,#1c2230); }
.trip-row .tr-sub{ font-size:11px; color:#9aa0ab; font-variant-numeric:tabular-nums; margin-top:2px; }
.trip-row .tr-meta{ font-size:11.5px; font-weight:700; color:#5b6472; text-align:right;
  font-variant-numeric:tabular-nums; white-space:nowrap; }

/* Historique — speed chart */
#histChart{ margin:12px 16px 16px; background:#fff; border-radius:12px; box-shadow:0 1px 0 #e9ebf0; padding:12px; }
#histChart svg{ width:100%; height:auto; display:block; }

/* Historique — stop pin + playback cursor on the map */
.tdz-stop-pin{ display:flex; align-items:center; justify-content:center; width:22px; height:22px;
  border-radius:50% 50% 50% 0; transform:rotate(-45deg); background:var(--status-idle,#f4b400);
  color:#fff; box-shadow:0 2px 5px rgba(0,0,0,.35); border:2px solid #fff; }
.tdz-stop-pin span{ transform:rotate(45deg); font-size:11px; font-weight:800; line-height:1; }
.tdz-cursor{ width:16px; height:16px; border-radius:50%; background:var(--status-static,#2f7bda);
  border:3px solid #fff; box-shadow:0 2px 6px rgba(0,0,0,.45); }
/* Playback cursor = the vehicle's own icon, rotated by heading */
.tdz-cursor-veh{ width:36px; height:36px; object-fit:contain; transform-origin:50% 50%;
  transition:transform .25s linear; filter:drop-shadow(0 2px 4px rgba(0,0,0,.45)); }
/* Live playback info box (speed / mileage / course / time) */
.tdz-hist-tip{ background:rgba(28,34,48,.86); color:#fff; border:none; box-shadow:0 4px 14px rgba(0,0,0,.35);
  border-radius:9px; padding:7px 10px; font-size:11px; line-height:1.55; white-space:nowrap; }
.tdz-hist-tip .k{ color:#aeb6c4; }
.tdz-hist-tip b{ font-variant-numeric:tabular-nums; }
.leaflet-tooltip.tdz-hist-tip::before{ display:none; }
[dir="rtl"] .tdz-hist-tip{ direction:rtl; }

/* Permanent "Nom (12 km/h)" label beside each vehicle, as the classic web UI
   shows. pointer-events:none keeps the label from stealing the marker's click,
   and no arrow (::before) because at fleet zoom the arrows add more clutter
   than they resolve. Labels are a readability aid, so they must never cover the
   vehicle they belong to - z-index sits below the marker pane. */
.leaflet-tooltip.tdz-veh-label{
  /* Translucent on purpose: at fleet zoom these sit on top of roads and place
     names, and an opaque chip hides more map than the name is worth. The text
     stays fully opaque while the plate behind it does not, so the label reads
     without blanking what is underneath. */
  background:rgba(255,255,255,.62); color:#1c2230;
  border:1px solid rgba(120,132,152,.35);
  box-shadow:0 1px 3px rgba(20,30,50,.14);
  -webkit-backdrop-filter:blur(1.5px); backdrop-filter:blur(1.5px);
  border-radius:5px; padding:1px 5px;
  font-size:11px; line-height:1.5; font-weight:600; white-space:nowrap;
  pointer-events:none;
  /* The content is "Name (12 km/h)": an identifier, a figure and a Latin unit.
     None of it is prose, so it must read identically in every language. The
     page is dir="rtl" in Arabic, and inheriting that put bidi to work on the
     parentheses and the number and reordered them on screen. Isolating to LTR
     is what keeps the figures standard and in place - it is a RENDERING fix;
     the digits were already Western, the app formats them with plain
     Math.round and never calls toLocaleString. */
  direction:ltr; unicode-bidi:isolate;
}
.leaflet-tooltip.tdz-veh-label::before{ display:none; }
/* A selected/followed vehicle gets its label back to near-solid, so the one
   you are watching stays legible over busy basemaps. */
.leaflet-tooltip.tdz-veh-label.is-active{
  background:rgba(255,255,255,.93); border-color:rgba(90,104,126,.55);
}

/* Stop duration printed above each "P" pin on the history trace, so how long a
   vehicle stood somewhere is readable without tapping every pin. */
.leaflet-tooltip.tdz-stop-label{
  background:rgba(255,255,255,.94); color:#b34700; border:1px solid #f0a870;
  box-shadow:0 1px 4px rgba(20,30,50,.25);
  border-radius:5px; padding:1px 5px;
  font-size:10.5px; line-height:1.5; font-weight:700; white-space:nowrap;
  pointer-events:none;
}
.leaflet-tooltip.tdz-stop-label::before{ display:none; }
/* Same reasoning as the vehicle label: the content is a duration ("2h 15m"),
   so it is isolated to LTR rather than inheriting the page's RTL. */
.leaflet-tooltip.tdz-stop-label{ direction:ltr; unicode-bidi:isolate; }
.tdz-stop-pop{ font-size:12px; line-height:1.6; white-space:nowrap; }

/* Historique — big map that fills, with a collapsible bottom window */
.screen-panel[data-panel="historique"]{ display:flex; flex-direction:column; }
.hist-body{ flex:1; display:flex; flex-direction:column; min-height:0; padding-bottom:0; }
.hist-map-wrap{ position:relative; z-index:0; flex:1; min-height:200px; margin:12px 16px 0; border-radius:12px; overflow:hidden; background:#e7ede3; }
/* contain Leaflet's high z-index panes so the period modal (z-index 21) sits ABOVE the map */
#histSearch{ flex:1 1 100%; }
#histMap{ position:absolute; inset:0; }

/* left icon rail (opens the bottom window on a tab) */
.hist-rail{ position:absolute; left:8px; top:50%; transform:translateY(-50%); z-index:600;
  display:flex; flex-direction:column; gap:7px; }
.hist-rail-btn{ width:38px; height:38px; border:none; border-radius:9px; background:var(--amber,#e8a33d);
  color:#fff; display:flex; align-items:center; justify-content:center; cursor:pointer; box-shadow:0 2px 6px rgba(0,0,0,.25); }
.hist-rail-btn svg{ width:20px; height:20px; }
.hist-rail-btn.active{ background:var(--brand,#0f6e8c); }
[dir="rtl"] .hist-rail{ left:auto; right:8px; }

/* floating summary card on the map */
.hist-summary-pop{ position:absolute; top:10px; left:50%; transform:translateX(-50%); z-index:650;
  background:#fff; border-radius:11px; box-shadow:0 6px 20px rgba(20,30,50,.25); padding:9px 30px 9px 12px;
  font-size:12px; color:var(--ink,#1c2230); max-width:86%; }
.hist-summary-pop .hs-line{ line-height:1.6; white-space:nowrap; }
.hist-summary-pop .k{ color:#6b7280; }
.hist-summary-pop b{ font-variant-numeric:tabular-nums; }
.hsp-close{ position:absolute; top:5px; right:5px; width:22px; height:22px; border:none; background:transparent; color:#9aa0ab; cursor:pointer; padding:0; }
.hsp-close svg{ width:12px; height:12px; }
[dir="rtl"] .hist-summary-pop{ padding:9px 12px 9px 30px; }
[dir="rtl"] .hsp-close{ right:auto; left:5px; }

/* playback overlay pinned to the bottom of the map */
.hist-playbar{ position:absolute; left:10px; right:10px; bottom:10px; z-index:640; margin:0; }

/* collapsible bottom window (flex: expands to push the map up, map stays visible) */
.hist-sheet{ flex:0 0 auto; background:#fff; border-top:1px solid #eef1f6; max-height:0; overflow:hidden;
  transition:max-height .28s ease; display:flex; flex-direction:column; }
.hist-sheet.open{ max-height:56vh; box-shadow:0 -6px 18px rgba(20,30,50,.10); }
.hist-sheet-head{ display:flex; align-items:center; gap:8px; padding:10px 12px 8px; border-bottom:1px solid #f0f1f5; flex-shrink:0; }
.hist-sheet-head .hist-tabs{ flex:1; margin:0; }
.hist-sheet-close{ width:30px; height:30px; flex-shrink:0; border:none; background:#f2f3f6; border-radius:50%;
  color:#5b6472; cursor:pointer; display:flex; align-items:center; justify-content:center; }
.hist-sheet-close svg{ width:14px; height:14px; }
/* flex:1 + min-height:0 is what actually makes this scroll. A flex item defaults
   to min-height:auto, so without the override the body refuses to shrink below its
   content, grows past the sheet's max-height:56vh and is simply clipped by its
   overflow:hidden - overflow-y:auto can only scroll a BOUNDED box. That is why the
   Voyage list could not be scrolled once it was longer than the sheet. */
.hist-sheet-body{ flex:1 1 auto; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch; padding-bottom:14px; }

/* Détails — per-GPS-point list */
.pt-list{ margin:8px 16px 8px; }
.pt-row{ display:flex; align-items:center; gap:10px; padding:8px 2px; border-bottom:1px solid #f4f5f8; }
.pt-row .pt-idx{ font-size:10.5px; color:#b0b5bf; width:22px; flex-shrink:0; text-align:center; }
.pt-row .pt-main{ flex:1; min-width:0; }
.pt-row .pt-top{ font-size:12px; font-weight:650; color:var(--ink,#1c2230); font-variant-numeric:tabular-nums; }
.pt-row .pt-sub{ font-size:10.5px; color:#9aa0ab; font-variant-numeric:tabular-nums; margin-top:1px; }
.pt-row .pt-crs{ font-size:11px; font-weight:700; color:#5b6472; white-space:nowrap; }

/* Appareil — search box + richer rows */
.dev-search{ display:flex; align-items:center; gap:8px; margin:12px 16px 2px; background:#fff;
  border:1px solid #e6e8ee; border-radius:10px; padding:8px 12px; }
.dev-search svg{ width:16px; height:16px; color:#9aa0ab; flex-shrink:0; }
.dev-search input{ flex:1; min-width:0; border:none; outline:none; font-family:inherit; font-size:13.5px;
  color:var(--ink,#1c2230); background:transparent; }
.dev-model{ font-size:11px; font-weight:600; color:#9aa0ab; }
.dev-imei{ font-variant-numeric:tabular-nums; }
.devrow .dev-info .sub{ line-height:1.35; }
.dev-more{ flex-shrink:0; width:30px; height:30px; border:none; background:transparent; color:#b0b5bf;
  cursor:pointer; display:flex; align-items:center; justify-content:center; padding:0; }
.dev-more svg{ width:18px; height:18px; }

/* Device detail page */
.dev-copy{ margin-left:8px; border:none; background:transparent; color:var(--brand,#0f6e8c); cursor:pointer; padding:0; }
.dev-copy svg{ width:15px; height:15px; }
.dev-actions{ display:flex; gap:8px; margin:12px 16px 0; }
.da-btn{ flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding:12px 4px;
  border:1px solid #e6e8ee; border-radius:11px; background:#fff; cursor:pointer; color:var(--brand,#0f6e8c); font-family:inherit; }
.da-btn svg{ width:20px; height:20px; }
.da-btn span{ font-size:11px; font-weight:600; color:#5b6472; }
.menu-item.active{ background:var(--brand-tint,#e4f1f5); }
.ch-row{ display:flex; align-items:center; gap:10px; background:#fff; border-radius:10px; box-shadow:0 1px 0 #e9ebf0;
  padding:10px 12px; margin-bottom:8px; }
.ch-main{ flex:1; min-width:0; }
.ch-name{ font-size:13px; font-weight:700; color:var(--ink,#1c2230); }
.ch-sub{ font-size:11px; color:#9aa0ab; font-variant-numeric:tabular-nums; margin-top:2px; }
.ch-badge{ font-size:10px; font-weight:700; color:#fff; padding:3px 8px; border-radius:20px; white-space:nowrap; }

/* Lazy address (trip legs / history points) */
.tr-addr, .pt-addr{ font-size:10.5px; margin-top:2px; line-height:1.3; color:#6b7280; }
.addr-lazy{ cursor:pointer; color:var(--brand,#0f6e8c); font-weight:600; }

/* Alert row -> tap to locate on map */
.alertrow{ cursor:pointer; }
.alertrow:active{ background:#f6f8fb; }
.alertrow .a-chev{ width:18px; height:18px; color:#c2c7d0; flex-shrink:0; align-self:center; }
[dir="rtl"] .alertrow .a-chev{ transform:scaleX(-1); }
/* The row a tapped push notification refers to. The list can hold 200 rows, so
   arriving on it is not enough - this says which one. Fades out on its own; a
   permanent marker would still be there on the next unrelated visit. */
.alertrow.alert-focus{
  animation:tdzAlertFocus 4s ease-out forwards;
  position:relative; z-index:1;
}
@keyframes tdzAlertFocus{
  0%,55%{ box-shadow:0 0 0 2px var(--c), 0 6px 18px -6px rgba(20,30,50,.45); background:#fbfdff; }
  100%  { box-shadow:0 1px 0 #e9ebf0; background:#fff; }
}
@media (prefers-reduced-motion:reduce){
  .alertrow.alert-focus{ animation:none; box-shadow:0 0 0 2px var(--c); }
}
/* Speed badge on speed-related alert rows - the number that gives the alert its
   meaning, so it is readable without opening each one. */
.alertrow .a-speed{
  display:inline-block; margin-inline-start:8px; padding:1px 7px;
  border-radius:999px; font-size:11px; font-weight:700;
  background:color-mix(in srgb, var(--c) 15%, #fff); color:var(--c);
  vertical-align:middle; white-space:nowrap; font-variant-numeric:tabular-nums;
}

/* Relay reminder on engine commands. Amber, not red: this is a precondition to
   check, not an error the user has made. */
.cmd-warn{
  display:flex; gap:8px; align-items:flex-start;
  margin:0 0 12px; padding:10px 12px;
  background:#fff7e6; border:1px solid #f0d9a8; border-radius:10px;
  font-size:12px; line-height:1.45; color:#7a5b12; text-align:start;
}
.cmd-warn::before{ content:"\26A0"; font-size:14px; line-height:1.2; flex:none; }

/* Alert detail popup on the map. */
.tdz-alert-popup .leaflet-popup-content{ margin:11px 13px; min-width:190px; }
.tdz-alert-popup .ap-title{
  font-size:13px; font-weight:700; color:#1c2230;
  margin-bottom:7px; padding-bottom:6px; border-bottom:1px solid #e9ebf0;
}
.tdz-alert-popup .ap-row{ display:flex; justify-content:space-between; gap:14px; padding:2.5px 0; }
.tdz-alert-popup .ap-k{ font-size:11.5px; color:#8b93a0; }
.tdz-alert-popup .ap-v{ font-size:12px; font-weight:600; color:#1c2230; text-align:end; font-variant-numeric:tabular-nums; }
.tdz-alert-popup .ap-v.ap-hi{ color:var(--status-speeding,#e53935); font-weight:800; font-size:13px; }
.tdz-alert-divicon{ background:none; border:none; }
.tdz-alert-pin{ width:40px; height:40px; border-radius:50%; background:#fff; border:2px solid var(--status-speeding,#e53935); color:var(--status-speeding,#e53935); display:flex; align-items:center; justify-content:center; box-shadow:0 2px 7px rgba(0,0,0,.32); animation:tdzAlertPulse 1.3s ease-in-out infinite; }
.tdz-alert-pin svg{ width:22px; height:22px; }
@keyframes tdzAlertPulse{ 0%,100%{ box-shadow:0 2px 7px rgba(0,0,0,.32), 0 0 0 0 rgba(229,57,53,.45); } 50%{ box-shadow:0 2px 7px rgba(0,0,0,.32), 0 0 0 8px rgba(229,57,53,0); } }

/* Device Alertes — ProTrack-style alert catalog */
.alert-catalog-card{ padding:2px 0; }
.ac-row{ border-bottom:1px solid #f0f1f5; }
.ac-row:last-child{ border-bottom:none; }
.ac-main{ display:flex; align-items:center; gap:10px; padding:13px 14px; }
.ac-label{ flex:1; font-size:14px; font-weight:600; color:var(--ink,#1c2230); }
.ac-right{ display:flex; align-items:center; gap:10px; flex:none; }
.ac-cfg{ border:none; background:transparent; color:#8a90a0; font-size:13px; font-weight:600; cursor:pointer; padding:2px 2px; white-space:nowrap; }
.ac-cfg:active{ color:var(--brand,#0f6e8c); }
.ac-edit{ display:none; align-items:center; gap:8px; padding:0 14px 12px; }
.ac-edit.open{ display:flex; }
.ac-input{ width:90px; box-sizing:border-box; padding:8px 10px; border:1px solid #dfe3ec; border-radius:9px; font-size:14px; background:#fff; }
.ac-unit{ font-size:12px; color:#9aa0ab; }
.ac-save{ margin-left:auto; width:34px; height:34px; border-radius:9px; border:none; background:var(--brand,#0f6e8c); color:#fff; font-size:16px; cursor:pointer; flex:none; }
[dir="rtl"] .ac-save{ margin-left:0; margin-right:auto; }
.ac-save:active{ opacity:.85; }

/* Device Alertes (per-rule active + push) */
.da-row{ padding:12px 14px; border-bottom:1px solid #f0f1f5; }
.da-row:last-child{ border-bottom:none; }
.da-name{ font-size:14px; font-weight:650; color:var(--ink,#1c2230); margin-bottom:8px; }
.da-toggles{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.da-lbl{ font-size:11.5px; color:#9aa0ab; }
.da-toggles .toggle-switch{ margin-right:14px; }
[dir="rtl"] .da-toggles .toggle-switch{ margin-right:0; margin-left:14px; }
.cmd-row{ display:flex; align-items:center; justify-content:space-between; gap:10px; }
.cmd-del{ flex:none; width:30px; height:30px; border-radius:8px; border:1px solid #f0d3d3; background:#fdeeee; color:#d64545; font-size:14px; cursor:pointer; }
.cmd-del:active{ background:#f9dede; }
.pl-actions{ display:flex; align-items:center; gap:6px; flex:none; }
.place-edit{ width:30px; height:30px; border-radius:8px; border:1px solid #dfe3ec; background:#f3f5f9; color:#3d4451; font-size:14px; cursor:pointer; }
.place-edit:active{ background:#e8ebf2; }
/* Share links */
.sh-item{ padding:12px 14px; border-bottom:1px solid #f0f1f5; }
.sh-item:last-child{ border-bottom:none; }
.sh-top{ display:flex; align-items:center; justify-content:space-between; gap:10px; }
.sh-link{ font-size:11px; color:#6b7280; word-break:break-all; margin:4px 0 8px; }
.sh-actions{ display:flex; gap:8px; }
.sh-actions .filter-chip{ text-decoration:none; text-align:center; }
.eng-btn{ background:#eef2f8; color:var(--ink,#1c2230); border:1px solid #e2e6ee; }

/* Device Paramètres / edit */
.dp-edit{ border:none; background:transparent; color:var(--brand,#0f6e8c); font-family:inherit;
  font-size:14px; font-weight:700; cursor:pointer; flex-shrink:0; padding:0 4px; }
.dp-input{ border:1px solid #e6e8ee; border-radius:8px; padding:5px 8px; font-family:inherit;
  font-size:13px; color:var(--ink,#1c2230); text-align:right; max-width:58%; }
[dir="rtl"] .dp-input{ text-align:left; }
.mnt-actions{ display:flex; gap:8px; padding:10px 14px; }
.mnt-actions button{ flex:1; padding:8px; border:none; border-radius:8px; font-family:inherit; font-weight:700; font-size:13px; cursor:pointer; }
.mnt-actions .mnt-cancel{ background:#f2f3f6; color:#5b6472; }
.mnt-actions .mnt-save{ background:var(--brand,#0f6e8c); color:#fff; }
.svc-addbtn{ display:block; width:calc(100% - 28px); margin:8px 14px 12px; padding:9px; border:1px dashed #cfd4dd;
  border-radius:9px; background:#fff; color:var(--brand,#0f6e8c); font-family:inherit; font-weight:700; font-size:13px; cursor:pointer; }
.svc-add{ border-top:1px solid #f0f1f5; }
.svc-add .dp-input{ max-width:52%; }
.svc-type-row{ display:flex; gap:6px; flex-wrap:wrap; justify-content:flex-end; }
.svc-type-row .svc-type{ padding:5px 12px; }
.svc-right{ display:flex; align-items:center; gap:8px; }
.svc-right .svc-edit, .svc-right .svc-del{ width:26px; height:26px; font-size:13px; }

/* Reports form */
.rep-field{ padding:11px 0; border-bottom:1px solid #f0f1f5; }
.rep-field:last-child{ border-bottom:none; }
.rep-field > label{ display:block; font-size:11px; color:#9aa0ab; margin-bottom:5px; font-weight:600; }
.rep-field .hist-select{ width:100%; box-sizing:border-box; }
.rep-formats{ display:flex; gap:8px; }
.rep-field.rep-row{ display:flex; align-items:center; justify-content:space-between; }
.rep-field.rep-row > label{ margin-bottom:0; }
.rep-devlist{ max-height:150px; overflow-y:auto; border:1px solid #eef1f6; border-radius:9px; padding:2px 4px; }
.rep-dev-opt{ display:flex; align-items:center; gap:9px; padding:8px 6px; font-size:13.5px; color:var(--ink,#1c2230); cursor:pointer; border-bottom:1px solid #f4f5f8; }
.rep-dev-opt:last-child{ border-bottom:none; }
.rep-dev-opt input{ width:17px; height:17px; accent-color:var(--brand,#0f6e8c); }

/* Notifications settings rows */
.notif-row{ display:flex; align-items:center; justify-content:space-between; padding:14px; border-bottom:1px solid #f0f1f5; }
.notif-row:last-child{ border-bottom:none; }
.notif-label{ font-size:14px; font-weight:600; color:var(--ink,#1c2230); }
/* Per-event notifications */
.notif-grp{ margin-top:12px; }
.notif-grp-h{ font-size:12px; font-weight:700; color:#9aa0ab; padding:0 16px 4px; text-transform:uppercase; letter-spacing:.03em; }
.ne-row{ border-bottom:1px solid #f0f1f5; }
.ne-row:last-child{ border-bottom:none; }
.ne-head{ display:flex; align-items:center; gap:8px; padding:12px 14px; }
.ne-exp{ flex:none; width:24px; height:24px; border:none; background:transparent; color:#6b7280; cursor:pointer; display:flex; align-items:center; justify-content:center; padding:0; }
.ne-name{ flex:1; min-width:0; font-size:14px; font-weight:600; color:var(--ink,#1c2230); }
.ne-off{ font-size:11px; color:#9aa0ab; font-weight:400; }
.ne-sub{ font-size:11.5px; font-weight:400; color:#9aa0ab; margin-top:2px; }
.notif-channels{ padding:2px 14px 12px 44px; }
.nch{ display:flex; align-items:center; justify-content:space-between; padding:7px 0; }
.nch-l{ font-size:13px; color:#5b6472; }
.toggle-switch.disabled{ opacity:.4; pointer-events:none; }
[dir="rtl"] .notif-channels{ padding:2px 44px 12px 14px; }
[dir="rtl"] .ne-exp svg{ transform:scaleX(-1); }

/* Richer callout: wider, address line + action row */
.map-wrap .callout{ width:212px; }
.callout-addr{ font-size:9.5px; color:#6b7280; margin-top:6px; line-height:1.35;
  max-height:26px; overflow:hidden; }
.callout-addr:empty{ display:none; }
.callout-actions{ display:flex; gap:2px; margin-top:8px; padding-top:7px; border-top:1px solid rgba(0,0,0,.08); }
.ca-btn{ flex:1; display:flex; flex-direction:column; align-items:center; gap:2px; border:none;
  background:transparent; cursor:pointer; padding:3px 0; color:var(--brand,#0f6e8c); }
.ca-btn svg{ width:16px; height:16px; }
.ca-btn span{ font-size:8px; color:#5b6472; font-weight:600; }
.ca-btn.active{ color:var(--status-moving,#34a853); }
.ca-btn.active span{ color:var(--status-moving,#34a853); }

/* command list in the modal */
.cmd-item{ display:block; width:100%; text-align:left; padding:11px 12px; margin-top:6px; border:1px solid #e6e8ee;
  border-radius:9px; background:#fff; font-family:inherit; font-size:13.5px; font-weight:650; color:var(--ink,#1c2230); cursor:pointer; }
.cmd-item:hover{ border-color:var(--brand,#0f6e8c); }
.cmd-item .ci-gw{ float:right; font-size:10px; font-weight:700; color:#9aa0ab; text-transform:uppercase; }
[dir="rtl"] .cmd-item{ text-align:right; }
[dir="rtl"] .cmd-item .ci-gw{ float:left; }
#cmdModal .pw-sub{ margin-bottom:6px; }

/* Tablet / desktop: give the app column more room */
@media (min-width:700px){ .screen{ max-width:640px; } }
@media (min-width:1100px){ .screen{ max-width:760px; } }

/* Boot overlay shown until the SPA has hydrated. */
.tdz-boot{
  position:fixed; inset:0; z-index:9999;
  display:flex; align-items:center; justify-content:center;
  background:#f4f6f9; transition:opacity .25s ease;
}
.tdz-boot.hide{ opacity:0; pointer-events:none; }
.tdz-boot .spin{
  width:34px; height:34px; border-radius:50%;
  border:3px solid var(--brand-tint,#e4f1f5);
  border-top-color:var(--brand,#0f6e8c);
  animation:tdzspin .8s linear infinite;
}
@keyframes tdzspin{ to{ transform:rotate(360deg); } }

/* ===========================================================================
   Desktop two-pane layout
   ---------------------------------------------------------------------------
   Laptops and iPads get a real web layout instead of a narrow phone column:
   a left nav rail + a sidebar (device list / active screen) with the LIVE MAP
   always visible, filling the rest of the window.
   This is driven by <html data-tdzview="desktop"> (set by tdz.core.js from the
   Mobile/Desktop switcher in Compte, default = auto-by-width at 768px), NOT a
   raw media query — so the user can force either view. It is further gated by
   `.screen:has(.screen-panel[data-panel="suivi"])` so it applies only to the
   signed-in app; the signed-out login page keeps its centred column.
   Panels are position:absolute; inset:0 inside .panels-wrap (see base css); we
   pin the non-map panels to a fixed-width sidebar and the map to the rest, and
   leave the existing .active toggling to pick which sidebar panel shows.
   =========================================================================== */
html[data-tdzview="desktop"] .screen:has(.screen-panel[data-panel="suivi"]){
  --tdz-rail:72px; --tdz-sidebar:360px;
  max-width:none; width:100vw; height:100dvh; margin:0; box-shadow:none;
  display:grid;
  grid-template-columns:var(--tdz-rail) 1fr;
  grid-template-rows:100dvh;
}
html[data-tdzview="desktop"][dir="rtl"] .screen:has(.screen-panel[data-panel="suivi"]){
  grid-template-columns:1fr var(--tdz-rail);
}

/* bottom nav -> vertical left rail (first grid column) */
html[data-tdzview="desktop"] .screen:has(.screen-panel[data-panel="suivi"]) > .bottomnav{
  grid-column:1; grid-row:1;
  flex-direction:column; justify-content:flex-start; gap:2px;
  border-top:none; border-inline-end:1px solid #e6e8ee;
  padding:14px 4px; overflow-y:auto;
}
html[data-tdzview="desktop"][dir="rtl"] .screen:has(.screen-panel[data-panel="suivi"]) > .bottomnav{ grid-column:2; }
html[data-tdzview="desktop"] .screen:has(.screen-panel[data-panel="suivi"]) > .bottomnav button{
  flex:0 0 auto; padding:11px 2px; border-radius:10px;
}
html[data-tdzview="desktop"] .screen:has(.screen-panel[data-panel="suivi"]) > .bottomnav button.active{
  background:var(--brand-tint,#e4f1f5);
}

/* content region (second grid column) holds the sidebar + the map */
html[data-tdzview="desktop"] .screen:has(.screen-panel[data-panel="suivi"]) > .panels-wrap{
  grid-column:2; grid-row:1; position:relative; overflow:hidden; height:100dvh;
}
html[data-tdzview="desktop"][dir="rtl"] .screen:has(.screen-panel[data-panel="suivi"]) > .panels-wrap{ grid-column:1; }

/* every non-map panel is pinned to the fixed-width sidebar; .active still
   controls which single one is visible (logical props => RTL-aware) */
html[data-tdzview="desktop"] .screen:has(.screen-panel[data-panel="suivi"]) .screen-panel{
  inset-inline-start:0; inset-inline-end:auto; inline-size:var(--tdz-sidebar);
  background:#f4f6f9; z-index:2;
  border-inline-end:1px solid #e6e8ee;
  box-shadow:6px 0 18px -12px rgba(20,30,50,.40);
}

/* the live map: always visible, fills everything right of the sidebar */
html[data-tdzview="desktop"] .screen:has(.screen-panel[data-panel="suivi"]) .screen-panel[data-panel="suivi"]{
  inset-inline-start:var(--tdz-sidebar); inset-inline-end:0; inline-size:auto;
  opacity:1 !important; pointer-events:auto !important; transform:none !important;
  z-index:1; border:none; box-shadow:none; background:none;
}

/* Historique carries its own map + controls, so let it span the whole content
   area (it covers the live map while you are in it, then reveals it again). */
html[data-tdzview="desktop"] .screen:has(.screen-panel[data-panel="suivi"]) .screen-panel[data-panel="historique"]{
  inset-inline-start:0; inset-inline-end:0; inline-size:auto;
  z-index:3; border:none; box-shadow:none;
}

/* Keep scrolling content inside the sidebar (no horizontal bleed onto the map). */
html[data-tdzview="desktop"] .screen:has(.screen-panel[data-panel="suivi"]) .app-body{ overflow-x:hidden; }

/* Device rows: the name/IMEI/meta spans are inline in the base css, so long test
   names + IMEIs ran together and overflowed. In the sidebar, stack them on their
   own lines and truncate with an ellipsis; keep the status pill on one line. */
html[data-tdzview="desktop"] .screen:has(.screen-panel[data-panel="suivi"]) .dev-info{
  display:flex; flex-direction:column; align-items:flex-start;
}
html[data-tdzview="desktop"] .screen:has(.screen-panel[data-panel="suivi"]) .dev-info > *{
  display:block; max-width:100%; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
html[data-tdzview="desktop"] .screen:has(.screen-panel[data-panel="suivi"]) .dev-tag{ white-space:nowrap; }

/* --- device-row status glyphs (ported from /neweb) -------------------------
   Inline SVG on currentColor, so colour is a CSS concern and the glyphs theme
   with everything else. All colours come from the existing status tokens
   rather than fresh hex, which is what keeps them correct in dark mode -
   trackdz.base.css redefines those tokens under [data-theme="dark"] and
   prefers-color-scheme, and anything hardcoded here would ignore both.
   --------------------------------------------------------------------------- */
.dev-io{ display:flex; align-items:center; gap:6px; flex-shrink:0; }
.dev-io-i{ display:inline-flex; width:17px; height:17px; color:var(--ink-soft); }
.dev-io-i svg{ width:100%; height:100%; display:block; }

/* Engine: lit green when running, resting grey when not. */
.dev-io-i.eng.on{ color:var(--status-moving); }
.dev-io-i.eng.off{ color:var(--ink-soft); opacity:.55; }

/* Battery: threshold colour only where a percentage was actually measured.
   The unqualified .bat (no level class) keeps the resting grey. */
.dev-io-i.bat.low{ color:var(--status-speeding); }
.dev-io-i.bat.mid{ color:var(--status-idle); }
.dev-io-i.bat.full{ color:var(--status-moving); }

/* Signal: s0 offline, s1 connected-but-parked, s2 live. */
.dev-io-i.sig.s0{ color:var(--ink-soft); opacity:.55; }
.dev-io-i.sig.s1{ color:var(--status-idle); }
.dev-io-i.sig.s2{ color:var(--status-moving); }

/* Circled speed, shown only while moving. The tint is color-mix so one rule
   serves both themes; the plain `transparent` before it is the fallback for
   browsers without color-mix, where the ring and the figure still read. */
.dev-spd{
  flex-shrink:0; min-width:30px; height:30px; padding:0 7px; border-radius:16px;
  display:inline-flex; align-items:center; justify-content:center;
  font-size:12.5px; font-weight:700; font-variant-numeric:tabular-nums;
  color:var(--status-moving); border:1.5px solid var(--status-moving);
  background:transparent;
  background:color-mix(in srgb, var(--status-moving) 12%, transparent);
}
.dev-spd.hi{
  color:var(--status-speeding); border-color:var(--status-speeding);
  background:transparent;
  background:color-mix(in srgb, var(--status-speeding) 12%, transparent);
}

/* Narrow phones: the strip is the first thing to go - the status tag and the
   speed still carry the row, and the glyphs are a scan aid, not the content. */
@media (max-width:360px){ .dev-io{ display:none; } }

/* --- device row: two lines, nothing overlapping ---------------------------
   The old row put name, IMEI, glyphs, speed, status and age on ONE flex line,
   and .dev-info had no display:flex - so its spans flowed inline rather than
   stacking, and text-overflow:ellipsis on .name could not apply because
   ellipsis needs a block box. On a phone that printed text over text.

   Now .devrow-main is a COLUMN of two rows. Only .name may grow; everything on
   line 2 is fixed-width or shrink-proof, so there is nothing left to collide.
   --------------------------------------------------------------------------- */
.devrow-main{
  flex-direction:column; align-items:stretch; gap:3px;
  /* overrides align-items:center from the single-line layout in base css */
}
.dev-l1{ display:flex; align-items:center; gap:6px; min-width:0; }
/* block + min-width:0 are what make the ellipsis work inside a flex child */
.dev-l1 .name{ display:block; flex:1 1 auto; min-width:0;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.dev-l1 .dev-model{ font-weight:500; color:var(--ink-soft); }

.dev-l2{ display:flex; align-items:center; gap:8px; min-width:0; }
/* Age is pushed to the far end and never shrinks, so line 2 reads
   glyphs - speed - status .......... age */
.dev-l2 .dev-age{ margin-inline-start:auto; flex-shrink:0; margin-top:0; }
.dev-l2 .dev-io, .dev-l2 .dev-spd, .dev-l2 .dev-tag{ flex-shrink:0; }

/* Speed pill is a line-2 citizen now: smaller than the stand-alone circle so
   it sits level with the glyphs instead of setting the row height. */
.dev-l2 .dev-spd{ min-width:26px; height:22px; font-size:11.5px; border-width:1.2px; }

/* The IMEI line is gone from the markup; drop the rule so nothing re-reserves
   space for it if a cached script still emits one. */
.devrow .dev-imei{ display:none; }

/* Very narrow: the glyph strip goes first - it is a scan aid, while the status
   tag and age are the content. Supersedes the earlier 360px rule. */
@media (max-width:360px){
  .dev-l2{ gap:6px; }
  .dev-l2 .dev-io{ display:none; }
}

/* --- Arabic: keep figures and identifiers reading the same as every other
   language ------------------------------------------------------------------
   The page is dir="rtl" in Arabic, and anything numeric that inherits it gets
   run through the bidi algorithm. A speed, a duration, an IMEI, a plate or a
   timestamp is not prose: "12 km/h", "04:54 15/09" and "2h 15m" each mix
   digits, punctuation and Latin units, which is exactly the input bidi
   reorders - so they appeared rearranged in Arabic while being correct in
   French and English.

   isolate (not override) is the right tool: it stops these runs taking part in
   the surrounding paragraph's bidi resolution without touching the text
   itself. Nothing here changes the digits, which were already Western - the
   app builds them with Math.round and string concatenation and never calls
   toLocaleString, which is what would have produced ٠١٢٣.

   Layout still follows the language: these rules set direction for the content
   of a numeric field only, never for the row or panel that contains it. */
.dev-spd, .dev-age,
.callout-plate, .callout-row .v,
.alertrow .a-time, .alertrow .a-speed,
.info-row .v, .odo-val, .odo-sub, .odo-total,
.devrow .dev-imei, .dev-info .sub{
  direction:ltr; unicode-bidi:isolate;
}
/* The sub-line and the age sit at the start of their line in LTR and at the
   end in RTL; isolation above fixes the CONTENT, text-align keeps the BOX
   aligned the way the language expects. */
[dir="rtl"] .dev-info .sub, [dir="rtl"] .dev-l2 .dev-age{ text-align:right; }

/* Labels show/hide, fourth in the fab stack (legend 12 / places 56 /
   layers 100 / labels 144). The layers popup moves down to clear it. */
.labels-fab{
  position:absolute; right:12px; top:144px; z-index:600;
  width:36px; height:36px; border-radius:50%; background:#fff; border:none;
  box-shadow:0 6px 16px -4px rgba(20,30,50,.35);
  display:flex; align-items:center; justify-content:center; cursor:pointer; color:#3d4451;
}
.labels-fab svg{ width:18px; height:18px; }
.labels-fab.on{ background:var(--brand,#0f6e8c); color:#fff; }
[dir="rtl"] .labels-fab{ right:auto; left:12px; }
.layers-pop{ top:188px; }
