/* liveDispatch.css — the Live Dispatch screensaver overlay.
 *
 * EVERY selector here is prefixed .ld- and lives inside #ldOverlay, so nothing in this file can
 * reach an existing RMS control. Nothing here overrides an existing rule: the overlay contains no
 * <button>, <input>, <table>, <th> or <td> (style.css:1 styles all of those unscoped), so there is
 * nothing to beat. The top-bar trigger is NOT styled here — it reuses .nb-btn, the icon-button its
 * neighbours in that bar already use, so it matches them by construction.
 *
 * DARK IN BOTH THEMES, BY DESIGN. The colours below are literal, not var(--…), so neither theme can
 * repaint them. dark.css loads 5th of 36 and wins on specificity rather than order, but it only
 * targets its own selectors — none of which are .ld-. No html[data-theme="dark"] override is
 * needed, and adding one would be the override this file is not allowed to make.
 *
 * MOTION comes from motion.css's --t-* / --e-* tokens. No raw millisecond value, no raw
 * cubic-bezier. The flights themselves are drawn frame by frame in JS (an arc growing along its own
 * length is not expressible as a keyframe), and they are switched off entirely under reduced
 * motion — see the guard at the bottom and ldReduced() in liveDispatch.js.
 */

/* ── the overlay ──────────────────────────────────────────────────────────────────────────── */
/* z-index sits above every real surface (the highest in use is 1000100) but deliberately BELOW
   legacy-polyfills.js's 2147483647 error box, so a genuine error report is still readable if one
   appears while the screensaver is up. */
#ldOverlay{
  position:fixed; inset:0; z-index:2147483000;
  background:radial-gradient(ellipse at 30% 40%,#0b2236 0%,#051019 60%,#03090f 100%);
  color:#e7eef6;
  font-family:Inter,"Segoe UI",Arial,sans-serif;
  opacity:0; pointer-events:none; visibility:hidden;
  transition:opacity var(--t-slow,320ms) var(--e-in-out,ease);
}
#ldOverlay.ld-on{ opacity:1; pointer-events:auto; visibility:visible; cursor:none; }

.ld-screen{
  display:grid; grid-template-columns:minmax(0,1.25fr) minmax(340px,1fr); grid-template-rows:auto 1fr;
  height:100%; gap:0;
}

/* ── top strip ────────────────────────────────────────────────────────────────────────────── */
.ld-top{ grid-column:1 / -1; display:flex; align-items:center; gap:14px; padding:18px 28px 6px; }
.ld-mk{
  width:30px; height:30px; border-radius:8px; background:#0369a1; display:grid; place-items:center;
  font:900 18px Arial,Helvetica,sans-serif; color:#fff; flex:0 0 auto;
}
.ld-title{ margin:0; font-size:18px; font-weight:800; letter-spacing:.01em; }
.ld-live{
  display:inline-flex; align-items:center; gap:7px; font-size:12px; font-weight:700; color:#fca5a5;
  letter-spacing:.12em; padding:4px 10px; border:1px solid rgba(252,165,165,.35); border-radius:99px;
}
.ld-live i{ width:7px; height:7px; border-radius:50%; background:#f87171; animation:ld-blink var(--t-pulse,1400ms) var(--e-in-out,ease) infinite; }
@keyframes ld-blink{ 50%{ opacity:.25 } }
.ld-replay{ font-size:12px; color:#8aa0b6; opacity:0; transition:opacity var(--t-slow,320ms) var(--e-in-out,ease); }
.ld-replay.ld-shown{ opacity:1; }
.ld-spacer{ flex:1; }
/* The clock is no longer in this strip — it is drawn inside the map, in viewBox units, so it
   reads from across a room and keeps its place against the coastline. See .ld-svg-time below. */

/* ── map ──────────────────────────────────────────────────────────────────────────────────── */
.ld-mapwrap{ position:relative; min-height:0; display:flex; align-items:center; justify-content:center; padding:4px 10px 18px 20px; }
.ld-map{ height:100%; max-height:calc(100vh - 90px); width:auto; max-width:100%; overflow:visible; }
.ld-land{ fill:#0c2437; stroke:rgba(125,211,252,.35); stroke-width:.8; }
.ld-ie{ fill:#081a28; stroke:rgba(125,211,252,.12); stroke-width:.6; }
.ld-arc{ fill:none; stroke-width:1.4; stroke-linecap:round; opacity:.85; }
.ld-residue{ opacity:.55; }
.ld-hub-ring{ fill:none; stroke:#7dd3fc; stroke-width:1.2; }
.ld-hub-label{ fill:#e7eef6; font-size:13px; font-weight:800; letter-spacing:.04em; }
.ld-hub-sub{ fill:#8aa0b6; font-size:10.5px; }
.ld-tag-code{ fill:#e7eef6; font-size:11px; font-weight:800; paint-order:stroke; stroke:#051019; stroke-width:3px; }
.ld-tag-town{ fill:#8aa0b6; font-size:9.5px; paint-order:stroke; stroke:#051019; stroke-width:3px; }
/* The wall clock. Sizes are in viewBox units, like every other label on this map, so 91 here is
   not 91 screen pixels — it is 91 units of a 560-wide map, which is what makes it read across a
   warehouse at any width. Colours are the two this file already uses: the map's own ink for the
   time, and the muted blue every secondary label uses for the date. */
.ld-svg-time{
  fill:#e7eef6; font-size:91px; font-weight:900; letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;
}
.ld-svg-date{ fill:#8aa0b6; font-size:17px; letter-spacing:.14em; text-transform:uppercase; }

/* ── panel ────────────────────────────────────────────────────────────────────────────────── */
.ld-panel{ padding:8px 28px 22px 10px; display:flex; flex-direction:column; gap:16px; min-height:0; }
.ld-block{ background:rgba(255,255,255,.035); border:1px solid rgba(125,211,252,.12); border-radius:16px; padding:18px 20px; }
/* display:block IS LOAD-BEARING ON BOTH OF THESE, and its absence was the whole of the panel bug.
   Each pair is written as <span class="ld-lbl">…</span><span class="ld-count">…</span>. Left to
   default, a <span> is an inline box, and three things follow at once: the two share a LINE instead
   of stacking, so the figure sits to the RIGHT of its own label; inline boxes align on their
   BASELINE, so a 112px figure is lifted far ABOVE an 11.5px label; and margin-top below is silently
   dropped, because vertical margins do not apply to a non-replaced inline box. The approved
   prototype used <div> for both, which is why it never showed this. Making them blocks restores
   the intended stack and lets that margin work. */
.ld-lbl{ display:block; font-size:11.5px; letter-spacing:.14em; color:#8aa0b6; font-weight:700; }
.ld-twin{ display:grid; grid-template-columns:1.25fr 1fr; gap:18px; align-items:end; }
.ld-count{
  display:block;
  font-size:clamp(64px,9vw,112px); font-weight:900; letter-spacing:-.04em; line-height:1;
  font-variant-numeric:tabular-nums; margin-top:6px;
}
.ld-count.ld-small{ font-size:clamp(44px,5.6vw,72px); color:#bae6fd; }
.ld-count.ld-bump{ animation:ld-bump var(--t-slow,320ms) var(--e-out,ease-out); }
@keyframes ld-bump{ 40%{ color:#fff; text-shadow:0 0 22px rgba(125,211,252,.55) } }
.ld-legend{
  grid-column:1 / -1; display:flex; flex-wrap:wrap; gap:8px 16px; font-size:12.5px; color:#8aa0b6;
  padding-top:12px; border-top:1px solid rgba(125,211,252,.12);
}
.ld-legend span{ display:inline-flex; align-items:center; gap:7px; }
.ld-legend i{ width:9px; height:9px; border-radius:50%; flex:0 0 auto; }

/* ── just shipped ─────────────────────────────────────────────────────────────────────────── */
.ld-feedblock{ flex:1; min-height:0; display:flex; flex-direction:column; }
.ld-feed{ list-style:none; margin:12px 0 0; padding:0; overflow:hidden; flex:1; min-height:0; }
.ld-feed li{
  display:grid; grid-template-columns:10px 1fr auto; gap:10px; align-items:center;
  font-size:13px; padding:8px 0; border-bottom:1px solid rgba(255,255,255,.05);
  animation:ld-slide var(--t-base,200ms) var(--e-out,ease-out);
}
@keyframes ld-slide{ from{ opacity:0; transform:translateY(-8px) } to{ opacity:1; transform:none } }
.ld-feed li i{ width:9px; height:9px; border-radius:50%; }
.ld-where{ color:#e7eef6; font-weight:600; }
.ld-what{ color:#8aa0b6; }
.ld-when{ color:#4d6479; font-size:12px; font-variant-numeric:tabular-nums; }
/* THE EMPTY STATE IS NOT A THREE-PART ROW, SO IT MUST NOT BE LAID OUT AS ONE.
   .ld-feed li above is a grid of dot / text / time. The empty state is one sentence, and a bare
   text node inside a grid becomes an anonymous grid item in the FIRST track — the 10px dot column —
   so it wrapped one word per line beside 743px of empty space. The message is therefore wrapped in
   its own element and told to span every track.
   Deliberately a NEW class rather than a display/grid-template override on .ld-empty: .ld-feed li
   is the more specific selector (class + element), so any such override would lose without widening
   the selector to beat it, and a rule that exists only to out-specify another is the thing this
   stylesheet is not allowed to grow. Spanning the tracks needs no fight — .ld-feed li sets no
   grid-column, so there is nothing here to override.
   The old padding:10px was already dead for the same specificity reason (.ld-feed li's 8px won);
   removed rather than left to read as if it did something. */
.ld-empty{ color:#4d6479; font-size:13px; }
.ld-empty-txt{ grid-column:1 / -1; }

/* ── hint + hold ring ─────────────────────────────────────────────────────────────────────── */
.ld-hint{
  position:absolute; left:50%; bottom:18px; transform:translateX(-50%);
  font-size:12.5px; color:rgba(231,238,246,.4); padding:6px 14px;
  border:1px solid rgba(255,255,255,.1); border-radius:99px; background:rgba(0,0,0,.2);
}
#ldHold{
  position:fixed; left:50%; top:50%; width:84px; height:84px; margin:-42px 0 0 -42px;
  z-index:2147483001; pointer-events:none; opacity:0;
  transition:opacity var(--t-fast,120ms) var(--e-out,ease-out);
}
#ldHold.ld-on{ opacity:1; }
#ldHold circle{
  fill:none; stroke:#0369a1; stroke-width:6; stroke-linecap:round;
  transform:rotate(-90deg); transform-origin:50% 50%;
  stroke-dasharray:226; stroke-dashoffset:226;
}
#ldHold.ld-on circle{ animation:ld-holdfill var(--t-hold,700ms) var(--e-linear,linear) forwards; }
@keyframes ld-holdfill{ to{ stroke-dashoffset:0 } }

/* ── mobile ───────────────────────────────────────────────────────────────────────────────── */
/* Gated at the house breakpoint. Desktop is untouched by everything below. There is deliberately
   no <table> anywhere in the overlay, so mobileEnhance.js's table rewrite has nothing to act on. */
@media (max-width:1180px){
  .ld-screen{ grid-template-columns:minmax(0,1fr); grid-template-rows:auto auto 1fr; }
  .ld-top{ padding:12px 16px 4px; gap:10px; flex-wrap:wrap; }
  .ld-title{ font-size:15px; }
  .ld-mapwrap{ padding:4px 12px 8px; }
  .ld-map{ max-height:42vh; }
  .ld-panel{ padding:0 16px 16px; gap:10px; }
  .ld-block{ padding:12px 14px; border-radius:14px; }
  .ld-count{ font-size:clamp(38px,12vw,56px); }
  .ld-count.ld-small{ font-size:clamp(26px,8vw,40px); }
  .ld-legend{ font-size:11.5px; gap:6px 12px; padding-top:8px; }
  .ld-feed li{ font-size:12.5px; padding:6px 0; }
  .ld-hint{ bottom:10px; font-size:11.5px; }
}

/* ── the reduced-motion fallback ──────────────────────────────────────────────────────────── */
/* motion.css:112-123 forces animation-duration:1ms !important on * under this query, which would
   freeze a flight mid-arc rather than shorten it. So under reduced motion the flights are not
   drawn at all (liveDispatch.js checks the same query and only drops the dot), and the pieces
   whose animation would now be a 1ms flicker are quietly switched off. The numbers, the clock and
   the day's accumulated dots all stay live — the screen still tells you everything, it just does
   not move. */
@media (prefers-reduced-motion:reduce){
  #ldOverlay{ transition:none; }
  .ld-live i{ animation:none; opacity:.9; }
  .ld-count.ld-bump{ animation:none; }
  .ld-feed li{ animation:none; }
  #ldHold.ld-on circle{ animation:none; stroke-dashoffset:0; }
}
