/* Schwing Trade — dashboard stylesheet (ADR-0035).

   Served from /static rather than inlined into every page. Three reasons,
   in order of importance:

   1. It is CONTENT when inlined. Several tests assert things about the text
      of a rendered page (no issuer name leaks; an empty table shows few em
      dashes). A commented stylesheet in the <head> is part of that text, so
      a comment could fail a test about data, and did.
   2. It is cacheable. Ten pages no longer each carry the same ~9 KB.
   3. Comments can now explain the reasoning at length without any of it
      being paid for on every request.

   StaticFiles sends ETag and Last-Modified, so a deploy that changes this
   file is picked up on the next revalidation without a version string. */

:root {
  /* The ADR-0043 public identity, adopted inside (ADR-0050): instrument
     amber on near-black, hairline rules, no gradients. The SAME values are
     declared on .hero-page in login.html — deliberately twice, not shared:
     the .hero-page scope is the firewall that lets either surface change
     without repainting the other, and ADR-0043 explains why it must stay. */
  --bg: #0a0a0b;
  --panel: #101012;
  --border: #303036; --border-soft: #232326;
  --text: #edeae4; --muted: #a9a49b; --faint: #8f8a82;
  --accent: #ffb020; --accent-soft: rgba(255,176,32,.12);
  /* Dark-on-amber ink for text sitting ON the accent (buttons, brand mark). */
  --accent-ink: #14100a;
  /* ok/bad are the public page's semantic green and redline. warn EQUALS the
     accent by operator decision (ADR-0050): on an instrument panel amber is
     both the brand of the gauge and the warning lamp; the ringed-pill badge
     shape is what separates a warning from an interactive accent. */
  --ok: #57c08a; --bad: #e4362a; --warn: #ffb020;
  --radius: 5px;
  /* Solid surface for sticky table cells; equals --panel now that panels are
     flat, kept as its own name so a future panel treatment cannot silently
     put a scrolling cell on a mismatched background (ADR-0035). */
  --sticky: #101012;
  /* Minimum comfortable touch target. 44px is the iOS Human Interface
     Guidelines figure, and is close enough to Material's 48dp that one
     number serves both.
     (Kept free of company names on purpose: test_dashboard_pages asserts
     no issuer name appears in a rendered page, and a comment is rendered
     output like any other.) */
  --tap: 44px;
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  /* 100dvh, not 100vh: mobile Safari's 100vh is the toolbar-collapsed
     height, so a 100vh page overshoots the visible viewport and leaves a
     phantom scroll. dvh tracks the toolbar. */
  margin: 0; min-height: 100dvh; color: var(--text);
  background: var(--bg);
  font: 15px/1.55 -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  /* Stops a sideways-scrollable table from dragging the whole page (and
     the sticky header) with it. */
  overflow-x: hidden;
}

/* An in-page anchor target (the chart panel behind the timeframe pills) must
   land BELOW the sticky header, not underneath it. Sized to the header's
   real height — 28px mark + 0.7rem padding top and bottom — with a little
   breathing room; generous rather than exact, because a target hidden under
   the masthead reads as "the link did nothing". */
.panel[id] { scroll-margin-top: 4.5rem; }

/* ------------------------------------------------ header / nav */
header {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.7rem 1.4rem;
  padding-left: max(1.4rem, var(--safe-l));
  padding-right: max(1.4rem, var(--safe-r));
  background: rgba(10,10,11,.88); backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border-soft);
}
.header-left { display: flex; align-items: center; min-width: 0; }
/* The old gradient underline is gone (ADR-0050: no gradients); the hairline
   border-bottom above is the masthead's rule, same as the public page. */
.brand { display: flex; align-items: center; gap: .6rem; }
.brand .mark {
  width: 28px; height: 28px; border-radius: 8px; flex: none;
  background: var(--accent);
  display: grid; place-items: center;
  font-weight: 800; font-size: .8rem; color: var(--accent-ink); letter-spacing: -.02em;
}
header h1 { font-size: 1rem; margin: 0; letter-spacing: .01em; font-weight: 650; white-space: nowrap; }
nav { display: flex; gap: .25rem; margin-left: 1.25rem; }
nav a {
  color: var(--muted); text-decoration: none; font-size: .9rem;
  padding: .35rem .8rem; border-radius: 999px; transition: all .15s ease;
}
nav a.active { color: var(--text); background: var(--accent-soft); }
/* Hover styling only where a pointer can actually hover. On a touch screen
   :hover latches after a tap and leaves a row looking selected. */
@media (hover: hover) {
  nav a:hover { color: var(--text); background: rgba(255,255,255,.05); }
}

/* --- the hamburger ---------------------------------------------------
   A checkbox and a label, deliberately: it is the one disclosure pattern
   that needs no JavaScript AND behaves identically in every browser.
   (<details> would be better semantics, but revealing a closed <details>
   body at desktop width relies on overriding a UA-internal slot, which
   browsers implement differently — a bug waiting to happen.)
   The checkbox is off-screen but focusable, so the menu is keyboard- and
   screen-reader-operable. Every nav click is a full page load, so the
   menu resets closed on its own; there is no state to manage. */
.nav-toggle { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.burger {
  display: none; /* desktop: the links speak for themselves */
  width: var(--tap); height: var(--tap); margin-right: .6rem;
  border-radius: 10px; cursor: pointer; flex: none;
  align-items: center; justify-content: center;
  border: 1px solid var(--border-soft); background: rgba(255,255,255,.04);
}
.burger span, .burger span::before, .burger span::after {
  content: ""; display: block; position: relative;
  width: 18px; height: 2px; border-radius: 2px; background: var(--text);
  transition: transform .18s ease, opacity .18s ease;
}
.burger span::before { position: absolute; top: -6px; }
.burger span::after  { position: absolute; top: 6px; }
/* Open state: the bars fold into a cross, so the control says what it does. */
.nav-toggle:checked ~ .header-left .burger span { background: transparent; }
.nav-toggle:checked ~ .header-left .burger span::before { transform: translateY(6px) rotate(45deg); }
.nav-toggle:checked ~ .header-left .burger span::after  { transform: translateY(-6px) rotate(-45deg); }
.nav-toggle:focus-visible ~ .header-left .burger { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ------------------------------------------------ layout / panels */
main { max-width: 64rem; margin: 1.75rem auto 3rem; padding: 0 1.25rem; }
.panel {
  /* Flat surface on a hairline (ADR-0050): structure comes from rules and
     whitespace, not depth effects — the public page's grammar. */
  background: var(--panel);
  border: 1px solid var(--border-soft); border-radius: var(--radius);
  padding: 1.4rem 1.5rem; margin-bottom: 1.1rem;
}
h2 { font-size: 1.05rem; margin: 0 0 1rem; font-weight: 650; letter-spacing: .01em; }
h3 { font-size: .95rem; margin: 0 0 .8rem; font-weight: 650; }
.muted { color: var(--muted); }
.empty {
  color: var(--muted); text-align: center; padding: 2.2rem 1rem;
  border: 1px dashed var(--border); border-radius: var(--radius);
  background: rgba(255,255,255,.015); margin: 0;
}

/* ------------------------------------------------ help panels (no JS: <details>) */
details.help {
  border: 1px solid var(--border-soft); border-radius: var(--radius);
  background: rgba(255,176,32,.04); margin-bottom: 1.1rem;
}
details.help summary {
  cursor: pointer; list-style: none;
  padding: .65rem 1.2rem; color: var(--muted); font-size: .85rem; font-weight: 650;
  display: flex; align-items: center; gap: .55rem; user-select: none;
}
details.help summary::before {
  content: "i"; flex: none; width: 1.05rem; height: 1.05rem; border-radius: 50%;
  display: grid; place-items: center; font-size: .7rem; font-weight: 800;
  font-style: italic; font-family: Georgia, serif;
  background: var(--accent-soft); color: var(--accent);
  box-shadow: inset 0 0 0 1px rgba(255,176,32,.4);
}
details.help summary::-webkit-details-marker { display: none; }
details.help summary:hover { color: var(--text); }
details.help[open] summary { color: var(--text); border-bottom: 1px solid var(--border-soft); }
details.help .help-body {
  padding: .95rem 1.25rem 1.15rem; font-size: .88rem; color: var(--muted);
  line-height: 1.65;
}
details.help .help-body p { margin: 0 0 .7rem; }
details.help .help-body p:last-child, details.help .help-body ul:last-child { margin-bottom: 0; }
details.help .help-body strong { color: var(--text); font-weight: 650; }
details.help .help-body ul { margin: 0 0 .7rem; padding-left: 1.2rem; }
details.help .help-body li { margin-bottom: .3rem; }

/* ------------------------------------------------ stats */
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr)); gap: .9rem; }
.stat {
  background: rgba(255,255,255,.02); border: 1px solid var(--border-soft);
  border-radius: var(--radius); padding: .95rem 1.1rem;
}
.stat .label {
  color: var(--faint); font-size: .72rem; font-weight: 650;
  text-transform: uppercase; letter-spacing: .1em; margin-bottom: .35rem;
}
.stat .value { font-size: 1.45rem; font-weight: 700; font-variant-numeric: tabular-nums; }

/* ------------------------------------------------ badges */
.badge {
  display: inline-block; padding: .14rem .6rem; border-radius: 999px;
  font-size: .78rem; font-weight: 650; letter-spacing: .01em;
}
.badge.ok   { background: rgba(87,192,138,.14); color: var(--ok);   box-shadow: inset 0 0 0 1px rgba(87,192,138,.35); }
.badge.bad  { background: rgba(228,54,42,.12);  color: var(--bad);  box-shadow: inset 0 0 0 1px rgba(228,54,42,.35); }
.badge.warn { background: rgba(255,176,32,.12); color: var(--warn); box-shadow: inset 0 0 0 1px rgba(255,176,32,.35); }
/* A badge carrying hover text advertises itself: the native title tooltip
   is otherwise invisible until you happen to rest the pointer on it. */
.badge[title] { cursor: help; text-decoration: underline dotted currentColor; text-underline-offset: .18em; }

.alert {
  border-left: 3px solid var(--warn);
  background: rgba(255,176,32,.06);
}
.alert-reason { margin: .6rem 0 0; }

/* ------------------------------------------------ shared text utilities
   (ADR-0049) These existed as inline `style=` attributes scattered through
   the templates, which no stylesheet rule can override — the same defect the
   Orders filter chips had. Consolidated here; templates carry classes. */
.footnote { color: var(--muted); font-size: .85rem; margin: .6rem 0 0; }
.stat-note { color: var(--muted); font-size: .75rem; margin-top: .3rem; }
.no-margin { margin: 0; }
/* The delta chip's colour, keyed on DeltaChip.direction. */
.delta-up   { color: var(--ok); }
.delta-down { color: var(--bad); }
.delta-flat, .delta-na { color: var(--text); }

/* ------------------------------------------------ compact status line
   (ADR-0049) Mode, environment and the trading switch almost never change —
   one row, not three hero tiles. */
.statline {
  display: flex; flex-wrap: wrap; gap: .5rem 1.6rem; align-items: center;
  padding: .8rem 1.5rem;
}
.statline-item { display: inline-flex; align-items: center; gap: .5rem; font-weight: 650; font-size: .95rem; }
.statline-label {
  color: var(--faint); font-size: .72rem; font-weight: 650;
  text-transform: uppercase; letter-spacing: .1em;
}

/* ------------------------------------------------ ticker strip
   (ADR-0049) A wrapping row of chips on the desktop; a one-line snapping
   rail on phones (see the 700px layer). */
.ticker-strip { padding: .55rem .9rem; display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.ticker-rail { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; min-width: 0; flex: 1; }
.ticker-chip {
  background: rgba(255,255,255,.04); border: 1px solid var(--border-soft);
  border-radius: 7px; padding: .18rem .5rem;
  font-size: .78rem; font-weight: 650; font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.ticker-note { color: var(--muted); font-size: .72rem; margin-left: auto; }

/* ------------------------------------------------ folded panels
   (ADR-0049) A panel that is content, not help: the Overview's low-demand
   panels fold closed so they stop sitting between the operator and the
   account. Same no-JS <details> pattern as the help panels. */
details.fold {
  background: var(--panel);
  border: 1px solid var(--border-soft); border-radius: var(--radius);
  margin-bottom: 1.1rem;
}
details.fold summary {
  cursor: pointer; list-style: none; user-select: none;
  display: flex; align-items: center; gap: .6rem;
  padding: .85rem 1.5rem; min-height: var(--tap);
  font-size: .95rem; font-weight: 650;
}
details.fold summary::-webkit-details-marker { display: none; }
details.fold summary::before {
  content: "\25B8"; color: var(--faint); display: inline-block;
  transition: transform .15s ease;
}
details.fold[open] summary::before { transform: rotate(90deg); }
details.fold[open] summary { border-bottom: 1px solid var(--border-soft); }
details.fold .fold-body { padding: 1.1rem 1.5rem 1.3rem; }

/* Totals surfaced above the positions table (ADR-0049). */
.totals-strip { margin-bottom: 1rem; }

/* The drawer's sign-out and the bottom tab bar exist in the markup at every
   width; the mobile layer decides which controls are the visible ones. */
.drawer-signout { display: none; }
.tabbar { display: none; }

/* ------------------------------------------------ view transitions
   (ADR-0049) Every nav click is a full page load. Browsers that implement
   cross-document view transitions fade between pages instead of flashing;
   browsers that do not are unaffected. No script, no state. */
@view-transition { navigation: auto; }

/* ------------------------------------------------ filter chips
   The Orders status filters. These carried their styling as an inline
   `style=` attribute, which no stylesheet rule can override — so the mobile
   touch-target rule silently did not apply to them. Styling that lives in the
   markup cannot be made responsive; that is the whole reason this class
   exists. */
.chip-row { display: flex; flex-wrap: wrap; gap: .4rem; margin: 0 0 .8rem; }
.chip {
  display: inline-flex; align-items: center;
  text-decoration: none; font-size: .8rem; padding: .25rem .6rem;
  border-radius: 999px;
  background: rgba(255,255,255,.05); color: var(--muted);
  box-shadow: inset 0 0 0 1px var(--border-soft);
}
.chip.on {
  background: var(--accent-soft); color: var(--text);
  box-shadow: inset 0 0 0 1px var(--accent);
}
@media (hover: hover) {
  .chip:hover { color: var(--text); }
}

/* ------------------------------------------------ tables */
table { width: 100%; border-collapse: collapse; font-size: .9rem; }
th, td { text-align: left; padding: .55rem .65rem; border-bottom: 1px solid var(--border-soft); }
th {
  color: var(--faint); font-weight: 650; font-size: .72rem;
  text-transform: uppercase; letter-spacing: .08em;
}
tr:last-child td { border-bottom: 0; }
tbody tr, table tr { transition: background .12s ease; }
.num { font-variant-numeric: tabular-nums; }
a.rowlink { color: var(--accent); text-decoration: none; }
a.rowlink:hover { text-decoration: underline; }

/* --- sortable column headings ----------------------------------------
   The heading IS the control, so it inherits the header's small-caps look
   rather than announcing itself as a link — a row of blue underlined
   headings would read as navigation. The arrow marks the active column;
   `aria-sort` on the <th> is what actually tells a screen reader, since the
   arrow is aria-hidden. */
a.sortlink {
  color: inherit; text-decoration: none;
  display: inline-flex; align-items: center; gap: .25rem;
  white-space: nowrap;
}
a.sortlink.on { color: var(--text); }
.sortarrow { font-size: .62rem; line-height: 1; color: var(--accent); }
@media (hover: hover) {
  a.sortlink:hover { color: var(--text); }
}
@media (hover: hover) {
  tr:hover td { background: rgba(255,176,32,.05); }
}

/* --- horizontally scrollable tables ----------------------------------
   These tables carry up to sixteen columns. Nothing sensible fits that in
   a phone's width, and dropping columns would mean deciding on the
   operator's behalf which facts do not matter. So the table keeps every
   column and scrolls sideways inside its panel — the page itself never
   moves, which is why <body> is overflow-x: hidden.

   The shadow on the right edge is not decoration: it is the only cue that
   there is more table off-screen. It is drawn with the local/scroll
   background technique, so it appears only while content remains in that
   direction and fades out when you reach the end — the same information a
   scrollbar gives on a desktop, which phones do not draw. */
.table-wrap {
  overflow-x: auto; overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  background:
    linear-gradient(to right, var(--panel) 30%, rgba(16,16,18,0)) left center / 28px 100% no-repeat local,
    linear-gradient(to left,  var(--panel) 30%, rgba(16,16,18,0)) right center / 28px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgba(0,0,0,.45), rgba(0,0,0,0)) left center / 12px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,.45), rgba(0,0,0,0)) right center / 12px 100% no-repeat scroll;
}
.table-wrap table { min-width: 100%; }

/* ------------------------------------------------ key/value lists */
.kv { display: grid; grid-template-columns: minmax(11rem, auto) 1fr; gap: .35rem 1.25rem; font-size: .9rem; margin: 0; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }

/* ------------------------------------------------ forms / buttons */
label { display: block; margin-bottom: .35rem; color: var(--muted); }
input[type=password] {
  width: 100%; padding: .65rem .8rem; border-radius: 8px; font-size: 1rem;
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
  outline: none; transition: border-color .15s ease;
}
input[type=password]:focus { border-color: var(--accent); }
button {
  margin-top: 1rem; width: 100%; padding: .65rem 1rem; border: 0; border-radius: 8px;
  background: var(--accent);
  color: var(--accent-ink); font-weight: 700; font-size: .95rem; cursor: pointer;
  transition: filter .15s ease, transform .05s ease;
}
button:hover { filter: brightness(1.1); }
button:active { transform: translateY(1px); }
header button {
  width: auto; margin: 0; padding: .38rem 1rem; font-size: .85rem;
  background: rgba(255,255,255,.06); color: var(--text); font-weight: 600;
  border: 1px solid var(--border-soft);
}
button.danger  { background: var(--bad); color: #fff4f2; }
button.confirm { background: var(--ok); color: #06170d; }
.inline-btn { width: auto; margin: 0; padding: .5rem 1.1rem; }
form.inline { display: inline; }
.inline-code {
  width: 7.5rem; margin: 0 .4rem 0 0; padding: .5rem .6rem; display: inline-block;
  letter-spacing: .15em; text-align: center;
}
.error { color: var(--bad); margin-top: .75rem; }
.login-wrap { max-width: 22rem; margin: 16vh auto 0; padding: 0 1rem; }
.login-wrap h1 { display: flex; align-items: center; gap: .6rem; margin-top: 0; }

/* ------------------------------------------------ charts
   Two art-directed canvases per chart (ADR-0035); exactly one is shown.
   The desktop canvas is the default so a browser that somehow applies no
   media query still gets the historical chart rather than nothing. */
.chart-narrow { display: none; }
.chart-wide, .chart-narrow { line-height: 0; }

/* ================================================================
   MOBILE — everything below this line is additive. Above 860px the
   dashboard renders exactly as it did before ADR-0035; there is no
   shared rule that changes behaviour at both widths.
   860px is the width at which the ten nav links stop fitting beside
   the brand, measured rather than guessed.
   ================================================================ */
@media (max-width: 860px) {
  .burger { display: flex; }
  /* Collapsed by default; the checkbox reveals it. Absolute so opening
     the menu overlays the page instead of shoving the content down. */
  nav {
    display: none; position: absolute; left: 0; right: 0; top: 100%;
    margin: 0; padding: .5rem max(.9rem, var(--safe-l)) .8rem;
    flex-direction: column; gap: .15rem;
    background: rgba(10,10,11,.97); backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--border-soft);
    box-shadow: 0 18px 40px rgba(0,0,0,.45);
    max-height: calc(100dvh - 4rem); overflow-y: auto;
  }
  .nav-toggle:checked ~ .header-left nav { display: flex; }
  /* Full-width rows at a real touch size, not 30px pills. */
  nav a {
    display: flex; align-items: center; min-height: var(--tap);
    padding: .55rem .9rem; border-radius: 8px; font-size: 1rem;
  }
  nav a.active {
    background: var(--accent-soft);
    box-shadow: inset 3px 0 0 var(--accent);
  }
  /* Sign out relocates into the drawer (ADR-0049): the header button hides,
     an identical form shows as the drawer's last row. */
  .header-signout { display: none; }
  .drawer-signout {
    display: block; margin-top: .4rem; padding-top: .5rem;
    border-top: 1px solid var(--border-soft);
  }
  .drawer-signout button {
    margin: 0; width: 100%; min-height: var(--tap);
    background: rgba(255,255,255,.06); color: var(--text);
    font-weight: 600; font-size: 1rem; border: 1px solid var(--border-soft);
  }

  /* --- the bottom tab bar (ADR-0049) --------------------------------
     The five high-demand pages, one thumb-tap away. Fixed, safe-area
     aware; the body reserves clearance below so the last panel is never
     hidden behind it. */
  .tabbar {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
    background: rgba(10,10,11,.94); backdrop-filter: blur(12px);
    border-top: 1px solid var(--border-soft);
    padding: .3rem max(.4rem, var(--safe-l)) calc(.3rem + var(--safe-b)) max(.4rem, var(--safe-r));
  }
  .tabbar a {
    flex: 1; display: flex; flex-direction: column; align-items: center;
    justify-content: center; gap: .15rem; min-height: var(--tap);
    color: var(--muted); text-decoration: none; border-radius: 10px;
    font-size: .68rem; font-weight: 650; letter-spacing: .02em;
  }
  .tabbar svg { width: 22px; height: 22px; }
  .tabbar a.active { color: var(--text); }
  .tabbar a.active svg { color: var(--accent); }
  body { padding-bottom: calc(4.6rem + var(--safe-b)); }
}

@media (max-width: 700px) {
  /* Reclaim the ~23% of a 390px screen that was horizontal chrome. */
  main {
    margin: 1.1rem auto 2.5rem;
    padding-left: max(.85rem, var(--safe-l));
    padding-right: max(.85rem, var(--safe-r));
  }
  .panel { padding: 1.05rem .95rem; margin-bottom: .85rem; border-radius: 12px; }
  details.help summary { padding: .7rem .95rem; min-height: var(--tap); }
  details.help .help-body { padding: .85rem .95rem 1rem; }

  /* Two stat tiles per row reads better than one wide one. */
  .grid { grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); gap: .6rem; }
  .stat { padding: .8rem .85rem; }
  .stat .value { font-size: 1.25rem; }

  /* A definition list is a stack on a phone. Two columns at this width
     gave the label 11rem of a 342rem-wide screen and squeezed the value
     that mattered. */
  .kv { grid-template-columns: 1fr; gap: .1rem .75rem; }
  .kv dt { color: var(--faint); font-size: .74rem; text-transform: uppercase; letter-spacing: .08em; margin-top: .6rem; }
  .kv dd { margin-bottom: .2rem; }

  /* The panel already provides the side padding, so the scroll area is
     pushed out to the panel edge — the table can then use the full width
     and the shadow sits exactly where the content is cut off. */
  .table-wrap {
    margin-left: -.95rem; margin-right: -.95rem;
    padding-left: .95rem; padding-right: .95rem;
  }
  .table-wrap th:first-child, .table-wrap td:first-child {
    position: sticky; left: 0; z-index: 2;
    background: var(--sticky);
    box-shadow: 1px 0 0 var(--border-soft);
  }
  /* Data cells must not wrap: a price broken across two lines is harder to
     read than a table you swipe. PROSE cells are the opposite — nowrapping a
     sentence turns it into an endless horizontal scroll, which is exactly what
     happened to the strategy descriptions on the System page. Any cell holding
     a sentence carries `.prose` and opts out. */
  .table-wrap th, .table-wrap td { white-space: nowrap; padding: .6rem .6rem; }
  .table-wrap td.prose, .table-wrap td.prose * { white-space: normal; }
  .table-wrap td.prose { min-width: 15rem; }

  /* --- control tables stack into cards -------------------------------
     A table of numbers should stay a table and scroll. A table that is
     really a control panel — a couple of rows, each a paragraph plus a
     button — should not: swiping sideways to reach the button that turns a
     strategy off is the wrong interaction for the job. `.stacked` turns
     those rows into labelled blocks, so each one reads top to bottom with
     nothing off screen. The labels come from `data-label`, so a stacked cell
     still says which column it was. */
  table.stacked, table.stacked tbody, table.stacked tr, table.stacked td { display: block; }
  table.stacked th { display: none; }  /* the data-labels replace the header row */
  table.stacked tr {
    border: 1px solid var(--border-soft); border-radius: 12px;
    padding: .95rem 1rem; margin-bottom: .8rem;
    background: rgba(255,255,255,.02);
  }
  table.stacked td {
    border: 0; padding: .3rem 0; white-space: normal; width: auto;
  }
  table.stacked td[data-label]::before {
    content: attr(data-label); display: block;
    color: var(--faint); font-size: .7rem; font-weight: 650;
    text-transform: uppercase; letter-spacing: .09em; margin-bottom: .15rem;
  }
  /* A stacked row is not a scrolling row, so the pinned first column and the
     scroll shadow would both be nonsense here. */
  .table-wrap table.stacked td:first-child {
    position: static; background: transparent; box-shadow: none;
  }
  table.stacked .inline-btn { width: 100%; margin-top: .4rem; }
  table.stacked .inline-code { width: 100%; margin: 0 0 .5rem; text-align: left; }

  .chart-wide { display: none; }
  .chart-narrow { display: block; }

  /* Buttons and inputs at a size a thumb can actually hit. Sign out and the
     Orders filter chips are included: a header control is still a control,
     and exempting them would have made the guide's "targets meet the 44px
     guideline" untrue — which a fact-check duly caught. */
  button, .inline-btn, header button, .chip, a.sortlink { min-height: var(--tap); }
  header button { padding: .4rem .95rem; }
  .chip { padding-top: .55rem; padding-bottom: .55rem; }
  /* Sortable headings are controls too. Left out of the first cut, which the
     fact-check caught: at .55rem padding they were ~38px, under the bar this
     stylesheet sets everywhere else. The link supplies the height, so the cell
     gives up its vertical padding rather than adding to it — scoped to the
     sortable table so ordinary headers keep their spacing. */
  table.sortable th { padding-top: 0; padding-bottom: 0; }
  /* 16px is the threshold below which iOS Safari zooms the page on focus
     — anything smaller makes the whole layout jump. This deliberately covers
     textarea and select, not just the three input types originally listed:
     the review-note box on a signal is a textarea and zoomed exactly like
     the rest until the same fact-check caught it. */
  input:not([type=hidden]), textarea, select { font-size: 16px; }
  h2 { font-size: 1rem; }

  /* The ticker strip becomes a one-line snapping rail (ADR-0049); the same
     edge-shadow cue as .table-wrap says there is more off-screen.
     `flex-wrap: nowrap` on the STRIP is load-bearing, not tidiness: the base
     layer sets wrap, and a WRAPPED column flex container does not clamp its
     children's cross size — the rail resolved to max-content (~1,400px with
     twelve chips), poked out of every ancestor and widened the whole page,
     which on a phone let the document pan sideways past its own content
     (found in production, 2026-08-14 (f)). A flex container that changes
     direction at a breakpoint must restate flex-wrap in the same rule. */
  .ticker-strip { flex-direction: column; flex-wrap: nowrap; align-items: stretch; gap: .3rem; }
  .ticker-rail {
    max-width: 100%; /* guards the same regression class if wrap ever returns */
    flex-wrap: nowrap; overflow-x: auto; padding-bottom: .15rem;
    -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    background:
      linear-gradient(to right, var(--panel) 30%, rgba(16,16,18,0)) left center / 28px 100% no-repeat local,
      linear-gradient(to left,  var(--panel) 30%, rgba(16,16,18,0)) right center / 28px 100% no-repeat local,
      radial-gradient(farthest-side at 0 50%, rgba(0,0,0,.45), rgba(0,0,0,0)) left center / 12px 100% no-repeat scroll,
      radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,.45), rgba(0,0,0,0)) right center / 12px 100% no-repeat scroll;
  }
  .ticker-chip { scroll-snap-align: start; }
  .ticker-note { margin-left: 0; }

  .statline { padding: .7rem .95rem; gap: .35rem 1.1rem; }
  details.fold summary { padding: .8rem .95rem; }
  details.fold .fold-body { padding: .9rem .95rem 1.05rem; }
  /* Clearance for the fixed tab bar is set in the 860px layer; the safe-area
     inset is already part of that calc, so nothing further here. */
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
  /* The universal selector cannot reach the ::view-transition pseudo-tree,
     so the cross-document fade is switched off explicitly. */
  @view-transition { navigation: none; }
}
