  :root { color-scheme: dark; }
  html, body { height: 100%; }
  body {
    -webkit-tap-highlight-color: transparent; overscroll-behavior-y: none;
    /* viewport-fit=cover lets the page draw under the notch/home-indicator
       area — without this it also draws *content* under them (header
       clipped by the status bar/Dynamic Island, bottom controls under the
       home-indicator gesture bar). */
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
    padding-left: env(safe-area-inset-left);
    padding-right: env(safe-area-inset-right);
  }

  /* The preview allows one-finger page panning in both directions (so a
     page that was pinch-zoomed before scanning can still be moved around
     and zoomed back out from outside the camera) but not native pinch:
     two fingers on the camera enlarge only the preview. app.js switches
     the stage to touch-action: none while the preview is zoomed. */
  .header-brand-wrap { min-width: 0; }
  #videoStage { touch-action: pan-x pan-y; }
  .plate-viewfinder { aspect-ratio: 2 / 1; }
  .app-footer { margin-top:18px;padding:18px 0 36px;border-top:1px solid #334155;text-align:center;font-size:12px;color:#94a3b8;line-height:2; }
  .footer-nav { display:flex;justify-content:center;flex-wrap:wrap;gap:4px 18px; }
  .app-footer a { color:#a7f3d0; }

  .sr-only { position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0; }
  #status { display:block;max-width:92px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
  #scanNotice[hidden], #historyDetails[hidden], #scanProgress[hidden] { display:none; }
  #scanNotice { margin:.75rem 0; padding:.75rem 1rem; border:1px solid #334155; border-radius:.75rem; background:#0f172a; color:#cbd5e1; font-size:.875rem; }
  #scanNotice[data-kind="error"] { border-color:#be123c; background:#4c0519; color:#ffe4e6; }
  #scanNotice p { margin-top:.25rem; overflow-wrap:anywhere; }
  #scanProgress { height:4px; margin-top:.75rem; border-radius:4px; background:#334155; overflow:hidden; }
  #scanProgressBar { height:100%;width:0;background:#34d399;transition:width .2s; }
  #historyDetails { margin-top:.75rem; }
  #historyDetails > summary, #advancedSettings > summary { cursor:pointer; color:#a7f3d0; font-size:.8rem; font-weight:600; padding:.65rem .25rem; }
  #historyDetails > summary:focus-visible, #advancedSettings > summary:focus-visible { outline:2px solid #34d399; outline-offset:2px; }
  #historyDetails > summary, #advancedSettings > summary { min-height:44px;display:list-item; }
  #locationChoice { min-height:44px;padding:.5rem .25rem; }
  #locationChoice input { width:18px;height:18px;flex:none; }
  #historyDetails > summary span { color:#94a3b8; font-weight:400; }
  #advancedSettings { margin-top:.25rem; }
  #advancedSettings > div { display:flex;flex-wrap:wrap;gap:.5rem 1.25rem;padding:.5rem .75rem;background:#0f172a;border-radius:.75rem; }
  #advancedSettings p { color:#94a3b8;font-size:.7rem;line-height:1.5;width:100%; }
  @media (max-width:639px) {
    body:not(.camera-active) #videoStage { height:42dvh;min-height:220px;max-height:420px; }
    /* Full-bleed like the camera stage below it (both cancel the page's
       .75rem side padding), so the feed can't show beside the buttons once
       it scrolls under them. The upward shadow fills the safe-area band
       above the bar, where the feed would otherwise show on notched phones. */
    #scanControls { position:sticky;top:env(safe-area-inset-top);z-index:30;margin-inline:-.75rem;padding:.375rem .75rem;background:#101214;box-shadow:0 calc(-1 * env(safe-area-inset-top)) 0 #101214; }
  }

  .vf-corner { position: absolute; width: 26px; height: 26px; border-color: #34d399; filter: drop-shadow(0 0 6px rgba(52,211,153,.55)); }
  .vf-tl { top: -2px; left: -2px;  border-top: 3px solid; border-left: 3px solid;  border-top-left-radius: 10px; }
  .vf-tr { top: -2px; right: -2px; border-top: 3px solid; border-right: 3px solid; border-top-right-radius: 10px; }
  .vf-bl { bottom: -2px; left: -2px;  border-bottom: 3px solid; border-left: 3px solid;  border-bottom-left-radius: 10px; }
  .vf-br { bottom: -2px; right: -2px; border-bottom: 3px solid; border-right: 3px solid; border-bottom-right-radius: 10px; }

  /* #scanline is a transparent full-height track that slides via
     transform, so the sweep runs on the compositor and stays smooth while
     scanTick's frame capture and inference occupy the main thread
     (animating `top` forced a main-thread layout every frame and
     stuttered). The visible 2px line is its ::before; translateY(100%)
     is one viewfinder height, since percentages resolve against the
     track itself. */
  #scanline {
    position: absolute; left: 2%; right: 2%; top: 0; bottom: 0;
    opacity: .9;
    will-change: transform;
    animation: scanmove 2.2s cubic-bezier(.45,0,.55,1) infinite;
  }
  #scanline::before {
    content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
    background: linear-gradient(90deg, transparent, #34d399, transparent);
    box-shadow: 0 0 8px 1px rgba(52,211,153,.7);
  }
  /* Constant brightness: the detector searches the whole frame, so the
     sweep must not imply a favoured region. */
  @keyframes scanmove {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(calc(100% - 2px)); }
  }
  @media (prefers-reduced-motion: reduce) {
    #scanline { animation: none; transform: translateY(calc(50% - 1px)); opacity: .6; }
  }

  tr[data-rec] { animation: rowin .28s ease-out; }
  @keyframes rowin { from{opacity:0;transform:translateY(-6px)} to{opacity:1;transform:none} }

  .thin-scroll::-webkit-scrollbar { height: 6px; width: 6px; }
  .thin-scroll::-webkit-scrollbar-thumb { background: #334155; border-radius: 9999px; }

  /* Detection box overlay. One element per actively-tracked plate (see
     MAX_ACTIVE_TRACKS) — each left permanently mounted (opacity-toggled,
     never display:none) so both the show/hide fade and the position
     transition below actually run — a display:none swap can't animate,
     which is why this used to hard-cut in and snap between frames
     instead of tracking smoothly like a real scanner overlay. Only the
     top box gets the full dim-surround vignette, so a second/third box
     doesn't stack darkening on top of itself.
     Tone is set via a class (border color/style) + a --tone-rgb custom
     property (glow color), so #detBox and the pooled extras share one
     box-shadow rule instead of a separate rule per tone per element.
     tone-none (not yet read) starts at the same emerald as the old
     hardcoded default; tone-low is also dashed, not just rose, so the
     "barely readable" state doesn't rely on color alone. */
  .det-box {
    position: absolute; border: 2px solid #34d399; border-radius: 6px;
    pointer-events: none; opacity: 0; --tone-rgb: 52,211,153;
    transition: opacity .15s ease, left .12s ease-out, top .12s ease-out,
                width .12s ease-out, height .12s ease-out, border-color .15s ease;
  }
  .det-box.tone-none { border-color: #94a3b8; --tone-rgb: 148,163,184; }
  .det-box.tone-low  { border-color: #f43f5e; border-style: dashed; --tone-rgb: 244,63,94; }
  .det-box.tone-mid  { border-color: #fbbf24; --tone-rgb: 251,191,36; }
  .det-box.tone-high { border-color: #34d399; --tone-rgb: 52,211,153; }
  .det-box.det-visible { opacity: 1; }
  #detBox.det-visible { box-shadow: 0 0 0 9999px rgba(2,6,23,.45), 0 0 14px rgba(var(--tone-rgb),.5); }
  .det-box:not(#detBox).det-visible { box-shadow: 0 0 14px rgba(var(--tone-rgb),.5); }

  /* Capture flash: same dim+ring technique as the old vf-hit, but pooled
     per-track and positioned at the committed box's own screen rect
     (see commitDetection/mapBoxToScreen) instead of always at the centered
     viewfinder, so the flash actually points at the plate that was read.
     --tone-rgb is set in JS per flash (emerald for verified, amber for
     unverified — same palette as the log's badge/legend colors). */
  .capture-flash {
    position: absolute; border-radius: 8px; pointer-events: none; opacity: 0;
    --tone-rgb: 52,211,153;
  }
  .capture-flash.flash-hit { animation: capturehit .5s ease-out; }
  @keyframes capturehit {
    0%   { opacity: 1; box-shadow: 0 0 0 9999px rgba(2,6,23,.55), 0 0 0 0 rgba(var(--tone-rgb),.8); }
    100% { opacity: 0; box-shadow: 0 0 0 9999px rgba(2,6,23,0),   0 0 0 26px rgba(var(--tone-rgb),0); }
  }

  /* Dim+blur the feed while swapping lenses (see switchCameraSafely) so a
     mid-swap frame from the outgoing stream never looks like a normal,
     trustworthy read. */
  #videoStage.stage-switching video { opacity: .35; filter: blur(1px); }
  #videoStage video { transition: opacity .2s ease, filter .2s ease; }

  /* ── HUD chrome ────────────────────────────────────────────────────────
     vendor/tailwind.css is a pre-purged, hand-committed static file (see
     package.json: this app deliberately has no build step) containing only
     the utility classes index.html already used — an arbitrary new
     Tailwind class added here would silently do nothing. Everything below
     is plain CSS for that reason, not a stylistic choice. */
  .hud-row {
    position: absolute; top: .5rem; left: .5rem; right: .5rem; z-index: 10;
    display: flex; flex-wrap: wrap; gap: .375rem; pointer-events: none;
  }
  .badge-col {
    position: absolute; top: .5rem; right: .5rem; z-index: 10;
    display: flex; flex-direction: column; align-items: flex-end; gap: .375rem;
    pointer-events: none;
  }
  .hud-chip {
    display: inline-flex; align-items: center; gap: .25rem; color: #e2e8f0;
    border-radius: 9999px; background: rgba(15,23,42,.85); white-space: nowrap;
    padding: .25rem .5rem; font-size: 10px; font-weight: 500; line-height: 1.3;
  }
  .shape-banner {
    position: absolute; bottom: 3.75rem; left: .5rem; z-index: 10; max-width: 75%;
    border-radius: .5rem; background: rgba(245,158,11,.15);
    box-shadow: inset 0 0 0 1px rgba(245,158,11,.3);
    padding: .375rem .625rem; font-size: 10px; font-weight: 500; color: #fcd34d;
    pointer-events: none;
  }
  .preview-stack {
    position: absolute; bottom: 3.75rem; right: .5rem; z-index: 10;
    display: flex; flex-direction: column; align-items: flex-end; gap: .25rem;
  }
  /* The two overlays above sit clear of this bar (bottom: 3.75rem). */
  .zoom-bar {
    position: absolute; bottom: .5rem; left: 50%; z-index: 11;
    transform: translateX(-50%);
    display: flex; gap: .125rem; padding: .125rem;
    border-radius: 9999px; background: rgba(15,23,42,.55);
  }
  .zoom-bar[hidden] { display: none; }
  .zoom-bar button {
    display: grid; place-items: center; width: 2.75rem; height: 2.75rem;
    border-radius: 9999px; color: #e2e8f0; font-size: 11px; font-weight: 600;
    font-variant-numeric: tabular-nums; touch-action: manipulation;
    transition: background-color .15s ease, color .15s ease;
  }
  .zoom-bar button[aria-pressed="true"] {
    background: rgba(2,6,23,.85); color: #f6a176;
    box-shadow: inset 0 0 0 1px rgba(240,106,43,.55);
  }
  .zoom-bar button:focus-visible { outline: 2px solid #f6a176; outline-offset: 1px; }
  .preview-item { position: relative; }
  .preview-item canvas {
    display: block; height: 3.5rem; width: auto; border-radius: .375rem;
    background: #0f172a; box-shadow: inset 0 0 0 1px rgba(255,255,255,.2);
  }
  .preview-item .idx-badge {
    position: absolute; left: -.375rem; top: -.375rem;
    display: grid; place-items: center; height: 1rem; width: 1rem;
    border-radius: 9999px; background: #101214; font-size: 9px; font-weight: 700;
    color: #6ee7b7; box-shadow: inset 0 0 0 1px #334155;
  }
  /* Visibility for the three above is managed entirely through this
     is-visible modifier, not Tailwind's `hidden` utility — mixing the two
     on one element would make the winner depend on cascade source order
     between vendor/tailwind.css and this block, which is exactly the kind
     of fragile coupling a static, no-build-step stylesheet can't afford. */
  .hud-chip, .shape-banner, .preview-stack { display: none; }
  .hud-chip.is-visible { display: inline-flex; }
  .shape-banner.is-visible { display: block; }
  .preview-stack.is-visible { display: flex; }
  /* #viewfinder keeps Tailwind's opacity-40 for its default "searching"
     state; the ID selector here outranks that utility's specificity
     regardless of stylesheet order, so toggling this one class is enough
     to fade it out while a plate is actively tracked (see scanTick). */
  #viewfinder.vf-tracking { opacity: 0; }
  /* #exportSection holds 4 buttons but vendor/tailwind.css (pre-purged to
     only classes already used elsewhere in this file) has neither
     sm:grid-cols-4 nor sm:col-span-2 — same reasoning as #viewfinder above,
     an ID selector reliably overrides the sm:grid-cols-3 utility on this
     element regardless of stylesheet order, so this is a real 4-across
     row on desktop instead of the 3-then-1 gap a span-free 3-col grid
     would otherwise leave. */
  @media (min-width: 640px) {
    #exportSection { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  }

  /* PLAATES field-instrument brand layer */
  .vf-corner { border-color: var(--brand-orange); filter: drop-shadow(0 0 6px rgba(240,106,43,.55)); }
  #scanline::before { background: linear-gradient(90deg,transparent,var(--brand-orange),transparent); box-shadow: 0 0 8px 1px rgba(240,106,43,.7); }
  #overlayMsg .text-emerald-400 { color: #f6a176 !important; }
  #scanNotice { background: var(--brand-graphite); border-color: var(--brand-steel); }
  #scanNotice[data-kind="error"] { background:#321719; border-color:var(--signal-red); }
  #scanControls { border-bottom:1px solid transparent; }
  @media (max-width:639px) { body.camera-active #scanControls { border-bottom-color:var(--brand-steel); } }
