/* ISTA Online — front-end prototype
   Design language lifted verbatim from the "ISTA Legacy Workstation" spec.
   Colours, sizes and spacing are the Rheingold workshop skin, not an approximation. */

:root {
  --bg: #EDEDEB;
  --surface: #FFFFFF;
  --surface-2: #F5F5F3;
  --surface-3: #DDDDD9;
  --ink: #000000;
  --ink-2: #22262B;
  --muted: #4E545A;
  --line: #B9B9B4;
  --line-strong: #8C8C86;

  --red: #A4231B;
  --yellow: #9C6B00;
  --yellow-mark: #C9971A;
  --yellow-ink: #7A5300;
  --green: #2E7D32;
  --info: #2C6FB5;
  --accent: #46807F;
  --accent-ink: #FFFFFF;
  --accent-line: #7FA8A6;

  --tab-off: #141414;
  --tab-mid: #C6C6C2;
  --tab-folder: #D2D2CE;
  --row-alt: #D2D2CE;
  --thead: #575757;

  --display: Tahoma, "Segoe UI", Verdana, sans-serif;
  --body: Tahoma, "Segoe UI", Verdana, sans-serif;
  --mono: Consolas, "Lucida Console", monospace;
}

/* The "BMW i" skin — ISTA reskins itself per brand; this is the one alternate we carry. */
[data-skin="bmwi"] {
  --accent: #0653B6;
  --accent-line: #7FA6D8;
}
[data-skin="mini"] {
  --accent: #8B1A1A;
  --accent-line: #C08A8A;
}

* { box-sizing: border-box }
html, body { margin: 0; padding: 0; height: 100% }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--body);
  font-size: 12.5px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}
button { font: inherit; color: inherit }
input, select, textarea { font: inherit }
::selection { background: rgba(70, 128, 127, .3) }

@keyframes spin { to { transform: rotate(360deg) } }
@keyframes pulse { 0%, 100% { opacity: 1 } 50% { opacity: .35 } }
@keyframes fadeUp { from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: none } }
@keyframes sweep { from { transform: translateX(-110%) } to { transform: translateX(330%) } }

/* ── shell ─────────────────────────────────────────────────────────── */

#app { display: flex; flex-direction: column; height: 100vh; background: var(--bg) }

/* Row 0 — title bar: session slots, clock, window controls */
.titlebar {
  display: flex; align-items: center; gap: 6px;
  padding: 3px 6px;
  background: #EDEDEB;
  border-bottom: 1px solid #B9B9B4;
  flex: 0 0 auto;
}
.sessions { display: flex; gap: 3px }
.session {
  width: 22px; height: 17px;
  border: 1px solid #B9B9B4; background: #F6F6F4;
  display: grid; place-items: center;
  font-size: 10px; color: #7A7A7A; cursor: pointer;
}
.session.on { border-color: var(--accent); background: #fff; color: #1E1E1E }
.clock { font-family: var(--mono); font-size: 11px; color: #1E1E1E }
.winbtns { display: flex; gap: 5px; margin-left: 8px }
.winbtn {
  width: 22px; height: 18px; display: grid; place-items: center;
  font-size: 12px; color: #1E1E1E; cursor: pointer; user-select: none;
}
.winbtn:hover { background: #D6D6D2 }
.winbtn[data-disabled] { color: #A8A8A4; cursor: default }
.winbtn[data-disabled]:hover { background: transparent }

/* Row 1 — vehicle identity band */
.idband {
  padding: 5px 8px 6px;
  background: #F4F4F2;
  border-bottom: 1px solid #B9B9B4;
  color: #1E1E1E;
  flex: 0 0 auto;
}
.idband .app { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap }
.idband .app b { font-size: 17px; font-weight: 700; color: #1E1E1E }
.idband .app .ver { font-size: 11px }
.idband .app .pdata { font-size: 10.5px; color: #5A5A5A }
.idrow { display: flex; gap: 26px; flex-wrap: wrap; margin-top: 3px }
.idrow > span { font-size: 11px; white-space: nowrap }
.idrow b { color: #1E1E1E }
.idrow .v { font-family: var(--mono); margin-left: 7px }
.grow { flex: 1 }
.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block }
.dot.live { animation: pulse 2s infinite }

/* Rows 2–4 — the three tab strips */
.tabrow { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 2px; background: #fff; flex: 0 0 auto }
.tabrow.mid { margin-top: 2px }
.tabrow.folder { gap: 5px; padding: 5px 0 0; border-bottom: 1px solid var(--accent-line) }

.tab {
  display: flex; align-items: center;
  padding: 9px 11px 10px;
  font-size: 13.5px; line-height: 1.18;
  white-space: pre-line; min-height: 44px;
  background: var(--tab-off); color: #fff;
  cursor: pointer; user-select: none;
}
.tab.dim { color: #6E6E6A; cursor: default }
/* An active tab always reads as active, even when its guard has since closed —
   a dimmed-but-selected tab looks like a rendering fault rather than a state. */
.tab.on, .tab.on.dim { background: var(--accent); color: #fff }

.tab.m {
  padding: 8px 11px 9px; font-size: 13px; min-height: 42px;
  background: var(--tab-mid); color: #1E1E1E;
}
.tab.m.dim { color: #6A6A66; cursor: default }
.tab.m.on, .tab.m.on.dim { background: var(--accent); color: #fff }

.tab.f {
  padding: 8px 11px 9px; font-size: 13px; min-height: 44px;
  background: var(--tab-folder); color: #1E1E1E;
  clip-path: polygon(0 0, calc(100% - 22px) 0, 100% 22px, 100% 100%, 0 100%);
}
.tab.f.dim { color: #8A8A86; cursor: default }
.tab.f.on, .tab.f.on.dim { background: var(--accent); color: #fff }

/* Body */
.body { display: flex; flex: 1; min-height: 0; align-items: stretch }
main { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: auto }
.screen { animation: fadeUp .22s ease both; flex: 1; min-width: 0 }
.screen.pad { padding: 9px 11px 14px; max-width: 1280px; width: 100%; margin: 0 auto }
.screen.flush { padding: 0; width: 100%; margin: 0 }

/* Status bar */
.statusbar {
  display: flex; align-items: center; gap: 16px;
  padding: 3px 8px;
  background: #DDDDD9; border-top: 1px solid var(--line-strong);
  font-size: 11px; color: #1E1E1E; flex: 0 0 auto;
}
.statusbar .seg { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap }

/* ── primitives ────────────────────────────────────────────────────── */

.panel { border: 1px solid var(--accent-line); background: #fff; display: flex; flex-direction: column }
.panel > .phead {
  display: flex; align-items: center; gap: 10px;
  background: var(--thead); color: #fff;
  font-size: 12px; font-weight: 700; padding: 6px 10px;
}

.card { background: var(--surface); border: 1px solid var(--line); padding: 7px 9px }
.eyebrow { font-family: var(--mono); font-size: 10px; color: var(--muted); margin: 0 0 6px }
.rule { display: flex; align-items: center; gap: 10px; font-family: var(--mono); font-size: 10px; color: var(--muted); margin: 26px 0 12px }
.rule i { flex: 1; height: 1px; background: var(--line); font-style: normal }
h1.t { font-family: var(--display); font-weight: 600; font-size: 17px; line-height: 1.02; margin: 0 0 10px }
h2.t { font-family: var(--display); font-weight: 700; font-size: 13px; margin: 0 0 4px }
p.sub { margin: 0; color: var(--muted); font-size: 11.5px; max-width: 62ch }

/* Data table — the ISTA list look: dark head, zebra rows, teal selection */
table.dt { width: 100%; border-collapse: collapse; font-size: 12.5px }
table.dt thead th {
  background: var(--thead); color: #fff; font-weight: 700; text-align: left;
  padding: 6px 10px; border-right: 1px solid #6E6E6A; white-space: nowrap;
}
table.dt thead th:last-child { border-right: none }
table.dt tbody td { padding: 5px 10px; border-right: 1px solid #E4E4E0; vertical-align: middle }
table.dt tbody tr { background: #fff; cursor: pointer }
table.dt tbody tr:nth-child(even) { background: var(--row-alt) }
table.dt tbody tr.on, table.dt tbody tr.on:nth-child(even) { background: var(--accent); color: #fff }
table.dt tbody tr.on td { border-right-color: rgba(255, 255, 255, .25) }
table.dt.nohover tbody tr { cursor: default }
.num { font-family: var(--mono) }

/* Buttons */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--display); font-weight: 600; font-size: 11.5px;
  padding: 6px 14px; border: 1px solid #7B7871;
  background: var(--tab-folder); color: #1E1E1E; cursor: pointer;
}
.btn:hover { background: #C4C4C0 }
.btn.primary { background: var(--accent); color: #fff }
.btn.primary:hover { background: #3C716F }
.btn:disabled, .btn[data-disabled] { color: #8A8A86; cursor: default; background: #E2E2DE }
.btn.lg { padding: 12px 22px; font-size: 12px }

/* Toolbar strip above a panel */
.toolbar { display: flex; align-items: center; gap: 6px; padding: 5px 0; flex-wrap: wrap }
.toolbar .grow { flex: 1 }

/* Inputs */
.field { display: flex; align-items: center; gap: 8px; border: 1px solid var(--line-strong); background: #fff; padding: 3px 7px }
.field input { flex: 1; min-width: 0; border: none; background: transparent; outline: none; font-family: var(--mono); font-size: 12px }
select.sel { border: 1px solid var(--line-strong); background: #fff; padding: 3px 6px; font-size: 12px }
label.chk { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; cursor: pointer }

/* Status marks — the ISTA legend squares */
.mark { width: 13px; height: 13px; border: 1px solid #4A4A46; display: inline-grid; place-items: center; flex: 0 0 13px }
.mark i { width: 5px; height: 5px; background: rgba(0, 0, 0, .55); display: block }
.mark.idle { background: #fff } .mark.idle i { background: #1E1E1E }
.mark.ok { background: var(--green) }
.mark.warn { background: var(--yellow-mark) }
.mark.err { background: var(--red) }
.mark.info { background: var(--info) }
.mark.black { background: #1E1E1E }
.legend { display: flex; gap: 18px; flex-wrap: wrap; padding: 6px 10px; font-size: 11.5px; align-items: center }
.legend .it { display: inline-flex; align-items: center; gap: 7px }

/* Badges */
.badge { display: inline-block; font-family: var(--mono); font-size: 10px; padding: 1px 6px; border: 1px solid var(--line-strong); background: var(--surface-2) }
.badge.err { border-color: var(--red); color: var(--red) }
.badge.warn { border-color: var(--yellow); color: var(--yellow-ink) }
.badge.ok { border-color: var(--green); color: var(--green) }
.badge.info { border-color: var(--info); color: var(--info) }

/* Tree */
.tree { font-size: 12.5px }
.tree .node { display: flex; align-items: center; gap: 9px; padding: 4px 10px; cursor: pointer; border-bottom: 1px solid #E4E4E0 }
.tree .node:nth-child(even) { background: #F6F6F4 }
.tree .node.on { background: var(--accent); color: #fff }
.tree .twisty { flex: 0 0 16px; font-size: 17px; line-height: .8; color: #5E5E5A }
.tree .node.on .twisty { color: #fff }

/* Progress */
.bar { height: 10px; background: var(--surface-3); border: 1px solid var(--line); overflow: hidden }
.bar > i { display: block; height: 100%; background: var(--accent) }
.bar.indet > i { width: 32%; animation: sweep 1.2s linear infinite }

/* ── dialogs ───────────────────────────────────────────────────────── */

.overlay {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(20, 20, 20, .42);
  display: grid; place-items: center;
}
.dialog {
  background: var(--bg); border: 1px solid #6E6E6A;
  box-shadow: 0 6px 26px rgba(0, 0, 0, .35);
  min-width: 380px; max-width: min(920px, 94vw);
  max-height: 90vh; display: flex; flex-direction: column;
  animation: fadeUp .16s ease both;
}
.dialog > .dhead {
  display: flex; align-items: center; gap: 10px;
  background: var(--accent); color: #fff;
  padding: 6px 10px; font-size: 12.5px; font-weight: 700;
}
.dialog > .dhead .x { margin-left: auto; cursor: pointer; padding: 0 4px }
.dialog > .dbody { padding: 12px; overflow: auto; background: var(--bg) }
.dialog > .dfoot {
  display: flex; gap: 8px; justify-content: flex-end;
  padding: 8px 10px; border-top: 1px solid var(--line); background: #E6E6E2;
}

/* Dialog tab strip (fault code dialog uses folder tabs) */
.dtabs { display: flex; gap: 3px; padding: 0 0 0 0 }
.dtab {
  padding: 8px 20px 9px; font-size: 12.5px; cursor: pointer;
  background: var(--tab-folder); color: #1E1E1E;
  clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 14px, 100% 100%, 0 100%);
}
.dtab.on { background: var(--accent); color: #fff }

/* Definition grid used across detail panes */
.dl { display: grid; grid-template-columns: max-content 1fr; gap: 3px 18px; font-size: 12px; align-items: baseline }
.dl dt { color: var(--muted) }
.dl dd { margin: 0; font-family: var(--mono); min-width: 0; overflow-wrap: anywhere }

ul.bul { margin: 4px 0 0; padding-left: 18px; font-size: 12px }
ul.bul li { margin: 2px 0 }

/* Notification strip (NotificationMessageView) */
.notice { display: flex; align-items: center; gap: 10px; padding: 6px 10px; border: 1px solid var(--line-strong); background: #FFF8E1; font-size: 12px }
.notice.err { background: #FDEBEA; border-color: var(--red) }
.notice.ok { background: #EDF7EE; border-color: var(--green) }
.notice.info { background: #EAF2FB; border-color: var(--info) }

/* Sidebar (background process view / favourites drawer) */
.side { width: 268px; flex: 0 0 268px; border-left: 1px solid var(--line-strong); background: var(--surface-2); overflow: auto }
.side h3 { font-size: 11.5px; margin: 0; padding: 6px 10px; background: var(--thead); color: #fff; font-weight: 700 }

.hide { display: none !important }
.mono { font-family: var(--mono) }
.muted { color: var(--muted) }
.right { text-align: right }
.nowrap { white-space: nowrap }

/* ── confidence (architecture P3, §9) ──────────────────────────────────
   ISTA has no equivalent, so this is designed rather than reconstructed.

   The chip borrows the geometry of ISTA's own status square (.mark): 13×13,
   hairline border, sitting on the text baseline, so a graded value looks like
   it belongs to the product rather than bolted on. What it does not borrow is
   ISTA's colour vocabulary — severity is already red/amber/blue there, and a
   second traffic light would be unreadable next to the first.

   So confidence is carried by SHAPE first and colour second:
     ✓ confirmed    line grey  — the quiet default, a table of these reads calm
     ~ plausible    amber ink on a pale ground
     ? speculative  amber ink, dashed border (it is not merely weaker, it is
                    a different kind of claim: nothing states it at all)
     ! conflict     white on red, filled — the only filled chip in the set

   Two accent colours over the whole scale, which is what keeps a dense list
   from becoming a rainbow. Conflict additionally stripes its row and raises a
   banner above the list, because a chip in a row you have scrolled past is not
   "impossible to miss". */

.conf {
  width: 13px; height: 13px; flex: 0 0 13px;
  display: inline-grid; place-items: center;
  vertical-align: -2px;
  border: 1px solid var(--line-strong);
  background: #fff;
  font-family: var(--mono); font-size: 9.5px; font-weight: 700; line-height: 1;
  color: var(--muted); cursor: help; user-select: none;
}
.conf.confirmed   { border-color: var(--line); color: #7C7C77 }
.conf.plausible   { border-color: var(--yellow-mark); background: #FFF6E0; color: var(--yellow-ink) }
.conf.speculative { border-style: dashed; border-color: var(--yellow-mark); background: #FFFDF6; color: var(--yellow-ink) }
.conf.conflict    { border-color: #7A1913; background: var(--red); color: #fff }
.conf.none        { border-style: dotted; border-color: var(--line); color: #B0B0AB; background: transparent }

/* Spelled out, for a detail pane where there is room for the word. */
.conflabel { font-size: 10.5px; font-weight: 700; margin-left: 6px; letter-spacing: .2px }
.conflabel.confirmed   { color: #5E5E59 }
.conflabel.plausible,
.conflabel.speculative { color: var(--yellow-ink) }
.conflabel.conflict    { color: var(--red) }

/* The chip inside a selected (teal) row has to stay legible. */
table.dt tbody tr.on .conf.confirmed { border-color: rgba(255,255,255,.65); background: transparent; color: #fff }
table.dt tbody tr.on .conf.plausible,
table.dt tbody tr.on .conf.speculative { background: #FFF6E0 }

/* A conflicting row is marked on the row itself, not only in one cell. */
table.dt tbody tr.conflict > td:first-child { box-shadow: inset 3px 0 0 var(--red) }
table.dt tbody tr.conflict.on > td:first-child { box-shadow: inset 3px 0 0 #FFD9D6 }

.conflegend { border-top: 1px solid var(--line); gap: 14px }
.conflegend .it { gap: 6px }
.conflegend .it b { margin-left: 2px }

.notice.conflict-banner {
  background: #FDEBEA; border-color: var(--red); align-items: flex-start;
  border-left-width: 4px;
}
.notice.conflict-banner .conf { margin-top: 1px }

/* The alternative reading a conflict carries: what the *other* source said. */
.confalt {
  border-left: 3px solid var(--red); background: #FDEBEA;
  padding: 5px 9px; margin: 6px 0 0; font-size: 11.5px;
}
.confalt b { color: var(--red) }
.confalt .src { color: var(--muted); font-size: 10.5px }

/* ── would-run preview (§10) ───────────────────────────────────────────
   A telegram is bytes, and bytes are read positionally, so the header fields
   are tinted rather than annotated: length, target, source, then payload. */

.tel { display: inline-flex; gap: 3px; flex-wrap: wrap; font-family: var(--mono); font-size: 12px }
.tel .tb {
  display: inline-block; min-width: 21px; text-align: center;
  padding: 1px 3px; border: 1px solid var(--line); background: #fff; letter-spacing: .4px;
}
.tel .tb.len { background: #EDEDEB; color: var(--muted) }
.tel .tb.tgt { background: #E3EEF0; border-color: var(--accent-line); font-weight: 700 }
.tel .tb.src { background: #F2F2EF; color: var(--muted) }
.tel .tb.pl  { background: #fff }
table.dt tbody tr.on .tel .tb { color: #1E1E1E }

.telnote { font-size: 10.5px; color: var(--muted); margin: 3px 0 0 }

/* The gate list a plan demands, drawn as a sequence rather than a bullet list:
   the order is the contract. */
.gates { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none }
.gates li { display: flex; align-items: flex-start; gap: 8px; font-size: 12px }
.gates .n {
  width: 16px; height: 16px; flex: 0 0 16px; display: grid; place-items: center;
  border: 1px solid var(--line-strong); background: #fff;
  font-family: var(--mono); font-size: 9.5px; font-weight: 700;
}
.gates li.done .n { background: var(--green); border-color: var(--green); color: #fff }
.gates li.done { color: var(--muted) }

/* The tier a capability sits at (§7 T0–T3) — read at a glance next to a button. */
.tier { font-family: var(--mono); font-size: 10px; font-weight: 700; padding: 1px 5px; border: 1px solid var(--line-strong); background: var(--surface-2) }
.tier.t2 { border-color: var(--yellow-mark); color: var(--yellow-ink); background: #FFF6E0 }
.tier.t3 { border-color: var(--red); color: #fff; background: var(--red) }

/* A real information object from the documentation database (docs.get federation) —
   BMW's rendered XML injected into the browser panel. Media resolves through the
   hub's /api/docgraphic route; keep it inside the pane. */
.corpus-doc { font-size: 12.5px; line-height: 1.5 }
.corpus-doc img { max-width: 100%; height: auto; background: #fff }
.corpus-doc img.sym { max-width: 40px; vertical-align: middle }
.corpus-doc video { max-width: 100%; background: #000 }
.corpus-doc figure { margin: 10px 0; text-align: center }
.corpus-doc table { border-collapse: collapse; font-size: 12px }
.corpus-doc td, .corpus-doc th { border: 1px solid var(--line-strong); padding: 3px 8px; text-align: left }

/* ── phones ─────────────────────────────────────────────────────────────────────────────────
   ISTA is a 1280×800 workshop application and the FIDELITY IS THE PRODUCT — a phone layout that
   reorganised the tabs, dropped the window chrome or collapsed the identity band would no longer
   be the thing this project reconstructs. So nothing here changes the information architecture:
   every control, label and order survives. What changes is how a 430 px viewport is fitted to a
   layout built for 1280.

   Two rules carry most of the work:
     · a strip that cannot fit scrolls ITSELF, and never the document. Measured before the change:
       the document was 475 px wide at a 430 px viewport, so the whole shell slid sideways and the
       status bar and window buttons hung off the edge.
     · tabs stop being seven equal columns. At 430 px that is 61 px each, which clipped every
       label — "Operatio", "Vehicle informat", "Favourite". Auto width restores the real labels
       and the partially visible next tab is its own affordance; no invented gradient needed.
   ──────────────────────────────────────────────────────────────────────────────────────────── */
@media (max-width: 760px) {
  /* iOS inflates text in "narrow" columns unless told not to, which silently breaks a layout
     tuned to Tahoma metrics. `manipulation` drops the 300 ms double-tap-zoom delay. */
  html { -webkit-text-size-adjust: 100% }
  body { touch-action: manipulation }

  /* 100vh on iOS Safari counts the space UNDER the address bar, so the status bar sits below the
     fold and the layout jumps as the bar hides. dvh is the viewport you can actually see. */
  #app { height: 100vh; height: 100dvh; overflow-x: hidden; max-width: 100vw }

  /* Row 0 — the title bar. The window buttons are 10 targets in ~265 px; they get their own
     scrolling line rather than pushing the document sideways. */
  .titlebar { flex-wrap: wrap; gap: 4px; padding: 4px 6px calc(4px + env(safe-area-inset-left, 0px)) }
  .titlebar .clock { order: 2; margin-left: auto; font-size: 12px }
  .winbtns {
    order: 3; flex: 1 0 100%; margin-left: 0;
    overflow-x: auto; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; gap: 2px;
  }
  .winbtns::-webkit-scrollbar { display: none }
  /* 44 px is the Apple HIG minimum and these were 22×18. The glyph keeps its size; only the
     hit area grows, so the chrome still reads as ISTA's. Scoped to the TITLE bar: the identity
     band carries the same class for four inline icons, and 44 px there tore the row in half. */
  .titlebar .winbtn { width: 44px; height: 44px; flex: 0 0 auto; font-size: 15px }
  .session { width: 38px; height: 36px; font-size: 12px }

  /* Row 1 — identity band. Its icons stay inline and merely grow to a usable 38 px. The `.grow`
     spacer is what pushed the last icon onto a line of its own with a hand-width of blank above
     it: on a 1280 px window it right-aligns the icons, on a phone it is just a hole. */
  .idband { padding: 5px 8px 6px }
  /* `baseline` alignment against 38 px icon boxes makes a very tall line box, so the row that
     wraps leaves a hand-width of blank above the icon that fell off it. Centre them, and shrink
     the icons just enough that the whole band fits one line at 430 px — measured, not guessed:
     ISTA+ + version + "Programming data" + four icons came to ~428 px at 38 px each. */
  .idband .app { gap: 6px; align-items: center }
  .idband .app .grow { display: none }
  .idband .app b { line-height: 1.1 }
  .idband .winbtn { width: 34px; height: 34px; font-size: 14px }
  /* With a car identified this band carries six fields, and at desktop spacing each one takes a
     line of its own on a phone — measured at ~400 px, nearly half the viewport, before any
     content appears. Nothing is hidden (every field is a fact about the car in front of you);
     the rows are simply set at their own density instead of the 1280 px one. */
  .idrow { gap: 3px 14px; margin-top: 3px }
  .idrow > span { font-size: 11.5px; line-height: 1.35 }
  .idrow .v { margin-left: 5px }

  /* Rows 2–4 — the tab strips: one scrollable line, real labels, thumb-sized. */
  .tabrow {
    display: flex; grid-template-columns: none;
    overflow-x: auto; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
  }
  .tabrow::-webkit-scrollbar { display: none }
  .tab {
    flex: 0 0 auto; scroll-snap-align: start;
    min-height: 50px; padding: 9px 14px 10px; font-size: 13.5px;
  }
  .tab.m, .tab.f { min-height: 46px; padding: 9px 14px 10px }
  /* Empty spacer tabs exist only to fill the 7-column grid; in a scroller they are dead width. */
  .tabrow .tab.m.dim:empty, .tabrow .tab.dim:empty { display: none }

  /* ── the collapsed identity band ─────────────────────────────────────────────────────────
     One tappable line instead of six. Same surface, same rules, same monospace values. */
  .idband.slim {
    display: flex; align-items: center; gap: 8px;
    padding: 0 10px; min-height: 42px;
    cursor: pointer; user-select: none;
  }
  .idband.slim .brand { font-weight: 700; font-size: 14px; letter-spacing: -.2px }
  .idband.slim .slimbits {
    display: flex; align-items: center; gap: 7px; min-width: 0;
    overflow-x: auto; scrollbar-width: none; white-space: nowrap; font-size: 11.5px;
  }
  .idband.slim .slimbits::-webkit-scrollbar { display: none }
  .idband.slim .sep { width: 1px; height: 11px; background: var(--line); display: inline-block; flex: 0 0 auto }
  .idband.slim .chev { color: var(--muted); font-size: 12px; flex: 0 0 auto }
  .slimclose {
    padding: 7px 10px 2px; font-size: 11px; color: var(--muted);
    cursor: pointer; user-select: none;
  }

  /* ── the path navigator ──────────────────────────────────────────────────────────────────
     ISTA's three stacked strips, collapsed to where-you-are. The segments are tabs, in the
     application's own colours, so the chrome still reads as ISTA rather than as a breadcrumb
     borrowed from somewhere else. */
  .pathbar {
    display: flex; align-items: stretch; gap: 0;
    background: var(--tab-off); color: #fff;
    overflow-x: auto; scrollbar-width: none;
    flex: 0 0 auto;
  }
  .pathbar::-webkit-scrollbar { display: none }
  .pseg {
    display: flex; align-items: center; gap: 6px;
    flex: 0 0 auto; min-height: 46px; padding: 8px 12px 9px;
    font-size: 13px; line-height: 1.18; white-space: nowrap;
    color: #C8C8C4; cursor: pointer; user-select: none;
  }
  .pseg.on { color: #fff }
  /* The level whose strip is open is the one wearing the accent — the same signal a selected
     ISTA tab gives, pointed at "this list is the one you are looking at". */
  .pseg.live { background: var(--accent); color: #fff }
  .pseg .pchev { font-size: 10px; opacity: .8 }
  .psep { display: flex; align-items: center; padding: 0 2px; color: #6E6E6A; font-style: normal }

  /* An opened strip sits directly under the path and scrolls on its own, as before. */
  .pathbar + .tabrow { border-top: 1px solid rgba(255,255,255,.14) }

  /* The status bar states facts about the session; it scrolls rather than truncating them. */
  .statusbar {
    overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
    padding-bottom: calc(3px + env(safe-area-inset-bottom, 0px));
  }
  .statusbar::-webkit-scrollbar { display: none }

  /* Content. A wide table scrolls inside its own panel — never the shell. */
  .screen.pad { padding: 9px 10px 16px }
  .panel { overflow-x: auto }
  table.dt tbody td { padding: 11px 10px }     /* measured: a 42 px row */
  table.dt thead th { padding: 8px 10px }

  /* Dialogs. `min-width: 380px` plus a 430 px screen is how the OK button ended up off-screen:
     the box could not shrink and the footer's right-aligned buttons went with it. */
  .overlay { place-items: end center; padding: 0 }
  .dialog {
    min-width: 0; width: 100%; max-width: 100vw;
    max-height: 92dvh; border-left: none; border-right: none; border-bottom: none;
    border-radius: 0;
  }
  .dialog > .dhead { padding: 10px 12px; font-size: 13.5px }
  .dialog > .dbody { padding: 12px; overscroll-behavior: contain }
  .dialog > .dfoot {
    padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
    flex-wrap: wrap;
  }
  .dialog > .dfoot .btn { flex: 1 1 auto; justify-content: center; min-height: 46px }
  .dtabs { overflow-x: auto; scrollbar-width: none }
  .dtabs::-webkit-scrollbar { display: none }
  .dtab { flex: 0 0 auto; min-height: 44px; display: flex; align-items: center }

  /* ── screen CONTENT, not just the shell ──────────────────────────────────────────────────
     The first phone pass fixed the chrome and left the screens alone, which is why the inner
     pages still read as a puzzle you solve by scrolling sideways. The screens are built for
     1280 px with inline layout — 61 multi-column grids, table columns pinned in pixels, filter
     fields at a fixed 262 px — and none of it can be reached by editing a stylesheet the normal
     way. Attribute selectors can, and that is what they are for: one rule each, at the
     breakpoint, instead of 61 edits that would each have to be kept in step with the desktop.

     Every override below is deliberately narrow: it changes WIDTH and FLOW, never colour,
     spacing, borders or type. The page still looks like ISTA; it just stops being wider than
     the phone. */

  /* Two- and three-column layouts become one column. At 430 px a 240 px minimum column and its
     neighbour cannot both fit, so the browser was overflowing rather than wrapping. */
  .screen [style*="grid-template-columns"] { grid-template-columns: minmax(0, 1fr) !important }

  /* Column widths in a data table are a desktop proportion. Pinned in pixels they add up past
     the viewport, and the table then hides its right-hand columns off screen — the "Body"
     column in the model-code list, the whole right half of the interface list. Let the table
     size itself and let headings wrap. */
  table.dt { table-layout: auto }
  table.dt th[style*="width"], table.dt td[style*="width"] { width: auto !important }
  table.dt thead th, table.dt tbody td { white-space: normal }

  /* ── a wide table becomes one card per row ───────────────────────────────────────────────
     Eight columns do not fit in 430 px, and both ways of forcing them are worse than the
     problem. Squeezing gave columns one character wide ("M o d e l c o d e" reading downwards);
     leaving them wide gave a table you scroll sideways to read, which is the "puzzle" this was
     reported as. So a table of five or more columns is redrawn as a list of records — the shape
     the data actually has — with each value labelled by its own column heading.

     The ISTA skin is kept deliberately: the zebra, the teal selection, the borders and the row
     as the click target are all unchanged, so it still reads as an ISTA list, just one that
     runs down the screen instead of across it. */
  table.dt.dt-wide, table.dt.dt-wide tbody, table.dt.dt-wide tr, table.dt.dt-wide td {
    display: block; width: auto;
  }
  table.dt.dt-wide thead { display: none }          /* the headings ride on the cells instead */
  table.dt.dt-wide tbody tr {
    border: 1px solid var(--line); border-left-width: 3px;
    margin: 0 0 6px; padding: 2px 0;
  }
  table.dt.dt-wide tbody tr.on { border-left-color: var(--accent) }
  table.dt.dt-wide tbody td {
    display: flex; gap: 10px; align-items: baseline;
    border-right: none; padding: 5px 10px; min-height: 0;
  }
  table.dt.dt-wide tbody td::before {
    content: attr(data-label);
    flex: 0 0 38%; min-width: 0;
    color: var(--muted); font-size: 11px; line-height: 1.35;
    overflow-wrap: anywhere;
  }
  /* A selected row inverts, so its labels have to come with it. */
  table.dt.dt-wide tbody tr.on td::before { color: rgba(255, 255, 255, .82) }
  /* A cell the column left empty would otherwise show a label pointing at nothing. */
  table.dt.dt-wide tbody td:empty { display: none }
  /* The empty-state row is a message, not a record. */
  table.dt.dt-wide tbody td[colspan]::before { content: none }

  /* A filter field pinned to 262 px on a 430 px screen leaves a stub of a box next to a stripe
     of empty toolbar. Search is the primary control on those screens; give it the row. */
  .toolbar .field, .screen .field { flex: 1 1 100%; width: auto !important; min-width: 0 }
  .toolbar select.sel { flex: 1 1 44% }
  .toolbar label.chk { flex: 1 1 100% }

  /* A definition list stays two columns: `max-content 1fr` already shrinks to the label, and
     stacking it would double the height of every detail pane to no benefit. It only needs to be
     allowed to break a long value rather than push the pane wide. */
  .dl { gap: 3px 12px }
  .dl dd { overflow-wrap: anywhere }

  /* Anything a finger has to hit gets a finger-sized box. */
  .btn { min-height: 44px; padding-left: 14px; padding-right: 14px }
  .toolbar { gap: 8px; flex-wrap: wrap }
  .field input, .field select { min-height: 42px }
}

/* Landscape phones: the same rules, but the shell must not lose its vertical room to chrome. */
@media (max-width: 900px) and (orientation: landscape) and (max-height: 460px) {
  .idband { padding: 3px 8px 4px }
  .idrow { margin-top: 2px }
  .tab { min-height: 42px }
  .winbtn { height: 38px }
}
