.deal-reseller { font-size: .85rem; margin-top: 4px; }

/* Revenue categories reuse theme-aware brand roles; labels use the existing
   text colours and retain their documented light/dark contrast. */
:root {
  --revenue-1: var(--color-brand);
  --revenue-2: var(--color-slate);
  --revenue-3: var(--color-success);
  --revenue-4: var(--color-danger);
  --revenue-5: var(--color-brand-hover);
  --revenue-6: var(--color-text-dim);
  --revenue-7: var(--color-success-ink);
  --revenue-8: var(--color-warning-ink);
}
.revenue-intro > .hint { max-width: 95ch; }
.revenue-section .card-head { flex-wrap: wrap; gap: 16px; }
.revenue-section .card-head p { margin: 6px 0 0; }
.revenue-section .revenue-panel {
  min-width: 0;
  padding: 24px;
  margin-top: 24px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}
.revenue-panel > .card-head {
  border-bottom: 1px solid var(--color-border);
  padding-bottom: 18px;
  margin-bottom: 20px;
}
.revenue-section .revenue-panel h3 {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 750;
  line-height: 1.2;
  letter-spacing: -.015em;
}
.revenue-section .revenue-grid { align-items: stretch; margin-top: 24px; gap: 24px; }
.revenue-grid .revenue-panel { margin-top: 0; }
.revenue-grid .card-head { min-height: 86px; }
.revenue-section label { display: flex; flex-direction: column; gap: 4px; font-size: .85rem; font-weight: 600; }
.revenue-section select, .revenue-section input { max-width: 100%; min-width: 0; width: auto; }
.revenue-controls { display: flex; flex-wrap: wrap; gap: 12px; margin: 16px 0; }
.revenue-controls label { flex: 1 1 125px; }
.revenue-controls { padding: 14px; background: var(--color-surface-muted); border-radius: var(--radius-sm); }
.revenue-total { font-size: 1.1rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.revenue-plot { min-height: 180px; }
.revenue-scroll { overflow-x: auto; max-width: 100%; padding-bottom: 8px; }
.revenue-scroll svg { display: block; min-width: 100%; }
.revenue-gridline { stroke: var(--color-border); stroke-width: 1; }
.revenue-axis { fill: var(--color-text-dim); font: 12px var(--font-sans); }
.revenue-scroll rect:focus { outline: 2px solid var(--color-focus); outline-offset: 2px; }
.revenue-pie { width: 190px; height: 190px; border-radius: 50%; margin: 24px auto; border: 1px solid var(--color-border); }
.revenue-legend { padding: 0; margin: 16px 0; list-style: none; display: flex; flex-wrap: wrap; gap: 12px 24px; }
.revenue-legend li { display: flex; align-items: center; gap: 8px; font-size: .85rem; flex-wrap: wrap; }
.revenue-legend strong, .revenue-legend small { font-variant-numeric: tabular-nums; }
.revenue-swatch { display: inline-block; width: 12px; height: 12px; border-radius: 3px; flex: 0 0 12px; }
.revenue-data { border-top: 1px solid var(--color-border); padding-top: 14px; margin-top: 20px; }
.revenue-data summary { cursor: pointer; color: var(--color-brand); font-weight: 600; font-size: .85rem; }
.revenue-data .table-shell { margin-top: 12px; }
.revenue-country-list { list-style: none; padding: 0; max-height: 310px; overflow-y: auto; }
.revenue-country-list li { display: flex; justify-content: space-between; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--color-border); }
@media (max-width: 600px) {
  .revenue-section .revenue-panel { padding: 20px 16px; }
  .revenue-section .revenue-panel h3 { font-size: 1.3rem; }
  .revenue-grid .card-head { min-height: 0; }
  .revenue-controls label { flex-basis: 100%; }
  .revenue-section .card-head > label { width: 100%; }
}

/* Forentrics dashboard — visual language adapted from hemovision.be
   Palette extracted from production CSS on www.hemovision.be (Oxygen builder).
   Primary deep teal #08636c matches the Forentrics shield/network logo.        */

:root {
  /* Lets native form controls (date/time pickers, scrollbars, etc.) render
     with matching chrome. Theme here is a manual toggle (data-theme), not
     prefers-color-scheme, so this is pinned to "light" and flipped
     explicitly in the [data-theme="dark"] override below — never "light
     dark", which would let the OS preference fight the site's own toggle. */
  color-scheme: light;

  /* brand — deep teal of the Forentrics mark */
  --color-brand: #08636c;
  --color-brand-hover: #0f2833;
  --color-brand-soft: #eaf2f3;
  --color-brand-line: #cfe0e2;

  /* secondary slate — hemovision body / link tone */
  --color-slate: #566d8f;
  --color-slate-soft: #eff4f7;

  /* warm gold accent — used sparingly for highlights / CTAs */
  --color-accent: #d9a647;
  --color-accent-hover: #b88a32;
  --color-accent-soft: #f6ecd2;

  /* warm cream — hemovision hero / section background */
  --color-cream: #e4e0db;

  /* semantic */
  --color-success: #65bec2;
  --color-success-ink: #0f5a5d;
  --color-success-soft: #e6f4f5;
  --color-warning: #d9a647;
  --color-warning-ink: #7a5a17;
  --color-warning-soft: #fbf1d8;
  --color-danger: #b94545;
  --color-danger-ink: #842c2c;
  --color-danger-soft: #f6e3e0;
  --color-danger-line: #e7c2bd;
  --color-success-line: #b8dfe1;

  /* ink on a filled brand / danger / accent surface. White on the light
     theme's deep teal; the dark theme's brand is a light teal, so the ink
     flips to near-black there (white on #4fb3bd was 2.5:1). */
  --color-on-brand: #ffffff;
  --color-on-danger: #ffffff;
  --color-on-accent: #2a1f08;

  /* focus: an outline, not a halo, so it reads on any surface */
  --color-focus: var(--color-brand);
  --color-focus-ring: rgba(8, 99, 108, 0.22);

  /* surfaces */
  --color-bg: #fafafa;
  --color-surface: #ffffff;
  --color-surface-muted: #f5f7f8;

  /* text */
  --color-text: #0f2833;
  --color-text-muted: #566d8f;
  /* The quietest text that still reads: 4.65:1 on the table-header surface,
     the darkest ground it sits on. It was #8a9bb0 — 2.6:1 — and it carries
     every column header, result count, hint and date in the app. */
  --color-text-dim: #5f7188;

  /* borders */
  --color-border: #e1e6ea;
  --color-border-strong: #cfd7de;

  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;

  /* The top bar's height; the section bar sticks one rule below it. */
  --app-nav-h: 64px;

  --shadow-sm: 0 1px 2px rgba(15, 40, 51, 0.05);
  --shadow-md: 0 4px 14px rgba(15, 40, 51, 0.08);
  --shadow-lg: 0 14px 36px rgba(15, 40, 51, 0.12);

  --font-sans: "Catamaran", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
}

/* ---------- dark theme ----------
   Toggled by setting data-theme="dark" on <html> (see _nav.html / base.html).
   Selection is remembered in localStorage; this override wins on specificity
   over the :root defaults above, no !important needed. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --color-brand: #4fb3bd;
  --color-brand-hover: #7ecdd5;
  --color-brand-soft: rgba(79, 179, 189, 0.14);
  --color-brand-line: rgba(79, 179, 189, 0.35);

  --color-slate: #9fb3cc;
  --color-slate-soft: rgba(86, 109, 143, 0.18);

  --color-accent: #d9a647;
  --color-accent-hover: #eabb63;
  --color-accent-soft: rgba(217, 166, 71, 0.16);

  --color-cream: #e4e0db;

  --color-success: #6fd0da;
  --color-success-ink: #8fe0e3;
  --color-success-soft: rgba(101, 190, 194, 0.15);
  --color-warning: #d9a647;
  --color-warning-ink: #e8c176;
  --color-warning-soft: rgba(217, 166, 71, 0.15);
  --color-danger: #e0807f;
  --color-danger-ink: #f0a8a8;
  --color-danger-soft: rgba(185, 69, 69, 0.2);
  --color-danger-line: rgba(224, 128, 127, 0.45);
  --color-success-line: rgba(111, 208, 218, 0.4);

  --color-on-brand: #0d161d;
  --color-on-danger: #0d161d;
  --color-on-accent: #2a1f08;

  --color-focus-ring: rgba(79, 179, 189, 0.35);

  --color-bg: #0d161d;
  --color-surface: #16232b;
  --color-surface-muted: #1c2b34;

  --color-text: #e7edf0;
  --color-text-muted: #9db2c4;
  --color-text-dim: #8398a8;

  --color-border: #263540;
  --color-border-strong: #34424d;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-md: 0 4px 14px rgba(0, 0, 0, 0.35);
  --shadow-lg: 0 14px 36px rgba(0, 0, 0, 0.45);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.5;
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
  transition: background-color 0.15s ease, color 0.15s ease;
}
/* The page is laid out on the whole window, scrollbar or not, and a
   scrollbar — when the page has one — sits over its right edge, the way an
   overlay scrollbar (macOS, phones) always does. Otherwise a page long enough
   to scroll is ~15px narrower than one that is not, and everything centred —
   the nav, the shell — jumped sideways between them. Reserving the room
   instead (scrollbar-gutter) left an empty strip on short pages that no
   element can paint, cut out of the end of the top bar.

   What the scrollbar covers is the edge of the page's own padding. Wider
   content scrolls inside its own box (tables, the pipeline board), so
   clipping the page sideways hides nothing; base.html undoes the sideways
   scroll script could still cause. (Clipping at the root with `contain:
   paint` would avoid that too, but turns every position: fixed element into
   one that scrolls with the page.) */
html { overflow-x: hidden; }
body { width: 100vw; }

a {
  color: var(--color-brand);
  text-decoration: none;
  transition: color 0.15s ease;
}
a:hover { color: var(--color-brand-hover); }

/* ---------- focus, selection, and the parts the browser draws ----------

   Links, buttons and summaries relied entirely on the browser's own focus
   ring — present, but a different colour and shape per browser and theme.
   One designed outline instead, drawn only for keyboard focus. Form fields
   keep their own border-and-ring treatment (see input:focus below).       */
:where(a, button, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
/* The skip link's target: focusable so the jump lands, but not a control. */
main[tabindex="-1"]:focus { outline: none; }

::selection { background: var(--color-brand-line); color: var(--color-text); }

.skip-link {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 100;
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  background: var(--color-brand);
  color: var(--color-on-brand);
  font-weight: 700;
  transform: translateY(-160%);
  transition: transform 0.15s ease;
}
.skip-link:focus { transform: none; }
/* a:hover is more specific than .skip-link and would repaint it dark on teal. */
.skip-link:hover,
.skip-link:focus { color: var(--color-on-brand); }

/* Present for assistive technology, absent on screen. */
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
/* Components set display (.field is flex), which beats the UA's own
   [hidden] rule; the attribute has to win for scripts that toggle it. */
[hidden] { display: none !important; }

h1, h2, h3, h4 {
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--color-text);
  margin: 0 0 0.5em;
}
h1 { font-size: 2rem; line-height: 1.15; }
h2 { font-size: 1.35rem; }
h3 { font-size: 1.1rem; }

.eyebrow {
  display: inline-block;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--color-brand);
  margin-bottom: 0.5rem;
}

.muted { color: var(--color-text-muted); }
.dim { color: var(--color-text-dim); }
.mono { font-family: var(--font-mono); font-size: 0.92em; }

/* Country flags (flag-icons, linked in base.html) — sized down from the
   library's own 1.333em default and given a hairline border, since a flag
   sitting directly on a plain background with no edge reads as a stray
   rectangle rather than a flag. */
.fi {
  width: 1.1em;
  height: 0.8em;
  vertical-align: -0.05em;
  border-radius: 2px;
  box-shadow: 0 0 0 1px var(--color-border);
}

/* ---------- layout ---------- */

/* ---------- top bar ----------

   One component, two workspaces, and they must not look alike: the console
   that can cancel a lab's licence used to be pixel-identical to that lab's
   own portal. The staff bar is inked in the mark's own dark with cream type
   and a Staff tag; the customer bar stays light and names the organisation.
   Everything the bar paints comes from the --nav-* roles below, remapped per
   workspace. The dropdowns hanging off the bar keep the page's own tokens —
   they are surfaces, not part of the bar. */

.app-nav {
  --nav-bg: var(--color-surface);
  --nav-ink: var(--color-text);
  --nav-link: var(--color-text-muted);
  --nav-link-hover: var(--color-brand);
  --nav-current: var(--color-brand);
  --nav-current-bg: var(--color-brand-soft);
  --nav-edge: var(--color-border);
  --nav-focus: var(--color-focus);
  background: var(--nav-bg);
  border-bottom: 1px solid var(--nav-edge);
  position: sticky;
  top: 0;
  z-index: 30;
}
.app-nav[data-workspace="staff"] {
  --nav-bg: #0f2833;
  --nav-ink: var(--color-cream);
  --nav-link: #aebdc7;          /* 7.9:1 on the ink */
  --nav-link-hover: #ffffff;
  --nav-current: #ffffff;
  --nav-current-bg: rgba(228, 224, 219, 0.14);
  --nav-edge: #0f2833;
  --nav-focus: #7ecdd5;         /* 8.4:1 on the ink */
}
/* In the dark theme every surface is near-black, so ink stops being a
   difference; the console moves to a deep teal instead. */
:root[data-theme="dark"] .app-nav[data-workspace="staff"] {
  --nav-bg: #0c3239;
  --nav-edge: #16464e;
}

.app-nav-inner {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 24px;
  height: var(--app-nav-h);
  display: flex;
  align-items: center;
  gap: 28px;
}
.app-nav .workspace {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  flex: 0 1 auto;
}
.app-nav .brand {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
  font-weight: 800;
  font-size: 1.15rem;
  color: var(--nav-ink);
  letter-spacing: 0.01em;
}
.app-nav .brand:hover { color: var(--nav-ink); }
.app-nav .brand .brand-mark {
  height: 32px;
  width: auto;
  flex: 0 0 auto;
}
/* The mark draws with --color-text and --color-brand; on the ink those
   would vanish, so the staff bar hands it the dark theme's pair. */
.app-nav[data-workspace="staff"] .brand {
  --color-text: var(--color-cream);
  --color-brand: #4fb3bd;
}
.app-nav .workspace-tag {
  flex: 0 0 auto;
  padding: 2px 7px 1px;
  border-radius: 4px;
  background: var(--color-cream);
  color: #0f2833;               /* ink on cream, 11.6:1 */
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.5;
}
/* The organisation the workspace belongs to — it used to be an eyebrow on
   every customer page instead. It gives way first as the bar narrows:
   truncated, then set aside while five links need the room, then back
   beside the menu once they fold into it. */
.app-nav .workspace-org {
  min-width: 0;
  max-width: 36ch;
  padding-left: 12px;
  border-left: 1px solid var(--nav-edge);
  color: var(--color-text-muted);
  font-weight: 600;
  font-size: 0.92rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.app-nav .section-nav { display: flex; gap: 4px; flex: 1 0 auto; }
.app-nav .section-nav a {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  color: var(--nav-link);
  font-weight: 600;
  font-size: 0.92rem;
  white-space: nowrap;
}
/* Hover is colour; current is colour, weight and a pill. They used to be the
   same rule, so sweeping the mouse along the bar lit a second "current". */
.app-nav .section-nav a:hover { color: var(--nav-link-hover); }
.app-nav .section-nav a.active {
  background: var(--nav-current-bg);
  color: var(--nav-current);
  font-weight: 700;
}
/* Reserve the bold width in every label, so the current section's heavier
   weight does not shift its neighbours from page to page. */
.nav-label { display: inline-flex; flex-direction: column; }
.nav-label::after {
  content: attr(data-label) / "";
  height: 0;
  overflow: hidden;
  visibility: hidden;
  font-weight: 700;
  user-select: none;
  pointer-events: none;
}
.app-nav .section-nav a:focus-visible,
.app-nav .brand:focus-visible,
.app-nav .nav-menu > summary:focus-visible,
.app-nav .account-menu > summary:focus-visible {
  outline-color: var(--nav-focus);
}
/* Gold means "renews soon" everywhere in the app (.renews-soon, the warning
   badge); here it marks the Licences link when a licence the viewer holds
   does. The darker rim keeps the dot at 3:1 on white and on the pill. */
.nav-flag {
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-left: 6px;
  border-radius: 50%;
  background: var(--color-accent);
  border: 1px solid var(--color-accent-hover);
  flex: 0 0 auto;
  align-self: flex-start;
  margin-top: 2px;
}

/* ---------- section bar (second nav level) ----------

   The top bar names the workspace and its sections; this one names the pages
   inside the selected section — or, inside one record, the trail back to its
   list. Two levels rather than one deep dropdown because the staff area has
   six sections with their own list/dashboard pairs, and a dropdown hides
   exactly the thing you are trying to move between. Sticks under the top
   bar, so both stay visible while a long table scrolls.                     */

.app-subnav {
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-border);
  position: sticky;
  /* Under the top bar and its 1px rule, not over it. */
  top: calc(var(--app-nav-h) + 1px);
  z-index: 29;
}
.app-subnav-inner {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 24px;
  height: 46px;
  display: flex;
  align-items: stretch;
  gap: 18px;
}
.app-subnav .section-name {
  align-self: center;
  font-weight: 700;
  font-size: 0.76rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-dim);
  flex: 0 0 auto;
}
/* Tabs, seated on the bar's rule: the current tab's underline replaces the
   border beneath it instead of floating 7px above it. */
.app-subnav .page-tabs {
  position: relative;
  display: flex;
  gap: 4px;
  flex: 1;
  min-width: 0;
}
.app-subnav .page-tabs a {
  display: flex;
  align-items: center;
  margin-bottom: -1px;
  padding: 0 12px;
  border-bottom: 2px solid transparent;
  color: var(--color-text-muted);
  font-weight: 600;
  font-size: 0.88rem;
  white-space: nowrap;
}
.app-subnav .page-tabs a:hover { color: var(--color-brand); }
.app-subnav .page-tabs a.active {
  color: var(--color-text);
  border-bottom-color: var(--color-brand);
}
.app-subnav .page-tabs a:focus-visible { outline-offset: -3px; }

/* The trail inside a record. The list it came from, any step between, and
   the record itself — which truncates before anything else does. */
.app-subnav .trail {
  display: flex;
  align-items: center;
  min-width: 0;
  flex: 1;
}
.app-subnav .trail ol {
  display: flex;
  align-items: center;
  min-width: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}
.app-subnav .trail li {
  display: flex;
  align-items: center;
  min-width: 0;
  font-size: 0.88rem;
  font-weight: 600;
}
.app-subnav .trail li:first-child { flex: 0 0 auto; }
.app-subnav .trail li + li::before {
  content: "";
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  margin: 0 12px 0 8px;
  border-right: 1.5px solid var(--color-text-dim);
  border-bottom: 1.5px solid var(--color-text-dim);
  transform: rotate(-45deg);
}
.app-subnav .trail a,
.app-subnav .trail [aria-current] {
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Padded to the bar's height, so the way back is a 44px target. */
.app-subnav .trail a { padding-block: 12px; color: var(--color-text-muted); }
.app-subnav .trail a:hover {
  color: var(--color-brand);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.app-subnav .trail [aria-current] { color: var(--color-text); }

/* ---------- record tabs (third nav level, inside one record) ----------

   The two nav bars locate you in the application; these locate you inside a
   single record. Anchored links, not JavaScript panels — each tab is a real
   URL, so it can be linked to, opened in a new tab and reloaded after a
   form post lands on it.                                                    */

.record-tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--color-border);
  margin-bottom: 20px;
}
.record-tabs a {
  padding: 10px 16px;
  color: var(--color-text-muted);
  font-weight: 600;
  font-size: 0.92rem;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.record-tabs a:hover { color: var(--color-brand); }
.record-tabs a.active {
  color: var(--color-brand);
  border-bottom-color: var(--color-brand);
}
.record-tabs .tab-count {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--color-bg);
  color: var(--color-text-muted);
  font-size: 0.78rem;
  font-weight: 700;
}
.record-tabs a.active .tab-count {
  background: var(--color-brand-soft);
  color: var(--color-brand);
}

/* ---------- definition list for record detail ----------

   Read-only facts about a record, paired label/value. Distinct from .field,
   which is an editable form control and carries its own spacing.            */

.detail-list {
  display: grid;
  grid-template-columns: minmax(140px, max-content) 1fr;
  gap: 10px 20px;
  margin: 0;
}
.detail-list dt {
  color: var(--color-text-muted);
  font-size: 0.86rem;
  font-weight: 600;
}
.detail-list dd {
  margin: 0;
  font-size: 0.92rem;
  overflow-wrap: anywhere;
}
.detail-list dd.empty { color: var(--color-text-muted); }

/* ---------- placeholder for a section not built yet ---------- */

.placeholder {
  text-align: center;
  padding: 56px 24px;
}
.placeholder .placeholder-phase {
  display: inline-block;
  margin-bottom: 14px;
}
.placeholder h1 { margin-bottom: 8px; }
.placeholder p {
  color: var(--color-text-muted);
  max-width: 52ch;
  margin: 0 auto 18px;
}

/* ---------- theme toggle switch ----------

   A row in the account menu, not a slot in the bar: it is set once. The
   whole row is the label, so the target is the row, not the 24px track. */

.theme-toggle {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  cursor: pointer;
  user-select: none;
}
.theme-toggle input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
/* Off is slate, not the border grey it was — that track was 1.5:1 against
   the menu, a switch you could barely see was there. */
.theme-toggle .switch-track {
  position: relative;
  width: 44px;
  height: 24px;
  border-radius: 999px;
  background: var(--color-text-muted);
  transition: background 0.2s ease;
  flex: 0 0 auto;
}
/* faint fixed sun/moon marks in the track, revealed as the thumb passes over them */
.theme-toggle .switch-track .track-icon {
  position: absolute;
  top: 50%;
  width: 12px;
  height: 12px;
  transform: translateY(-50%);
  color: var(--color-surface);
  opacity: 0.9;
}
.theme-toggle .switch-track .track-icon.icon-sun { left: 5px; }
.theme-toggle .switch-track .track-icon.icon-moon { right: 5px; }
.theme-toggle .switch-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--color-surface);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  transition: transform 0.2s ease;
  display: flex;
  align-items: center;
  justify-content: center;
}
.theme-toggle .switch-thumb .icon {
  position: absolute;
  width: 13px;
  height: 13px;
  transition: opacity 0.15s ease, transform 0.25s ease;
}
.theme-toggle .switch-thumb .icon-sun { color: var(--color-text-muted); opacity: 1; transform: rotate(0deg) scale(1); }
.theme-toggle .switch-thumb .icon-moon { color: var(--color-brand); opacity: 0; transform: rotate(45deg) scale(0.5); }
.theme-toggle input:checked + .switch-track {
  background: var(--color-brand);
}
.theme-toggle input:checked + .switch-track .switch-thumb {
  transform: translateX(20px);
}
.theme-toggle input:checked + .switch-track .switch-thumb .icon-sun {
  opacity: 0;
  transform: rotate(-45deg) scale(0.5);
}
.theme-toggle input:checked + .switch-track .switch-thumb .icon-moon {
  opacity: 1;
  transform: rotate(0deg) scale(1);
}
/* The same outline every other control gets. It was a 3px halo in
   --color-brand-line, 1.4:1 on white: keyboard focus you could not see. */
.theme-toggle input:focus-visible + .switch-track {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

/* Account menu — plain <details>/<summary>, so the disclosure itself needs no
   JavaScript; its links leave the page, so the open state never persists.
   Inherited from the one-bar nav's "My organisation" dropdown that phase 1
   replaced: same mechanism, re-pointed at the user chip, which is why the
   menu hangs off the right edge rather than the left. */
.app-nav .account-menu { position: relative; flex: 0 0 auto; margin-left: auto; }
.app-nav .account-menu > summary {
  /* A 4px inset on every side of the 28px avatar keeps the pill at 38px and
     makes the disc sit concentric in the capsule rather than crowding one end. */
  padding: 4px 10px 4px 4px;
  border-radius: 999px;
  border: 1px solid var(--nav-edge);
  color: var(--nav-link);
  font-weight: 600;
  font-size: 0.88rem;
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
  user-select: none;
}
.app-nav[data-workspace="staff"] .account-menu > summary { border-color: rgba(228, 224, 219, 0.22); }
.app-nav .account-name {
  max-width: 18ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.app-nav .account-menu > summary::-webkit-details-marker { display: none; }
.app-nav .account-menu > summary::after {
  content: "";
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  margin: -4px 2px 0 2px;
  opacity: 0.7;
}
.app-nav .account-menu > summary:hover,
.app-nav .account-menu[open] > summary,
.app-nav .account-menu > summary.active {
  border-color: var(--color-brand-line);
  background: var(--nav-current-bg);
  color: var(--nav-current);
}
.app-nav[data-workspace="staff"] .account-menu > summary:hover,
.app-nav[data-workspace="staff"] .account-menu[open] > summary,
.app-nav[data-workspace="staff"] .account-menu > summary.active {
  border-color: rgba(228, 224, 219, 0.4);
}
.app-nav .account-menu .dropdown-menu {
  position: absolute;
  top: calc(100% + 6px);
  /* Anchored right: the chip sits at the end of the bar, so a left-anchored
     menu would run off the viewport. Sized to its content, not the chip — a
     chip-wide menu broke staff addresses at the hyphen. */
  right: 0;
  width: max-content;
  min-width: 220px;
  max-width: min(320px, calc(100vw - 32px));
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  z-index: 40;
}
.app-nav .account-menu .dropdown-head {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 10px 10px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--color-border);
  color: var(--color-text-muted);
  font-size: 0.82rem;
  font-weight: 500;
}
.app-nav .account-menu .dropdown-head strong {
  color: var(--color-text);
  font-size: 0.9rem;
}
.app-nav .account-menu .dropdown-head .mono { overflow-wrap: anywhere; }
/* The form wrapping "Sign out" must not become a layout box of its own —
   logout stays a POST, but the button has to sit in the menu like the link. */
.app-nav .account-menu .dropdown-menu form { display: contents; }
.app-nav .account-menu .dropdown-menu a,
.app-nav .account-menu .dropdown-menu button,
.app-nav .account-menu .theme-toggle {
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  font-weight: 600;
  font-size: 0.9rem;
  text-align: left;
  font-family: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}
.app-nav .account-menu .dropdown-menu a:hover,
.app-nav .account-menu .dropdown-menu a.active,
.app-nav .account-menu .dropdown-menu button:hover,
.app-nav .account-menu .theme-toggle:hover {
  background: var(--color-brand-soft);
  color: var(--color-brand);
}
/* Sign out is the one entry that ends something; a rule sets it apart. */
.app-nav .account-menu .dropdown-menu button {
  margin-top: 4px;
  border-top: 1px solid var(--color-border);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  padding-top: 10px;
}

/* ---------- dev account switcher ----------
   DEV_LOGIN only (app/routers/dev.py). Floats in a corner instead of taking a
   slot in the bar, so the chrome being designed is the chrome users get.
   Dashed and gold so it never reads as part of the product. */
.dev-switcher {
  position: fixed;
  left: 12px;
  bottom: 12px;
  z-index: 60;
  font-size: 0.8rem;
}
.dev-switcher > summary {
  list-style: none;
  cursor: pointer;
  padding: 4px 10px;
  border: 1px dashed var(--color-accent);
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-weight: 600;
  box-shadow: var(--shadow-md);
}
.dev-switcher > summary::-webkit-details-marker { display: none; }
.dev-switcher .dropdown-menu {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 0;
  width: max-content;
  min-width: 200px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.dev-switcher form { display: contents; }
.dev-switcher button {
  padding: 6px 10px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--color-text-muted);
  font: inherit;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.dev-switcher button:hover { background: var(--color-brand-soft); color: var(--color-brand); }
.dev-switcher button[aria-current] { color: var(--color-brand); }

/* ---------- avatar ----------
   Initials on a plate (docs/dashboard_restructure_plan.md §4). Two things are
   encoded, on two independent axes, so a glance answers both:

     shape  — a circle is a person, a rounded square an organisation;
     colour — teal is us (the signed-in account, a deal's staff owner), and
              the customer hues below are everyone else.

   So a customer's single-user "Personal" account is a hued circle: one person,
   not ours. In a list the avatar is a soft plate — tint, deep ink of the same
   hue, hairline ring — the same language as the badges it sits beside. In the
   top bar it is the account control rather than an entry, so it goes solid.

   Sizes step with the line they sit beside: --sm next to one line of small
   text, the default in the bar, --lg anchoring a two-line name block. */
.avatar {
  --av-size: 28px;
  --av-bg: var(--color-brand-soft);
  --av-ink: var(--color-brand);
  --av-ring: var(--color-brand-line);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  box-sizing: border-box;
  width: var(--av-size);
  height: var(--av-size);
  border-radius: 999px;
  background: var(--av-bg);
  color: var(--av-ink);
  box-shadow: inset 0 0 0 1px var(--av-ring);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-variant-numeric: lining-nums;
  /* Optical centring, measured rather than guessed. Catamaran's deep descent
     lifts its caps 0.065em above the box centre, so the box takes 0.13em off
     the top (half of it moves the centre). The left pad gives back the
     tracking the last letter trails, which pushed the pair off-centre. */
  padding: 0.13em 0 0 0.04em;
  user-select: none;
}
.avatar--sm { --av-size: 24px; font-size: 10px; }
.avatar--lg { --av-size: 34px; font-size: 13px; }

/* Radius as a share of the side, so every size keeps the same silhouette. */
.avatar--org { border-radius: 26%; }

/* The customer hues. The server picks one of eight from a hash of the name
   (pages._org_avatar), none within reach of the brand teal, which means us.
   OKLCH keeps them at one perceived weight — equal L and C read as equally
   light whatever the hue, where hex picked by eye let olive run darker than
   rose and fall to 4.43:1. Measured ink on plate: 6.9–7.3:1 light, 8.7:1
   dark, across all eight.

   The plate's chroma is held low on purpose. An avatar says who; a status
   badge says what state — and a row should spend its colour on the state.
   At higher chroma the pinks and mint outshouted the badges beside them. */
.avatar--hued {
  --av-bg: oklch(0.94 0.03 var(--av-h, 255));
  --av-ink: oklch(0.42 0.09 var(--av-h, 255));
  --av-ring: oklch(0.86 0.045 var(--av-h, 255));
}
:root[data-theme="dark"] .avatar--hued {
  --av-bg: oklch(0.34 0.06 var(--av-h, 255));
  --av-ink: oklch(0.9 0.06 var(--av-h, 255));
  --av-ring: oklch(0.46 0.07 var(--av-h, 255));
}

/* The account control: solid, not a plate. */
.app-nav .avatar {
  --av-bg: var(--color-brand);
  --av-ink: var(--color-on-brand);
  --av-ring: transparent;
}
/* Light teal on the ink, as in the mark itself. */
.app-nav[data-workspace="staff"] .avatar { --av-bg: #4fb3bd; --av-ink: #0f2833; }

/* Where the bar runs short, the name gives way first: the avatar still says
   who is signed in and the menu spells it out. Hidden from sight only — it
   stays the summary's accessible name, which otherwise fell back to the
   e-mail in its title. Staff have six sections to fit, so they give it up
   sooner. */
@media (max-width: 1140px) {
  .app-nav[data-workspace="staff"] .account-name {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}
@media (max-width: 960px) {
  .app-nav .account-name {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}

main.shell {
  max-width: 1280px;
  margin: 0 auto;
  padding: 40px 24px 80px;
}
/* `.grid` spaces its own children, but nothing spaced the page's top-level
   blocks, so stacked cards and tables sat flush against each other. Margins
   collapse, so blocks that already ask for more (`.page-header` wants 32px)
   keep what they had. */
main.shell > * + * { margin-top: 20px; }

.page-header {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 32px;
  flex-wrap: wrap;
}
.page-header h1 { margin: 0; }
.page-header p.lede {
  color: var(--color-text-muted);
  margin: 4px 0 0;
  max-width: 60ch;
}

/* ---------- grid ---------- */

.grid {
  display: grid;
  gap: 20px;
}
.grid.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 900px) {
  /* minmax(0, …) as above: a bare 1fr cannot shrink below its content, so a
     card holding a table grew past the screen instead of letting the table's
     own scroller do its job. */
  .grid.cols-2, .grid.cols-3, .grid.cols-4 { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- cards ---------- */

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 24px;
  box-shadow: var(--shadow-sm);
}
.card.tight { padding: 18px 20px; }
.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}
.card-head h2, .card-head h3 { margin: 0; }
.card .subtitle { color: var(--color-text-muted); margin-top: -4px; margin-bottom: 16px; }
/* A second, lesser task inside one card, set off by a rule rather than a
   card of its own (offline activation, beneath the one-time code). */
.card-section {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--color-border);
}

/* stat tiles */
.stat {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.stat .label {
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.72rem;
  color: var(--color-text-dim);
  font-weight: 700;
}
.stat .value {
  font-size: 2rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--color-brand-hover);
}
.stat .hint { color: var(--color-text-muted); font-size: 0.85rem; }
/* The denominator in a "28 of 66" figure: secondary to the number it
   qualifies, but an absolute size rather than a multiple of the 2rem value,
   so it stays legible if that ramp ever moves. */
.stat .value .qualifier {
  font-size: 1.1rem;
  font-weight: 600;
  letter-spacing: 0;
}

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: 999px;
  font-family: inherit;
  font-weight: 700;
  font-size: 0.92rem;
  letter-spacing: 0.01em;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.05s ease;
  text-decoration: none;
}
.btn:active { transform: translateY(1px); }
.btn.primary {
  background: var(--color-brand);
  color: var(--color-on-brand);
}
.btn.primary:hover { background: var(--color-brand-hover); color: var(--color-on-brand); }
.btn.accent {
  background: var(--color-accent);
  color: var(--color-on-accent);
}
/* Dark ink holds on the hover gold too; white on it was 3.1:1. */
.btn.accent:hover { background: var(--color-accent-hover); color: var(--color-on-accent); }
.btn.ghost {
  background: transparent;
  color: var(--color-brand);
  border-color: var(--color-brand-line);
}
.btn.ghost:hover { background: var(--color-brand-soft); }
.btn.danger {
  background: transparent;
  color: var(--color-danger-ink);
  border-color: var(--color-danger-line);
}
.btn.danger:hover { background: var(--color-danger-soft); }
/* The committing button in a confirmation: filled, because by then the
   question has been asked and this is the answer. */
.btn.danger-solid {
  background: var(--color-danger);
  color: var(--color-on-danger);
}
.btn.danger-solid:hover { background: var(--color-danger-ink); color: var(--color-on-danger); }
.btn:disabled,
.btn[disabled] { opacity: 0.45; cursor: not-allowed; transform: none; }
.btn.small { padding: 6px 12px; font-size: 0.82rem; }

/* ---------- forms ---------- */

.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.field label {
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--color-text);
}
.field .hint { color: var(--color-text-dim); font-size: 0.8rem; }
/* A group's heading when there is no single control for a <label> to name. */
.field .field-label {
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--color-text);
}
input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="date"], input[type="datetime-local"], select, textarea {
  font-family: inherit;
  font-size: 0.95rem;
  padding: 10px 14px;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  outline: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
input:focus, select:focus, textarea:focus {
  border-color: var(--color-brand);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
/* The field a server-side error names (aria-invalid, set by the template). */
input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] {
  border-color: var(--color-danger);
}
input[aria-invalid="true"]:focus, select[aria-invalid="true"]:focus, textarea[aria-invalid="true"]:focus {
  box-shadow: 0 0 0 3px var(--color-danger-soft);
}
/* The UA placeholder grey was never re-tinted for dark (3.5:1 there). */
::placeholder { color: var(--color-text-dim); opacity: 1; }

/* ---------- date picker (static/js/datepicker.js) ----------

   Stands in front of every date / datetime-local input: a typed-date field
   with a calendar button, an HH:MM field for datetimes, and a popover. The
   popover sits under the sticky nav (z 29/30) rather than over it. */
.datepicker { position: relative; }
.datepicker-control { display: flex; gap: 8px; }
.datepicker-field { position: relative; flex: 1; min-width: 0; }
.datepicker .datepicker-input { width: 100%; padding-right: 46px; }
.datepicker .datepicker-time {
  flex: none;
  width: 5.25em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.datepicker [aria-invalid="true"] { border-color: var(--color-danger); }
.datepicker [aria-invalid="true"]:focus { box-shadow: 0 0 0 3px var(--color-danger-soft); }
.datepicker-toggle {
  position: absolute;
  top: 4px;
  right: 4px;
  bottom: 4px;
  width: 38px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.datepicker-toggle:hover,
.datepicker-toggle[aria-expanded="true"] { background: var(--color-brand-soft); color: var(--color-brand); }
.datepicker-toggle:disabled { cursor: not-allowed; opacity: 0.45; background: transparent; }
.datepicker svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.datepicker-popover {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 25;
  width: 19rem;
  max-width: calc(100vw - 32px);
  padding: 12px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}
.datepicker-popover.is-above { top: auto; bottom: calc(100% + 6px); }

.datepicker-head { display: flex; align-items: center; gap: 4px; margin-bottom: 8px; }
.datepicker-period { flex: 1; display: flex; justify-content: center; gap: 2px; }
.datepicker .datepicker-period select {
  min-height: 0;
  padding: 4px 6px;
  border-color: transparent;
  background: transparent;
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
}
.datepicker .datepicker-period select:hover { border-color: var(--color-border-strong); }
.datepicker-nav {
  width: 32px;
  height: 32px;
  flex: none;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
}
.datepicker-nav:hover { background: var(--color-brand-soft); color: var(--color-brand); }
.datepicker-nav:disabled { opacity: 0.35; cursor: not-allowed; background: transparent; }

.datepicker-grid { width: 100%; border-collapse: collapse; table-layout: fixed; }
.datepicker-grid th {
  padding: 0 0 6px;
  text-align: center;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-dim);
}
.datepicker-grid td { padding: 1px 0; text-align: center; }
.datepicker-day {
  width: 36px;
  height: 36px;
  display: inline-grid;
  place-items: center;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--color-text);
  font: inherit;
  font-size: 0.88rem;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.datepicker-day:hover { background: var(--color-brand-soft); }
.datepicker-day.is-outside { color: var(--color-text-dim); }
.datepicker-day[aria-current="date"] {
  box-shadow: inset 0 0 0 1.5px var(--color-brand);
  color: var(--color-brand);
  font-weight: 700;
}
.datepicker-day[aria-pressed="true"],
.datepicker-day[aria-pressed="true"]:hover {
  background: var(--color-brand);
  color: var(--color-on-brand);
  font-weight: 700;
}
.datepicker-day:disabled {
  background: transparent;
  color: var(--color-text-dim);
  opacity: 0.4;
  text-decoration: line-through;
  cursor: not-allowed;
}

.datepicker-foot {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--color-border);
}
.datepicker-presets { flex: 1; display: flex; flex-wrap: wrap; gap: 6px; }
.datepicker-chip,
.datepicker-clear {
  padding: 4px 10px;
  border-radius: 999px;
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}
.datepicker-chip {
  border: 1px solid var(--color-border-strong);
  background: transparent;
  color: var(--color-text);
}
.datepicker-chip:hover { border-color: var(--color-brand); background: var(--color-brand-soft); color: var(--color-brand); }
.datepicker-clear {
  border: 1px solid transparent;
  background: transparent;
  color: var(--color-text-muted);
}
.datepicker-clear:hover { color: var(--color-danger-ink); background: var(--color-danger-soft); }

.alert {
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  font-size: 0.9rem;
  margin-bottom: 16px;
}
.alert.error {
  background: var(--color-danger-soft);
  color: var(--color-danger-ink);
  border: 1px solid var(--color-danger-line);
}
.alert.success {
  background: var(--color-success-soft);
  color: var(--color-success-ink);
  border: 1px solid var(--color-success-line);
}
.alert.info {
  background: var(--color-brand-soft);
  color: var(--color-brand-hover);
  border: 1px solid var(--color-brand-line);
}

/* ---------- table ---------- */

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.93rem;
}
.table th, .table td {
  text-align: left;
  padding: 12px 14px;
  border-bottom: 1px solid var(--color-border);
}
.table th {
  font-weight: 700;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.74rem;
}
.table tbody tr:last-child td { border-bottom: none; }
.table tbody tr:hover { background: var(--color-surface-muted); }

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.badge.brand   { background: var(--color-brand-soft); color: var(--color-brand); }
.badge.success { background: var(--color-success-soft); color: var(--color-success-ink); }
.badge.warning { background: var(--color-warning-soft); color: var(--color-warning-ink); }
.badge.danger  { background: var(--color-danger-soft); color: var(--color-danger-ink); }
.badge.neutral { background: var(--color-surface-muted); color: var(--color-text-muted); }

.badge .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor;
}

/* ---------- org overview table (sleek, minimal — scoped) ---------- */

.table-shell {
  /* The containing block for anything absolutely positioned inside the table
     (the visually-hidden "Actions" header). Without it that label is placed
     against the viewport, escapes this scroller's clipping, and makes the
     whole page scroll sideways on a phone. */
  position: relative;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
  overflow-x: auto;
}
/* A row this wide has no scrollbar to say it scrolls — the sticky first
   column (below) reads as the whole row, so Licences/Renewal/Delete were
   invisible with no cue they existed. Same fade-on-scroll signal as
   .page-tabs, toggled by the same kind of scroll listener (base.html). */
.table-shell.fade-start {
  mask-image: linear-gradient(to left, #000 calc(100% - 32px), transparent);
}
.table-shell.fade-end {
  mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent);
}
.table-shell.fade-start.fade-end {
  mask-image: linear-gradient(to right, transparent, #000 32px, #000 calc(100% - 32px), transparent);
}

.org-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}
/* 14px sides: at 16px the organisation list's ten columns came to 12px more
   than their shell, which put a scrollbar under the landing table on every
   visit. */
.org-table th,
.org-table td {
  padding: 15px 14px;
  text-align: left;
  vertical-align: middle;
  border-bottom: 1px solid var(--color-border);
}
.org-table thead th {
  padding-top: 11px;
  padding-bottom: 11px;
  background: var(--color-surface-muted);
  border-bottom: 1px solid var(--color-border-strong);
  color: var(--color-text-dim);
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.org-table tbody tr { transition: background 0.12s ease; }
.org-table tbody tr:hover { background: var(--color-surface-muted); }
.org-table tbody tr:last-child td { border-bottom: none; }
/* The open row of a list whose detail renders beneath it (the customer
   Licences page). The row stays where it was, so the selection is marked in
   place: tinted, with a bar down the leading edge. */
.org-table tbody tr.is-selected td { background: var(--color-brand-soft); }
.org-table tbody tr.is-selected td:first-child { box-shadow: inset 3px 0 0 var(--color-brand); }
/* a touch more breathing room against the frame */
.org-table th:first-child,
.org-table td:first-child { padding-left: 20px; }
.org-table th:last-child,
.org-table td:last-child { padding-right: 20px; }

/* numeric band: right-aligned, tabular figures so columns line up */
.org-table .num { text-align: right; font-variant-numeric: tabular-nums; }
/* A bare digit was a 5px-wide target; pad it out without moving the column.
   Centered, not flex-end: flex-end pinned the digit to the target's right
   edge and left all the extra width as dead space on the left, so the hover
   highlight looked shifted off the number instead of wrapping it. */
.org-table .count-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  min-height: 28px;
  margin: -6px;
  padding: 0 6px;
  border-radius: var(--radius-sm);
  color: var(--color-brand);
  font-weight: 600;
}
.org-table .count-link:hover { color: var(--color-brand-hover); background: var(--color-brand-soft); }

/* The record link: the name is what you click in a CRM. */
.org-table .org-link { color: inherit; }
.org-table .org-link:hover .org-name { color: var(--color-brand); text-decoration: underline; text-underline-offset: 3px; }

/* organisation cell */
.org-table .org-name {
  font-weight: 700;
  color: var(--color-text);
  letter-spacing: -0.01em;
}
.org-table .org-email {
  display: block;
  max-width: 300px;
  margin-top: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.8rem;
  color: var(--color-text-dim);
}

/* quieter, smaller badges inside the table */
.org-table .badge { padding: 3px 9px; font-size: 0.78rem; font-weight: 600; white-space: nowrap; }

/* Flag + name is one token, not two: without this the space between them is
   a wrap point, so a long name (Netherlands) drops the flag onto its own
   line while a short one (Belgium) keeps sitting next to it — inconsistent
   for no reason, since both fit the column's normal width just fine. */
.org-table .col-country { white-space: nowrap; }

/* renewal date / label stays on one line, tabular figures */
.org-table .col-renews { white-space: nowrap; font-variant-numeric: tabular-nums; }
/* An ISO date is one token; breaking it across two lines to save a few pixels
   costs more legibility than the width is worth. The shell scrolls instead. */
.org-table .col-date { white-space: nowrap; }

/* subtle outline tags (Personal / Workshop / Archived) */
.row-tag {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 7px;
  border: 1px solid var(--color-border-strong);
  border-radius: 999px;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  vertical-align: 2px;
}
.row-tag.workshop {
  border-color: var(--color-brand-line);
  background: var(--color-brand-soft);
  color: var(--color-brand);
}

/* The organisation list (/admin): one row per organisation, and a row can
   run to several lines once an organisation holds several licences. Middle
   alignment floated the name halfway down a tall row; aligning every cell on
   its first line keeps the name, the first licence and the dates on one
   reading line, however many licences follow. */
.org-roster td { vertical-align: baseline; }

/* The licences an organisation holds: product on the left, its status on the
   right. The stack is capped rather than filling the cell, so the badges
   stand at the same x on every row — the column still scans as a status
   column — without drifting a screen-width away from their product on a
   wide monitor. */
.lic-stack {
  min-width: 14rem;
  max-width: 21rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.lic-line {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.lic-line + .lic-line,
.lic-more { margin-top: 6px; }
/* A licence is one line, tags and cues included: on a phone the table
   already scrolls sideways, and letting "Devices full" wrap under its product
   made one licence read as two. */
.lic-what { white-space: nowrap; }
.lic-product { color: var(--color-text); font-weight: 500; }
.lic-product:hover { color: var(--color-brand); text-decoration: underline; text-underline-offset: 3px; }
/* Shown only on a licence in force, and only when it is at its cap: a reason
   to call the customer, not a usage readout. */
.lic-cue {
  margin-left: 8px;
  color: var(--color-warning-ink);
  font-size: 0.75rem;
  font-weight: 700;
  white-space: nowrap;
}
.lic-more a { font-size: 0.8rem; font-weight: 600; }

/* row actions: evenly sized, whisper-quiet until hover */
.org-table .col-actions { text-align: right; white-space: nowrap; }
/* This table carries three row actions where most other .row-actions lists
   carry one or two, so it's the widest column by far — shaving a couple of
   px off just here (not the shared .row-actions .btn) buys back the table
   width the larger header/badge type above needed. */
.org-table .row-actions .btn { padding: 0 9px; }
.row-actions {
  display: flex;
  gap: 4px;
  justify-content: flex-end;
  align-items: center;
}
.row-actions form { display: contents; }
.row-actions .btn {
  height: 30px;
  padding: 0 11px;
  font-size: 0.8rem;
  font-weight: 600;
  border-radius: var(--radius-sm);
  background: transparent;
  border-color: transparent;
  color: var(--color-text-muted);
}
.row-actions .btn:hover { background: var(--color-brand-soft); color: var(--color-brand); }
/* Delete keeps its colour at rest and stands apart from the other actions —
   it used to be muted to match them, turning red only once the pointer was
   already on it. */
.row-actions .btn.danger {
  color: var(--color-danger-ink);
  margin-left: 8px;
  position: relative;
}
.row-actions .btn.danger::before {
  content: "";
  position: absolute;
  left: -7px;
  top: 7px;
  bottom: 7px;
  width: 1px;
  background: var(--color-border-strong);
}
.row-actions .btn.danger:hover { background: var(--color-danger-soft); color: var(--color-danger-ink); }

.org-empty {
  text-align: center;
  padding: 36px 24px;
  color: var(--color-text-dim);
}

/* ---------- list toolbar / filters / pagination ---------- */

.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}
.toolbar .search-wrap { position: relative; flex: 1 1 280px; min-width: 200px; }
.toolbar .search { width: 100%; padding: 8px 12px; font-size: 0.9rem; }
.toolbar select { padding: 8px 12px; font-size: 0.9rem; }
/* Search narrows the list on its own; the selects only ever narrow it
   further. Proximity says so — tight within the group, generous from
   search — without a border pretending they're a separate control. */
.toolbar-filters { display: flex; flex-wrap: wrap; gap: 6px; }

/* Tiny spinner shown (via htmx) while a live search/filter request is in flight. */
.search-indicator {
  position: absolute;
  top: 50%;
  right: 12px;
  width: 14px;
  height: 14px;
  margin-top: -7px;
  border-radius: 50%;
  border: 2px solid var(--color-brand-line);
  border-top-color: var(--color-brand);
  animation: spin 0.6s linear infinite;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.search-indicator.htmx-request { opacity: 1; }
@keyframes spin { to { transform: rotate(360deg); } }

.result-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  margin: 0 2px 12px;
  font-size: 0.85rem;
  color: var(--color-text-dim);
}
.result-summary .clear-filters { font-weight: 600; }
/* One pill per active status/mode filter, in the same badge language as the
   table's own badges — hover shifts toward danger since the click removes
   the filter, the same cue .row-actions .btn.danger uses for Delete. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--color-brand-soft);
  color: var(--color-brand);
  font-size: 0.78rem;
  font-weight: 600;
  text-decoration: none;
}
.chip:hover { background: var(--color-danger-soft); color: var(--color-danger-ink); }
.chip span { font-size: 0.7em; }

/* Sortable column headers (partials/sort_th.html). The link reaches into
   the cell's padding, so the target is the header, not the 15px of text. */
.th-sort {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: 28px;
  margin: -6px -8px;
  padding: 6px 8px;
  border-radius: var(--radius-sm);
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}
.th-sort:hover { color: var(--color-brand); background: var(--color-brand-soft); }
.th-sort.active { color: var(--color-text); }
.th-sort .sort-caret {
  width: 11px;
  height: 11px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.th-sort.is-desc .sort-caret { transform: rotate(180deg); }
.th-sort:not(.active) .sort-caret { opacity: 0; transition: opacity 0.12s ease; }
.th-sort:not(.active):hover .sort-caret,
.th-sort:not(.active):focus-visible .sort-caret { opacity: 0.55; }

/* Inline usage / renewal cues inside table cells. */
.usage-full { color: var(--color-warning-ink); font-weight: 600; }
.renews-soon { color: var(--color-warning-ink); font-weight: 600; }
.renews-expired { color: var(--color-danger-ink); font-weight: 600; }
.renews-none { color: var(--color-text-dim); }
.renews-ok { color: var(--color-text); }
/* "in Nd" looks the same whether it's soon or comfortably ok; the dot
   (borrowed from .badge .dot) marks urgency by shape as well as colour. */
.renews-soon .dot,
.renews-expired .dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-bottom: 1px;
  border-radius: 50%;
  background: currentColor;
}

/* ---------- numbered pager ---------- */
.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 18px;
}
.page-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  height: 36px;
  padding: 0 10px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-size: 0.88rem;
  font-weight: 600;
  text-decoration: none;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.page-btn:hover {
  background: var(--color-brand-soft);
  border-color: var(--color-brand-line);
  color: var(--color-brand);
}
.page-btn.current {
  background: var(--color-brand);
  border-color: var(--color-brand);
  color: var(--color-on-brand);
  cursor: default;
}
.page-btn.disabled {
  opacity: 0.4;
  pointer-events: none;
}
.page-btn.page-step { font-weight: 700; }
.page-gap {
  display: inline-flex;
  align-items: flex-end;
  padding: 0 4px;
  color: var(--color-text-dim);
}
@media (max-width: 560px) {
  .page-step-label { display: none; }
}

/* ---------- login page (centered) ---------- */

.auth-stage {
  min-height: 100vh;
  background:
    radial-gradient(at 18% 12%, rgba(8, 99, 108, 0.55) 0%, transparent 55%),
    radial-gradient(at 82% 88%, rgba(7, 20, 28, 0.7) 0%, transparent 60%),
    linear-gradient(155deg, #08636c 0%, #0f2833 60%, #06141c 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 56px 24px;
  position: relative;
  overflow: hidden;
  color: var(--color-cream);
}

.auth-glow {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  filter: blur(8px);
  z-index: 0;
}
.auth-glow-gold {
  width: 620px; height: 620px;
  top: -200px; right: -180px;
  background: radial-gradient(circle, rgba(217, 166, 71, 0.32), transparent 65%);
}
.auth-glow-teal {
  width: 520px; height: 520px;
  bottom: -200px; left: -160px;
  background: radial-gradient(circle, rgba(101, 190, 194, 0.26), transparent 60%);
}

.auth-frame {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 460px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 40px;
}

.brand-lockup {
  display: flex;
  align-items: center;
  gap: 18px;
}
.brand-lockup .brand-mark {
  height: 68px;
  width: auto;
  flex: 0 0 auto;
}
.brand-lockup-text {
  display: flex;
  flex-direction: column;
  line-height: 1;
}
.brand-lockup-wordmark {
  font-family: "Exo 2", var(--font-sans);
  font-weight: 600;
  font-size: 2.15rem;
  letter-spacing: 0.03em;
  color: var(--color-cream);
}
.brand-lockup-tagline {
  font-family: "Exo 2", var(--font-sans);
  font-weight: 600;
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #65bec2;
  margin-top: 10px;
}

.auth-card {
  width: 100%;
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  padding: 40px 36px 36px;
  box-shadow:
    0 30px 60px -20px rgba(6, 20, 28, 0.55),
    0 8px 22px -10px rgba(6, 20, 28, 0.4);
  border: 1px solid rgba(228, 224, 219, 0.08);
  color: var(--color-text);
}
.auth-card .eyebrow { margin-bottom: 8px; }
.auth-card h1 {
  font-size: 1.55rem;
  margin: 0 0 6px;
  color: var(--color-brand-hover);
}
.auth-card p.lede {
  color: var(--color-text-muted);
  margin: 0 0 24px;
  font-size: 0.95rem;
}
.auth-card .field:last-of-type { margin-bottom: 22px; }
.auth-submit { width: 100%; padding: 12px 18px; font-size: 0.98rem; }
.auth-card-meta {
  margin: 22px 0 0;
  text-align: center;
  font-size: 0.82rem;
  color: var(--color-text-muted);
}

.auth-foot {
  font-size: 0.78rem;
  color: rgba(228, 224, 219, 0.55);
  letter-spacing: 0.04em;
  text-align: center;
}

@media (max-width: 540px) {
  .auth-stage { padding: 36px 18px; }
  .auth-frame { gap: 30px; }
  .brand-lockup { gap: 14px; }
  .brand-lockup .brand-mark { height: 56px; }
  .brand-lockup-wordmark { font-size: 1.7rem; }
  .brand-lockup-tagline { font-size: 0.62rem; }
  .auth-card { padding: 30px 24px 26px; }
  .auth-card h1 { font-size: 1.35rem; }
}

/* ---------- modal-ish dialog (for activation code reveal) ---------- */

.reveal {
  background: var(--color-brand-soft);
  border: 1px dashed var(--color-brand-line);
  border-radius: var(--radius-md);
  padding: 24px;
  text-align: center;
}
.reveal .code {
  font-family: var(--font-mono);
  font-size: 1.8rem;
  font-weight: 700;
  color: var(--color-brand);
  letter-spacing: 0.15em;
  margin: 12px 0;
}
/* A fleet token is forty-odd characters, not a short code: at the code's
   size and spacing it ran past the edge of the dialog it is shown in. */
.reveal .code.is-token {
  font-size: 1.05rem;
  letter-spacing: 0.02em;
  overflow-wrap: anywhere;
}
/* Where a just-issued code or token lands, beneath the button that asked for
   it. Takes no room until something arrives. */
.activation-slot:not(:empty) { margin-top: 16px; }

/* ---------- utility ---------- */

.row { display: flex; align-items: center; gap: 12px; }
.row.between { justify-content: space-between; }
.row.wrap { flex-wrap: wrap; }
.stack { display: flex; flex-direction: column; gap: 8px; }
.hidden { display: none !important; }

.htmx-indicator { display: none; }
.htmx-request .htmx-indicator { display: inline-flex; }
.htmx-request.htmx-indicator { display: inline-flex; }

.spinner {
  width: 14px; height: 14px;
  border: 2px solid var(--color-brand-line);
  border-top-color: var(--color-brand);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- activity monitor ---------- */

/* Label, bar, figure (partials/hbar.html). The bar has a floor: in a
   three-up card the old 220px label column left the 1fr track 43px wide,
   and an 11px difference between two bars is not a difference anyone reads.
   The figure column sizes to its content, so a count, a "count · money"
   pair and a percentage all fit without a per-panel variant. */
.hbar-row {
  display: grid;
  grid-template-columns: minmax(84px, 140px) minmax(88px, 1fr) auto;
  align-items: center;
  gap: 12px;
  padding: 3px 0;
}
.hbar-row + .hbar-row { margin-top: 4px; }
.hbar-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--color-text-muted);
}
a.hbar-label { color: var(--color-text); font-weight: 600; }
a.hbar-label:hover { color: var(--color-brand); text-decoration: underline; text-underline-offset: 3px; }
.hbar-value {
  justify-self: end;
  font-family: var(--font-mono);
  font-size: 0.86rem;
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
  white-space: nowrap;
}
.hbar-value small {
  display: inline-block;
  min-width: 3.4ch;
  margin-left: 8px;
  text-align: right;
  color: var(--color-text-dim);
  font-size: 0.8rem;
}
/* Remainder rows ("Other countries", "Not recorded"): present so the parts
   sum to the whole, quieter because they are not a category of their own. */
.hbar-row.is-muted .hbar-label { font-style: italic; }
.hbar-row.is-muted .hbar-fill > span { background: var(--color-border-strong); }

/* A breakdown with one category is a sentence, not a chart. */
.breakdown-single {
  margin: 0;
  color: var(--color-text-muted);
  font-size: 0.95rem;
}
.breakdown-single a { font-weight: 600; }
.hbar-fill {
  height: 10px;
  background: var(--color-surface-muted);
  border-radius: 999px;
  overflow: hidden;
}
.hbar-fill > span {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--color-brand) 0%, var(--color-success) 100%);
  border-radius: 999px;
}

.spark {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 88px;
  padding: 8px 0 4px;
}
.spark-bar {
  flex: 1;
  min-width: 2px;
  background: var(--color-brand);
  border-radius: 2px 2px 0 0;
  transition: background 0.15s ease, opacity 0.15s ease;
  opacity: 0.85;
}
.spark-bar:hover { opacity: 1; background: var(--color-brand-hover); }
.spark-bar.zero {
  background: var(--color-border);
  opacity: 0.5;
  min-height: 2px;
}

.activity-feed {
  display: flex;
  flex-direction: column;
}
.activity-item {
  display: grid;
  grid-template-columns: minmax(140px, 200px) 1fr auto;
  align-items: flex-start;
  gap: 16px;
  padding: 12px 0;
  border-bottom: 1px solid var(--color-border);
}
.activity-item:last-child { border-bottom: none; }
.activity-event-badge {
  justify-self: start;
  align-self: center;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}
.activity-body { min-width: 0; }
.activity-line {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 0.92rem;
}
.activity-actor { font-weight: 600; color: var(--color-text); }
.activity-meta {
  margin-top: 4px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  font-size: 0.78rem;
  color: var(--color-text-muted);
}
.meta-pill {
  background: var(--color-surface-muted);
  padding: 2px 8px;
  border-radius: 999px;
  max-width: 380px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.meta-pill strong { color: var(--color-text); font-weight: 600; }
.activity-time {
  text-align: right;
  font-size: 0.8rem;
  color: var(--color-text);
  white-space: nowrap;
  line-height: 1.3;
}

/* ---------- feature-flag picker ---------- */

.flag-picker {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 10px;
}
.flag-option {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 12px 14px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
}
.flag-option:hover {
  border-color: var(--color-brand-line);
  background: var(--color-surface-muted);
}
.flag-option input[type="checkbox"] {
  margin-top: 3px;
  accent-color: var(--color-brand);
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}
.flag-option-body { min-width: 0; flex: 1; }
.flag-option-key {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--color-text-muted);
  display: block;
  margin-top: 2px;
}
.flag-option-label {
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--color-text);
  display: block;
}
.flag-option-desc {
  color: var(--color-text-muted);
  font-size: 0.82rem;
  margin-top: 4px;
}
.flag-option:has(input:checked) {
  border-color: var(--color-brand);
  background: var(--color-brand-soft);
  box-shadow: 0 0 0 1px var(--color-brand) inset;
}
/* The flag picker's tiles, shrunk to fit a table cell: one member's
   licences on the members page. */
.access-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 160px;
}
.access-list label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.88rem;
  cursor: pointer;
}
.access-list input[type="checkbox"] {
  accent-color: var(--color-brand);
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}
.access-list .access-error {
  color: var(--color-danger-ink);
  font-size: 0.8rem;
  max-width: 32ch;
}
.flag-empty {
  padding: 14px 16px;
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  font-size: 0.9rem;
}
.flag-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--color-brand-soft);
  color: var(--color-brand-hover);
  font-family: var(--font-mono);
  font-size: 0.78rem;
}
/* A licence's staff notes, under its facts and above its actions. Kept with
   the line breaks they were typed with, like the organisation's address. */
.licence-note {
  margin: 0 0 10px;
  color: var(--color-text-muted);
  font-size: 0.85rem;
  line-height: 1.5;
  white-space: pre-line;
}

/* A contact's tags, as typed on the contact form: free text, so unlike
   .flag-chip (a catalogue code) they are set in the body face, not mono, and
   sit quietly under the name rather than competing with it. */
.tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 3px;
}
.tag-chip {
  display: inline-flex;
  align-items: center;
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--color-surface-muted);
  color: var(--color-text-muted);
  font-size: 0.72rem;
  line-height: 1.5;
}
/* A note under the row it belongs to: readable, clearly secondary, and
   clamped so one long note cannot set the height of the whole table. */
.row-note {
  margin: 4px 0 0;
  color: var(--color-text-muted);
  font-size: 0.8rem;
  line-height: 1.45;
  white-space: pre-line;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.flag-usage-list {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

/* Pipeline board (deals)
   ---------------------------------------------------------------------------
   The only horizontally-scrolling surface in the app. Columns keep a fixed
   width rather than sharing the row, so adding a stage lengthens the board
   instead of squeezing every card — a five-stage pipeline and an eight-stage
   one should read the same.                                                  */
.pipeline-stage-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 20px; }
.pipeline-statistics .card-head, .pipeline-closing .card-head, .pipeline-work-table .card-head { padding-bottom: 18px; border-bottom: 1px solid var(--color-border); flex-wrap: wrap; gap: 12px; }
.pipeline-statistics .card-head h2, .pipeline-closing .card-head h2, .pipeline-work-table .card-head h2 { font-size: 1.5rem; }
.stage-summary { display: flex; flex-direction: column; gap: 8px; padding: 20px; border: 1px solid var(--color-border); border-radius: var(--radius-sm); color: var(--color-text); background: var(--color-surface-muted); }
.stage-summary:hover { border-color: var(--color-brand); text-decoration: none; }
.stage-summary-name { font-size: 1.1rem; font-weight: 700; }
.stage-summary strong { font-size: 1.7rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.stage-summary .dim { font-size: .85rem; }
.stage-summary-track { height: 6px; background: var(--color-border); border-radius: 6px; margin-top: 8px; overflow: hidden; }
.stage-summary-track > span { display: block; height: 100%; background: var(--color-brand); border-radius: inherit; }
.pipeline-work { display: grid; gap: 24px; margin-top: 24px; }
.pipeline-work-table { scroll-margin-top: 130px; }
.pipeline-work-table .card-head .muted { font-size: .9rem; }
.pipeline-page .kanban { align-items: stretch; gap: 20px; }
.pipeline-page .kanban-col { flex: 1 0 270px; padding: 0; overflow: hidden; }
.pipeline-page .kanban-head { display: flex; flex-direction: column; align-items: stretch; gap: 12px; padding: 18px; margin: 0; background: var(--color-surface); border-bottom: 1px solid var(--color-border); }
.pipeline-page .kanban-head h2 { color: var(--color-text); text-transform: none; font-size: 1.25rem; font-weight: 700; letter-spacing: -.01em; }
.kanban-stage-title { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.kanban-stage-value { font-size: 1.5rem; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--color-brand); }
.pipeline-page .kanban-cards { padding: 14px; gap: 12px; min-height: 120px; }
.pipeline-page .deal-card { padding: 14px; }
.pipeline-page .deal-card-title { font-weight: 700; }
.pipeline-page .deal-card-foot { border-top: 1px solid var(--color-border); padding-top: 10px; margin-top: 12px; }
.pipeline-page .closed-lanes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-bottom: 0; overflow: visible; }
.pipeline-closing .card-head p { margin: 8px 0 0; font-size: .9rem; }
.pipeline-page .closing .kanban-head { border-bottom: 0; padding-bottom: 8px; background: transparent; }
.pipeline-page .closing .is-terminal { margin: 0 14px 14px; min-height: 100px; gap: 6px; }
.pipeline-page .closing--won { border-top: 3px solid var(--color-success-ink); }
.pipeline-page .closing--lost { border-top: 3px solid var(--color-danger); }
@media (max-width: 600px) {
  .pipeline-page .closed-lanes { grid-template-columns: minmax(0, 1fr); }
  .pipeline-stage-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .stage-summary { padding: 14px; }
}

.kanban {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  overflow-x: auto;
  padding-bottom: 10px;
  margin-bottom: 22px;
}
.kanban-col {
  flex: 0 0 272px;
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 12px;
}
.kanban-col.drop-target {
  border-color: var(--color-brand);
  background: var(--color-brand-soft);
}
.kanban-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 10px;
}
.kanban-head h2 {
  margin: 0;
  font-size: 0.92rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.kanban-count { font-size: 0.8rem; white-space: nowrap; }
.kanban-cards {
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* A column with no cards still has to be a drop target, so it keeps a
     height of its own rather than collapsing to nothing. */
  min-height: 72px;
}
.kanban-cards.is-terminal {
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: 56px;
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius-sm);
}
.kanban-cards.is-terminal p { margin: 0; font-size: 0.85rem; }
.deal-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  padding: 10px 12px;
  cursor: grab;
}
.deal-card:active { cursor: grabbing; }
.deal-card.dragging { opacity: 0.45; }
.deal-card.pending { opacity: 0.6; }
/* The touch/pen drag's floating copy of the card (admin_deals_pipeline.html)
   — native drag-and-drop draws its own; this path has to draw one itself. */
.deal-card-ghost {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 200;
  pointer-events: none;
  box-shadow: var(--shadow-lg);
  transform: rotate(-2deg);
  opacity: 0.95;
}
.deal-card-title {
  display: block;
  font-weight: 600;
  color: var(--color-text);
}
.deal-card-title:hover { color: var(--color-brand); }
.deal-card-org {
  color: var(--color-text-muted);
  font-size: 0.85rem;
  margin-top: 2px;
}
.deal-card-foot {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 8px;
  font-size: 0.82rem;
  color: var(--color-text-muted);
}
.closed-lanes { margin-bottom: 0; }

/* Activity feed — logged interactions and stage history.
   Chronological entries with a meta line, distinct from .detail-list, which
   is label/value pairs about one record.                                     */
.activity-feed {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.activity-feed li {
  border-left: 2px solid var(--color-border-strong);
  padding-left: 12px;
}
.activity-feed li:first-child { border-left-color: var(--color-brand); }
.activity-feed p {
  margin: 4px 0 0;
  white-space: pre-wrap;
}
.activity-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  font-size: 0.82rem;
}
/* Editing and deleting one entry: quiet until the entry is hovered or one of
   them is focused, so a feed of notes stays a feed of notes. */
.activity-actions {
  margin-left: auto;
  display: flex;
  gap: 10px;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.activity-feed li:hover .activity-actions,
.activity-actions:focus-within { opacity: 1; }
@media (hover: none) {
  .activity-actions { opacity: 1; }
}
.link-button {
  border: 0;
  padding: 0;
  background: none;
  font: inherit;
  color: var(--color-brand);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.link-button:hover { color: var(--color-brand-hover); }
.link-button.danger { color: var(--color-danger-ink); }

/* Chart palette. Named here rather than as literals in the view that builds
   the gradient, so a slice colour is a theme decision like any other and the
   dark override below applies to it too. Drawn from the existing brand
   tokens: nothing new, just an order to use them in.                        */
:root {
  --chart-1: #08636c;
  --chart-2: #66aeb3;
  --chart-3: #d9a647;
  --chart-4: #566d8f;
  --chart-5: #b3c4d6;
  --chart-6: #cfd7de;
}
:root[data-theme="dark"] {
  --chart-1: #2a9aa4;
  --chart-2: #66aeb3;
  --chart-3: #d9a647;
  --chart-4: #7f93b4;
  --chart-5: #4d5f78;
  --chart-6: #3a4553;
}

/* Status donut (organisations dashboard).
   A ring, not a pie: the hole carries the total, which is the number the
   panel is really about — the slices only say how it splits.               */
.donut-layout {
  display: flex;
  align-items: center;
  gap: 22px;
  flex-wrap: wrap;
}
.donut {
  position: relative;
  flex: 0 0 auto;
  width: 132px;
  height: 132px;
  border-radius: 50%;
}
.donut::after {
  /* The hole. A mask would be tidier but leaves the legend's contrast at the
     mercy of whatever is behind the card; an opaque disc is predictable. */
  content: "";
  position: absolute;
  inset: 26px;
  border-radius: 50%;
  background: var(--color-surface);
}
.donut-hole {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1.15;
}
.donut-hole strong { font-size: 1.5rem; color: var(--color-text); }
.donut-hole span {
  font-size: 0.72rem;
  color: var(--color-text-muted);
}
.donut-legend {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1 1 160px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.donut-legend li {
  display: grid;
  grid-template-columns: 10px 1fr auto 36px;
  align-items: center;
  gap: 8px;
  font-size: 0.88rem;
}
.donut-legend .legend-label { color: var(--color-text-muted); }
.donut-legend small { text-align: right; font-size: 0.8rem; }
.legend-dot {
  width: 10px;
  height: 10px;
  border-radius: 3px;
}

/* Name with its avatar and a qualifier beneath, in a table cell. The gap is a
   third of the --lg avatar, so mark and name read as one unit, not two. */
.org-cell {
  display: flex;
  align-items: center;
  gap: 12px;
}
.org-cell small {
  display: block;
  font-size: 0.78rem;
  text-transform: capitalize;
}

/* A single call to action under the stat row — used when something is time
   sensitive and the detail lives on another page. */
.callout {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  padding: 16px 20px;
  border: 1px solid var(--color-warning);
  border-radius: var(--radius-md);
  background: var(--color-warning-soft);
  color: var(--color-warning-ink);
}
.callout p { margin: 2px 0 0; font-size: 0.9rem; }

.panel-note {
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--color-border);
  color: var(--color-text-dim);
  font-size: 0.85rem;
}


/* ---------- stat tiles that open their list ---------- */

a.card.is-link {
  display: block;
  color: inherit;
  text-decoration: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
a.card.is-link:hover {
  border-color: var(--color-brand-line);
  box-shadow: var(--shadow-md);
}
a.card.is-link:hover .stat .value { color: var(--color-brand); }

/* A stat tile that opens its list and also holds an action (Invite member):
   a link can't contain a button, so the link's hit area is stretched over
   the whole card and the action sits above it. */
.stat-card {
  position: relative;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.stat-card .stat-link { color: inherit; text-decoration: none; }
.stat-card .stat-link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}
.stat-card .btn { position: relative; z-index: 1; }
.stat-card:hover {
  border-color: var(--color-brand-line);
  box-shadow: var(--shadow-md);
}
.stat-card:hover .stat .value { color: var(--color-brand); }
.stat-card:has(.stat-link:focus-visible) {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}
.stat-card .stat-link:focus-visible { outline: none; }

/* ---------- staff licence card (partials/admin_licence_card.html) ---------- */

.licence-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 40px;
  margin: 0;
}
.licence-facts > div { display: flex; flex-direction: column; gap: 3px; }
.licence-facts dt {
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--color-text-dim);
}
.licence-facts dd {
  margin: 0;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.licence-facts dd .dim { font-weight: 400; }
.licence-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 20px;
}

/* ---------- licence card (partials/app_licence_card.html) ----------
   A header row (mark, name, mode and status, actions) over one row of
   facts split by hairlines. Seats and devices carry a pip per slot while the
   limit is countable, a bar beyond that. */

.lc {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}
.lc-head {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 20px 24px;
  border-bottom: 1px solid var(--color-border);
  flex-wrap: wrap;
}
.lc-mark {
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
  border-radius: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--band-bg);
  color: var(--band-text);
}
.lc-mark svg { width: 24px; height: 24px; }
.lc-title { min-width: 0; }
.lc-title h3 { margin: 0; font-size: 1.2rem; }
.lc-sub {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 2px;
  color: var(--color-text-muted);
  font-size: 0.9rem;
}
.lc-actions { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
.lc-facts {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.lc-facts > div { padding: 16px 24px 20px; border-left: 1px solid var(--color-border); }
.lc-facts > div:first-child { border-left: 0; }
.lc-facts dt { color: var(--color-text-muted); font-size: 0.85rem; margin-bottom: 4px; }
.lc-facts dd { margin: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.lc-figure {
  font-size: 1.2rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}
.lc-figure span { font-size: 0.95rem; font-weight: 600; color: var(--color-text-muted); letter-spacing: 0; }
.lc-figure.usage-full { color: var(--color-danger-ink); }
.lc-pips { display: flex; gap: 4px; width: 100%; max-width: 180px; }
.lc-pips i { flex: 1; max-width: 26px; height: 6px; border-radius: 3px; background: var(--color-border); }
.lc-pips i.on { background: var(--color-brand); }
.lc-bar { display: block; width: 100%; max-width: 180px; height: 6px; border-radius: 3px; background: var(--color-border); overflow: hidden; }
.lc-bar i { display: block; height: 100%; background: var(--color-brand); }
.lc-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.8rem;
  font-weight: 700;
  padding: 3px 9px 2px;
  border-radius: 999px;
}
.lc-chip::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.lc-chip.tone-soon { color: var(--color-warning-ink); background: var(--color-warning-soft); }
.lc-chip.tone-soon::before { background: var(--color-accent); }
.lc-chip.tone-expired { color: var(--color-danger-ink); background: var(--color-danger-soft); }
.lc-block {
  margin: 0;
  padding: 12px 24px 16px;
  border-top: 1px solid var(--color-border);
  color: var(--color-text-dim);
  font-size: 0.88rem;
}
@media (max-width: 700px) {
  .lc-head { padding: 18px; }
  .lc-actions { margin-left: 0; width: 100%; }
  .lc-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .lc-facts > div { padding: 14px 18px 16px; border-left: 0; border-top: 1px solid var(--color-border); }
  .lc-facts > div:nth-child(2n) { border-left: 1px solid var(--color-border); }
  .lc-facts > div:nth-child(-n + 2) { border-top: 0; }
}

/* ---------- list empty state and inline flash ---------- */

/* One message where a list would be, instead of a sentence in the summary
   line and the same sentence again in a table row under ten headers. */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 40px 24px;
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text-muted);
  text-align: center;
}
.empty-state p { margin: 0; max-width: 52ch; }

.alert-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

/* ---------- confirmation dialog (base.html) ---------- */

.confirm-dialog {
  width: min(460px, calc(100vw - 32px));
  padding: 24px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: var(--shadow-lg);
}
.confirm-dialog::backdrop {
  background: rgba(6, 20, 28, 0.45);
}
.confirm-dialog form { display: flex; flex-direction: column; gap: 18px; }
.confirm-message {
  margin: 0;
  font-size: 1rem;
  line-height: 1.55;
  overflow-wrap: anywhere;
}
.confirm-dialog .field { margin: 0; }
.confirm-dialog .field input { width: 100%; }
.confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  flex-wrap: wrap;
}

/* ---------- modal forms (base.html #modal, static/js/modal.js) ----------

   Same surface and backdrop as the confirmation above, wider, because it
   holds a form. The dialog scrolls when the form is taller than the window;
   the page behind it does not. */

.modal {
  width: min(620px, calc(100vw - 32px));
  max-height: calc(100dvh - 48px);
  overflow-y: auto;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: var(--shadow-lg);
}
.modal::backdrop { background: rgba(6, 20, 28, 0.45); }
.modal[open] { animation: modal-in 0.16s ease-out; }
@keyframes modal-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}
/* A backdrop click on a dialog holding unsaved input (static/js/modal.js). */
.modal.modal-nudge { animation: modal-nudge 0.28s ease-in-out; }
@keyframes modal-nudge {
  0%, 100% { transform: none; }
  25% { transform: translateX(-6px); }
  75% { transform: translateX(6px); }
}
/* Reduced motion: the fade stays, the lift goes. */
@keyframes modal-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
/* The page stays put as its scrollbar goes: it is laid out on the whole
   window either way (body, above). */
html.modal-open { overflow: hidden; }

.modal-frame { padding: 24px; }
.modal-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}
.modal-head h2 { margin: 0; }
.modal-close {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  margin: -4px -6px 0 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.modal-close:hover { background: var(--color-brand-soft); color: var(--color-brand); }
.modal-close svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
}
.modal-lede { margin: -8px 0 18px; color: var(--color-text-muted); }
.modal-frame .grid { gap: 0 16px; }
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 8px;
  padding-top: 18px;
  border-top: 1px solid var(--color-border);
}

/* The same form as a page (modal_page.html): the card is the frame. */
.modal-page { max-width: 620px; }
.modal-page .modal-frame { padding: 0; }

/* Separate tasks in one dialog (a code, or a file), set off by a rule. */
.modal-section + .modal-section {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--color-border);
}
.modal-section h3 { margin: 0 0 4px; font-size: 1rem; }
.modal-section > p.dim { margin: 0 0 14px; }
/* A group of radios or ticks: a fieldset for its legend, without the frame. */
.modal-fieldset { margin: 0 0 16px; padding: 0; border: 0; min-width: 0; }
.modal-fieldset legend { padding: 0; margin-bottom: 8px; }
.modal-fieldset .hint { display: block; margin: -4px 0 10px; }
.modal-frame .flag-picker { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }

/* ---------- type-to-filter picker (static/js/combobox.js) ---------- */

.combobox { position: relative; min-width: 14rem; }
.combobox .combobox-input { width: 100%; padding-right: 44px; }
.combobox-toggle {
  position: absolute;
  top: 4px;
  right: 4px;
  bottom: 4px;
  width: 36px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
}
.combobox-toggle:hover { background: var(--color-brand-soft); color: var(--color-brand); }
.combobox-toggle:disabled { cursor: not-allowed; opacity: 0.45; background: transparent; }
.combobox-toggle svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.15s ease;
}
.combobox-input[aria-expanded="true"] + .combobox-toggle svg { transform: rotate(180deg); }
.combobox-list {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 25;
  max-height: 17rem;
  overflow-y: auto;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}
.combobox.is-above .combobox-list { top: auto; bottom: calc(100% + 6px); }
.combobox-option {
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  overflow-wrap: anywhere;
}
.combobox-option:hover { background: var(--color-surface-muted); }
.combobox-option[aria-selected="true"] { background: var(--color-brand-soft); color: var(--color-brand); }
.combobox-option.is-current { font-weight: 700; }
.combobox-option mark {
  background: transparent;
  color: inherit;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.combobox-note { padding: 8px 10px; color: var(--color-text-dim); font-size: 0.85rem; }

/* ---------- narrow screens: the section bar becomes a menu ----------

   The collapse point is the whole bar's width, not the links': the old
   760px rule was sized to the six staff links alone ("about 520px"), so
   between 761 and 879px the staff bar pushed the account menu — Settings,
   Sign out — off the right edge and the page scrolled sideways. Each
   workspace now folds where its own row stops fitting. The disclosure's
   summary names the current section, so where you are stays visible. */

/* It does not shrink: the organisation's name beside it is what gives. */
.nav-menu { display: none; position: relative; flex: 0 0 auto; }
.nav-menu > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid var(--nav-edge);
  border-radius: var(--radius-sm);
  color: var(--nav-current);
  background: var(--nav-current-bg);
  font-weight: 700;
  font-size: 0.92rem;
  white-space: nowrap;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.app-nav[data-workspace="staff"] .nav-menu > summary { border-color: rgba(228, 224, 219, 0.22); }
.nav-menu > summary::-webkit-details-marker { display: none; }
.nav-menu > summary::after {
  content: "";
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  margin: -4px 0 0 2px;
}
.nav-menu[open] > summary::after { transform: rotate(-135deg); margin-top: 3px; }
.nav-menu .dropdown-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 220px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  z-index: 40;
}
.nav-menu .dropdown-menu a {
  display: flex;
  align-items: center;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  font-weight: 600;
}
.nav-menu .dropdown-menu a:hover { color: var(--color-brand); }
.nav-menu .dropdown-menu a[aria-current] {
  background: var(--color-brand-soft);
  color: var(--color-brand);
  font-weight: 700;
}

/* Staff: six sections, a tag and the account chip need ~860px. */
@media (max-width: 920px) {
  .app-nav[data-workspace="staff"] .section-nav { display: none; }
  .app-nav[data-workspace="staff"] .nav-menu { display: block; margin-right: auto; }
}
/* Customer: the organisation's name gives way before the links do... */
@media (max-width: 880px) and (min-width: 781px) {
  .app-nav[data-workspace="customer"] .workspace-org { display: none; }
}
/* ...and five sections fold into the menu below ~780px, where the name has
   room to come back beside the menu. */
@media (max-width: 780px) {
  .app-nav[data-workspace="customer"] .section-nav { display: none; }
  .app-nav[data-workspace="customer"] .nav-menu { display: block; margin-right: auto; }
}

@media (max-width: 760px) {
  .app-nav-inner { gap: 12px; padding: 0 16px; }
  .app-subnav-inner { padding: 0 16px; gap: 12px; }
  /* The menu's summary already names the section. */
  .app-subnav .section-name { display: none; }
  .app-subnav .page-tabs { overflow-x: auto; scrollbar-width: none; }
  .app-subnav .page-tabs::-webkit-scrollbar { display: none; }
  /* Tabs can run past the edge with no scrollbar to say so: fade the side
     that has more (the script in _nav.html sets these). */
  .app-subnav .page-tabs.fade-end {
    mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent);
  }
  .app-subnav .page-tabs.fade-start {
    mask-image: linear-gradient(to left, #000 calc(100% - 32px), transparent);
  }
  .app-subnav .page-tabs.fade-start.fade-end {
    mask-image: linear-gradient(to right, transparent, #000 32px, #000 calc(100% - 32px), transparent);
  }
  main.shell { padding: 28px 16px 64px; }
  /* The plain .table (members, devices, tokens, flags) has no scroller of
     its own the way .org-table has .table-shell; its card becomes one. */
  .card:has(table.table) { overflow-x: auto; position: relative; }

  /* Same sticky-first-column treatment .org-table gets below: a plain
     .table that overflows (tokens, devices) otherwise loses the row's
     identity — the name — off-screen along with everything else scrolled
     past, leaving only actions with no label to say whose they are. */
  .table th:first-child,
  .table td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--color-surface);
    border-right: 1px solid var(--color-border);
  }
  .table thead th:first-child { background: var(--color-surface-muted); z-index: 2; }
  .table tbody tr:hover td:first-child { background: var(--color-surface-muted); }

  /* A wide list table scrolls inside .table-shell rather than the page —
     correct, but it used to take the row's identity with it: reaching a
     row's "Delete" meant losing sight of which organisation you were about
     to act on. The first column stays put; everything else
     scrolls under it. Sticky is relative to .table-shell, the nearest
     scrolling ancestor, not the viewport. */
  .org-table th:first-child,
  .org-table td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--color-surface);
    border-right: 1px solid var(--color-border);
  }
  .org-table thead th:first-child { background: var(--color-surface-muted); z-index: 2; }
  .org-table tbody tr:hover td:first-child { background: var(--color-surface-muted); }
}
@media (max-width: 480px) {
  /* The mark identifies the product; the wordmark is the first thing to
     give up its room to the controls. Out of sight, not out of the tree:
     with the mark aria-hidden it is the home link's only name. */
  .app-nav .brand-name {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .app-nav .workspace-org { padding-left: 10px; }
  /* Anchored under its button, the section menu ran past the right edge on
     a small phone; it drops as a full-width panel under the bar instead. */
  .nav-menu .dropdown-menu {
    position: fixed;
    top: calc(var(--app-nav-h) + 6px);
    left: 12px;
    right: 12px;
    min-width: 0;
  }
  .page-header { margin-bottom: 20px; }
  h1 { font-size: 1.6rem; }
}
@media (max-width: 359px) {
  /* A name cut to "N…" says nothing; at this width the bar keeps only what
     you act on. */
  .app-nav .workspace-org { display: none; }
}

/* ---------- touch: targets sized for a finger ----------

   Pointer type, not screen width: a touchscreen laptop needs these, a phone
   with a mouse does not. Desktop density stays as it was for the mouse. */
@media (pointer: coarse) {
  .app-nav .section-nav a,
  .record-tabs a,
  .nav-menu > summary,
  .app-nav .account-menu > summary,
  .nav-menu .dropdown-menu a,
  .app-nav .account-menu .dropdown-menu a,
  .app-nav .account-menu .dropdown-menu button,
  .app-nav .account-menu .theme-toggle,
  .btn,
  .row-actions .btn,
  .page-btn,
  .th-sort {
    min-height: 44px;
  }
  .page-btn { min-width: 44px; }
  .row-actions { gap: 6px; }
  .org-table .count-link { min-width: 44px; min-height: 44px; }
  input[type="text"], input[type="email"], input[type="password"], input[type="number"],
  input[type="date"], input[type="datetime-local"], select, textarea { min-height: 44px; font-size: 16px; }
  .datepicker-popover { width: 21rem; }
  .datepicker-day { width: 40px; height: 40px; }
  .datepicker-nav { width: 44px; height: 44px; }
  .datepicker-chip, .datepicker-clear { min-height: 36px; }
}

/* ---------- docs ----------

   Product documentation (docs/release_pipeline_plan.md §7.7), rendered from
   Markdown into the dashboard's own chrome. `.docs-content` styles what the
   renderer emits — Python-Markdown's admonitions, figures, tables, details,
   footnotes and embeds — so the docs repo never ships CSS of its own. The
   bar, the sidebar and "On this page" stick under the top bar and trail. */

:root {
  --docs-top: calc(var(--app-nav-h) + 48px);
  --docs-bar-h: 56px;
}

.docs-bar {
  position: sticky;
  top: var(--docs-top);
  z-index: 28;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}
.docs-bar-inner {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 24px;
  min-height: var(--docs-bar-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.docs-bar-title {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.docs-home {
  font-weight: 700;
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.docs-home:hover { color: var(--color-brand); }
.docs-version {
  padding: 4px 28px 4px 10px;
  font-size: 0.85rem;
  font-family: var(--font-mono);
}
.docs-search {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 1 360px;
}
.docs-search input[type="search"] {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 0.92rem;
  padding: 7px 12px;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-text);
  outline: none;
}
.docs-search input[type="search"]:focus {
  border-color: var(--color-brand);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.docs-layout {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 24px;
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr) 200px;
  gap: 40px;
  align-items: start;
}

/* Sidebar */
.docs-sidebar {
  position: sticky;
  top: calc(var(--docs-top) + var(--docs-bar-h) + 1px);
  max-height: calc(100vh - var(--docs-top) - var(--docs-bar-h) - 1px);
  overflow-y: auto;
  padding: 24px 4px 40px 0;
  font-size: 0.92rem;
}
.docs-menu > summary {
  display: none;
}
.docs-nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.docs-nav-list.nested {
  margin: 2px 0 6px 10px;
  padding-left: 8px;
  border-left: 1px solid var(--color-border);
}
.docs-nav-list a {
  display: block;
  padding: 5px 10px;
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  line-height: 1.35;
}
.docs-nav-list a:hover {
  color: var(--color-brand);
  background: var(--color-brand-soft);
}
.docs-nav-list a.active {
  color: var(--color-brand);
  background: var(--color-brand-soft);
  font-weight: 700;
}
.docs-nav-section > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 7px 10px;
  margin-top: 6px;
  border-radius: var(--radius-sm);
  font-weight: 700;
  color: var(--color-text);
}
.docs-nav-section > summary::-webkit-details-marker { display: none; }
.docs-nav-section > summary::after {
  content: "";
  width: 7px;
  height: 7px;
  border-right: 2px solid var(--color-text-dim);
  border-bottom: 2px solid var(--color-text-dim);
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
  flex: 0 0 auto;
  margin-left: 8px;
}
.docs-nav-section[open] > summary::after { transform: rotate(45deg); }
.docs-nav-section > summary:hover { background: var(--color-surface-muted); }
.docs-nav-list.nested .docs-nav-section > summary {
  font-weight: 600;
  color: var(--color-text-muted);
  margin-top: 2px;
}

/* Page */
.docs-main {
  min-width: 0;
  padding: 32px 0 80px;
}
.docs-main:focus { outline: none; }
.docs-banner { margin: 0 0 24px; }

.docs-content {
  max-width: 78ch;
  font-size: 1rem;
  line-height: 1.7;
  color: var(--color-text);
  overflow-wrap: break-word;
}
.docs-content > :first-child { margin-top: 0; }
.docs-content h1 { font-size: 2.1rem; margin: 0 0 0.6em; }
.docs-content h2 {
  font-size: 1.45rem;
  margin: 2em 0 0.6em;
  padding-top: 1.1em;
  border-top: 1px solid var(--color-border);
}
/* A heading straight after a title, or after a rule the page drew itself,
   needs no second line. */
.docs-content h1 + h2,
.docs-content h1 + p + h2,
.docs-content hr + h2 { border-top: 0; padding-top: 0; }
.docs-content h3 { font-size: 1.15rem; margin: 1.6em 0 0.5em; }
.docs-content h4 { font-size: 1rem; margin: 1.4em 0 0.4em; }
.docs-content h1, .docs-content h2, .docs-content h3, .docs-content h4 {
  scroll-margin-top: calc(var(--docs-top) + var(--docs-bar-h) + 16px);
}
.docs-content p, .docs-content ul, .docs-content ol, .docs-content dl,
.docs-content table, .docs-content figure, .docs-content pre,
.docs-content .admonition, .docs-content details { margin: 0 0 1.1em; }
.docs-content ul, .docs-content ol { padding-left: 1.5em; }
.docs-content li + li { margin-top: 0.25em; }
.docs-content li > ul, .docs-content li > ol { margin: 0.3em 0 0.3em; }
.docs-content a { color: var(--color-brand); text-decoration: underline; text-decoration-color: var(--color-brand-line); text-underline-offset: 2px; }
.docs-content a:hover { text-decoration-color: currentColor; }
.docs-content hr { border: 0; border-top: 1px solid var(--color-border); margin: 2em 0; }
.docs-content strong { font-weight: 700; }
.docs-content img { max-width: 100%; height: auto; vertical-align: middle; }
.docs-content img.zoomable { cursor: zoom-in; }
.docs-content blockquote {
  margin: 0 0 1.1em;
  padding: 0.2em 1em;
  border-left: 3px solid var(--color-border-strong);
  color: var(--color-text-muted);
}
.docs-content code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  padding: 0.1em 0.35em;
  border-radius: 4px;
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
}
.docs-content pre {
  padding: 14px 16px;
  overflow-x: auto;
  border-radius: var(--radius-md);
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
  line-height: 1.5;
}
.docs-content pre code { padding: 0; border: 0; background: none; font-size: 0.85rem; }
.docs-content dt { font-weight: 700; }
.docs-content dd { margin: 0 0 0.6em 1.2em; }
.docs-content mark,
.docs-hits mark {
  background: var(--color-accent-soft);
  color: inherit;
  padding: 0 2px;
  border-radius: 3px;
}

/* Tables scroll inside themselves rather than widening the page. */
.docs-content table {
  display: block;
  max-width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  font-size: 0.92rem;
  line-height: 1.5;
}
.docs-content th, .docs-content td {
  padding: 9px 12px;
  border-bottom: 1px solid var(--color-border);
  vertical-align: top;
  text-align: left;
}
.docs-content th {
  background: var(--color-surface-muted);
  font-weight: 700;
  white-space: nowrap;
  border-bottom-color: var(--color-border-strong);
}

/* Figures: screenshots at column width, captions under them. */
.docs-content figure {
  margin: 1.4em 0;
  text-align: center;
}
.docs-content figure > p { margin: 0; }
.docs-content figure img {
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  background: var(--color-surface);
}
.docs-content figcaption {
  margin-top: 8px;
  font-size: 0.86rem;
  color: var(--color-text-dim);
  line-height: 1.45;
}

/* Embedded video: the docs wrap it in inline-styled boxes the sanitiser
   strips, so the frame sizes itself. */
.docs-content iframe {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--color-surface-muted);
  margin: 0 0 1.1em;
}

/* Screen recordings stored with the docs: column width like the screenshots
   (their inline width is stripped too). */
.docs-content video {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  background: var(--color-surface);
}

/* Admonitions (!!! tip) and collapsible ones (??? note). */
.docs-content .admonition,
.docs-content details {
  --adm: var(--color-brand);
  --adm-soft: var(--color-brand-soft);
  padding: 12px 16px;
  border-left: 3px solid var(--adm);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--adm-soft);
}
.docs-content .admonition > :last-child,
.docs-content details > :last-child { margin-bottom: 0; }
.docs-content .admonition-title,
.docs-content details > summary {
  margin: 0 0 4px;
  font-weight: 700;
  color: var(--adm);
}
.docs-content details > summary { cursor: pointer; margin: 0; }
.docs-content details[open] > summary { margin-bottom: 6px; }
.docs-content :is(.admonition, details):is(.tip, .hint, .important, .success, .check, .done, .example) {
  --adm: var(--color-success-ink);
  --adm-soft: var(--color-success-soft);
}
.docs-content :is(.admonition, details):is(.warning, .caution, .attention, .question, .help, .faq) {
  --adm: var(--color-warning-ink);
  --adm-soft: var(--color-warning-soft);
}
.docs-content :is(.admonition, details):is(.danger, .error, .failure, .fail, .missing, .bug) {
  --adm: var(--color-danger-ink);
  --adm-soft: var(--color-danger-soft);
}
.docs-content :is(.admonition, details):is(.quote, .cite) {
  --adm: var(--color-slate);
  --adm-soft: var(--color-slate-soft);
}

/* Cards: a list inside <div class="grid cards">. The dashboard's own .grid
   makes the wrapper a grid; the list inside is what lays out as cards. */
.docs-content .grid.cards { display: block; }
.docs-content .grid.cards > ul {
  list-style: none;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
}
.docs-content .grid.cards > ul > li {
  margin: 0;
  padding: 16px 18px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}
.docs-content .grid.cards > ul > li > p { margin: 0 0 0.5em; }
.docs-content .grid.cards > ul > li > p:last-child { margin-bottom: 0; }
.docs-content .grid.cards img { border: 0; margin-right: 6px; }

/* Maths from pymdownx.arithmatex, typeset by MathJax on pages that have it. */
.docs-content .arithmatex { overflow-x: auto; }
.docs-content div.arithmatex { margin: 0 0 1.1em; }

.docs-content .footnote { font-size: 0.88rem; color: var(--color-text-muted); }

/* Release notes shown outside the docs: the release page, downloads. */
.docs-content.compact { font-size: 0.93rem; line-height: 1.6; max-width: none; }
.docs-content.compact h3 { font-size: 0.95rem; margin: 1em 0 0.3em; }
.release-notes { margin-top: 12px; }
.release-notes > summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--color-brand);
}
.release-notes[open] > summary { margin-bottom: 8px; }

/* Previous / next */
.docs-pager {
  max-width: 78ch;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  margin-top: 48px;
}
.docs-pager a {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 14px 16px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
}
.docs-pager a:hover { border-color: var(--color-brand-line); color: var(--color-brand); }
.docs-pager .dim { font-size: 0.8rem; }
.docs-pager-next { text-align: right; grid-column: 2; }

.docs-feedback {
  max-width: 78ch;
  margin-top: 40px;
  padding-top: 16px;
  border-top: 1px solid var(--color-border);
  font-size: 0.88rem;
}

/* Search results */
.docs-hits { list-style: none; padding: 0; }
.docs-hits li {
  padding: 14px 0;
  border-bottom: 1px solid var(--color-border);
}
.docs-hits li + li { margin: 0; }
.docs-hits a { font-weight: 700; font-size: 1.05rem; text-decoration: none; }
.docs-hits p { margin: 4px 0 0; color: var(--color-text-muted); font-size: 0.92rem; }

/* On this page */
.docs-toc {
  position: sticky;
  top: calc(var(--docs-top) + var(--docs-bar-h) + 1px);
  max-height: calc(100vh - var(--docs-top) - var(--docs-bar-h) - 1px);
  overflow-y: auto;
  padding: 32px 0 40px;
  font-size: 0.85rem;
}
.docs-toc-title {
  margin: 0 0 8px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-text-dim);
}
.docs-toc ul { list-style: none; margin: 0; padding: 0; border-left: 1px solid var(--color-border); }
.docs-toc a {
  display: block;
  padding: 3px 0 3px 12px;
  color: var(--color-text-muted);
  line-height: 1.35;
}
.docs-toc .level-3 a { padding-left: 24px; font-size: 0.82rem; }
.docs-toc a:hover { color: var(--color-brand); }

/* Full-size image */
.docs-lightbox {
  max-width: min(96vw, 1600px);
  max-height: 94vh;
  padding: 12px;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--color-surface);
  box-shadow: var(--shadow-lg);
}
.docs-lightbox::backdrop { background: rgba(8, 20, 26, 0.72); }
.docs-lightbox form { display: flex; justify-content: flex-end; margin-bottom: 8px; }
.docs-lightbox img { display: block; max-width: 100%; max-height: calc(94vh - 72px); margin: 0 auto; }

@media (max-width: 1180px) {
  .docs-layout { grid-template-columns: 240px minmax(0, 1fr); }
  .docs-toc { display: none; }
}
@media (max-width: 900px) {
  .docs-bar { position: static; }
  .docs-layout { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .docs-sidebar {
    position: static;
    max-height: none;
    overflow: visible;
    padding: 12px 0 0;
  }
  .docs-menu {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    padding: 4px 8px;
  }
  .docs-menu > summary {
    display: block;
    cursor: pointer;
    padding: 8px 4px;
    font-weight: 700;
  }
  .docs-menu[open] > summary { border-bottom: 1px solid var(--color-border); margin-bottom: 6px; }
  .docs-main { padding-top: 20px; }
  .docs-content h1 { font-size: 1.7rem; }
  .docs-search { flex-basis: 100%; }
  .docs-bar-inner { padding: 10px 16px; }
  .docs-layout { padding: 0 16px; }
  .docs-pager { grid-template-columns: 1fr; }
  .docs-pager-next { grid-column: 1; }
}

@media print {
  .app-nav, .app-subnav, .skip-link, .docs-bar, .docs-sidebar, .docs-toc, .docs-pager,
  .docs-feedback, .docs-banner { display: none !important; }
  .docs-layout { display: block; max-width: none; padding: 0; }
  .docs-main { padding: 0; }
  .docs-content { max-width: none; }
  .docs-content details { display: block; }
}

/* ---------- manual test plan (/admin/test-plan) ----------

   A run page is a totals card, then a sticky jump list of suites beside the
   suites themselves. A suite with several columns (licence profiles, builds,
   entry points) is a matrix of compact result chips; a suite with one column
   is a checklist. Each outcome has one tint, used by the chip, the matrix
   cell, the checklist line, the bar and the dialog's choice alike. */

.qa-run { max-width: 1440px; }

/* --- outcome tints --- */
.qa-is-passed   { --qa-ink: var(--color-success-ink); --qa-soft: var(--color-success-soft); --qa-line: var(--color-success-line); }
.qa-is-failed   { --qa-ink: var(--color-danger-ink);  --qa-soft: var(--color-danger-soft);  --qa-line: var(--color-danger-line); }
.qa-is-blocked  { --qa-ink: var(--color-warning-ink); --qa-soft: var(--color-warning-soft); --qa-line: var(--color-warning); }
.qa-is-skipped  { --qa-ink: var(--color-text-muted);  --qa-soft: var(--color-surface-muted); --qa-line: var(--color-border-strong); }
.qa-is-untested { --qa-ink: var(--color-text-dim);    --qa-soft: transparent;               --qa-line: var(--color-border-strong); }

.qa-icon {
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.qa-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px 2px 6px;
  border-radius: 999px;
  border: 1px solid var(--qa-line);
  background: var(--qa-soft);
  color: var(--qa-ink);
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.4;
  white-space: nowrap;
}
.qa-chip.qa-is-untested { border-style: dashed; font-weight: 600; }
/* An untested matrix cell: a faint ring, so the cells with results stand out. */
.qa-chip-empty { padding: 2px 6px; opacity: 0.55; }
.qa-cell:hover .qa-chip-empty, .qa-cell:focus-visible .qa-chip-empty { opacity: 1; }

/* --- progress --- */
.qa-progress {
  display: flex;
  height: 8px;
  border-radius: 999px;
  overflow: hidden;
  background: var(--color-surface-muted);
  box-shadow: inset 0 0 0 1px var(--color-border);
}
.qa-progress-lg { height: 12px; }
.qa-progress-passed  { background: var(--color-success); }
.qa-progress-failed  { background: var(--color-danger); }
.qa-progress-blocked { background: var(--color-warning); }
.qa-progress-skipped { background: var(--color-border-strong); }
.qa-progress-caption { display: block; margin-top: 6px; font-size: 0.8rem; color: var(--color-text-dim); }

/* --- run page: totals --- */
.qa-run-notes { white-space: pre-line; }

.qa-summary { display: flex; flex-direction: column; gap: 14px; }
.qa-summary-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 20px;
  flex-wrap: wrap;
}
.qa-summary-head p { margin: 0; }
.qa-figure strong { font-size: 1.9rem; font-weight: 800; letter-spacing: -0.02em; margin-right: 6px; }
.qa-latest { font-size: 0.86rem; }
.qa-summary-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px 24px;
  flex-wrap: wrap;
}
.qa-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.9rem;
  color: var(--color-text-muted);
}
.qa-stats li { display: inline-flex; align-items: center; gap: 6px; }
.qa-stats strong { color: var(--color-text); }
.qa-swatch { width: 10px; height: 10px; border-radius: 3px; }
.qa-swatch-untested { box-shadow: inset 0 0 0 1px var(--color-border-strong); }

/* Segmented filter: one control, three positions. */
.qa-filter {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  border-radius: var(--radius-md);
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
}
.qa-filter a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: calc(var(--radius-md) - 3px);
  color: var(--color-text-muted);
  font-size: 0.86rem;
  font-weight: 600;
  text-decoration: none;
}
.qa-filter a:hover { color: var(--color-brand); }
.qa-filter a.active {
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: var(--shadow-sm);
}
.qa-filter-count {
  min-width: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--color-bg);
  font-size: 0.75rem;
  text-align: center;
}

/* --- run page: jump list beside the suites --- */
.qa-layout {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap: 24px;
  align-items: start;
  margin-top: 20px;
}
.qa-aside {
  position: sticky;
  top: calc(var(--app-nav-h) + 64px);
  max-height: calc(100vh - var(--app-nav-h) - 88px);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.qa-nav-heading {
  margin: 0 0 6px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-text-dim);
}
.qa-nav ol { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 2px; }
.qa-nav a {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 8px;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  color: var(--color-text);
  text-decoration: none;
  font-size: 0.86rem;
}
.qa-nav a:hover { background: var(--color-brand-soft); }
.qa-nav-title { font-weight: 600; line-height: 1.3; }
.qa-nav-meta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78rem;
  color: var(--color-text-dim);
  font-variant-numeric: tabular-nums;
}
.qa-nav-bar {
  grid-column: 1 / -1;
  height: 3px;
  border-radius: 999px;
  background: var(--color-border);
  overflow: hidden;
}
.qa-nav-bar > span { display: block; height: 100%; background: var(--color-brand); }
.qa-done { color: var(--color-success-ink); font-weight: 700; }
.qa-flag {
  padding: 0 7px;
  border-radius: 999px;
  background: var(--color-danger-soft);
  color: var(--color-danger-ink);
  font-size: 0.75rem;
  font-weight: 700;
}

.qa-legend {
  padding: 10px 12px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  font-size: 0.84rem;
}
.qa-legend summary { cursor: pointer; font-weight: 600; }
.qa-legend dl { margin: 10px 0 0; display: grid; gap: 8px; }
.qa-legend dt { font-weight: 700; }
.qa-legend dd { margin: 0; color: var(--color-text-muted); }

/* --- run page: suites --- */
.qa-suites { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.qa-suite { padding: 0; overflow: hidden; scroll-margin-top: calc(var(--app-nav-h) + 64px); }
.qa-suite-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  padding: 18px 20px 14px;
  border-bottom: 1px solid var(--color-border);
}
.qa-suite-head h2 { margin: 0; font-size: 1.1rem; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.qa-suite-intro { margin: 4px 0 0; font-size: 0.88rem; color: var(--color-text-muted); max-width: 75ch; }
.qa-suite-count {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: none;
  font-size: 0.86rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.qa-suite-empty { margin: 0; padding: 18px 20px; color: var(--color-text-dim); font-size: 0.9rem; }

.qa-check-title { font-weight: 600; color: var(--color-text); }
.qa-steps { display: block; font-size: 0.8rem; color: var(--color-text-dim); }

/* Checklist: a line per check, the whole line a link. */
.qa-checklist { margin: 0; padding: 0; list-style: none; }
.qa-checklist li + li { border-top: 1px solid var(--color-border); }
.qa-checklist li { scroll-margin-top: calc(var(--app-nav-h) + 64px); }
.qa-check {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr) minmax(0, 0.8fr);
  gap: 4px 16px;
  align-items: start;
  padding: 12px 20px;
  color: inherit;
  text-decoration: none;
  border-left: 3px solid transparent;
}
.qa-check:not(.qa-is-untested) { border-left-color: var(--qa-line); }
.qa-check.qa-is-failed, .qa-check.qa-is-blocked { background: var(--qa-soft); }
.qa-check:hover { background: var(--color-brand-soft); }
.qa-check:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
.qa-check .qa-chip { justify-self: start; margin-top: 1px; }
.qa-check-body { display: flex; flex-direction: column; gap: 2px; font-size: 0.9rem; }
.qa-check-expect { color: var(--color-text-muted); font-size: 0.86rem; }
.qa-check-meta { display: flex; flex-direction: column; gap: 2px; font-size: 0.82rem; text-align: right; }
.qa-check-note { color: var(--color-text); overflow-wrap: anywhere; }
.qa-check-by { color: var(--color-text-dim); }

/* Matrix: the check, then one compact cell per column. */
.qa-matrix-wrap { overflow-x: auto; }
.qa-matrix { width: 100%; border-collapse: collapse; font-size: 0.86rem; }
.qa-matrix th, .qa-matrix td {
  padding: 8px;
  border-bottom: 1px solid var(--color-border);
  vertical-align: top;
  text-align: left;
}
.qa-matrix tbody tr:last-child > * { border-bottom: 0; }
.qa-matrix tbody tr { scroll-margin-top: calc(var(--app-nav-h) + 64px); }
.qa-matrix tbody tr:hover > * { background: var(--color-surface-muted); }
.qa-matrix thead th {
  position: sticky;
  top: 0;
  background: var(--color-surface);
  font-weight: 600;
  white-space: nowrap;
  color: var(--color-text-muted);
}
.qa-matrix thead th[title] { cursor: help; }
.qa-matrix .qa-col-check {
  padding-left: 20px;
  min-width: 230px;
  width: 30%;
  font-weight: 400;
}
.qa-matrix tbody .qa-col-check { display: table-cell; }
.qa-matrix tbody .qa-col-check > span { display: block; }
.qa-col-code {
  display: inline-block;
  margin-right: 4px;
  padding: 0 6px;
  border-radius: 4px;
  background: var(--color-brand-soft);
  color: var(--color-brand);
  font-weight: 800;
  font-size: 0.75rem;
}
.qa-col-name { font-size: 0.8rem; }
.qa-matrix thead .qa-col-name { display: block; margin-top: 2px; }
.qa-usual { margin-top: 2px; font-size: 0.8rem; color: var(--color-text-muted); }
.qa-matrix td { min-width: 104px; }
.qa-matrix td:last-child { padding-right: 20px; }
.qa-na { color: var(--color-text-dim); text-align: center !important; vertical-align: middle !important; }

.qa-cell {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  margin: -4px;
  padding: 4px;
  border-radius: var(--radius-sm);
  color: inherit;
  text-decoration: none;
}
.qa-cell:hover .qa-chip { box-shadow: 0 0 0 3px var(--color-focus-ring); }
.qa-cell:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 1px; }
.qa-cell-expect {
  font-size: 0.76rem;
  line-height: 1.3;
  color: var(--color-text-muted);
  padding-left: 6px;
  border-left: 2px solid var(--color-accent);
}
.qa-cell-note {
  font-size: 0.76rem;
  line-height: 1.3;
  color: var(--color-text);
  font-style: italic;
  overflow-wrap: anywhere;
}

/* --- cell dialog --- */
.qa-expect-box {
  margin-bottom: 16px;
  padding: 12px 14px;
  border-radius: var(--radius-md);
  background: var(--color-brand-soft);
  border: 1px solid var(--color-brand-line);
}
.qa-expect-box p { margin: 2px 0 0; }
.qa-expect-label {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-brand);
}
.qa-expect-steps { margin-top: 10px !important; font-size: 0.88rem; color: var(--color-text-muted); }
.qa-recorded { display: flex; align-items: center; gap: 8px; margin: 0 0 14px; font-size: 0.88rem; }

.qa-outcomes { display: flex; flex-wrap: wrap; gap: 8px; }
.qa-outcome {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  color: var(--color-text-muted);
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
}
.qa-outcome input { position: absolute; opacity: 0; pointer-events: none; }
.qa-outcome:hover { border-color: var(--qa-line); color: var(--qa-ink); }
.qa-outcome:has(input:checked) {
  border-color: var(--qa-line);
  background: var(--qa-soft);
  color: var(--qa-ink);
  box-shadow: inset 0 0 0 1px var(--qa-line);
}
.qa-outcome.qa-is-untested:has(input:checked) { background: var(--color-surface-muted); color: var(--color-text); }
.qa-outcome:has(input:focus-visible) { outline: 2px solid var(--color-focus); outline-offset: 2px; }

.qa-note {
  white-space: pre-line;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: var(--color-surface-muted);
}

/* --- runs list --- */
.qa-runs td { vertical-align: top; }
.qa-run-link { font-weight: 700; margin-right: 6px; }
.qa-run-build { display: block; margin-top: 2px; font-size: 0.8rem; color: var(--color-text-dim); }
.qa-runs-progress { min-width: 200px; }
.qa-empty { text-align: center; padding: 40px 24px; }
.qa-empty h2 { margin: 0 0 6px; font-size: 1.15rem; }
.qa-empty p { margin: 0 auto 18px; max-width: 52ch; }
.qa-reference { margin-top: 24px; }
.qa-card-title { margin: 0 0 6px; font-size: 1.05rem; }
.qa-reference .card > p.dim { margin: 0 0 14px; font-size: 0.88rem; }
.qa-profile-list { margin: 0; display: grid; gap: 10px; }
.qa-profile-list dt { font-weight: 600; font-size: 0.9rem; }
.qa-profile-list dd { margin: 2px 0 0; font-size: 0.85rem; color: var(--color-text-muted); }
.qa-suite-group {
  margin: 16px 0 6px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-text-dim);
}
.qa-suite-group:first-of-type { margin-top: 0; }
.qa-suite-list { margin: 0; padding: 0; list-style: none; }
.qa-suite-list li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 0;
  border-bottom: 1px solid var(--color-border);
  font-size: 0.9rem;
}
.qa-suite-list li:last-child { border-bottom: 0; }

@media (max-width: 960px) {
  .qa-layout { grid-template-columns: minmax(0, 1fr); }
  .qa-aside { position: static; max-height: none; }
  .qa-check { grid-template-columns: 96px minmax(0, 1fr); }
  .qa-check-meta { grid-column: 2; text-align: left; }
}

/* ---------- reduced motion ----------

   Movement goes; state changes stay. Colour and opacity fades are kept —
   they carry state without moving anything — while slides, lifts and the
   theme toggle's rolling thumb become instant. The loading spinners slow
   down rather than stop: a frozen spinner reads as a hung page. */
@media (prefers-reduced-motion: reduce) {
  html, body { transition: none; }
  .btn:active { transform: none; }
  .skip-link { transition: none; }
  .theme-toggle .switch-thumb,
  .theme-toggle .switch-thumb .icon { transition: none; }
  .modal[open] { animation-name: modal-fade; }
  .modal.modal-nudge { animation: modal-fade 0.28s ease-in-out; }
  .combobox-toggle svg { transition: none; }
  .search-indicator,
  .spinner { animation-duration: 2.4s; }
}

/* ---------- customer overview ----------
   The landing page (app/templates/dashboard.html): a welcome, the release
   card, then the licences. The licence mark keeps
   its own teal and cream in both themes (--band-*), because --color-brand
   turns light in dark mode. */
:root {
  --band-bg: #08636c;
  --band-text: #f3f0eb;    /* brand cream, lifted: 6.0:1 on --band-bg */
}
:root[data-theme="dark"] { --band-bg: #0f3a40; }

.ov-head h1 { margin: 0; }
.ov-head .lede {
  margin: 6px 0 0;
  color: var(--color-text-muted);
  max-width: 65ch;
}
.ov-head .lede strong { color: var(--color-text); font-weight: 700; }
main.shell.overview > .ov-head + * { margin-top: 28px; }

/* Trial panel (docs/trial_signup_plan.md): above the release card while a
   trial runs. One line with the time left, then the steps to a working
   install; a step that is done turns teal and loses its button. */
.trial-panel {
  background: var(--color-brand-soft);
  border: 1px solid var(--color-brand-line);
  border-radius: var(--radius-lg);
  padding: 24px 28px;
}
.trial-panel + * { margin-top: 24px; }
.trial-panel.is-ended { background: var(--color-accent-soft); border-color: var(--color-accent); }
.trial-line {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px 28px;
  flex-wrap: wrap;
}
.trial-line h2 { margin: 0; font-size: 1.45rem; font-weight: 800; line-height: 1.2; }
.trial-line p { margin: 6px 0 0; color: var(--color-text-muted); max-width: 70ch; }
.trial-line p strong { color: var(--color-text); }
.trial-line .trial-eyebrow {
  margin: 0 0 4px;
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-brand);
}
.trial-panel.is-ended .trial-line .trial-eyebrow { color: var(--color-warning-ink); }
.trial-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.trial-steps {
  list-style: none;
  margin: 22px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
  counter-reset: trial-step;
}
.trial-steps li {
  counter-increment: trial-step;
  display: flex;
  gap: 12px;
  align-items: flex-start;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 16px;
}
.trial-steps h3 { margin: 2px 0 4px; font-size: 1rem; }
.trial-steps p { margin: 0 0 10px; font-size: 0.9rem; color: var(--color-text-muted); }
.trial-steps p:last-child { margin-bottom: 0; }
.trial-step-mark {
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: 0.9rem;
  color: var(--color-brand);
  border: 2px solid var(--color-brand-line);
}
.trial-step-mark::before { content: counter(trial-step); }
.trial-steps li.is-done { background: transparent; }
.trial-steps li.is-done .trial-step-mark {
  background: var(--color-brand);
  border-color: var(--color-brand);
  color: var(--color-on-brand);
}
.trial-steps li.is-done .trial-step-mark::before { content: "\2713"; }
.trial-steps li.is-done h3 { color: var(--color-text-muted); }
@media (max-width: 820px) {
  .trial-panel { padding: 20px; }
  .trial-steps { grid-template-columns: minmax(0, 1fr); }
}

/* The trial confirmation page's summary of what is being started. */
.trial-terms {
  margin: 18px 0 20px;
  padding: 14px 16px;
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  display: grid;
  gap: 8px;
}
.trial-terms div { display: grid; grid-template-columns: 7.5rem minmax(0, 1fr); gap: 12px; }
.trial-terms dt { color: var(--color-text-muted); font-size: 0.88rem; }
.trial-terms dd { margin: 0; font-size: 0.92rem; }
@media (max-width: 480px) {
  .trial-terms div { grid-template-columns: minmax(0, 1fr); gap: 2px; }
}

/* Release card: what this release lets people do, under a headline with
   the download beside it. Light, like the rest of the page; each feature
   is led by HemoVision's own icon on a teal tile. Gold stays on the
   download button. */
.release-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 30px 32px;
}
.release-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 20px 32px;
  flex-wrap: wrap;
}
.release-eyebrow {
  margin: 0 0 6px;
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-brand);
}
.release-card h2 {
  margin: 0;
  font-size: 2.1rem;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
}
.release-actions { display: flex; gap: 10px; flex-wrap: wrap; }
/* With a video the top is one centred column: eyebrow, headline, the
   buttons, then the video, capped so it leads the card without filling the
   page. Download stays above it at every width. */
.release-top.has-video {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  text-align: center;
  gap: 18px;
}
.release-top.has-video .release-actions { justify-content: center; }
.release-video { width: min(100%, 820px); margin-top: 8px; }
.release-video iframe {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--color-surface-muted);
}
.release-features {
  list-style: none;
  margin: 26px 0 0;
  padding: 22px 0 0;
  border-top: 1px solid var(--color-border);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
}
.release-features li {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr);
  gap: 14px;
  align-items: start;
}
.release-icon {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  background: var(--color-brand-soft);
  color: var(--color-brand);
}
.release-icon > span {
  width: 24px;
  height: 24px;
  background: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
  mask: var(--icon) center / contain no-repeat;
}
.release-features h3 { margin: 0; font-size: 1.1rem; }
.release-features h3 a { color: var(--color-text); text-decoration: none; }
.release-features h3 a:hover { color: var(--color-brand); text-decoration: underline; text-underline-offset: 3px; }
.release-features p { margin: 4px 0 0; color: var(--color-text-muted); font-size: 0.95rem; }

/* Licences */
main.shell.overview > .ov-licences { margin-top: 36px; }
.ov-licences > h2 { margin-bottom: 14px; }

/* Documentation page (app/templates/docs.html) */
.docs-card-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px 24px;
  flex-wrap: wrap;
}
.docs-card h2 { margin: 0; font-size: 1.3rem; }
.docs-card-head p { margin: 4px 0 0; }
.docs-start { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--color-border); }
.docs-start h3 { margin: 0 0 8px; font-size: 1rem; }
.docs-start ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 6px 24px;
}
.docs-start a { font-weight: 600; }

@media (max-width: 760px) {
  .release-card { padding: 24px 20px; border-radius: var(--radius-md); }
  .release-card h2 { font-size: 1.75rem; }
  .release-features { grid-template-columns: minmax(0, 1fr); gap: 18px; margin-top: 22px; padding-top: 20px; }
}
