/* Ridderholt design system: components. Uses only the tokens in
   tokens.css. Dense, not crowded: compact rows, hairlines instead of
   boxes, colour only for meaning and the one accent. Phone first: every
   rule that changes on a desktop is under min-width: 561px. */

/* ---- Base */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body { margin: 0; background: var(--bg); color: var(--text); font: var(--weight-regular) var(--text-base)/var(--leading) var(--font-sans);
  font-variant-numeric: tabular-nums; overflow-wrap: break-word; -webkit-font-smoothing: antialiased; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 2px; }
:focus-visible { outline: none; box-shadow: var(--focus); border-radius: var(--radius-1); }
h1, h2, h3 { margin: 0; font-weight: var(--weight-strong); line-height: var(--leading-tight); letter-spacing: -0.005em; }
h1 { font-size: var(--text-xl); }
h2 { font-size: var(--text-lg); }
h3 { font-size: var(--text-base); }
p { margin: 0; }
p + p { margin-top: var(--space-3); }
ul, ol { margin: 0; }
code, kbd, pre { font-family: var(--font-mono); font-size: 0.92em; }
code { background: var(--surface-2); padding: 0 var(--space-2); border-radius: var(--radius-1); overflow-wrap: anywhere; }
pre { margin: 0; padding: var(--space-3) var(--space-4); background: var(--surface-2); border-radius: var(--radius-2); overflow-x: auto; }
hr { border: 0; border-top: 1px solid var(--line); margin: var(--space-5) 0; }
img, svg { max-width: 100%; }
[hidden] { display: none !important; }
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* Text roles */
.muted { color: var(--text-2); }
.faint { color: var(--text-3); }
.small { font-size: var(--text-sm); }
.tiny { font-size: var(--text-xs); }
.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.unit { color: var(--text-3); font-size: var(--text-xs); margin-left: 2px; }
.nowrap { white-space: nowrap; }
.up { color: var(--up); }
.down { color: var(--down); }
.strong { font-weight: var(--weight-strong); }
.est { font-style: italic; color: var(--text-2); }

/* ---- Page shell: a thin header, then the page. */
.app-header { position: sticky; top: 0; z-index: 20; background: var(--surface); border-bottom: 1px solid var(--line); }
.app-bar { display: flex; align-items: center; gap: var(--space-4); height: var(--header); max-width: var(--page-max); margin: 0 auto; padding: 0 var(--space-4); }
.app-brand { display: inline-flex; align-items: baseline; gap: var(--space-2); flex: none; color: var(--text); font-weight: var(--weight-strong); font-size: 15px; }
.app-brand:hover { text-decoration: none; }
.app-brand .app-family { color: var(--text-3); font-weight: var(--weight-regular); }
.app-nav { display: none; }
.app-nav a { display: inline-flex; align-items: center; gap: var(--space-2); height: var(--header); padding: 0 var(--space-3); color: var(--text-2);
  font-weight: var(--weight-medium); border-bottom: 2px solid transparent; white-space: nowrap; }
.app-nav a:hover { color: var(--text); text-decoration: none; }
.app-nav a[aria-current="page"] { color: var(--text); border-bottom-color: var(--accent); }
.app-search { position: relative; flex: 1; min-width: 0; max-width: 420px; margin: 0; }
.app-search input { width: 100%; height: 32px; padding: 0 var(--space-4); border: 1px solid var(--line); border-radius: var(--radius-2);
  background: var(--bg); color: var(--text); font: inherit; font-size: var(--text-md); }
.app-search input::placeholder { color: var(--text-3); }
.app-search input:focus { border-color: var(--accent); outline: none; box-shadow: none; background: var(--surface); }
.app-spacer { flex: 1; }
.app-main { max-width: var(--page-max); margin: 0 auto; padding: var(--space-4) var(--space-4) var(--space-8); display: grid; gap: var(--space-6); min-width: 0; }
.app-main > * { min-width: 0; }
.app-main.narrow { max-width: var(--form-max); padding-top: var(--space-8); }
@media (min-width: 561px) {
  .app-nav { display: flex; align-items: stretch; gap: 0; }
  .app-search input { font-size: var(--text-base); }
  .app-main { padding: var(--space-5) var(--space-5) var(--space-8); }
  .app-menu .menu-nav { display: none; }
}
@media (min-width: 900px) { .app-search { max-width: 360px; } }
@media (max-width: 899px) and (min-width: 561px) { .app-nav a { padding: 0 var(--space-2); } }

/* The account menu (and, on a phone, the whole nav): a <details>, so it
   opens without JavaScript. */
.app-menu > summary { display: inline-flex; align-items: center; gap: var(--space-2); height: 32px; padding: 0 var(--space-3); border-radius: var(--radius-2);
  color: var(--text-2); font-weight: var(--weight-medium); cursor: pointer; list-style: none; white-space: nowrap; }
.app-menu > summary::-webkit-details-marker { display: none; }
.app-menu > summary:hover, .app-menu[open] > summary { background: var(--surface-2); color: var(--text); }
.app-menu .menu-panel { right: 0; min-width: 240px; }
.app-menu .menu-who { padding: var(--space-3) var(--space-4); color: var(--text-3); font-size: var(--text-sm); border-bottom: 1px solid var(--line);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- Overflow menu: a ⋯ or a word that opens a short list of actions. */
details.menu { position: relative; display: inline-block; }
details.menu > summary { list-style: none; cursor: pointer; }
details.menu > summary::-webkit-details-marker { display: none; }
.menu-dots { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: var(--radius-2); color: var(--text-2); }
.menu-dots:hover, details.menu[open] > .menu-dots { background: var(--surface-2); color: var(--text); }
.menu-dots svg { width: 16px; height: 16px; fill: currentColor; }
.menu-panel { position: absolute; top: calc(100% + 4px); right: 0; z-index: 30; min-width: 200px; max-width: min(320px, calc(100vw - 24px));
  padding: var(--space-2) 0; background: var(--surface); border-radius: var(--radius-2); box-shadow: var(--shadow-pop); }
.menu-panel.left { right: auto; left: 0; }
.menu-panel.is-floating { position: fixed; left: auto; }
.menu-panel a, .menu-panel button { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); width: 100%; min-height: 36px;
  padding: 0 var(--space-4); border: 0; background: none; color: var(--text); font: inherit; text-align: left; cursor: pointer; }
.menu-panel a:hover, .menu-panel button:hover { background: var(--surface-2); text-decoration: none; }
.menu-panel a[aria-current="page"] { color: var(--accent); font-weight: var(--weight-medium); }
.menu-panel form { margin: 0; }
.menu-panel .danger { color: var(--fail-fg); }
.menu-panel hr { margin: var(--space-2) 0; }
.menu-panel .menu-label { padding: var(--space-2) var(--space-4) 0; color: var(--text-3); font-size: var(--text-xs); }
.menu-theme { display: flex; gap: var(--space-2); padding: var(--space-2) var(--space-4) var(--space-3); }
.menu-theme button { flex: 1; justify-content: center; min-height: 28px; padding: 0 var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-2);
  font-size: var(--text-sm); color: var(--text-2); }
.menu-theme button[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }

/* ---- Title row: the page's name, a line under it, and at most one
   primary action; the rest as text links or in the ⋯ menu. */
.titlebar { display: flex; align-items: flex-start; gap: var(--space-3) var(--space-5); flex-wrap: wrap; }
.titlebar-main { flex: 1 1 240px; min-width: 0; }
.titlebar h1 { overflow-wrap: anywhere; }
.titlebar .crumb { display: block; font-size: var(--text-sm); margin-bottom: var(--space-1); }
.titlebar-sub { margin-top: var(--space-2); color: var(--text-2); font-size: var(--text-sm); }
.titlebar-actions { display: flex; align-items: center; gap: var(--space-4); flex: none; }
.titlebar-actions form { margin: 0; }
.titlebar-figure { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-2) var(--space-3); margin-top: var(--space-2); }
.titlebar-figure .big { font-size: var(--text-num); font-weight: var(--weight-strong); line-height: 1.1; letter-spacing: -0.01em; }
.titlebar-figure .change { font-size: var(--text-lg); font-weight: var(--weight-medium); }

/* ---- Sections: a heading and its content; no box around it. A card
   (.panel) only for something that is a separate object. */
.section { display: grid; gap: var(--space-3); min-width: 0; }
.section > *, .stack > * { min-width: 0; }
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2) var(--space-4); flex-wrap: wrap;
  padding-bottom: var(--space-2); border-bottom: 1px solid var(--line); }
.section-head h2 .unit, .section-head h3 .unit { font-size: var(--text-sm); font-weight: var(--weight-regular); }
.section-head .aside { display: flex; align-items: center; gap: var(--space-4); font-size: var(--text-sm); }
.section h3 { margin-top: var(--space-3); }
.panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-2); padding: var(--space-5); }
.lead { color: var(--text-2); }
.meta { color: var(--text-3); font-size: var(--text-sm); }
.stack { display: grid; gap: var(--space-3); }
.row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-4); }

/* ---- Buttons: one primary per page; everything else is quiet. */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); height: 32px; padding: 0 var(--space-4);
  border: 1px solid var(--line-strong); border-radius: var(--radius-2); background: var(--surface); color: var(--text);
  font: inherit; font-weight: var(--weight-medium); white-space: nowrap; cursor: pointer; text-decoration: none;
  transition: background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease); }
.btn:hover { background: var(--surface-2); text-decoration: none; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn-link { display: inline; height: auto; padding: 0; border: 0; background: none; color: var(--accent); font: inherit; cursor: pointer; text-align: left; }
.btn-link:hover { text-decoration: underline; text-underline-offset: 2px; background: none; }
.btn-link.danger { color: var(--fail-fg); }
.btn-sm { height: 28px; padding: 0 var(--space-3); font-size: var(--text-sm); }
form.inline { display: inline; margin: 0; }
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-4); }

/* ---- Tabs: links (?tab=), so a tab is shareable and needs no script. */
.tabs { position: relative; display: flex; gap: 0; border-bottom: 1px solid var(--line); overflow-x: auto; overscroll-behavior-x: contain;
  scrollbar-width: none; scroll-behavior: smooth; }
.tabs::-webkit-scrollbar { display: none; }
.tabs a { flex: none; display: inline-flex; align-items: center; gap: var(--space-2); height: 40px; padding: 0 var(--space-4); margin-bottom: -1px;
  color: var(--text-2); font-weight: var(--weight-medium); border-bottom: 2px solid transparent; white-space: nowrap; }
.tabs a:hover { color: var(--text); text-decoration: none; }
.tabs a[aria-current="page"] { color: var(--text); border-bottom-color: var(--accent); }
.tabs a:first-child { padding-left: 0; }
/* A sticky tab bar stays at the top as the page scrolls; when tabs are
   hidden to a side the edge fades and an arrow scrolls to them (ui.js). */
.tabbar { position: sticky; top: var(--header); z-index: 10; background: var(--bg); min-width: 0; }
.tabbar::after { content: ""; position: absolute; top: 0; right: 0; bottom: 1px; width: 64px; pointer-events: none; opacity: 0;
  background: linear-gradient(to left, var(--bg) 45%, transparent); transition: opacity var(--dur-2) var(--ease); }
.tabbar[data-right]::after { opacity: 1; }
.tabbar .tabs-arrow { position: absolute; top: 0; right: 0; z-index: 1; width: 40px; height: 40px; padding: 0; border: 0; background: none;
  color: var(--text); font: inherit; font-size: 20px; cursor: pointer; }
/* Sub-navigation: the same tabs, smaller, for a section's pages. */
.tabs.tabs-sub a { height: 36px; font-size: var(--text-sm); }

/* Segmented control: a few choices of one setting (1M 6M 1Y; Quarterly / Annual). */
.seg { display: inline-flex; border: 1px solid var(--line-strong); border-radius: var(--radius-2); overflow: hidden; flex: none; }
.seg > a, .seg > button { display: inline-flex; align-items: center; height: 28px; padding: 0 var(--space-3); border: 0; border-left: 1px solid var(--line);
  background: var(--surface); color: var(--text-2); font: inherit; font-size: var(--text-sm); font-weight: var(--weight-medium); cursor: pointer; }
.seg > :first-child { border-left: 0; }
.seg > a:hover, .seg > button:hover { color: var(--text); text-decoration: none; }
.seg > [aria-current="page"], .seg > [aria-pressed="true"] { background: var(--accent-soft); color: var(--accent); }

/* ---- Data list: one partial, a table on a desktop and two-line rows on
   a phone, from the same data. */
.scroll { position: relative; overflow-x: auto; overscroll-behavior-x: contain; max-width: 100%; }
.table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--text-base); }
.table th, .table td { height: var(--row); padding: var(--space-2) var(--space-3); text-align: left; vertical-align: middle; border-bottom: 1px solid var(--line); }
.table thead th { height: 30px; color: var(--text-2); font-size: var(--text-xs); font-weight: var(--weight-medium); white-space: nowrap; vertical-align: bottom;
  background: var(--bg); border-bottom-color: var(--line-strong); }
.table tbody tr:hover > * { background: var(--surface-2); }
.table tbody th[scope="row"] { font-weight: var(--weight-regular); }
.table .num { text-align: right; }
/* Numbers stay on one line and line up; a long heading wraps to two lines
   instead of widening its column. */
.table td.num { white-space: nowrap; font-variant-numeric: tabular-nums; }
.table thead .th-label { display: inline-block; max-width: 11em; white-space: normal; overflow-wrap: normal; line-height: var(--leading-tight); vertical-align: bottom; }
.table td.num, .table th.num { padding-left: var(--space-4); }
.table tfoot th, .table tfoot td { font-weight: var(--weight-strong); border-top: 1px solid var(--line-strong); border-bottom: 0; }
.table .sub { display: block; color: var(--text-3); font-size: var(--text-xs); line-height: var(--leading-tight); }
.table .rule { display: block; color: var(--text-3); font-weight: var(--weight-regular); }
/* The first column (the names) stays put while the values scroll under it. */
.scroll > .table:not(.no-sticky) > * > tr > :first-child { position: sticky; left: 0; z-index: 1; background: var(--bg); box-shadow: inset -1px 0 0 var(--line); }
.scroll > .table:not(.no-sticky) tbody tr:hover > :first-child { background: var(--surface-2); }
/* Sortable headings are buttons (ui.js), or links when the server sorts. */
.table th .sort { display: inline-flex; align-items: center; gap: 2px; padding: 0; border: 0; background: none; color: inherit; font: inherit; cursor: pointer; white-space: nowrap; }
.table th.num .sort { flex-direction: row-reverse; }
.table th .sort::after { content: ""; width: 8px; opacity: 0.6; }
.table th[aria-sort="ascending"] .sort::after { content: "\2191"; }
.table th[aria-sort="descending"] .sort::after { content: "\2193"; }
.table th[aria-sort] { color: var(--text); }
.table tr.is-on > * { background: var(--pass-bg); }

.rows { display: none; list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.rows > li { border-bottom: 1px solid var(--line); }
.rows details > summary { display: grid; gap: 1px; padding: var(--space-3) 0; list-style: none; cursor: pointer; }
.rows details > summary::-webkit-details-marker { display: none; }
.rows .r1 { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); min-width: 0; }
.rows .r1 > :first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: var(--weight-medium); }
.rows .r1 > :last-child { flex: none; font-weight: var(--weight-medium); }
.rows .r2 { display: flex; gap: var(--space-2) var(--space-4); color: var(--text-2); font-size: var(--text-sm); white-space: nowrap; overflow: hidden; }
.rows .r2 > span { flex: none; }
.rows .r2 > span:last-child { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.rows .more { display: grid; gap: var(--space-3); padding: 0 0 var(--space-4); }
.rows details[open] > summary .r2 { flex-wrap: wrap; }
.kv { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2) var(--space-4); margin: 0; }
.kv > div { min-width: 0; }
.kv dt { color: var(--text-3); font-size: var(--text-xs); }
.kv dd { margin: 0; font-size: var(--text-sm); overflow-wrap: anywhere; }
@media (max-width: 560px) {
  .dlist > .scroll { display: none; }
  .dlist > .rows { display: block; }
  .scroll > .table:not(.no-sticky) > * > tr > :first-child { max-width: 46vw; white-space: normal; }
}
/* A value against its rule: clears it, close to the line, misses it. */
.pass { background: var(--pass-bg); color: var(--pass-fg); }
.near { background: var(--near-bg); color: var(--near-fg); }
.fail { color: var(--fail-fg); }
.rows .r2 .pass, .rows .r2 .near { padding: 0 3px; border-radius: var(--radius-1); }
.swatch { display: inline-block; width: 10px; height: 10px; border-radius: 2px; vertical-align: -1px; margin-right: var(--space-2); }
.swatch.pass { box-shadow: inset 0 0 0 1px var(--pass-fg); }
.swatch.near { box-shadow: inset 0 0 0 1px var(--near-fg); }

/* ---- Stat strip: key numbers as label over value, as many to a row as fit. */
.stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); gap: var(--space-3) var(--space-5); margin: 0; }
.stats > div { min-width: 0; }
.stats dt { color: var(--text-3); font-size: var(--text-xs); line-height: var(--leading-tight); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.stats dd { margin: 0; font-weight: var(--weight-medium); }
.stats dd .chg { display: inline-block; font-size: var(--text-xs); white-space: nowrap; }
.stats-more { display: none; }
.stats-toggle { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
@media (max-width: 560px) {
  .stats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
  .stats-toggle:not(:checked) ~ .stats .extra { display: none; }
  .stats-more { display: inline-flex; align-items: center; min-height: var(--tap); color: var(--accent); font-size: var(--text-sm); cursor: pointer; }
  .stats-toggle:checked ~ .stats-more .closed, .stats-toggle:not(:checked) ~ .stats-more .open { display: none; }
  .stats-toggle:focus-visible ~ .stats-more { box-shadow: var(--focus); }
}

/* A stat strip that says how many it holds (data-n, as ui-stats does) gets
   rows of equal columns: one row when they fit, else rows as even as can
   be (9 at 1024 px is 5 + 4, never 8 + 1). Hand-written strips without
   data-n fill as many columns as fit. */
@media (min-width: 561px) {
  .stats[data-n] { grid-template-columns: repeat(var(--stat-cols, 4), minmax(0, 1fr)); }
  .stats[data-n="1"] { --stat-cols: 1; }
  .stats[data-n="2"] { --stat-cols: 2; }
  .stats[data-n="3"] { --stat-cols: 3; }
  .stats[data-n="4"] { --stat-cols: 4; }
  .stats[data-n="5"] { --stat-cols: 3; }
  .stats[data-n="6"] { --stat-cols: 3; }
  .stats[data-n="7"] { --stat-cols: 4; }
  .stats[data-n="8"] { --stat-cols: 4; }
  .stats[data-n="9"] { --stat-cols: 3; }
  .stats[data-n="10"] { --stat-cols: 4; }
  .stats[data-n="11"] { --stat-cols: 4; }
  .stats[data-n="12"] { --stat-cols: 4; }
  .stats[data-n="13"] { --stat-cols: 4; }
  .stats[data-n="14"] { --stat-cols: 4; }
  .stats[data-n="15"] { --stat-cols: 4; }
  .stats[data-n="16"] { --stat-cols: 4; }
}
@media (min-width: 900px) {
  .stats[data-n="1"] { --stat-cols: 1; }
  .stats[data-n="2"] { --stat-cols: 2; }
  .stats[data-n="3"] { --stat-cols: 3; }
  .stats[data-n="4"] { --stat-cols: 4; }
  .stats[data-n="5"] { --stat-cols: 5; }
  .stats[data-n="6"] { --stat-cols: 6; }
  .stats[data-n="7"] { --stat-cols: 4; }
  .stats[data-n="8"] { --stat-cols: 4; }
  .stats[data-n="9"] { --stat-cols: 5; }
  .stats[data-n="10"] { --stat-cols: 5; }
  .stats[data-n="11"] { --stat-cols: 6; }
  .stats[data-n="12"] { --stat-cols: 6; }
  .stats[data-n="13"] { --stat-cols: 5; }
  .stats[data-n="14"] { --stat-cols: 5; }
  .stats[data-n="15"] { --stat-cols: 5; }
  .stats[data-n="16"] { --stat-cols: 6; }
}
@media (min-width: 1100px) {
  .stats[data-n="1"] { --stat-cols: 1; }
  .stats[data-n="2"] { --stat-cols: 2; }
  .stats[data-n="3"] { --stat-cols: 3; }
  .stats[data-n="4"] { --stat-cols: 4; }
  .stats[data-n="5"] { --stat-cols: 5; }
  .stats[data-n="6"] { --stat-cols: 6; }
  .stats[data-n="7"] { --stat-cols: 7; }
  .stats[data-n="8"] { --stat-cols: 8; }
  .stats[data-n="9"] { --stat-cols: 9; }
  .stats[data-n="10"] { --stat-cols: 5; }
  .stats[data-n="11"] { --stat-cols: 6; }
  .stats[data-n="12"] { --stat-cols: 6; }
  .stats[data-n="13"] { --stat-cols: 7; }
  .stats[data-n="14"] { --stat-cols: 7; }
  .stats[data-n="15"] { --stat-cols: 8; }
  .stats[data-n="16"] { --stat-cols: 8; }
}

/* ---- Badges: a word of state next to a name. */
.badge { display: inline-flex; align-items: center; height: 18px; padding: 0 6px; margin-left: var(--space-2); border-radius: var(--radius-1);
  background: var(--surface-2); color: var(--text-2); font-size: var(--text-xs); font-weight: var(--weight-medium); line-height: 1; white-space: nowrap; vertical-align: 1px; }
.badge-held { background: var(--held-bg); color: var(--held-fg); }
.badge-watched { background: transparent; box-shadow: inset 0 0 0 1px var(--accent); color: var(--accent); }
.badge-warn, .badge-stale, .badge-partial { background: var(--warn-bg); color: var(--warn-fg); }
.badge-pass { background: var(--pass-bg); color: var(--pass-fg); }
.badge-count, .badge-new { background: var(--accent); color: var(--accent-fg); border-radius: var(--radius-pill); }
.badge-bank, .badge-extra { background: transparent; box-shadow: inset 0 0 0 1px var(--line-strong); }

/* ---- Messages and states: one line of plain words. */
.notice, .alert, .warn { padding: var(--space-3) var(--space-4); border-radius: var(--radius-2); font-size: var(--text-sm); }
.notice { background: var(--ok-bg); }
.alert { background: var(--surface); color: var(--fail-fg); box-shadow: inset 3px 0 0 var(--fail-fg); }
.warn { background: var(--warn-bg); color: var(--warn-fg); }
.state { padding: var(--space-6) 0; color: var(--text-2); text-align: center; }
.state-error { color: var(--fail-fg); }
/* A cell or line still being fetched: faint, gently pulsing. */
.loading { color: var(--text-3); animation: ui-pulse 1.6s var(--ease) infinite; }
@keyframes ui-pulse { 50% { opacity: 0.45; } }
@media (prefers-reduced-motion: reduce) { .loading { animation: none; } }

/* ---- Pager */
.pager { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); padding: var(--space-3) 0; font-size: var(--text-sm); color: var(--text-2); }
.pager a { white-space: nowrap; }

/* ---- Disclosure: details with a quiet summary; Show all / Show fewer. */
details.disclosure > summary { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--accent); cursor: pointer; list-style: none; font-size: var(--text-sm); }
details.disclosure > summary::-webkit-details-marker { display: none; }
details.disclosure > summary::before { content: ""; width: 6px; height: 6px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg); transition: transform var(--dur-2) var(--ease); }
details.disclosure[open] > summary::before { transform: rotate(45deg); }
details.disclosure > .disclosure-body { padding-top: var(--space-3); }
.showall { font-size: var(--text-sm); }
.showall a { display: inline-flex; align-items: center; min-height: 32px; }

/* ---- Forms: labels over fields, stacked on a phone, inline when there is room. */
.field { display: grid; gap: var(--space-2); align-content: start; min-width: 0; margin: 0; }
.field > span, .field > label, .field-label { color: var(--text-2); font-size: var(--text-sm); font-weight: var(--weight-medium); }
.field .hint { color: var(--text-3); font-size: var(--text-xs); }
.input, .field input:not([type="checkbox"]):not([type="radio"]), .field select, .field textarea, select.input, textarea.input {
  width: 100%; min-height: 32px; padding: var(--space-2) var(--space-3); border: 1px solid var(--line-strong); border-radius: var(--radius-2);
  background: var(--surface); color: var(--text); font: inherit; font-size: var(--text-md); }
.field textarea, textarea.input { font-family: var(--font-mono); font-size: var(--text-sm); line-height: 1.5; resize: vertical; }
.input:focus, .field input:focus, .field select:focus, .field textarea:focus { border-color: var(--accent); outline: none; box-shadow: 0 0 0 3px var(--accent-soft); }
.fields { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: var(--space-4); align-items: start; }
.check { display: inline-flex; align-items: center; gap: var(--space-3); min-height: 28px; font-size: var(--text-sm); cursor: pointer; }
.check input { width: 16px; height: 16px; margin: 0; accent-color: var(--accent); }
.form-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); }
.toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.toolbar .input { width: auto; flex: 1 1 200px; min-width: 0; }
.toolbar select.input { flex: 0 1 auto; }
@media (min-width: 561px) {
  .input, .field input:not([type="checkbox"]):not([type="radio"]), .field select, .field textarea, select.input { font-size: var(--text-base); }
}
/* Filters: on a phone one line saying what is chosen and Edit; the fields
   open under it. The page sends them open and ui.js folds them on a
   phone, so without a script they are simply shown. */
details.filters > summary { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); min-height: var(--tap);
  font-size: var(--text-sm); color: var(--text-2); cursor: pointer; list-style: none; }
details.filters > summary::-webkit-details-marker { display: none; }
details.filters > summary .edit { color: var(--accent); flex: none; }
details.filters > summary .sum { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
details.filters[open] > summary .edit { visibility: hidden; }
details.filters > .filters-body { padding-top: var(--space-2); }
@media (min-width: 561px) {
  details.filters[open] > summary { display: none; }
  details.filters[open] > .filters-body { padding-top: 0; }
}

/* Picker: several choices out of many, grouped (exchanges by country). A
   field showing the choice in a few words opens a panel of checkboxes
   under it; on a phone a sheet from the bottom. A <details>, so it works
   without a script; the dimmed backdrop is the summary's own ::before, so
   a tap beside the panel closes it. */
.picker { position: relative; min-width: 0; }
.picker > summary { display: flex; align-items: center; gap: var(--space-3); cursor: pointer; list-style: none; user-select: none; }
.picker > summary::-webkit-details-marker { display: none; }
.picker > summary > span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker > summary::after { content: ""; flex: none; width: 7px; height: 7px; margin: 0 2px 3px 0; border: solid var(--text-2); border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg); transition: transform var(--dur-1) var(--ease); }
.picker[open] > summary::after { transform: rotate(-135deg); margin-bottom: -3px; }
.picker[open] > summary { border-color: var(--accent); }
.picker[open] > summary::before { content: ""; position: fixed; inset: 0; z-index: 29; cursor: default; }
.picker-panel { position: absolute; top: calc(100% + 4px); left: 0; z-index: 30; width: min(440px, calc(100vw - 24px)); max-height: min(70vh, 560px);
  overflow-y: auto; overscroll-behavior: contain; padding: 0 0 var(--space-2); background: var(--surface); border-radius: var(--radius-2); box-shadow: var(--shadow-pop); }
.picker-panel.right { left: auto; right: 0; }
.picker-inline .picker-panel { position: static; width: auto; max-height: none; margin-top: var(--space-2); box-shadow: none; border: 1px solid var(--line); }
.picker-inline[open] > summary::before { display: none; }
.picker-top { position: sticky; top: 0; z-index: 1; display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4);
  background: var(--surface); border-bottom: 1px solid var(--line); }
.picker-top .input { flex: 1; min-width: 0; }
.picker-group { margin: 0; padding: var(--space-3) var(--space-4) 0; border: 0; min-width: 0; }
.picker-group + .picker-group { border-top: 1px solid var(--line); margin-top: var(--space-2); }
.picker-group legend { float: left; width: 100%; padding: 0; font-weight: var(--weight-strong); }
.picker-group legend .check, .picker-group legend > .picker-name { font-size: var(--text-base); font-weight: var(--weight-strong); min-height: 32px; display: inline-flex; align-items: center; }
.picker-group ul { clear: both; list-style: none; margin: 0; padding: 0 0 0 var(--space-6); }
.picker-group li .check { align-items: flex-start; width: 100%; padding: var(--space-2) 0; }
.picker-group li .check input { margin-top: 2px; flex: none; }
.picker-group li .check > span { display: grid; gap: var(--space-1); min-width: 0; }
.picker-name { color: var(--text); }
.picker-note { color: var(--text-2); font-size: var(--text-xs); line-height: var(--leading-tight); }
.picker-foot:not(:has(> :not([hidden]))) { display: none; }
.picker-foot { position: sticky; bottom: calc(-1 * var(--space-2)); display: flex; justify-content: flex-end; gap: var(--space-4); margin-top: var(--space-2);
  padding: var(--space-3) var(--space-4); background: var(--surface); border-top: 1px solid var(--line); }
@media (max-width: 560px) {
  .picker[open]:not(.picker-inline) > summary::before { background: color-mix(in srgb, var(--text) 35%, transparent); }
  .picker:not(.picker-inline) .picker-panel { position: fixed; top: auto; left: 0; right: 0; bottom: 0; width: auto; max-height: 85vh; border-radius: var(--radius-2) var(--radius-2) 0 0;
    padding-bottom: env(safe-area-inset-bottom); }
  .picker-group li .check { min-height: var(--tap); }
  .picker-foot .btn { height: var(--tap); padding: 0 var(--space-6); }
}

/* ---- Charts and small graphics */
.chart { position: relative; width: 100%; min-height: 240px; }
.chart .u-legend { display: none; }
.chart .u-over { touch-action: pan-y; }
.chart-tip { position: absolute; z-index: 2; pointer-events: none; padding: var(--space-2) var(--space-3); background: var(--surface); border-radius: var(--radius-2);
  box-shadow: var(--shadow-pop); color: var(--text-2); font-size: var(--text-xs); line-height: 1.4; white-space: nowrap; }
.chart-tip b { color: var(--text); font-size: var(--text-sm); }
.legend { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); color: var(--text-2); font-size: var(--text-xs); }
.legend .key { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: var(--space-2); vertical-align: -1px; }
.legend .key.k1 { background: var(--chart-bar); }
.legend .key.k2 { background: var(--chart-2); }
.legend .key.k3 { background: var(--chart-3); }
.legend .key.k4 { background: var(--chart-1); }
.legend .key.k5 { background: var(--up); }
.legend .key.k6 { background: var(--down); }
.legend label { display: inline-flex; align-items: center; gap: var(--space-2); }
/* A sparkline keeps its width: the base rule svg { max-width: 100% } would
   let a table count it as nothing wide, so the cell came out too narrow
   and the chart and its label ran into the next column. */
.spark { display: block; flex: none; width: 84px; max-width: none; height: 22px; }
/* A sparkline with its change beside it ("+68.31 %"), one fixed width, so
   every row's chart and label line up and the cell holds both. */
.spark-line { display: inline-flex; align-items: center; gap: var(--space-2); width: calc(84px + 5.5em); white-space: nowrap; vertical-align: middle; }
.spark-line > .spark-chg { margin-left: auto; font-size: var(--text-sm); font-variant-numeric: tabular-nums; }
.table td.spark-cell { white-space: nowrap; }
.spark polyline { fill: none; stroke: var(--chart-1); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.spark.down polyline { stroke: var(--down); }
.wbar { display: block; width: 100%; min-width: 48px; height: 6px; }
.wbar rect { fill: var(--accent); }
.wbar rect.track { fill: var(--surface-2); }

/* ---- Phone */
@media (max-width: 560px) {
  .app-bar { gap: var(--space-3); padding: 0 var(--space-3); }
  .app-brand .app-family { display: none; }
  .app-spacer { display: none; }
  .app-main { padding: var(--space-3) var(--space-4) var(--space-8); gap: var(--space-5); }
  .titlebar-actions { gap: var(--space-3); }
  .btn { height: var(--tap); }
  .btn-sm { height: 32px; }
  .seg > a, .seg > button { height: 32px; }
  .menu-panel a, .menu-panel button { min-height: 44px; }
  .tabs a { height: 44px; padding: 0 var(--space-3); }
  .tabs a:first-child { padding-left: 0; }
  .section-head .aside { gap: var(--space-3); }
  .table th, .table td { padding: var(--space-2); }
  .toolbar .input, .toolbar select.input { min-height: var(--tap); }
}
.rows .sub { margin-left: var(--space-2); color: var(--text-3); font-size: var(--text-xs); font-weight: var(--weight-regular); }
.rows .badge { vertical-align: 0; }

/* Choice: one out of many, in groups (a figure out of a hundred). A plain
   select without a script; ui.js hides it behind a field that opens a
   panel with a search box (on a phone a sheet from the bottom). */
.choose { position: relative; display: block; min-width: 0; }
.choose > select { width: 100%; }
.choose.is-on > select { position: absolute; inset: 0; opacity: 0; pointer-events: none; }
.choose-btn { display: flex; align-items: center; gap: var(--space-3); width: 100%; text-align: left; cursor: pointer; }
.choose-btn > .choose-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.choose-btn > .choose-name.faint { color: var(--text-3); }
.choose-btn::after { content: ""; flex: none; width: 7px; height: 7px; margin: 0 2px 3px 0; border: solid var(--text-2); border-width: 0 1.5px 1.5px 0; transform: rotate(45deg); }
.choose-hint { flex: none; color: var(--text-3); font-size: var(--text-xs); }
.choose.is-open > .choose-btn { border-color: var(--accent); }
.choose-back { position: fixed; inset: 0; z-index: 29; }
.choose-panel { position: absolute; top: calc(100% + 4px); left: 0; z-index: 30; width: min(400px, calc(100vw - 24px)); max-height: min(60vh, 480px);
  display: flex; flex-direction: column; background: var(--surface); border-radius: var(--radius-2); box-shadow: var(--shadow-pop); overflow: hidden; }
.choose-panel.right { left: auto; right: 0; }
.choose-panel.up { top: auto; bottom: calc(100% + 4px); }
.choose-top { padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--line); }
.choose-list { flex: 1; overflow-y: auto; overscroll-behavior: contain; list-style: none; margin: 0; padding: var(--space-2) 0; }
.choose-group { padding: var(--space-3) var(--space-4) var(--space-1); color: var(--text-2); font-size: var(--text-xs); font-weight: var(--weight-strong); }
.choose-item { display: grid; grid-template-columns: 1fr auto; column-gap: var(--space-3); align-items: baseline; padding: var(--space-2) var(--space-4) var(--space-2) var(--space-6); cursor: pointer; }
.choose-item .choose-note { grid-column: 1 / -1; color: var(--text-2); font-size: var(--text-xs); line-height: var(--leading-tight); }
.choose-item[aria-selected="true"] { color: var(--accent); font-weight: var(--weight-medium); }
.choose-item.is-active { background: var(--surface-2); }
.choose-none { padding: var(--space-4); color: var(--text-2); font-size: var(--text-sm); }
@media (max-width: 560px) {
  .choose-back { background: color-mix(in srgb, var(--text) 35%, transparent); }
  .choose-panel, .choose-panel.up { position: fixed; top: auto; left: 0; right: 0; bottom: 0; width: auto; max-height: 80vh; border-radius: var(--radius-2) var(--radius-2) 0 0;
    padding-bottom: env(safe-area-inset-bottom); }
  .choose-item { min-height: var(--tap); align-content: center; }
}

/* Chips: a short ordered list of chosen things, each with a ✕, and a
   Choice to add another. The ✕ is a ticked checkbox: unticked (no
   script) the chip is struck through and dropped when the form goes. */
.chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.chip { display: inline-flex; align-items: center; gap: var(--space-2); min-height: 28px; padding: 0 var(--space-2) 0 var(--space-3);
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-pill); font-size: var(--text-sm); max-width: 100%; }
.chip-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip-x { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 50%;
  color: var(--text-2); font-size: var(--text-xs); cursor: pointer; }
.chip-x:hover { background: var(--line); color: var(--text); }
.chip-x input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.chip-x:has(input:focus-visible) { box-shadow: var(--focus); }
.chip:has(.chip-x input:not(:checked)) .chip-label { text-decoration: line-through; color: var(--text-3); }
.chips-empty { align-self: center; font-size: var(--text-sm); }
.chips-field > .choose { max-width: 280px; }
@media (max-width: 560px) {
  .chip { min-height: 32px; }
  .chip-x { width: 28px; height: 28px; }
  .chips-field > .choose { max-width: none; }
}

/* Gallery: a grid of pictures (scans, photos, a PDF's pages). Each tile is
   the picture centred on a quiet area, shown like a sheet of paper (white,
   with a soft shadow, in light and dark alike), then its name and a line
   under it. With a checkbox to pick it (over the picture's top-left
   corner): on a phone always shown; on a desktop under the pointer, with
   focus, or once any tile is picked. A picked tile gets an accent outline;
   an app's script may number the picks in the order made (data-n). */
.gallery { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.gallery-small { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); }
.tile { position: relative; display: flex; flex-direction: column; min-width: 0; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-2); }
.tile:has(.tile-pick input:checked) { outline: 2px solid var(--accent); outline-offset: -1px; }
.tile-pic { display: flex; align-items: center; justify-content: center; aspect-ratio: 3 / 4; min-height: 0; padding: var(--space-4); overflow: hidden;
  background: var(--surface-2); border-radius: calc(var(--radius-2) - 1px) calc(var(--radius-2) - 1px) 0 0; }
.tile-pic:focus-visible { box-shadow: inset 0 0 0 2px var(--accent); }
/* A sheet of paper is white in dark mode too. */
.tile-pic img { display: block; max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; background: #fff; box-shadow: var(--shadow-pop); }
.tile-gone { color: var(--text-3); font-size: var(--text-sm); text-align: center; }
.tile-body { display: grid; gap: var(--space-1); min-width: 0; padding: var(--space-3); }
.tile-name { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; overflow-wrap: anywhere;
  color: var(--text); font-weight: var(--weight-medium); line-height: var(--leading-tight); }
a.tile-name:hover { color: var(--accent); }
.tile-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-3); color: var(--text-3); font-size: var(--text-xs); }
.tile-meta .badge { margin-left: 0; }
.gallery-small .tile-pic { padding: var(--space-3); }
.gallery-small .tile-body { padding: var(--space-2) var(--space-3); }
.tile-pick { position: absolute; top: var(--space-2); left: var(--space-2); z-index: 1; display: inline-flex; align-items: center; justify-content: center;
  width: var(--tap); height: var(--tap); border-radius: var(--radius-2); background: var(--surface); box-shadow: var(--shadow-pop); cursor: pointer; }
.tile-pick input { width: 18px; height: 18px; margin: 0; accent-color: var(--accent); cursor: pointer; }
.tile-pick:has(input:focus-visible) { box-shadow: var(--focus); }
.tile-pick:not([data-n=""])[data-n]::after { content: attr(data-n); position: absolute; top: -6px; right: -6px; min-width: 18px; height: 18px; padding: 0 var(--space-2);
  border-radius: var(--radius-pill); background: var(--accent); color: var(--accent-fg); font-size: var(--text-xs); font-weight: var(--weight-strong); line-height: 18px; text-align: center; }
@media (min-width: 561px) {
  .gallery { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: var(--space-4); }
  .gallery-small { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--space-3); }
  .tile-pick { width: 32px; height: 32px; }
}
/* Where there is a pointer, a box waits until it is wanted; on a touch
   screen it stays, since there is nothing to hover. */
@media (min-width: 561px) and (hover: hover) {
  .tile-pick { opacity: 0; transition: opacity var(--dur-1) var(--ease); }
  .tile:hover .tile-pick, .tile:focus-within .tile-pick, .gallery:has(.tile-pick input:checked) .tile-pick { opacity: 1; }
}

/* Segments: a segmented control of radios, one choice of a few sent with
   the form (a scan's mode). Looks like .seg; the radio itself is hidden
   but still takes focus, and its span shows the choice and the ring.
   "wide" fills its box with equal segments; "big" is a finger's height,
   for a camera's controls. */
fieldset.seg-radio { margin: 0; padding: 0; min-width: 0; }
.seg-radio > label { position: relative; display: inline-flex; border-left: 1px solid var(--line); }
.seg-radio > legend + label { border-left: 0; }
.seg-radio > label > input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; pointer-events: none; }
.seg-radio > label > span { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); height: 32px; padding: 0 var(--space-3);
  background: var(--surface); color: var(--text-2); font-size: var(--text-sm); font-weight: var(--weight-medium); white-space: nowrap; cursor: pointer; user-select: none; }
.seg-radio > label > span svg { flex: none; width: 16px; height: 16px; }
.seg-radio > label:hover > span { color: var(--text); }
.seg-radio > label > input:checked + span { background: var(--accent-soft); color: var(--accent); }
.seg-radio > label > input:focus-visible + span { box-shadow: inset 0 0 0 2px var(--accent); }
.seg-radio.wide { display: flex; width: 100%; }
.seg-radio.wide > label { flex: 1 1 0; min-width: 0; }
.seg-radio.wide > label > span { width: 100%; overflow: hidden; text-overflow: ellipsis; }
.seg-radio.big > label > span { height: var(--tap); padding: 0 var(--space-4); font-size: var(--text-base); }
.seg-radio.big > label > span svg { width: 20px; height: 20px; }
@media (min-width: 561px) {
  .seg-radio > label > span { height: 28px; }
  .seg-radio.big > label > span { height: var(--tap); }
}
