/* inv cockpit — "the calm ledger"
   Deep-ink night instrument. Serif dispatches, mono numerals, one data-blue.
   Status colors never speak alone: every gain/loss carries its sign.
   Three skins share one token set: night (default), light paper, sepia.
   Every rule below reads tokens only — no literal colour outside :root/themes. */

:root {
    --bg: #0C0F16;
    --surface: #131826;
    --surface-2: #171E30;
    --line: #232B3D;
    --ink: #E8E6DF;
    --ink-dim: #97A0B5;
    --ink-faint: #5C657C;
    --blue: #6CA0F5;
    --gain: #58B98B;
    --loss: #E2635A;
    --amber: #D9A03F;
    --serif: Georgia, 'Times New Roman', serif;
    --mono: ui-monospace, 'Cascadia Mono', Consolas, Menlo, monospace;
    --sans: 'Segoe UI', system-ui, -apple-system, sans-serif;
}

/* ---- themes ----
   Set the skin on the root element: <html data-theme="light">, "sepia",
   or absent for the night default. Each block overrides token VALUES only;
   accents are picked to clear 3:1 (most clear 4.5:1) on that skin's surfaces. */

html[data-theme="light"] {
    --bg: #FAFAF7;
    --surface: #FFFFFF;
    --surface-2: #F1F3F8;
    --line: #E3E5EC;
    --ink: #1A1E28;
    --ink-dim: #5A6274;
    --ink-faint: #8A91A3;
    --blue: #2B63C9;
    --gain: #1E7A4F;
    --loss: #C13327;
    --amber: #9A6B00;
}

html[data-theme="sepia"] {
    --bg: #F4EBD9;
    --surface: #FBF4E5;
    --surface-2: #F0E4CB;
    --line: #E0D3B8;
    --ink: #3B3226;
    --ink-dim: #6E6250;
    --ink-faint: #978972;
    --blue: #2A5FA8;
    --gain: #2E6E4E;
    --loss: #A63A2E;
    --amber: #8A6100;
}

html[data-theme="dim"] {
    --bg: #131A24;
    --surface: #1A2330;
    --surface-2: #202B3B;
    --line: #2C3A4E;
    --ink: #E3E7ED;
    --ink-dim: #93A2B8;
    --ink-faint: #64748B;
    --blue: #6CA0F5;
    --gain: #58B98B;
    --loss: #E2635A;
    --amber: #D9A03F;
}

/* Native widgets (the key field, scrollbars, focus rings) follow the skin. */
html[data-theme="light"], html[data-theme="sepia"] { color-scheme: light; }

/* The only skin-specific values a token cannot carry: translucent chrome.
   Higher specificity than the base rules, so file order does not matter. */
html[data-theme="light"] nav.tabs { background: rgba(250, 250, 247, .88); }
html[data-theme="sepia"] nav.tabs { background: rgba(244, 235, 217, .88); }
html[data-theme="dim"] nav.tabs { background: rgba(19, 26, 36, .88); }

* { box-sizing: border-box; margin: 0; }
html { color-scheme: dark; }
body {
    background: var(--bg);
    color: var(--ink);
    font-family: var(--sans);
    -webkit-font-smoothing: antialiased;
    min-height: 100dvh;
}

/* ---- boot loader ---- */
.boot {
    height: 100dvh; display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 14px;
    font-family: var(--mono); color: var(--ink-faint); font-size: 13px;
    letter-spacing: .18em;
}
.boot-rule { width: 54px; height: 1px; background: var(--line); overflow: hidden; }
.boot-rule::after {
    content: ""; display: block; width: 40%; height: 100%;
    background: var(--blue); animation: sweep 1.1s ease-in-out infinite;
}
@keyframes sweep { 0% {transform: translateX(-100%)} 100% {transform: translateX(280%)} }
@media (prefers-reduced-motion: reduce) { .boot-rule::after { animation: none; width: 100%; } }

/* Blazor's own crash banner — toggled to display:block inline by the
   runtime itself when the WASM host dies, which can happen before our i18n
   ever loads, so its copy stays plain English rather than going through L[]. */
#blazor-error-ui {
    display: none;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 999;
    background: var(--loss); color: var(--bg);
    font-family: var(--sans); font-size: 14px;
    padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
}
#blazor-error-ui a { color: var(--bg); font-weight: 600; margin-inline-start: 6px; }

/* ---- shell ---- */
.shell { max-width: 680px; margin: 0 auto; padding: 20px 18px 92px; }

.masthead {
    display: flex; align-items: baseline; justify-content: space-between;
    padding-bottom: 14px; border-bottom: 1px solid var(--line); margin-bottom: 20px;
}
.brand { font-family: var(--mono); font-size: 13px; letter-spacing: .34em; color: var(--ink-dim); }
.brand b { color: var(--ink); font-weight: 600; }
.masthead .date { font-family: var(--mono); font-size: 12px; color: var(--ink-faint); }
.refresh {
    background: none; border: 1px solid var(--line); color: var(--ink-dim);
    border-radius: 6px; font-family: var(--mono); font-size: 11px;
    padding: 4px 10px; cursor: pointer; margin-inline-start: 12px;
}
.refresh:hover { color: var(--ink); border-color: var(--ink-faint); }

/* ---- network status banner ----
   Sits right under the masthead so it shows on every tab. Solid loss-token
   fill — the same treatment as the Blazor crash banner above — so a stale
   or blocked read reads as urgent rather than blending into a quiet tile. */
.alert {
    background: var(--loss); color: var(--bg);
    border-radius: 10px; padding: 10px 14px;
    font-family: var(--sans); font-size: 13px; line-height: 1.5;
    margin-top: 16px;
}

/* Action outcome toast — every button reports success or failure aloud. */
.toast {
    position: fixed; bottom: 72px; left: 50%; transform: translateX(-50%);
    z-index: 60; max-width: min(92vw, 480px);
    background: var(--surface); color: var(--ink);
    border: 1px solid var(--line); border-radius: 10px;
    padding: 10px 16px; font-family: var(--sans); font-size: 13.5px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, .35);
}
.toast.ok  { border-color: var(--gain); }
.toast.bad { border-color: var(--loss); }

/* ---- the dispatch (signature) ---- */
.dispatch { margin: 22px 0 26px; }
.dispatch .wire {
    font-family: var(--mono); font-size: 11px; letter-spacing: .22em;
    color: var(--ink-faint); text-transform: uppercase; display: flex;
    align-items: center; gap: 10px;
}
.dispatch .wire::after { content: ""; flex: 1; height: 1px;
    background: repeating-linear-gradient(90deg, var(--line) 0 6px, transparent 6px 11px); }
.dispatch p {
    font-family: var(--serif); font-size: 21px; line-height: 1.5;
    margin-top: 12px; color: var(--ink); text-wrap: pretty;
}
.dispatch p .lead { color: var(--blue); }

/* ---- tiles ---- */
.tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 18px 0 8px; }
.tile {
    background: var(--surface); border: 1px solid var(--line); border-radius: 10px;
    padding: 13px 14px 12px; display: flex; flex-direction: column; gap: 6px;
}
.tile .k { font-family: var(--mono); font-size: 10px; letter-spacing: .18em;
    text-transform: uppercase; color: var(--ink-faint); }
.tile .v { font-family: var(--mono); font-size: 21px; font-variant-numeric: tabular-nums; }
.tile .v small { font-size: 12px; color: var(--ink-dim); }
.tile.ring-tile { flex-direction: row; align-items: center; justify-content: space-between; }
.pill { display: inline-block; font-family: var(--mono); font-size: 11px;
    padding: 2px 9px; border-radius: 99px; border: 1px solid var(--line); }
/* Pill edges are derived, not literal, so they stay legible in every skin. */
.pill.ok { color: var(--gain); border-color: color-mix(in srgb, var(--gain) 50%, var(--line)); }
.pill.warn { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 50%, var(--line)); }
.pill.bad { color: var(--loss); border-color: color-mix(in srgb, var(--loss) 50%, var(--line)); }
/* .info is the one pill that carries no verdict: it says "this is the state",
   not "this is good" or "this is a problem". The account-posture badge needs
   exactly that, because both postures place the same paper orders. */
.pill.info { color: var(--blue); border-color: color-mix(in srgb, var(--blue) 50%, var(--line)); }
@supports not (color: color-mix(in srgb, #000 50%, #fff)) {
    .pill.ok { border-color: var(--gain); }
    .pill.warn { border-color: var(--amber); }
    .pill.bad { border-color: var(--loss); }
    .pill.info { border-color: var(--blue); }
}

/* ---- sections & charts ---- */
section.block { margin: 26px 0; }
.block h2 {
    font-family: var(--mono); font-size: 11px; letter-spacing: .22em;
    text-transform: uppercase; color: var(--ink-dim); font-weight: 500;
    margin-bottom: 12px;
}
.block h2 span { color: var(--ink-faint); }
.chart-wrap { position: relative; background: var(--surface); border: 1px solid var(--line);
    border-radius: 10px; padding: 14px 10px 8px; }
.chart-wrap svg { display: block; width: 100%; height: auto; cursor: crosshair; }
.tip {
    position: absolute; top: 6px; transform: translateX(-50%);
    background: var(--surface-2); border: 1px solid var(--line); border-radius: 6px;
    font-family: var(--mono); font-size: 11px; padding: 4px 8px; white-space: nowrap;
    pointer-events: none; color: var(--ink);
}
.tip small { color: var(--ink-faint); display: block; }
.empty { color: var(--ink-faint); font-size: 14px; font-family: var(--sans);
    padding: 18px 6px; }

/* ---- confidence meter ---- */
.meter { height: 6px; background: var(--line); border-radius: 99px; overflow: hidden; }
.meter > div { height: 100%; background: var(--blue); border-radius: 99px; }
.rr-badge { font-family: var(--mono); font-size: 11px; color: var(--ink-dim);
    border: 1px solid var(--line); border-radius: 6px; padding: 2px 8px; }

/* ---- symbol chips ---- */
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.chip {
    background: none; border: 1px solid var(--line); color: var(--ink-dim);
    font-family: var(--mono); font-size: 11px; letter-spacing: .06em;
    padding: 4px 10px; border-radius: 99px; cursor: pointer;
}
.chip.on { color: var(--ink); border-color: var(--blue); background: var(--surface-2); }
.chip:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
/* Language / theme pickers: same chip buttons, own row. */
.picker-row { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0 18px; }

/* ---- lists (journal) ---- */
.rows { display: flex; flex-direction: column; }
.row {
    display: flex; justify-content: space-between; align-items: center;
    padding: 13px 4px; border-bottom: 1px solid var(--line);
    background: none; border-left: 0; border-right: 0; border-top: 0;
    color: var(--ink); font-family: var(--mono); font-size: 14px;
    cursor: pointer; width: 100%; text-align: start;
}
.row:hover { color: var(--blue); }
.row .chev { color: var(--ink-faint); }
/* The currently open history entry — same "on" token pair as .chip.on and
   nav.tabs button.on, applied to a row. */
.row.on { color: var(--ink); background: var(--surface-2); }

/* ---- list hygiene: loading / show-more / archive ----
   A loading list, an empty list and a failed list are three different
   sentences (DocList.Status); this block is the loading one's look — the
   same sweeping rule as the boot loader, so "working" reads consistently
   everywhere, and reduced-motion is already handled by .boot-rule. */
.list-loading {
    display: flex; align-items: center; gap: 10px; padding: 18px 6px;
    font-family: var(--mono); font-size: 12px; color: var(--ink-faint);
    letter-spacing: .08em;
}
.list-loading .boot-rule { width: 42px; flex: 0 0 auto; }
/* The one element that trims a list is the element that discloses the trim:
   its label carries "{shown} of {total}" (Paging.MoreKey). Dashed, not
   solid — it is an aperture, not another row. */
.list-more {
    display: block; width: 100%; margin-top: 10px; background: none;
    border: 1px dashed var(--line); color: var(--ink-dim); border-radius: 8px;
    font-family: var(--mono); font-size: 11.5px; padding: 9px 12px; cursor: pointer;
}
.list-more:hover { color: var(--ink); border-color: var(--ink-faint); }
/* "Showing N of M" under a deliberately capped list (the incident board's
   caps mirror the engine's own report and must not grow). */
.list-total { font-family: var(--mono); font-size: 11px; color: var(--ink-faint); padding: 8px 6px 0; }
/* A row plus its archive/restore control. The control continues the row's
   own bottom rule so the pair reads as one line, and the archived view dims
   its rows — hidden-by-choice must look different from live. */
.rowline { display: flex; align-items: stretch; }
.rowline .row { flex: 1; min-width: 0; }
.rowline.dimmed .row { color: var(--ink-faint); }
.rowact {
    background: none; border: 0; border-bottom: 1px solid var(--line);
    color: var(--ink-faint); font-family: var(--mono); font-size: 15px;
    padding: 0 12px; cursor: pointer;
}
.rowact:hover { color: var(--ink); }
/* The archived view's standing explanation: device-only, journal untouched. */
.archive-note { color: var(--ink-faint); font-size: 12.5px; font-family: var(--sans);
    line-height: 1.5; margin: 0 0 10px; }

.doc { font-family: var(--sans); line-height: 1.65; font-size: 15px; }
.doc h1, .doc h2, .doc h3 { font-family: var(--serif); margin: 20px 0 8px; line-height: 1.3; }
.doc h1 { font-size: 22px; } .doc h2 { font-size: 18px; } .doc h3 { font-size: 16px; }
.doc p { margin: 10px 0; color: var(--ink); }
.doc li { margin: 5px 0; margin-inline-start: 18px; }
.doc a { color: var(--blue); }
.doc code { font-family: var(--mono); font-size: 13px; background: var(--surface-2);
    padding: 1px 5px; border-radius: 4px; }
.back { margin-bottom: 14px; }

/* ---- stats/gates ---- */
.gate-list { display: flex; flex-direction: column; gap: 9px; }
.gate-row { display: flex; justify-content: space-between; align-items: center;
    background: var(--surface); border: 1px solid var(--line); border-radius: 10px;
    padding: 11px 14px; font-size: 13.5px; }
.gate-row .name { color: var(--ink-dim); }
.gate-row .val { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* ---- system ---- */
.sys-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.danger {
    margin-top: 26px; background: none; border: 1px solid var(--line); color: var(--loss);
    font-family: var(--mono); font-size: 12px; border-radius: 8px; padding: 9px 14px;
    cursor: pointer;
}

/* ---- system health board ---- */
.subhead {
    font-family: var(--mono); font-size: 10px; letter-spacing: .18em;
    text-transform: uppercase; color: var(--ink-faint); margin: 14px 0 6px;
}
.subhead:first-child { margin-top: 0; }
/* Live workflow indicator — a queued or in-progress run gets a soft pulse so
   "the system is working right now" reads at a glance, not just from the
   status pill text. */
.live-dot {
    display: inline-block; width: 7px; height: 7px; border-radius: 50%;
    background: var(--blue); margin-inline-end: 7px; vertical-align: middle;
    animation: pulse 1.4s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .live-dot { animation: none; opacity: .9; } }

/* ---- LIVE pane (#122) ---- */
/* Deliberately unlike the ledger tiles: dashed accent border, no surface
   fill. The two surfaces answer different questions (published record vs
   held-now broker state) and must never be mistaken for one another. */
.live-pane {
    border: 1px dashed color-mix(in srgb, var(--blue) 55%, var(--line));
    border-radius: 12px; padding: 12px 14px; margin: 10px 0 18px;
    display: flex; flex-direction: column; gap: 4px;
}
@supports not (color: color-mix(in srgb, #000 50%, #fff)) {
    .live-pane { border-color: var(--blue); }
}
.live-pane .live-head { display: flex; justify-content: space-between; align-items: center; }
.live-pane .k { font-family: var(--mono); font-size: 10px; letter-spacing: .18em;
    text-transform: uppercase; color: var(--ink-faint); }
.ledger-stamp {
    font-family: var(--mono); font-size: 10px; letter-spacing: .18em;
    text-transform: uppercase; color: var(--ink-faint); margin: 14px 0 -10px;
}

/* ---- inline confirm panel (account-mode switch) ---- */
.confirm-panel {
    background: var(--surface); border: 1px solid var(--line); border-radius: 10px;
    padding: 14px; margin: 4px 0 18px; display: flex; flex-direction: column; gap: 10px;
}
.confirm-panel p { color: var(--ink-dim); font-size: 13.5px; line-height: 1.5; }
.confirm-panel input {
    background: var(--surface-2); border: 1px solid var(--line); border-radius: 8px;
    color: var(--ink); font-family: var(--mono); font-size: 14px; padding: 10px 12px;
    width: 100%;
}
.confirm-panel input:focus { outline: 2px solid var(--blue); outline-offset: 1px; border-color: transparent; }

/* ---- controls (workflow dispatch + manual command) ----
   One row shape shared by both Controls groups: a label + button on top,
   a one-line plain-language subtitle underneath, and — only on the rows
   that need one — a symbol/reason field or an inline .confirm-panel (reused
   as-is from the account-mode switch above) that swaps in for the button. */
.ctrl-row {
    background: var(--surface); border: 1px solid var(--line); border-radius: 10px;
    padding: 12px 14px; margin-bottom: 9px; display: flex; flex-direction: column; gap: 7px;
}
.ctrl-row .top { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.ctrl-row .top .label { font-family: var(--sans); font-size: 14.5px; color: var(--ink); }
.ctrl-row .sub { font-family: var(--sans); font-size: 12.5px; color: var(--ink-dim); line-height: 1.45; }
.ctrl-row .field input {
    background: var(--surface-2); border: 1px solid var(--line); border-radius: 8px;
    color: var(--ink); font-family: var(--sans); font-size: 13px; padding: 8px 10px;
    width: 100%;
}
.ctrl-row .field input.sym { font-family: var(--mono); text-transform: uppercase; max-width: 110px; }
.ctrl-row .field input:focus { outline: 2px solid var(--blue); outline-offset: 1px; border-color: transparent; }

/* ---- confidence dial ---- */
input[type="range"] { width: 100%; margin-top: 6px; accent-color: var(--blue); cursor: pointer; }
input[type="range"]:disabled { cursor: default; opacity: .6; }

/* ---- unlock gate ---- */
.gate {
    min-height: 100dvh; display: flex; flex-direction: column; justify-content: center;
    max-width: 420px; margin: 0 auto; padding: 24px; gap: 14px;
}
.gate .brand { font-size: 15px; letter-spacing: .4em; }
.gate h1 { font-family: var(--serif); font-size: 26px; font-weight: 500; }
.gate p { color: var(--ink-dim); font-size: 14px; line-height: 1.6; }
.gate input {
    background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
    color: var(--ink); font-family: var(--mono); font-size: 14px; padding: 12px 14px;
    width: 100%;
}
.gate input:focus { outline: 2px solid var(--blue); outline-offset: 1px; border-color: transparent; }
.gate button {
    background: var(--blue); color: var(--bg); border: 0; border-radius: 8px;
    font-family: var(--mono); font-size: 14px; font-weight: 600; letter-spacing: .08em;
    padding: 12px; cursor: pointer;
}
.gate .err { color: var(--loss); font-family: var(--mono); font-size: 12px; min-height: 16px; }

/* ---- bottom nav ----
   Six fixed tabs, three languages, one 360-400px phone width to fit in.
   Each button is an equal flex column (flex + min-width:0 so the row can
   never exceed its container, on any viewport) sized small enough that the
   longest label in en/fr/ar sits on one line; overflow-wrap is a safety net
   for whatever the next translation's longest word turns out to be — it
   wraps instead of clipping or spilling past the screen edge. */
nav.tabs {
    position: fixed; bottom: 0; left: 0; right: 0;
    background: rgba(12, 15, 22, .88);
    backdrop-filter: blur(14px); border-top: 1px solid var(--line);
    display: flex; gap: 2px;
    padding: 8px 6px calc(8px + env(safe-area-inset-bottom));
    /* Eight tabs do not fit a phone, and squeezing them made every label
       wrap to two lines of 10px mono. The row scrolls sideways instead:
       tabs keep a legible width, snap as you swipe, and the scrollbar is
       hidden because a fixed bottom bar with a visible track looks broken. */
    overflow-x: auto; scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
nav.tabs::-webkit-scrollbar { display: none; }
nav.tabs button {
    flex: 0 0 auto; min-width: 84px; scroll-snap-align: center;
    background: none; border: 0; color: var(--ink-faint); font-family: var(--mono);
    font-size: 10px; letter-spacing: .03em; padding: 8px 2px; border-radius: 8px;
    cursor: pointer; text-transform: uppercase;
    text-align: center; line-height: 1.25; overflow-wrap: anywhere;
}
nav.tabs button.on { color: var(--ink); background: var(--surface-2); }
nav.tabs button:focus-visible, .row:focus-visible, .gate button:focus-visible,
.refresh:focus-visible, .danger:focus-visible,
.rowact:focus-visible, .list-more:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

/* ---- tooltip ----
   One component, used everywhere: class "tt" + attribute data-tip.
   Markup adds tabindex="0" so it opens on keyboard focus and on touch tap
   (:focus-within covers both the element itself and anything focused inside).
   Optional modifiers: .tt-end aligns the bubble to the inline end (right-hand
   column tiles), .tt-below drops it under the trigger (top-of-page rows). */
.tt { position: relative; cursor: help; --tt-shadow: 0 6px 20px rgba(0, 0, 0, .25); }
html[data-theme="light"] .tt { --tt-shadow: 0 6px 18px rgba(26, 30, 40, .14); }
html[data-theme="sepia"] .tt { --tt-shadow: 0 8px 20px rgba(59, 50, 38, .16); }
.tt:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; border-radius: 6px; }

.tt[data-tip]:hover::after,
.tt[data-tip]:focus-visible::after,
.tt[data-tip]:focus-within::after {
    content: attr(data-tip);
    position: absolute; bottom: calc(100% + 8px); inset-inline-start: 0; z-index: 30;
    /* Leave a gutter on both sides: a bubble sized off the viewport rather
       than the page could still sit past the edge on a narrow phone. */
    max-width: min(260px, calc(100vw - 32px)); white-space: normal; text-align: start;
    background: var(--surface-2); color: var(--ink);
    border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px;
    font: 12px/1.5 var(--sans); letter-spacing: normal; text-transform: none;
    box-shadow: var(--tt-shadow); pointer-events: none;
}
/* Caret: a rotated square that merges into the bubble's bottom edge. */
.tt[data-tip]:hover::before,
.tt[data-tip]:focus-visible::before,
.tt[data-tip]:focus-within::before {
    content: ""; position: absolute; bottom: calc(100% + 4px);
    inset-inline-start: 16px; z-index: 31;
    width: 9px; height: 9px; background: var(--surface-2);
    border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
    transform: rotate(45deg); pointer-events: none;
}
.tt.tt-end[data-tip]:hover::after,
.tt.tt-end[data-tip]:focus-visible::after,
.tt.tt-end[data-tip]:focus-within::after { inset-inline-start: auto; inset-inline-end: 0; }
.tt.tt-end[data-tip]:hover::before,
.tt.tt-end[data-tip]:focus-visible::before,
.tt.tt-end[data-tip]:focus-within::before { inset-inline-start: auto; inset-inline-end: 16px; }
.tt.tt-below[data-tip]:hover::after,
.tt.tt-below[data-tip]:focus-visible::after,
.tt.tt-below[data-tip]:focus-within::after { bottom: auto; top: calc(100% + 8px); }
.tt.tt-below[data-tip]:hover::before,
.tt.tt-below[data-tip]:focus-visible::before,
.tt.tt-below[data-tip]:focus-within::before {
    bottom: auto; top: calc(100% + 4px);
    border: 0; border-left: 1px solid var(--line); border-top: 1px solid var(--line);
}
/* Bubbles must escape their containers — none of these may clip. */
.tiles, .tile, .chips, .picker-row, .rows, .row, .rowline, .gate-list, .gate-row,
.sys-grid, .chart-wrap, .masthead, section.block { overflow: visible; }

/* ---- rtl ----
   Latin tickers, dates and numerals stay left-to-right inside an RTL page. */
.v, .val, .date, .tip, .chip, .brand, .boot, .ctrl-row .field input.sym { direction: ltr; unicode-bidi: isolate; }
[dir="rtl"] .row .chev { display: inline-block; transform: scaleX(-1); }
/* .tip keeps translateX(-50%): its inline left:% is physical in both directions. */
[dir="rtl"] .dispatch .wire::after {
    background: repeating-linear-gradient(270deg, var(--line) 0 6px, transparent 6px 11px);
}

/* The owner's one always-visible control (2026-08-06): the brake on the
   first screen. Deliberately loud — this button is allowed to be ugly. */
.panic-row { margin: 10px 0 14px; }
.panic {
  width: 100%; padding: 14px 16px; border-radius: 12px;
  border: 1px solid var(--red, #b91c1c);
  background: var(--red, #b91c1c); color: #fff;
  font-size: 15px; font-weight: 700; letter-spacing: .02em;
  cursor: pointer;
}
.panic:active { filter: brightness(.9); }
.chip.danger { border-color: var(--red, #b91c1c); color: var(--red, #b91c1c); }
.halted-note {
  padding: 12px 14px; border-radius: 12px;
  border: 1px solid var(--line); color: var(--muted);
}
