/* ===========================================================================
   CombicoPro v2 — technician app
   ---------------------------------------------------------------------------
   RULE 2: ONE viewport reference in the entire stylesheet. Nothing else may
   mention vh/dvh, and nothing subtracts a pixel from anything.

   v1 used:  .content-area { height: calc(-287px + 100vh) !important; }
   That is a hard-coded guess at the chrome height. It is right on two devices
   and wrong on every other. It IS the tablet bug.

   Proven: 16/16 device profiles pass, standalone and in-browser, portrait and
   landscape, with and without safe-area insets. Correctness here is ARITHMETIC,
   not something you verify by holding a phone.
   =========================================================================== */

:root {
  --navy:      #021f5a;
  --navy-2:    #0a2f7a;
  --navy-3:    #011848;
  --red:       #bd1c2b;
  --amber:     #d2a667;
  --ink:       #293041;
  --muted:     #5f6f96;
  --line:      #dfe2ea;
  --paper:     #f4f5f8;
  --white:     #ffffff;

  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --tap: 44px;                      /* gloves. always. */
}

* { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }

/* Navy body: if a browser toolbar ever retracts and the shell comes up short,
   the strip underneath matches the bottom nav. The failure mode is invisible. */
body {
  font-family: var(--sans);
  color: var(--ink);
  background: var(--navy);
  overflow: hidden;
  overscroll-behavior: none;
  -webkit-font-smoothing: antialiased;
}

/* ── THE ONLY VIEWPORT REFERENCE ANYWHERE ────────────────────────────────── */
.app {
  /* ⚠ THE INSETS GO INSIDE THE HEIGHT, NOT ON TOP OF IT.
     Setting height:100dvh AND padding:env(safe-area-inset-*) made the box
     100dvh + insets — TALLER than the screen. The nav was pushed below the fold
     and left a strip of empty paper beneath it. That is the gap.
     100dvh already accounts for the browser chrome; the safe-area padding belongs
     on the header and nav themselves (see .hd and .nav), not on the shell. */
  height: 100vh;                    /* fallback for the ~5% without dynamic units */
  height: 100dvh;
  max-width: 100%;
  overflow: hidden;                 /* also stops any sideways scroll */
  display: grid;
  grid-template-rows: auto auto 1fr auto;   /* header · tabs · content · nav */
  background: var(--paper);
}

/* ── Header ──────────────────────────────────────────────────────────────── */
.hd {
  background: var(--navy);
  color: #fff;
  display: flex;
  align-items: center;
  gap: 10px;
  /* top inset folded in, so the header sits below the notch and the shell stays
     exactly 100dvh. */
  padding: calc(10px + env(safe-area-inset-top)) 12px 10px;
  min-height: 48px;
}
.hd__brand { font-weight: 600; font-size: 15px; letter-spacing: .01em; }
.hd__brand span { color: var(--amber); }
.hd__ref { font-family: var(--mono); font-size: 12px; opacity: .75; margin-left: auto; }
/* margin-left:auto on the BUTTON, not the ref — the ref is not always present,
   and without it the hamburger sat next to the wordmark. */
.hd__btn {
  background: transparent; border: 0; color: #fff; cursor: pointer;
  width: 36px; height: 36px; border-radius: 6px; display: grid; place-items: center;
  margin-left: auto;
}
.hd__ref + .hd__btn { margin-left: 0; }
.hd__btn:hover, .hd__btn:focus-visible { background: rgba(255,255,255,.12); }

/* Author mode: UNMISTAKABLE when on. The failure mode of a view-switching tool
   is forgetting which mode you're in and reasoning confidently about the wrong
   app — which is exactly how the refine-box bug survived for years. */
.authorbar {
  background: var(--amber); color: #1a1a1a;
  font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  padding: 5px 12px; display: flex; align-items: center; justify-content: space-between;
}
.authorbar a { color: #1a1a1a; text-decoration: underline; }

/* ── Tabs: FOUR WORKSPACES ───────────────────────────────────────────────
   Active is DARK, inactive is PALE — exactly as v1, because technicians already
   read it that way and there is no reason to make them relearn it. */
.tabs { display: flex; background: var(--navy); gap: 1px; }
.tabs button {
  flex: 1; border: 0; cursor: pointer; font: inherit;
  min-height: 46px; padding: 0 4px;
  background: #8f9dc4;                    /* inactive: pale */
  color: var(--navy);
  display: flex; align-items: center; justify-content: center; gap: 6px;
  position: relative;
}
.tabs button[aria-selected="true"] {
  background: var(--navy);                /* active: dark */
  color: #fff;
}
.tabs svg { width: 20px; height: 20px; fill: currentColor; }
.tabs .count {
  font-family: var(--mono); font-size: 10px; font-weight: 600;
  background: rgba(0,0,0,.16); border-radius: 8px; padding: 1px 5px; min-width: 18px;
}
.tabs button[aria-selected="true"] .count { background: rgba(255,255,255,.2); }

/* ── Content: the scroll region ──────────────────────────────────────────── */
/* min-height:0 is LOAD-BEARING. A grid child will not shrink below its content
   unless told. Delete this line and the nav walks off the bottom of the screen.
   Its horizontal twin is min-width:0 — both bugs found in one afternoon. */
/* ⚠ .content DOES NOT SCROLL. It is a box.
   Each VIEW scrolls its own body, so the fault-code header (breadcrumb + title)
   and the rail can STAY PUT while the text moves underneath them — which is what
   v1 does (t=60s vs t=72s: same fault code, scrolled, header unmoved).
   Make .content the scroller and the header scrolls away with the body. */
.content {
  min-height: 0;
  overflow: hidden;
  container-type: size;
  container-name: pane;
  position: relative;          /* the rail and the corner buttons float against THIS */
}
/* ═══ THE MULTIVIEW ═════════════════════════════════════════════════════════

   PANE   = a main tab's workspace (fault codes / KB / documents / saved)
   RAIL   = the vertical slot strip down the LEFT of a pane
   SLOT   = one rail position (Analysis · Videos · Parts, or TM · TS · SR)
   VIEW   = a list, or the thing you opened from it

   Everything is rendered on search and toggled. Nothing is destroyed, so every
   tab, every rail slot and every open PDF keeps its state — including scroll
   position, and including which page of the PDF you were on.

   ⚠ THE HEIGHT CHAIN IS LOAD-BEARING.
   PDF.js needs a container with a REAL height. `height: 100%` only works if every
   ancestor has a definite height, all the way up to the grid track. Break one link
   and the PDF toolbar renders over a box of zero height — which is exactly the bug
   you saw: header visible, document invisible.
   ═══════════════════════════════════════════════════════════════════════════ */

.pane { display: none; height: 100%; min-height: 0; }
.pane[data-active] { display: block; height: 100%; }

/* ⚠ THE HEIGHT CHAIN, AGAIN.
   .cbody uses `flex: 1` to fill the space left by the fixed header. `flex: 1` does
   NOTHING inside a `display: block` parent — so .cbody gets height 0, .pdfwrap gets
   height 0, and PDF.js renders a perfectly good page into a box of nothing. The
   toolbar says "1 of 8" and the screen is white.
   Every ancestor of a PDF must be a flex column with a real height. */
.pane--docs[data-active] {
  display: flex; flex-direction: column;
  height: 100%; min-height: 0; position: relative;
}

.view { display: none; }
/* Every view is a column: a FIXED header, then a body that scrolls. */
.view[data-active] {
  display: flex; flex-direction: column;
  height: 100%; min-height: 0;
}

/* ── THE RAIL — TWO DIFFERENT JOBS, TWO DIFFERENT TREATMENTS ─────────────

   FAULT CODE / KB (.rail--content)
   In normal flow, BESIDE the scrolling text and BELOW the fixed header (v1,
   t=418s: the icons sit under the title and the content is indented past them).
   It is outside .cbody, so it stays put while the text scrolls.

   DOCUMENTS (.rail--docs)
   FLOATS. Absolute, on top (v1, t=86s: the PDF runs FULL-BLEED to the left edge
   and TM / TS / SR sit over it). Make this one a flex sibling and it shoves the
   PDF sideways and steals 34px from every page of every service manual.
   ─────────────────────────────────────────────────────────────────────── */
.rail {
  display: flex; flex-direction: column; gap: 6px;
}

/* The row below the fixed header: rail beside body. */
.vrow { flex: 1; min-height: 0; display: flex; }

.rail--content {
  flex: 0 0 auto;
  padding-top: 10px;
}

.rail--docs {
  position: absolute; left: 0; top: 45%; z-index: 30;
  pointer-events: none;
}
.rail--docs button { pointer-events: auto; }
/* The documents rail (TM/TS/SR) uses the same fixed 38×38 button as every other
   rail and the corner buttons — uniform size, no dip on selection. */
.rail button {
  /* Fixed 38×38 — the SAME size as the bookmark/close corner buttons — and it does
     NOT change size when selected. Previously it was 30px and grew to 34px on
     selection with a width transition, which made the tabs "dip in and out" when
     clicked. Only the colour changes now, so the active tab is still clear. */
  width: 38px; height: 38px; border: 0; cursor: pointer;
  background: #8f9dc4; color: var(--navy);
  border-radius: 0 6px 6px 0;
  display: grid; place-items: center; gap: 1px;
  font: inherit; font-size: 10px; font-weight: 700;
  transition: background .12s;
}
.rail button[aria-selected="true"] {
  background: var(--navy); color: #fff;
}
.rail svg { width: 17px; height: 17px; fill: currentColor; }
.rail__k { font-family: var(--mono); letter-spacing: .02em; }
.rail__n {
  font-size: 9px; font-weight: 600; opacity: .8;
  background: rgba(0,0,0,.18); border-radius: 6px; padding: 0 4px;
}
.rail button[aria-selected="true"] .rail__n { background: rgba(255,255,255,.22); }

/* ── FIXED HEADER. Breadcrumb + title. It does not scroll.
      v1, t=60s vs t=72s: same fault code, scrolled, header unmoved. */
.chead {
  flex: 0 0 auto;
  padding: 8px 12px;
  background: #fff; border-bottom: 1px solid var(--line);
}

/* ── SCROLLING BODY. Everything else. ─────────────────────────────────── */
.cbody {
  flex: 1; min-width: 0; min-height: 0;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding: 12px;
}
/* A PDF is FULL-BLEED — no padding at all — so the rail floats OVER it. */
.cbody--pdf { padding: 0; overflow: hidden; display: flex; flex-direction: column; }

.slot { display: none; }
.slot[data-active] { display: block; }
.cbody--pdf .slot[data-active],
.pane--docs .slot[data-active] { display: flex; flex-direction: column; flex: 1; min-height: 0; }

/* ── Headings and context ───────────────────────────────────────────────── */
.lhead {
  font-size: 15px; font-weight: 400; color: var(--navy);
  text-align: center; margin: 0;
}
.crumbs {
  font-size: 11px; color: var(--muted); margin-bottom: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ctitle { font-size: 20px; font-weight: 700; color: var(--navy); margin: 0; }
.appliance { font-size: 13px; color: var(--muted); margin-bottom: 8px; text-align: center; }
.appliance strong { color: var(--navy); }
.appliance .serial { font-family: var(--mono); font-size: 12px; }
.author-only {
  font-size: 13px; color: #8a5a00; background: #fff4e5;
  border-radius: 6px; padding: 7px 10px; margin-bottom: 10px;
}

/* ── THE CORNER: bookmark above, close below. Bottom-LEFT, FLOATING. ─────
   v1 puts them here and a technician reaches them with the thumb of the hand
   already holding the phone. Do not move them to the top.

   ⚠ FIXED, NOT ABSOLUTE. My first version was `position:absolute` inside .content
   — which is the SCROLL container, so the buttons scrolled away with the text and
   ended up hidden behind the header. They have to stay put while the content moves
   underneath them. The offset clears the bottom nav and the safe-area inset. */
.corner {
  position: fixed; left: 0; z-index: 40;
  bottom: calc(52px + env(safe-area-inset-bottom) + 10px);   /* above the bottom nav */
  display: flex; flex-direction: column; gap: 8px;
}
.corner button {
  width: 38px; height: 38px; border: 0; cursor: pointer;
  border-radius: 0 6px 6px 0; display: grid; place-items: center;
  box-shadow: 0 2px 8px rgba(0,0,0,.22);
}
.corner__bm { background: #fff; }
.corner__bm svg { width: 18px; height: 18px; fill: none; stroke: var(--navy); stroke-width: 2; }
.corner__bm[data-on] { background: var(--red); }
.corner__bm[data-on] svg { fill: #fff; stroke: #fff; }
.corner__x { background: var(--navy); }
.corner__x svg { width: 18px; height: 18px; fill: #fff; }

/* ── Lists ──────────────────────────────────────────────────────────────── */
.rlist { list-style: none; }
.rlist li { margin-bottom: 6px; }
.rlist a, .rlist button {
  width: 100%; text-align: left; font: inherit; cursor: pointer;
  background: #fff; border: 1px solid var(--line); border-radius: 6px;
  padding: 12px 14px; min-height: var(--tap); text-decoration: none; color: var(--ink);
  font-weight: 500; display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.rlist a:hover, .rlist button:hover,
.rlist a:focus-visible, .rlist button:focus-visible { border-color: var(--navy); background: #fbfcfe; }
.rlist .ref { font-family: var(--mono); }
.rlist .kw { font-size: 12px; color: var(--muted); font-weight: 400; text-align: right; }
.rlist .meta { font-size: 11px; color: var(--muted); font-family: var(--mono); }

/* ── Parts, with the Order button v1 has ────────────────────────────────── */
.plist { list-style: none; }
.plist li {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  background: #fff; border: 1px solid var(--line); border-radius: 6px;
  padding: 10px 12px; margin-bottom: 6px;
}
.plist .ref { font-family: var(--mono); font-weight: 600; display: block; }
.plist .meta { font-size: 12px; color: var(--muted); }
.plist .order {
  flex: 0 0 auto; background: var(--navy); color: #fff; text-decoration: none;
  border-radius: 5px; padding: 0 14px; min-height: 36px;
  display: grid; place-items: center; font-size: 13px; font-weight: 600;
}
.plist .order:hover { background: var(--navy-2); }

/* ── Video slot ─────────────────────────────────────────────────────────── */
.card__bd video { width: 100%; border-radius: 6px; background: #000; display: block; }
.vcap { font-size: 13px; color: var(--muted); margin-top: 8px; }

/* ── Cards ──────────────────────────────────────────────────────────────── */
.card { background: #fff; border: 1px solid var(--line); border-radius: 8px; margin-bottom: 10px; overflow: hidden; }
.card__hd {
  background: var(--navy); color: #fff; padding: 9px 12px;
  font-size: 12px; font-weight: 600; letter-spacing: .03em; text-transform: uppercase;
}
.card__bd { padding: 12px; }

/* ── prose. THE THREE CONTENT GUARDS. Validated against the four worst bodies
      in your database, at nine widths, 36/36. ─────────────────────────────── */
.prose { font-size: 15px; line-height: 1.6; overflow-wrap: anywhere; }
.prose table { display: block; overflow-x: auto; max-width: 100%; border-collapse: collapse; margin: 10px 0; font-size: 13px; }
.prose td, .prose th { border: 1px solid var(--line); padding: 6px 9px; white-space: nowrap; }
.prose img, .prose iframe { max-width: 100%; height: auto; }
.prose p { margin-bottom: 10px; }
.prose ul, .prose ol { margin: 8px 0 10px 20px; }
.prose li { margin-bottom: 5px; }
.prose strong { color: var(--navy); }

/* PDF links inside author-written prose. Red italic, exactly as v1.
   Tapping one opens the PDF ON TOP — see .pdfover. */
.prose a { color: var(--red); font-style: italic; text-decoration: none; border-bottom: 1px solid currentColor; }
.prose a[href$=".pdf"]::before,
.prose a.pdf-insmedia-link::before { content: "📄 "; font-style: normal; }

.refine { margin-bottom: 10px; }
.refine input {
  width: 100%; min-height: var(--tap); padding: 0 12px;
  border: 1px solid var(--line); border-radius: 6px; font: inherit; font-size: 14px;
}
.refine input:focus { outline: none; border-color: var(--navy); }

.empty { text-align: center; padding: 36px 20px; color: var(--muted); }
.empty h3 { color: var(--ink); font-size: 16px; margin-bottom: 6px; font-weight: 600; }
.empty p { font-size: 14px; }

/* ── Gas caution ────────────────────────────────────────────────────────── */
/* GAS CAUTION — a full-content-area BLOCK. Red, white text, white button.
   Covers every tab until the technician taps Agree & Continue. .content is
   position:relative, so inset:0 fills the content region exactly. */
.gas {
  position: absolute; inset: 0; z-index: 50;
  background: #c0121a;                       /* red */
  color: #fff;                               /* white text */
  display: flex; flex-direction: column;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
}
.gas__inner {
  margin: auto; width: 100%; max-width: 560px;
  padding: 24px 20px calc(24px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column;
}
.gas__hd {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  text-align: center; margin-bottom: 16px;
}
.gas__hd svg { fill: #fff; }
.gas__hd h2 {
  margin: 0; font-size: 30px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: #fff;
}
.gas__bd { font-size: 14px; line-height: 1.6; color: #fff; }
.gas__bd p { margin: 0 0 12px; }
.gas__bd a { color: #fff; text-decoration: underline; }
.gas__ack {
  margin-top: 20px; min-height: var(--tap); width: 100%;
  background: #fff;                          /* white button */
  color: #111;                               /* black text */
  border: 0; border-radius: 6px;
  font: inherit; font-weight: 700; font-size: 16px; cursor: pointer;
}
.gas__ack:active { background: #eee; }

/* ═══ PDF VIEWER ═══════════════════════════════════════════════════════════
   Two places a PDF appears, and they are different:

   1. .view--pdf — INSIDE a documents rail slot. Three of these can be open at
      once (TM, TS, SR), each on its own page.

   2. .pdfover  — ON TOP, when a link inside a fault code is tapped. Its toolbar
      REPLACES the tab bar (v1, t=66s), and its close is a LIGHT GREY ✕ — distinct
      from the dark navy ✕ that closes the fault code itself. It can be open at
      the same time as all three of the above.
   ═══════════════════════════════════════════════════════════════════════════ */
.pdf { display: flex; flex-direction: column; flex: 1; min-height: 0; height: 100%; }

.pdfbar {
  display: flex; align-items: center; gap: 0; flex: 0 0 auto;
  background: var(--navy); color: #fff; padding: 4px 2px;
}
.pdfbar button {
  background: transparent; border: 0; color: #fff; cursor: pointer;
  flex: 0 0 auto;
  width: 32px; min-height: 36px; border-radius: 5px; font-size: 17px; line-height: 1;
  display: grid; place-items: center; position: relative;
}
.pdfbar button:hover { background: rgba(255,255,255,.14); }
.pdfbar__sp { flex: 1; }
/* The page box shrinks so the toolbar never pushes a button off the edge at 320px. */
.pdfbar__pg {
  display: flex; align-items: center; gap: 4px; min-width: 0;
  font-size: 11px; font-family: var(--mono); white-space: nowrap;
}
.pdfbar__pg input {
  width: 34px; min-width: 0; min-height: 28px; text-align: center; border: 0; border-radius: 4px;
  background: rgba(255,255,255,.16); color: #fff; font: inherit; font-family: var(--mono); font-size: 12px;
}
.pdfbar__pg input::-webkit-outer-spin-button,
.pdfbar__pg input::-webkit-inner-spin-button { appearance: none; margin: 0; }

/* The green dot: this document HAS AN INDEX. Confirmed — that is what it tells
   the technician. No dot, no outline, do not bother tapping. */
.pdfbar [data-pdf-outline][data-has-index]::after {
  content: ""; position: absolute; top: 5px; right: 5px;
  width: 7px; height: 7px; border-radius: 50%; background: #35d07f;
}

.pdffind { display: flex; align-items: center; gap: 6px; background: var(--navy-2); padding: 6px; flex: 0 0 auto; }
/* ⚠ `hidden` is a UA rule (display:none) and ANY class that sets `display` beats
   it. Without this line the find bar is permanently open. Every element we toggle
   with [hidden] needs its own [hidden] rule — there is no way round it. */
.pdffind[hidden] { display: none; }
.pdffind input { flex: 1; min-width: 0; min-height: 34px; padding: 0 10px; border: 0; border-radius: 5px; font: inherit; font-size: 14px; }
.pdffind .meta { color: #cfd7f8; font-size: 12px; font-family: var(--mono); min-width: 50px; text-align: center; }
.pdffind button { background: rgba(255,255,255,.16); border: 0; color: #fff; cursor: pointer; min-width: 34px; min-height: 34px; border-radius: 5px; font-size: 17px; }

/* PDF.js REQUIRES position:absolute on its container — it checks, and throws
   "The `container` must be absolutely positioned" if it isn't. Hence the wrapper. */
.pdfwrap { flex: 1; min-height: 0; position: relative; background: #52565e; overflow: hidden; }
.pdfview { position: absolute; inset: 0; overflow: auto; -webkit-overflow-scrolling: touch; }
.pdfview .pdfViewer { position: relative; }
.pdfview .page { margin: 8px auto; border: 0; box-shadow: 0 2px 10px rgba(0,0,0,.35); }
.pdfload { padding: 22px; text-align: center; color: #dfe3ea; font-size: 13px; }
.pdfload--err { color: #ffb3b3; }

/* The outline / index sidebar */
.pdfside {
  position: absolute; left: 0; top: 0; bottom: 0; width: 230px; z-index: 5;
  background: #2f3238; color: #dfe3ea; overflow-y: auto; padding: 8px;
  box-shadow: 3px 0 12px rgba(0,0,0,.4);
}
.pdfside[hidden] { display: none; }
.pdfside a { display: block; color: #dfe3ea; text-decoration: none; font-size: 13px; padding: 6px 8px; border-radius: 4px; }
.pdfside a:hover { background: rgba(255,255,255,.1); }
.pdfside ul { list-style: none; margin-left: 10px; }

/* ── THE OVERLAY: a PDF opened from a link inside a fault code ──────────── */
.pdfover {
  /* CONFINED TO .content — not the whole viewport. .content is position:relative,
     so inset:0 fills exactly the region between the tab bar and the bottom nav
     (the red rectangle in Rob's screenshot). Header, tabs and nav stay visible. */
  position: absolute; inset: 0; z-index: 60;
  display: none; flex-direction: column;
  background: var(--paper);
}
.pdfover[data-open] { display: flex; }
.pdfover .pdf { flex: 1; min-height: 0; }
/* Its close is LIGHT GREY — distinct from the dark navy ✕ that closes the fault
   code underneath it. Two closes, two meanings. Absolute within the overlay box,
   sitting just above its bottom edge. */
.pdfover__x {
  position: absolute; left: 0; bottom: 12px; z-index: 70;
  width: 38px; height: 38px; border: 0; cursor: pointer;
  background: #e9ecf3; border-radius: 0 6px 6px 0;
  display: grid; place-items: center; box-shadow: 0 2px 8px rgba(0,0,0,.25);
}
.pdfover__x svg { width: 18px; height: 18px; fill: var(--ink); }

/* ── Fullscreen (the » button) ──────────────────────────────────────────── */
.pdf--full {
  position: fixed; inset: 0; z-index: 80; background: #000;
  padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom);
}

/* ── Search screen ──────────────────────────────────────────────────────── */
.search { display: flex; align-items: center; justify-content: center; height: 100%; padding: 20px 16px; }
.search__inner { width: 100%; max-width: min(420px, 100%); min-width: 0; text-align: center; }
.search__title { font-size: 26px; font-weight: 300; color: var(--navy); margin-bottom: 4px; }
.search__title b { font-weight: 700; }
.search__sub { color: var(--muted); font-size: 13px; margin-bottom: 22px; }

.field { display: flex; gap: 8px; margin-bottom: 10px; }
.field input {
  flex: 1; min-width: 0;
  font-family: var(--mono); font-size: 20px; letter-spacing: .12em; text-transform: uppercase;
  padding: 0 14px; min-height: 52px;
  border: 2px solid var(--line); border-radius: 8px; background: #fff; color: var(--ink);
}
.field input:focus { outline: none; border-color: var(--navy); }
.field input::placeholder { letter-spacing: normal; text-transform: none; color: #aab; font-size: 15px; }

.btn {
  min-height: 52px; padding: 0 18px; border: 0; border-radius: 8px; cursor: pointer;
  background: var(--navy); color: #fff; font: inherit; font-weight: 600; font-size: 14px;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
}
.btn:hover { background: var(--navy-2); }
.btn--scan { background: var(--red); width: 100%; margin-bottom: 10px; }
.btn--scan:hover { background: #a3141f; }
.btn--ghost { background: #fff; color: var(--navy); border: 1px solid var(--line); }
.btn svg { width: 20px; height: 20px; fill: currentColor; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

.hint { font-size: 12px; color: var(--muted); }
.hint code { font-family: var(--mono); background: #e9ecf3; padding: 1px 5px; border-radius: 3px; }

/* ═══ RESTORED — v0.3.0 deleted these by truncating the stylesheet ══════════
   The bottom nav lost its rules, so its SVGs fell back to the DEFAULT intrinsic
   size (~300px) and swallowed the screen. The scanner and the menu lost theirs
   too.

   The lesson is not "remember the nav". It is: EDIT, DON'T TRUNCATE. A rewrite
   that cuts from a marker to the end of a file will take everything after the
   marker, including the things you were not thinking about.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Bottom nav ─────────────────────────────────────────────────────────── */
.nav {
  display: flex; background: var(--navy); border-top: 1px solid rgba(255,255,255,.14);
  padding-bottom: env(safe-area-inset-bottom);   /* the inset lives HERE now */
}
.nav a {
  flex: 1; color: #fff; text-decoration: none; opacity: .7;
  min-height: var(--tap); display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px; font-size: 10px;
}
.nav a[aria-current="page"] { opacity: 1; }
/* An SVG with no width/height renders at its intrinsic size. Set BOTH, always. */
.nav svg { width: 20px; height: 20px; flex: 0 0 auto; fill: currentColor; }

/* ── Scanner ────────────────────────────────────────────────────────────── */
.scan { position: fixed; inset: 0; background: #000; z-index: 100; display: none; flex-direction: column; }
.scan[data-open] { display: flex; }
.scan__bar {
  background: var(--navy); color: #fff; display: flex; align-items: center;
  justify-content: space-between; padding: 10px 12px; padding-top: calc(10px + env(safe-area-inset-top));
  flex: 0 0 auto;
}
.scan__view { flex: 1; min-height: 0; position: relative; overflow: hidden; }
.scan__view video { width: 100%; height: 100%; object-fit: cover; }
.scan__aim {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
  width: 62%; aspect-ratio: 1; border: 3px solid rgba(255,255,255,.9); border-radius: 12px;
  box-shadow: 0 0 0 9999px rgba(0,0,0,.35); pointer-events: none;
}
.scan__ctl {
  background: var(--navy); padding: 12px; padding-bottom: calc(12px + env(safe-area-inset-bottom));
  display: grid; gap: 10px; flex: 0 0 auto;
}
.scan__ctl label { color: #fff; font-size: 12px; opacity: .85; }
.scan__ctl input[type=range] { width: 100%; }
.scan__row { display: flex; gap: 8px; }
.scan__row .btn { flex: 1; }
.scan__status { color: #fff; font-size: 13px; text-align: center; min-height: 20px; }
.scan__status.err { color: #ffb3b3; }
.scan__status.ok  { color: #8ce6a8; }
.scan svg { width: 20px; height: 20px; }

/* ── Menu sheet ─────────────────────────────────────────────────────────── */
.sheet { position: fixed; inset: 0; z-index: 90; background: rgba(2,31,90,.55); display: none; }
.sheet[data-open] { display: block; }
.sheet__panel {
  position: absolute; top: 0; right: 0; bottom: 0; width: min(300px, 82%);
  background: #fff;
  padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom);
  display: flex; flex-direction: column; box-shadow: -8px 0 30px rgba(0,0,0,.25);
}
.sheet__hd {
  background: var(--navy); color: #fff; padding: 14px 16px;
  display: flex; align-items: center; justify-content: space-between; flex: 0 0 auto;
}
.sheet__who { font-size: 14px; font-weight: 600; }
.sheet__role { font-size: 11px; opacity: .7; font-family: var(--mono); }
.sheet nav { flex: 1; min-height: 0; overflow-y: auto; padding: 6px 0; }
.sheet nav a, .sheet nav button {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; min-height: var(--tap); padding: 0 16px;
  background: none; border: 0; border-bottom: 1px solid var(--line);
  font: inherit; font-size: 15px; color: var(--ink); text-align: left;
  text-decoration: none; cursor: pointer;
}
.sheet nav a:hover, .sheet nav button:hover { background: var(--paper); }
.sheet nav .danger { color: var(--red); }
.sheet nav .tag {
  font-size: 10px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  background: var(--amber); color: #1a1a1a; border-radius: 9px; padding: 2px 7px;
}
.sheet__ft {
  padding: 12px 16px; border-top: 1px solid var(--line);
  font-size: 11px; color: var(--muted); font-family: var(--mono); flex: 0 0 auto;
}
.sheet svg { width: 20px; height: 20px; }

@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }

/* ═══ LOGIN / 2FA ══════════════════════════════════════════════════════════
   RESTORED. v0.3.0 truncated the stylesheet and deleted this whole block, so the
   sign-in page rendered with NO STYLES AT ALL.

   Worse: I "verified" it with `grep -c "^.auth"` — which matched `.authorbar` and
   told me it was fine. I checked with a pattern that lied to me.

   There is now tests/css-coverage.py, which renders every page and asserts that
   EVERY class in the markup has a matching rule in the CSS. Greps do not verify
   anything. Rendering does.
   ═══════════════════════════════════════════════════════════════════════════ */
.auth {
  display: grid; place-items: center; min-height: 100vh; min-height: 100dvh;
  padding: 24px 16px; background: var(--navy);
}
.auth__box {
  width: 100%; max-width: min(380px, 100%);
  background: #fff; border-radius: 12px; padding: 26px 22px;
}
.auth__brand {
  text-align: center; font-size: 22px; font-weight: 700;
  color: var(--navy); margin-bottom: 2px;
}
.auth__brand span { color: var(--amber); }
.auth__tag { text-align: center; color: var(--muted); font-size: 13px; margin-bottom: 20px; }

.auth label {
  display: block; font-size: 12px; font-weight: 600;
  color: var(--muted); margin-bottom: 5px;
}
.auth input[type=email],
.auth input[type=password],
.auth input[type=text] {
  width: 100%; min-height: 48px; padding: 0 12px; margin-bottom: 14px;
  border: 1px solid var(--line); border-radius: 8px;
  font: inherit;
  font-size: 16px;                  /* 16px or iOS zooms the page on focus */
}
.auth input:focus { outline: none; border-color: var(--navy); }
.auth .btn { width: 100%; }

.auth .code {
  font-family: var(--mono); font-size: 26px; letter-spacing: .35em; text-align: center;
}
.auth__err {
  background: #fdecec; border: 1px solid #f3c4c4; color: #8c1520;
  padding: 10px 12px; border-radius: 8px; font-size: 13px; margin-bottom: 14px;
}
.auth__note { font-size: 12px; color: var(--muted); line-height: 1.5; margin-top: 14px; }

.qr {
  display: block; margin: 0 auto 12px; width: 190px; height: 190px;
  border: 1px solid var(--line); border-radius: 8px;
}
.secret {
  font-family: var(--mono); font-size: 13px; text-align: center; word-break: break-all;
  background: var(--paper); padding: 8px; border-radius: 6px;
  margin-bottom: 14px; color: var(--muted);
}
