/* Ridderholt design system, generation 2: components. Uses only the
   tokens in tokens.css. Made to be looked at: a page is a few cards, each
   with one thing to see (a number, a state, a picture) and at most one
   thing to press. Colour and shape say how something is before a word
   does; sentences wait behind a tap. Phone first: every rule that changes
   on a desktop is under min-width: 561px. */

/* A page fades into the next one, where the browser can. */
@media (prefers-reduced-motion: no-preference) { @view-transition { navigation: auto; } }

/* ---- Base */
*, *::before, *::after { box-sizing: border-box; }
/* The page scrolls up and down only: nothing wider than the window can
   pull it (and the sticky header) sideways. Something wide scrolls in its
   own box (.scroll, .tabs). clip keeps the header sticky; hidden is the
   fallback. Up and down it still stretches at the ends on a phone, so
   pulling down at the top refreshes the page (the browser's own refresh,
   and an app's around its web view). */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; overflow-x: hidden; overflow-x: clip; overscroll-behavior-x: none; }
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; overflow-x: clip; overscroll-behavior-x: none; }
a { color: var(--accent); text-decoration: none; -webkit-tap-highlight-color: transparent; }
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.015em; text-wrap: balance; }
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 header of frosted glass over the page; on a phone
   the app's sections are a dock at the bottom, under the thumb. */
.app-header { position: sticky; top: 0; z-index: 20; background: var(--glass); border-bottom: 1px solid var(--line);
  -webkit-backdrop-filter: saturate(180%) blur(16px); backdrop-filter: saturate(180%) blur(16px); }
.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-5); }
.app-brand { display: inline-flex; align-items: baseline; gap: var(--space-2); flex: none; color: var(--text); font-weight: var(--weight-strong); font-size: 17px; letter-spacing: -0.01em; }
.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-3); height: 36px; padding: 0 var(--space-4); border-radius: var(--radius-pill); color: var(--text-2);
  font-weight: var(--weight-medium); white-space: nowrap; transition: background var(--dur-1) var(--ease); }
.app-nav a:hover { background: var(--fill); color: var(--text); text-decoration: none; }
.app-nav a[aria-current="page"] { background: var(--accent-soft); color: var(--accent); }
.app-nav a .icon { width: 18px; height: 18px; }
.app-search { position: relative; flex: 1; min-width: 0; max-width: 420px; margin: 0; }
.app-search input { width: 100%; height: 40px; padding: 0 var(--space-5); border: 1px solid transparent; border-radius: var(--radius-pill);
  background: var(--fill); 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: 0 0 0 4px var(--accent-soft); background: var(--surface); }
.app-spacer { flex: 1; }
/* The menu is always at the far end, with or without a search box. */
.app-bar > .app-menu { margin-left: auto; }
.app-main { max-width: var(--page-max); margin: 0 auto; padding: var(--space-5) var(--space-5) 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); }
/* The dock: the app's sections along the bottom of a phone or a tablet, a
   picture and a word each. The header has them instead once it has room
   for them beside the search field: from 900 px, or from 1100 px when
   there are five or more. */
.app-dock { position: fixed; inset: auto 0 0 0; z-index: 20; display: flex; justify-content: space-around; gap: var(--space-1);
  height: calc(var(--dock) + env(safe-area-inset-bottom, 0px)); padding: 0 max(var(--space-3), calc((100% - 560px) / 2)) env(safe-area-inset-bottom, 0px);
  background: var(--glass); border-top: 1px solid var(--line); -webkit-backdrop-filter: saturate(180%) blur(16px); backdrop-filter: saturate(180%) blur(16px); }
.app-dock a { position: relative; flex: 1 1 0; min-width: 0; display: grid; align-content: center; justify-items: center; gap: 2px; color: var(--text-3);
  font-size: 11px; font-weight: var(--weight-medium); line-height: 1.2; transition: color var(--dur-1) var(--ease), transform var(--dur-1) var(--spring); }
.app-dock a:hover { text-decoration: none; }
.app-dock a:active { transform: scale(0.94); }
.app-dock a .icon { width: 24px; height: 24px; }
.app-dock a > span:not(.badge) { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-dock a[aria-current="page"] { color: var(--accent); }
.app-dock a .badge { position: absolute; top: 6px; left: calc(50% + 6px); margin: 0; }
body:has(> .app-dock) .app-main { padding-bottom: calc(var(--dock) + var(--space-8)); }
@media (min-width: 561px) {
  .app-search input { font-size: var(--text-base); }
  .app-main { padding: var(--space-6) var(--space-5) var(--space-8); }
  /* A page without a dock (signed out, or one section) has its links in
     the header from here. */
  body:not(:has(> .app-dock)) .app-nav { display: flex; align-items: center; gap: var(--space-1); }
  body:not(:has(> .app-dock)) .app-menu .menu-nav { display: none; }
}
@media (min-width: 900px) {
  .app-search { max-width: 360px; }
  /* Up to four sections fit in the header from here. */
  body:not(:has(.app-nav > a:nth-child(5))) .app-nav { display: flex; align-items: center; gap: var(--space-1); }
  body:not(:has(.app-nav > a:nth-child(5))) .app-menu .menu-nav { display: none; }
  body:not(:has(.app-nav > a:nth-child(5))) > .app-dock { display: none; }
  body:not(:has(.app-nav > a:nth-child(5))) .app-main { padding-bottom: var(--space-8); }
}
@media (min-width: 1100px) {
  .app-nav { display: flex; align-items: center; gap: var(--space-1); }
  .app-menu .menu-nav { display: none; }
  .app-dock { display: none; }
  body:has(> .app-dock) .app-main { padding-bottom: var(--space-8); }
}
@media (max-width: 899px) and (min-width: 561px) { .app-nav a { padding: 0 var(--space-3); } }
/* While the dock shows, the menu holds only what the dock doesn't (the
   dock has the first five sections). */
@media (max-width: 1099px) {
  body:has(> .app-dock) .app-menu .menu-nav > a:nth-child(-n+5) { display: none; }
  body:has(> .app-dock) .app-menu .menu-nav:not(:has(> a:nth-child(6))) { display: none; }
}
/* Five sections or more: the header has their words only (the pictures are
   the dock's), closer together until the screen is wide, so the search
   field keeps its room. */
.app-nav:has(> a:nth-child(5)) a .icon { display: none; }
@media (max-width: 1279px) { .app-nav:has(> a:nth-child(5)) a { padding: 0 var(--space-3); } }

/* The account menu (and, on a phone without a dock, the whole nav): a
   <details>, so it opens without JavaScript. */
.app-menu > summary { display: inline-flex; align-items: center; gap: var(--space-2); height: 40px; padding: 0 var(--space-4); border-radius: var(--radius-pill);
  background: var(--fill); color: var(--text); font-weight: var(--weight-medium); cursor: pointer; list-style: none; white-space: nowrap; }
.app-menu > summary::-webkit-details-marker { display: none; }
.app-menu > summary .avatar { margin-left: calc(-1 * var(--space-3)); }
.app-menu > summary .badge { margin-left: 0; }
/* On a phone the button is the picture alone. */
@media (max-width: 560px) {
  .app-menu > summary { gap: var(--space-2); padding: 0 6px; font-size: 0; }
  .app-menu > summary .avatar { margin: 0; }
  .app-menu > summary .badge { font-size: var(--text-xs); }
}
.app-menu > summary:hover, .app-menu[open] > summary { background: var(--fill-2); }
.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: var(--control); height: var(--control); border-radius: var(--radius-pill);
  background: var(--fill); color: var(--text-2); transition: background var(--dur-1) var(--ease); }
.menu-dots:hover, details.menu[open] > .menu-dots { background: var(--fill-2); color: var(--text); }
.menu-dots svg { width: 18px; height: 18px; 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); background: var(--surface); border-radius: var(--radius-3); box-shadow: var(--shadow-pop); }
.menu-panel.left { right: auto; left: 0; }
/* Opened where it would go past the window's edge (a ⋯ that wrapped to
   the left of a phone), ui.js turns it to open the other way. */
.menu-panel.is-flipped { right: auto; left: 0; }
.menu-panel.left.is-flipped { left: auto; right: 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: 40px;
  padding: 0 var(--space-4); border: 0; border-radius: var(--radius-2); background: none; color: var(--text); font: inherit; font-weight: var(--weight-medium); text-align: left; cursor: pointer; }
.menu-panel a:hover, .menu-panel button:hover { background: var(--fill); text-decoration: none; }
.menu-panel a[aria-current="page"] { color: var(--accent); font-weight: var(--weight-medium); }
.menu-panel form { margin: 0; }
/* An item with an icon: the icon, then its words. */
.menu-panel a > span, .menu-panel button > span { display: inline-flex; align-items: center; gap: var(--space-3); }
.menu-panel .icon { width: 18px; height: 18px; color: var(--text-3); }
.menu-panel .danger .icon { color: inherit; }
.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: 32px; padding: 0 var(--space-3); border: 0; border-radius: var(--radius-pill); background: var(--fill);
  font-size: var(--text-sm); color: var(--text-2); }
.menu-theme button[aria-pressed="true"] { background: var(--accent-soft); 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); max-width: 68ch; color: var(--text-2); font-size: var(--text-sm); }
/* A badge that starts the line sits on the edge, like the lines under it. */
.titlebar-sub > .badge:first-child { margin-left: 0; }
.titlebar-actions { display: flex; align-items: center; gap: var(--space-4); flex: none; }
.titlebar-actions form { margin: 0; }
/* With a crumb over the name, the actions sit on the name's line, not the
   crumb's (on a phone they wrap under the name instead). */
@media (min-width: 561px) {
  .titlebar:has(> .titlebar-main > .crumb) > .titlebar-actions { margin-top: calc(var(--text-sm) * var(--leading) + var(--space-1) - 2px); }
}
.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-4); 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; }
.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); }
/* An empty list or a state in a section reads as a line of the section,
   on its left edge, not as a page's message in the middle. */
.section .state, .dlist .state { padding: var(--space-3) 0; text-align: left; font-size: var(--text-sm); }
.panel { background: var(--surface); border-radius: var(--radius-3); box-shadow: var(--shadow-1); 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: round, and big enough to hit without aiming. One primary
   a page: it wears the accent and a glow, so it is found without reading.
   Everything else is quiet: a neutral pill, the accent's tint (.btn-tonal)
   for the second thing, or a text link. A button gives a little when it is
   pressed. */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-3); height: var(--control); padding: 0 var(--space-5);
  border: 0; border-radius: var(--radius-pill); background: var(--fill); color: var(--text);
  font: inherit; font-weight: var(--weight-strong); white-space: nowrap; cursor: pointer; text-decoration: none; -webkit-tap-highlight-color: transparent;
  transition: background var(--dur-1) var(--ease), filter var(--dur-1) var(--ease), transform var(--dur-1) var(--spring), box-shadow var(--dur-2) var(--ease); }
.btn:hover { background: var(--fill-2); text-decoration: none; }
.btn:active { transform: scale(0.97); }
.btn .icon { width: 18px; height: 18px; margin-left: -2px; }
.btn-primary { background: var(--accent-grad); color: var(--accent-fg); box-shadow: var(--glow); }
.btn-primary:hover { background: var(--accent-grad); filter: brightness(1.08); }
.btn-tonal { background: var(--accent-soft); color: var(--accent); }
.btn-tonal:hover { background: var(--accent-soft); filter: brightness(0.96); }
.btn-danger { background: var(--bad-bg); color: var(--bad-fg); }
.btn-danger:hover { background: var(--bad-bg); filter: brightness(0.96); }
.btn-lg { height: 52px; padding: 0 var(--space-6); font-size: var(--text-md); }
.btn-sm { height: var(--control-sm); padding: 0 var(--space-4); font-size: var(--text-sm); }
/* A button that is only a picture: round. Its name is its aria-label. */
.btn-icon { width: var(--control); padding: 0; }
.btn-icon.btn-sm { width: var(--control-sm); }
.btn-icon.btn-lg { width: 52px; }
.btn-icon .icon { margin: 0; width: 20px; height: 20px; }
.btn-link { display: inline; height: auto; padding: 0; border: 0; background: none; color: var(--accent); font: inherit; font-weight: var(--weight-medium); cursor: pointer; text-align: left; }
.btn-link:hover { text-decoration: underline; text-underline-offset: 2px; background: none; }
.btn-link.danger { color: var(--bad-fg); }
form.inline { display: inline; margin: 0; }
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-4); }
/* The page's one action, floating over the bottom corner of a phone, where
   the thumb is (over the dock, when there is one). On a desktop it sits
   where it is written. */
@media (max-width: 560px) {
  .fab { position: fixed; right: var(--space-5); bottom: calc(var(--space-5) + env(safe-area-inset-bottom, 0px)); z-index: 19; height: 56px; padding: 0 var(--space-6); font-size: var(--text-md); }
  .fab.btn-icon { width: 56px; padding: 0; }
  body:has(> .app-dock) .fab { bottom: calc(var(--dock) + var(--space-4) + env(safe-area-inset-bottom, 0px)); }
}

/* ---- Tabs: links (?tab=), so a tab is shareable and needs no script.
   Each is a pill; the one you are on is filled. The row scrolls sideways
   only, so a finger moving up or down scrolls the page, not the row. */
.tabs { position: relative; display: flex; gap: var(--space-2); overflow-x: auto; overflow-y: hidden;
  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: 36px; padding: 0 var(--space-5); border-radius: var(--radius-pill);
  color: var(--text-2); font-weight: var(--weight-medium); white-space: nowrap; transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease); }
.tabs a:hover { background: var(--fill); color: var(--text); text-decoration: none; }
.tabs a[aria-current="page"] { background: var(--text); color: var(--bg); }
.tabs a .icon { width: 16px; height: 16px; }
/* A sticky tab bar stays at the top as the page scrolls; where tabs are
   hidden to a side, that 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; padding: var(--space-2) 0; margin: calc(-1 * var(--space-2)) 0; }
.tabbar::before, .tabbar::after { content: ""; position: absolute; top: 0; bottom: 0; z-index: 1; width: 64px; pointer-events: none; opacity: 0;
  transition: opacity var(--dur-2) var(--ease); }
.tabbar::before { left: 0; background: linear-gradient(to right, var(--bg) 45%, transparent); }
.tabbar::after { right: 0; background: linear-gradient(to left, var(--bg) 45%, transparent); }
.tabbar[data-left]::before, .tabbar[data-right]::after { opacity: 1; }
.tabbar .tabs-arrow { position: absolute; top: var(--space-2); right: 0; z-index: 2; width: 36px; height: 36px; padding: 0; border: 0; background: none;
  color: var(--text); font: inherit; font-size: 20px; cursor: pointer; }
.tabbar .tabs-arrow-left { right: auto; left: 0; }
/* Sub-navigation: the same tabs, smaller and tinted, for a section's pages. */
.tabs.tabs-sub a { height: 30px; padding: 0 var(--space-4); font-size: var(--text-sm); }
.tabs.tabs-sub a[aria-current="page"] { background: var(--accent-soft); color: var(--accent); }

/* Segmented control: a few choices of one setting (1M 6M 1Y; Quarterly /
   Annual), in one track; the chosen one is a raised pill. */
.seg { display: inline-flex; gap: 2px; padding: 3px; border-radius: var(--radius-pill); background: var(--fill); flex: none; }
.seg > a, .seg > button { display: inline-flex; align-items: center; height: 28px; padding: 0 var(--space-4); border: 0; border-radius: var(--radius-pill);
  background: none; color: var(--text-2); font: inherit; font-size: var(--text-sm); font-weight: var(--weight-medium); cursor: pointer; }
.seg > a:hover, .seg > button:hover { color: var(--text); text-decoration: none; }
.seg > [aria-current="page"], .seg > [aria-pressed="true"] { background: var(--surface); color: var(--text); box-shadow: var(--shadow-1); }

/* ---- 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%; background: var(--surface); border-radius: var(--radius-3); box-shadow: var(--shadow-1); }
.table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--text-sm); }
.table th, .table td { height: var(--row); padding: var(--space-2) var(--space-4); text-align: left; vertical-align: middle; border-bottom: 1px solid var(--line); }
.table thead th { height: 36px; color: var(--text-3); font-size: var(--text-xs); font-weight: var(--weight-medium); white-space: nowrap; vertical-align: middle;
  background: var(--surface); }
.table tbody tr:last-child > * { border-bottom: 0; }
.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(--surface); }
/* Its edge shows once values have scrolled under it (ui.js marks the box). */
.scroll.is-scrolled > .table:not(.no-sticky) > * > tr > :first-child { 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; background: var(--surface); border-radius: var(--radius-3); box-shadow: var(--shadow-1); overflow: hidden; }
.rows > li + li { border-top: 1px solid var(--line); }
.rows details > summary { display: grid; gap: 2px; padding: var(--space-4) var(--space-5); 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; display: inline-flex; align-items: center; gap: var(--space-2); font-weight: var(--weight-medium); }
.rows .r1 > :last-child .badge { margin-left: 0; }
.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 var(--space-5) var(--space-5); }
.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(140px, 1fr)); gap: var(--space-3); margin: 0; }
.stats > div { min-width: 0; padding: var(--space-4) var(--space-5); background: var(--surface); border-radius: var(--radius-2); box-shadow: var(--shadow-1); }
.stats dt { color: var(--text-3); font-size: var(--text-xs); font-weight: var(--weight-medium); line-height: var(--leading-tight); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.stats dd { margin: var(--space-1) 0 0; font-size: var(--text-lg); font-weight: var(--weight-strong); letter-spacing: -0.01em; line-height: var(--leading-tight); overflow: hidden; text-overflow: ellipsis; }
.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(var(--stat-cols, 2), minmax(0, 1fr)); gap: var(--space-3); }
  /* An odd few: the first one is as wide as the two under it. */
  .stats[data-n="3"] > :first-child, .stats[data-n="5"] > :first-child { grid-column: 1 / -1; }
  /* Even rows on a phone too: 4 is 2 + 2, never 3 + 1, and 8 is four rows
     of two, since four in a row cut their names short. */
  .stats[data-n="1"] { --stat-cols: 1; }
  .stats[data-n="2"], .stats[data-n="4"], .stats[data-n="8"], .stats[data-n="10"] { --stat-cols: 2; }
  .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). Each column is at most 15rem
   wide, so a short strip sits together on the left instead of stretching
   across a wide page. Hand-written strips without data-n fill as many
   columns as fit. */
@media (min-width: 561px) {
  /* As many columns as the count asks for, when each is wide enough to
     hold its number; in a narrow box, as many as fit. */
  .stats[data-n] { grid-template-columns: repeat(auto-fit, minmax(max(148px, calc((100% - (var(--stat-cols, 4) - 1) * var(--space-3)) / var(--stat-cols, 4))), 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: 3; }
  .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: 4; }
  .stats[data-n="12"] { --stat-cols: 4; }
  .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: 4; }
}
@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: 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: 4; }
}

/* ---- Badges: a word of state next to a name. */
.badge { display: inline-flex; align-items: center; gap: var(--space-2); height: 22px; padding: 0 var(--space-3); margin-left: var(--space-2); border-radius: var(--radius-pill);
  background: var(--fill); color: var(--text-2); font-size: var(--text-xs); font-weight: var(--weight-strong); 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); }
/* A count that wants attention: amber when something has waited too long,
   red when something is late or has gone wrong. */
.badge-count-warn, .badge-count-alert { border-radius: var(--radius-pill); }
.badge-count-warn { background: var(--warn-strong); color: var(--warn-strong-fg); }
.badge-count-alert { background: var(--bad); color: #fff; }
.badge-bad { background: var(--bad-bg); color: var(--bad-fg); }
/* A count is a small circle, however short the number. */
.badge-count, .badge-new, .badge-count-warn, .badge-count-alert { min-width: 22px; justify-content: center; padding: 0 6px; }
.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-4) var(--space-5); border-radius: var(--radius-2); font-weight: var(--weight-medium); }
.notice { background: var(--good-bg); color: var(--good-fg); }
.alert { background: var(--bad-bg); color: var(--bad-fg); }
.warn { background: var(--caution-bg); color: var(--caution-fg); }
.state { padding: var(--space-7) 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; } }

/* ---- Busy: the bar along the top while the next page comes, and a
   button being sent (a spinner over its words, which keep its width). */
.ui-progress { position: fixed; inset: 0 0 auto 0; height: 3px; z-index: 1000; pointer-events: none; transform-origin: left; transform: scaleX(0); opacity: 0; background: var(--accent-grad); }
.ui-progress.is-on { opacity: 1; animation: ui-progress 8s cubic-bezier(.1, .7, .2, 1) forwards; }
@keyframes ui-progress { 0% { transform: scaleX(0); } 10% { transform: scaleX(.35); } 100% { transform: scaleX(.92); } }
.is-busy { position: relative; color: transparent !important; pointer-events: none; }
.is-busy::after { content: ""; position: absolute; inset: 0; margin: auto; width: 1.1em; height: 1.1em; border-radius: 50%; border: 2px solid var(--text-2); border-right-color: transparent; animation: ui-spin .7s linear infinite; }
.btn-primary.is-busy::after { border-color: var(--accent-fg); border-right-color: transparent; }
@keyframes ui-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .ui-progress.is-on { animation: none; transform: scaleX(.6); } .is-busy::after { animation-duration: 2s; } }

/* ---- 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); }

/* ---- Fold: a card that opens. A section most people don't need every
   time (a table by year, a form, settings): its name, one figure that sums
   it up, and the rest behind a tap. The whole top row is the tap target.
   The arrow is before the name, so the figure ends on the card's right
   edge, in line with the figures inside. Folds stacked in .folds are one
   card with a line between them, like one list. */
details.fold { background: var(--surface); border-radius: var(--radius-3); box-shadow: var(--shadow-1); }
details.fold > summary { display: flex; align-items: center; gap: var(--space-4); min-height: 56px; padding: var(--space-3) var(--space-5); cursor: pointer; list-style: none;
  border-radius: var(--radius-3); -webkit-tap-highlight-color: transparent; transition: background var(--dur-1) var(--ease); }
details.fold > summary::-webkit-details-marker { display: none; }
details.fold > summary::before { content: ""; flex: none; width: 8px; height: 8px; margin: 0 var(--space-1) 0 2px; border-right: 2px solid var(--text-3); border-bottom: 2px solid var(--text-3);
  border-radius: 1px; transform: rotate(-45deg); transition: transform var(--dur-2) var(--spring); }
details.fold[open] > summary::before { transform: translateY(-2px) rotate(45deg); }
details.fold > summary:hover { background: var(--surface-2); }
details.fold[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
details.fold > summary > :first-child { flex: 1 1 auto; min-width: 0; margin: 0; font-size: var(--text-md); font-weight: var(--weight-strong); line-height: var(--leading-tight); letter-spacing: -0.01em; }
/* A fold that is a person's (a message, a comment): their avatar, then
   their name. */
details.fold > summary > :first-child:has(> .avatar) { display: flex; align-items: center; gap: var(--space-3); }
details.fold > summary .avatar { width: 32px; height: 32px; font-size: 12px; }
details.fold > summary > .fold-sum:has(> .status) { overflow: visible; }
details.fold > summary > .fold-sum { flex: 0 1 auto; min-width: 0; color: var(--text-2); font-weight: var(--weight-medium); font-variant-numeric: tabular-nums; text-align: right;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
details.fold > summary > .fold-sum.up { color: var(--up); }
details.fold > summary > .fold-sum.down { color: var(--down); }
details.fold > summary > .fold-sum.pass { background: none; color: var(--good-fg); }
details.fold > summary > .fold-sum.fail { color: var(--bad-fg); }
details.fold > .fold-body { padding: var(--space-5); border-top: 1px solid var(--line); }
details.fold > .fold-body > :first-child { margin-top: 0; }
details.fold > .fold-body > :last-child { margin-bottom: 0; }
/* A table or a list inside a fold is part of the card, not a card in it. */
.fold-body .scroll, .fold-body .rows, .card .scroll, .card .rows { background: none; border-radius: 0; box-shadow: none; }
.folds { display: grid; background: var(--surface); border-radius: var(--radius-3); box-shadow: var(--shadow-1); overflow: hidden; }
.folds > details.fold { background: none; border-radius: 0; box-shadow: none; }
.folds > details.fold > summary { border-radius: 0; }
.folds > details.fold + details.fold { border-top: 1px solid var(--line); }
@media (max-width: 560px) {
  details.fold > summary { padding: var(--space-3) var(--space-4); }
  details.fold > .fold-body { padding: var(--space-4); }
}

/* ---- Figures: a short list of numbers, each with its name on the left
   and the number on the right, so the numbers stand in one column. */
dl.figures { display: grid; margin: 0; }
dl.figures > div { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); padding: var(--space-2) 0; border-bottom: 1px solid var(--line); }
dl.figures > div:last-child { border-bottom: 0; }
dl.figures dt { min-width: 0; color: var(--text-2); }
dl.figures dd { margin: 0; font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
dl.figures > div.total dt, dl.figures > div.total dd { color: var(--text); font-weight: var(--weight-strong); }
/* Two columns of them on a wide screen, where one would leave each name
   far from its number. */
@media (min-width: 900px) {
  dl.figures.two { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--space-8); }
  dl.figures.two > div:nth-last-child(2):nth-child(odd) { border-bottom: 0; }
}

/* ---- About: a page's or a section's small print, folded away: what the
   numbers mean and where they come from, one short fact a line. Quieter
   than a disclosure, since it is nothing to do, only something to read. */
details.about { max-width: 68ch; color: var(--text-2); font-size: var(--text-sm); }
details.about > summary { display: inline-flex; align-items: center; gap: var(--space-2); min-height: 28px; color: var(--text-3); cursor: pointer; list-style: none; }
details.about > summary::-webkit-details-marker { display: none; }
details.about > summary::before { content: "i"; display: inline-grid; place-items: center; flex: none; width: 14px; height: 14px; border: 1px solid currentColor; border-radius: 50%;
  font-size: 10px; font-weight: var(--weight-strong); line-height: 1; }
details.about > summary:hover, details.about[open] > summary { color: var(--text-2); }
details.about > ul { display: grid; gap: var(--space-2); margin: var(--space-1) 0 var(--space-2); padding: 0; list-style: none; }
details.about > ul > li { position: relative; padding-left: calc(14px + var(--space-2)); }
details.about > ul > li::before { content: ""; position: absolute; left: 5px; top: .62em; width: 4px; height: 4px; border-radius: 50%; background: var(--text-3); }
@media (max-width: 560px) { details.about > summary { min-height: 32px; } }
.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: var(--tap); padding: var(--space-3) var(--space-4); border: 1px solid transparent; border-radius: var(--radius-2);
  background: var(--fill); color: var(--text); font: inherit; font-size: var(--text-md); transition: background var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease); }
.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); background: var(--surface); outline: none; box-shadow: 0 0 0 4px var(--accent-soft); }
.input::placeholder, .field input::placeholder, .field textarea::placeholder { color: var(--text-3); }
/* Dates and times: iOS draws them at a width and height of their own,
   wider than a column on a phone and taller when empty; without its look
   they keep to the field like the other inputs, the value on the left. */
.input:is([type="date"], [type="time"], [type="datetime-local"], [type="month"], [type="week"]),
.field input:is([type="date"], [type="time"], [type="datetime-local"], [type="month"], [type="week"]) { display: block; min-width: 0; max-width: 100%; text-align: left; }
@supports (-webkit-touch-callout: none) {
  .input:is([type="date"], [type="time"], [type="datetime-local"], [type="month"], [type="week"]),
  .field input:is([type="date"], [type="time"], [type="datetime-local"], [type="month"], [type="week"]) { -webkit-appearance: none; appearance: none; }
}
input::-webkit-date-and-time-value { min-height: 1.5em; margin: 0; text-align: left; }
/* An empty date or time is faint like a placeholder (ui.js marks it). */
input.is-empty:not(:focus), input.is-empty:not(:focus)::-webkit-datetime-edit { color: var(--text-3); }
/* Selects: the Choice's chevron, the value where an input's text is, and
   nothing chosen (the empty option) faint like a placeholder. */
.field select, select.input { -webkit-appearance: none; appearance: none; padding-right: calc(var(--space-4) + 14px);
  background-image: linear-gradient(45deg, transparent calc(50% - .75px), var(--text-2) calc(50% - .75px), var(--text-2) calc(50% + .75px), transparent calc(50% + .75px)),
    linear-gradient(135deg, transparent calc(50% - .75px), var(--text-2) calc(50% - .75px), var(--text-2) calc(50% + .75px), transparent calc(50% + .75px));
  background-size: 5px 5px; background-repeat: no-repeat; background-position: right 17px center, right 12px center; }
.field select:has(option[value=""]:checked), select.input:has(option[value=""]:checked) { color: var(--text-3); }
.field select option, select.input option { color: var(--text); }
/* As many to a row as fit, four at most: a row of fields lines up with a
   whole-width field over or under it. */
.fields { display: grid; grid-template-columns: repeat(auto-fill, minmax(max(160px, calc((100% - 3 * var(--space-4)) / 4)), 1fr)); gap: var(--space-4); align-items: start; }
.fields > * { min-width: 0; }
/* A checkbox among fields sits on the inputs' line, under the labels. */
.fields > .check { align-self: start; min-height: calc(var(--text-md) * var(--leading) + 2 * var(--space-2) + 2px); margin-top: calc(var(--text-sm) * var(--leading) + var(--space-2)); }
/* Upload: a button that opens the file chooser; ui.js sends the form as
   soon as files are chosen. Without a script, a Send button shows. */
.upload { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin: 0; }
.upload-pick { position: relative; }
.upload-pick > input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; clip-path: inset(50%); }
.upload-pick:has(> input:focus-visible) { box-shadow: var(--focus); }
.upload.is-on .upload-send { display: none; }
/* Segments with their label shown over them, like a field's. */
.seg-field > .seg { justify-self: start; max-width: 100%; }
.check { display: inline-flex; align-items: center; gap: var(--space-3); min-height: var(--control-sm); cursor: pointer; }
.check input { flex: none; width: 20px; height: 20px; margin: 0; accent-color: var(--accent); }
/* A switch: a checkbox for something that is on or off right away. */
.check input.switch { -webkit-appearance: none; appearance: none; position: relative; width: 44px; height: 26px; border-radius: var(--radius-pill); background: var(--surface-3); cursor: pointer;
  transition: background var(--dur-2) var(--ease); }
.check input.switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: transform var(--dur-2) var(--spring); }
.check input.switch:checked { background: var(--good); }
.check input.switch:checked::after { transform: translateX(18px); }
.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 { min-height: var(--control); font-size: var(--text-base); }
  .fields > .check { min-height: var(--control); }
}
/* 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; }
/* A chart that starts on the page's left edge, with its scale on the
   right like a list's numbers: the canvas reaches 12 px into the page's
   margin so the first label under the plot has room, and the app pads its
   plot by the same. */
.chart.chart-edge { width: calc(100% + 12px); margin-left: -12px; }
.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-4); }
  .app-brand .app-family { display: none; }
  .app-spacer { display: none; }
  .app-main { padding-top: var(--space-4); padding-left: var(--space-4); padding-right: var(--space-4); gap: var(--space-5); }
  .titlebar-actions { gap: var(--space-3); }
  /* Under the title on a phone, the row starts with the button: a button
     never sits to the right of a link at the left edge. */
  .titlebar-actions > .btn-primary, .titlebar-actions > form:has(> .btn-primary) { order: -1; }
  .btn { height: var(--tap); }
  .btn-sm { height: 36px; }
  .btn-lg { height: 56px; }
  .btn-icon { width: var(--tap); }
  .btn-icon.btn-sm { width: 36px; }
  .seg > a, .seg > button { height: 32px; }
  .menu-panel a, .menu-panel button { min-height: 44px; }
  .tabs a { height: 38px; padding: 0 var(--space-5); }
  .section-head .aside { gap: var(--space-3); }
  .table th, .table td { padding: var(--space-2) var(--space-3); }
  .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). */
/* ---- Picture viewer ([data-viewer], built by ui.js): the whole picture on
   a dark ground over the page, ✕ in the corner, the others a swipe or an
   arrow away. Dark in both themes, as photos are looked at. */
.viewer { position: fixed; inset: 0; z-index: 100; display: grid; place-items: center; background: rgb(0 0 0 / 0.94);
  overscroll-behavior: contain; touch-action: manipulation; }
.viewer[hidden] { display: none; }
.viewer:focus { outline: none; }
.viewer-img { display: block; max-width: 100vw; max-height: 100vh; max-height: 100dvh; object-fit: contain; -webkit-user-select: none; user-select: none; }
.viewer button { position: absolute; display: inline-flex; align-items: center; justify-content: center; width: var(--tap); height: var(--tap); padding: 0;
  border: 0; border-radius: 50%; background: rgb(255 255 255 / 0.14); color: #fff; cursor: pointer; }
.viewer button:hover { background: rgb(255 255 255 / 0.24); }
.viewer button[hidden] { display: none; }
.viewer button svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.viewer-close { top: calc(var(--space-3) + env(safe-area-inset-top)); right: var(--space-3); }
.viewer-prev, .viewer-next { top: 50%; transform: translateY(-50%); }
.viewer-prev { left: var(--space-3); }
.viewer-next { right: var(--space-3); }
.viewer-count { position: absolute; left: 0; right: 0; bottom: calc(var(--space-4) + env(safe-area-inset-bottom)); margin: 0; text-align: center;
  color: rgb(255 255 255 / 0.75); font-size: var(--text-sm); pointer-events: none; }
.viewer-count:empty { display: none; }
/* On a phone the others are a swipe away, so no arrows over the picture. */
@media (pointer: coarse) { .viewer .viewer-prev, .viewer .viewer-next { display: none; } }
html.viewer-open { overflow: hidden; }

.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); }
}

/* ==== Generation 2: the parts made for looking. A page built from these
   says what it has to say before anything on it is read. */

/* ---- Icons: line drawings in the colour of the text around them (the
   template function icon). An icon stands where a word would have. */
.icon { display: inline-block; flex: none; width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
  vertical-align: -0.22em; max-width: none; }
.icon-sm { width: 16px; height: 16px; }
.icon-lg { width: 28px; height: 28px; }

/* ---- Cards: a thing, or one answer. At most: what kind (an icon), a
   label of a word or two, the number or the state, one short line, one
   button. A card that leads somewhere lifts when it is pointed at.
   data-tone says how it is: good, caution, bad. One card a page may be the
   hero: it wears the accent. Partials ui-card and ui-cards. */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 220px), 1fr)); gap: var(--space-4); margin: 0; padding: 0; list-style: none; }
.card { position: relative; display: grid; align-content: start; gap: var(--space-3); min-width: 0; padding: var(--space-5); background: var(--surface); border-radius: var(--radius-3);
  box-shadow: var(--shadow-1); color: var(--text); transition: transform var(--dur-2) var(--spring), box-shadow var(--dur-2) var(--ease); }
.card:has(> .card-link) { cursor: pointer; }
.card:has(> .card-link):hover { box-shadow: var(--shadow-2); transform: translateY(-2px); }
.card:has(> .card-link):active { transform: scale(0.985); }
/* The card's link covers the whole card; a button on it stays on top. */
.card-link { color: inherit; }
.card-link:hover { text-decoration: none; }
.card-link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.card .btn, .card form, .card details { position: relative; z-index: 1; }
.card-top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3); min-height: 40px; }
.card-icon { display: grid; place-items: center; flex: none; width: 40px; height: 40px; border-radius: var(--radius-2); background: var(--accent-soft); color: var(--accent); }
.card-icon .icon { width: 22px; height: 22px; }
.card-label { color: var(--text-3); font-size: var(--text-xs); font-weight: var(--weight-medium); line-height: var(--leading-tight); }
.card-title { font-size: var(--text-md); font-weight: var(--weight-strong); line-height: var(--leading-tight); letter-spacing: -0.01em; overflow-wrap: anywhere; }
.card-value { font-size: var(--text-num); font-weight: var(--weight-strong); line-height: 1.1; letter-spacing: -0.02em; overflow-wrap: anywhere; }
.card-value .unit { margin-left: 4px; color: var(--text-3); font-size: var(--text-base); font-weight: var(--weight-medium); letter-spacing: 0; }
.card-sub { color: var(--text-2); font-size: var(--text-sm); }
.card-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-top: var(--space-2); }
.card[data-tone="good"] .card-icon { background: var(--good-bg); color: var(--good-fg); }
.card[data-tone="caution"] .card-icon { background: var(--caution-bg); color: var(--caution-fg); }
.card[data-tone="bad"] .card-icon { background: var(--bad-bg); color: var(--bad-fg); }
.card.hero { background: var(--accent-grad); color: var(--accent-fg); box-shadow: var(--glow); }
.card.hero .card-label, .card.hero .card-sub, .card.hero .card-value .unit { color: inherit; opacity: 0.86; }
.card.hero .card-icon { background: rgba(255, 255, 255, 0.2); color: inherit; }
.card.hero .spark polyline { stroke: currentColor; }
.card .spark { width: 100%; height: 36px; }
@media (min-width: 561px) { .card.wide { grid-column: span 2; } }
@media (max-width: 560px) {
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
  .card { padding: var(--space-4); }
  .card.wide, .card.hero, .cards > :only-child { grid-column: 1 / -1; }
  /* An odd one out at the end is as wide as the two over it. */
  .cards > .card:last-child:nth-child(odd) { grid-column: 1 / -1; }
  .card-value { font-size: 28px; }
  .card.hero .card-value { font-size: var(--text-num); }
}

/* ---- Items: a list of things you open (a mail, a task, a document).
   Each row: a picture that says what or who, a name, one line, and what
   matters at the end (a time, a number, a state). The whole row is the
   link. Unread is bold with a dot. Partial ui-items. */
.items { margin: 0; padding: 0; list-style: none; background: var(--surface); border-radius: var(--radius-3); box-shadow: var(--shadow-1); overflow: hidden; }
.items > li { position: relative; }
.items > li + li::before { content: ""; position: absolute; top: 0; right: 0; left: calc(var(--space-5) + 40px + var(--space-4)); border-top: 1px solid var(--line); }
.item { position: relative; display: flex; align-items: center; gap: var(--space-4); min-height: 64px; padding: var(--space-3) var(--space-5); color: var(--text);
  transition: background var(--dur-1) var(--ease); }
.item:has(.item-link):hover { background: var(--surface-2); }
.item-link { color: inherit; }
.item-link:hover { text-decoration: none; }
.item-link::after { content: ""; position: absolute; inset: 0; }
.item .btn, .item form, .item details { position: relative; z-index: 1; }
.item-lead { display: grid; place-items: center; flex: none; width: 40px; height: 40px; border-radius: 50%; background: var(--fill); color: var(--text-2); }
.item-lead[data-tone="good"] { background: var(--good-bg); color: var(--good-fg); }
.item-lead[data-tone="caution"] { background: var(--caution-bg); color: var(--caution-fg); }
.item-lead[data-tone="bad"] { background: var(--bad-bg); color: var(--bad-fg); }
.item-lead[data-tone="accent"] { background: var(--accent-soft); color: var(--accent); }
.item-main { flex: 1 1 auto; min-width: 0; display: grid; gap: 1px; }
.item-title { font-weight: var(--weight-medium); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.item-sub { color: var(--text-2); font-size: var(--text-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.item-sub b { color: var(--text); font-weight: var(--weight-regular); }
.item-trail { flex: none; display: grid; justify-items: end; gap: var(--space-2); color: var(--text-3); font-size: var(--text-xs); white-space: nowrap; }
.item-trail .badge { margin: 0; }
.item.is-unread .item-title, .item.is-unread .item-sub b { font-weight: var(--weight-strong); }
.item.is-unread::before { content: ""; position: absolute; top: 50%; left: 5px; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); transform: translateY(-50%); }
@media (max-width: 560px) {
  .item { gap: var(--space-3); padding: var(--space-3) var(--space-4); }
  .items > li + li::before { left: calc(var(--space-4) + 40px + var(--space-3)); }
}

/* ---- Avatar: initials on a quiet colour, the same for the same name (the
   template function avatar). */
.avatar { display: inline-grid; place-items: center; flex: none; width: 40px; height: 40px; border-radius: 50%; background: var(--tint-1); color: var(--tint-1-fg);
  font-size: 14px; font-weight: var(--weight-strong); letter-spacing: 0.02em; line-height: 1; }
.avatar[data-c="2"] { background: var(--tint-2); color: var(--tint-2-fg); }
.avatar[data-c="3"] { background: var(--tint-3); color: var(--tint-3-fg); }
.avatar[data-c="4"] { background: var(--tint-4); color: var(--tint-4-fg); }
.avatar[data-c="5"] { background: var(--tint-5); color: var(--tint-5-fg); }
.avatar[data-c="6"] { background: var(--tint-6); color: var(--tint-6-fg); }
.avatar-sm { width: 28px; height: 28px; font-size: 11px; }

/* ---- Status: how something is, as a coloured dot and one word. Green
   is fine, amber wants a look, red is wrong, grey is off; a busy one
   breathes. Partial ui-status. */
.status { display: inline-flex; align-items: center; gap: var(--space-3); height: 26px; padding: 0 var(--space-4) 0 var(--space-3); border-radius: var(--radius-pill);
  background: var(--fill); color: var(--text-2); font-size: var(--text-sm); font-weight: var(--weight-strong); line-height: 1; white-space: nowrap; }
.status::before { content: ""; flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--text-3); }
.status[data-tone="good"] { background: var(--good-bg); color: var(--good-fg); }
.status[data-tone="good"]::before { background: var(--good); }
.status[data-tone="caution"] { background: var(--caution-bg); color: var(--caution-fg); }
.status[data-tone="caution"]::before { background: var(--caution); }
.status[data-tone="bad"] { background: var(--bad-bg); color: var(--bad-fg); }
.status[data-tone="bad"]::before { background: var(--bad); }
.status[data-tone="busy"] { background: var(--accent-soft); color: var(--accent); }
.status[data-tone="busy"]::before { background: var(--accent); animation: ui-pulse 1.4s var(--ease) infinite; }
@media (prefers-reduced-motion: reduce) { .status[data-tone="busy"]::before { animation: none; } }

/* ---- Meter and ring: how far along, or how much of the whole. A bar
   under a number, or a ring around one. Partials ui-meter and ui-ring. */
progress.meter { -webkit-appearance: none; appearance: none; display: block; width: 100%; height: 8px; border: 0; border-radius: var(--radius-pill); background: var(--surface-3);
  overflow: hidden; color: var(--accent); }
progress.meter::-webkit-progress-bar { background: var(--surface-3); border-radius: var(--radius-pill); }
progress.meter::-webkit-progress-value { background: var(--accent-grad); border-radius: var(--radius-pill); }
progress.meter::-moz-progress-bar { background: var(--accent); border-radius: var(--radius-pill); }
progress.meter[data-tone="good"]::-webkit-progress-value { background: var(--good); }
progress.meter[data-tone="good"]::-moz-progress-bar { background: var(--good); }
progress.meter[data-tone="caution"]::-webkit-progress-value { background: var(--caution); }
progress.meter[data-tone="caution"]::-moz-progress-bar { background: var(--caution); }
progress.meter[data-tone="bad"]::-webkit-progress-value { background: var(--bad); }
progress.meter[data-tone="bad"]::-moz-progress-bar { background: var(--bad); }
.card.hero progress.meter, .card.hero progress.meter::-webkit-progress-bar { background: rgba(255, 255, 255, 0.25); }
.card.hero progress.meter::-webkit-progress-value { background: currentColor; }
.card.hero progress.meter::-moz-progress-bar { background: currentColor; }
.ring { position: relative; display: inline-grid; place-items: center; flex: none; width: 56px; height: 56px; }
.ring svg { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; transform: rotate(-90deg); }
.ring circle { fill: none; stroke-width: 3.6; }
.ring .ring-track { stroke: var(--surface-3); }
.ring .ring-fill { stroke: var(--accent); stroke-linecap: round; }
.ring[data-tone="good"] .ring-fill { stroke: var(--good); }
.ring[data-tone="caution"] .ring-fill { stroke: var(--caution); }
.ring[data-tone="bad"] .ring-fill { stroke: var(--bad); }
.ring > b { position: relative; display: grid; place-items: center; font-size: 13px; font-weight: var(--weight-strong); }
.ring > b .icon { width: 20px; height: 20px; stroke-width: 2.4; }
.ring[data-tone="good"] > b .icon { color: var(--good-fg); }
.ring-lg { width: 88px; height: 88px; }
.ring-lg > b { font-size: var(--text-lg); }

/* ---- Empty: nothing here yet. A picture, a few words, and the one thing
   to do about it. Partial ui-empty. */
.empty { display: grid; justify-items: center; gap: var(--space-4); padding: var(--space-8) var(--space-5); text-align: center; }
.empty-icon { display: grid; place-items: center; width: 72px; height: 72px; border-radius: 50%; background: var(--accent-soft); color: var(--accent); }
.empty-icon .icon { width: 32px; height: 32px; }
.empty h2 { font-size: var(--text-lg); }
.empty p { max-width: 36ch; color: var(--text-2); }

/* ---- Skeleton: the shape of what is coming, while it comes. */
.skeleton { display: block; height: 1em; border-radius: var(--radius-1); background: linear-gradient(90deg, var(--fill) 25%, var(--fill-2) 37%, var(--fill) 63%); background-size: 400% 100%;
  animation: ui-shimmer 1.4s ease infinite; }
@keyframes ui-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; } }

/* ---- Signing in: one card, the app's name, one button. Every app has
   this page, so it is here and not in each app. */
.signin { display: grid; gap: var(--space-5); padding: var(--space-7) var(--space-6); background: var(--surface); border-radius: var(--radius-3); box-shadow: var(--shadow-2); text-align: center; }
.signin h1 { font-size: var(--text-xl); }
.google-button { display: flex; align-items: center; justify-content: center; gap: var(--space-4); width: 100%; height: 48px; padding: 0 var(--space-5);
  border: 1px solid #747775; border-radius: var(--radius-pill); background: #ffffff; color: #1f1f1f; font-weight: var(--weight-strong);
  transition: transform var(--dur-1) var(--spring), background var(--dur-1) var(--ease); }
.google-button:hover { background: #f2f2f2; text-decoration: none; }
.google-button:active { transform: scale(0.98); }
.google-mark { flex: none; }
[data-theme="dark"] .google-button { background: #131314; border-color: #8e918f; color: #e3e3e3; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .google-button { background: #131314; border-color: #8e918f; color: #e3e3e3; } }

/* ---- Letting an assistant in (mcpauth's consent page): who asks, as
   what, and two buttons. */
.mcpauth-consent { display: grid; gap: var(--space-4); }
.mcpauth-consent dl { display: grid; grid-template-columns: auto 1fr; gap: var(--space-2) var(--space-4); margin: 0; }
.mcpauth-consent dt { color: var(--text-3); }
.mcpauth-consent dd { margin: 0; overflow-wrap: anywhere; }
.mcpauth-note, .mcpauth-warning { padding: var(--space-4) var(--space-5); border-radius: var(--radius-2); font-size: var(--text-sm); }
.mcpauth-note { background: var(--fill); }
.mcpauth-warning { background: var(--caution-bg); color: var(--caution-fg); }
.mcpauth-consent form { display: flex; gap: var(--space-3); }
.mcpauth-consent button { display: inline-flex; align-items: center; justify-content: center; height: var(--tap); padding: 0 var(--space-6);
  border: 0; border-radius: var(--radius-pill); background: var(--fill); color: var(--text); font: inherit; font-weight: var(--weight-strong); cursor: pointer; }
.mcpauth-consent button:hover { background: var(--fill-2); }
.mcpauth-consent button[value="allow"] { flex: 1; background: var(--accent-grad); color: var(--accent-fg); box-shadow: var(--glow); }
.mcpauth-consent button[value="allow"]:hover { filter: brightness(1.08); }
