/* ============================================================
   Antdesign Hub Theme — glass design system for admin dashboards
   v1.3.7 (2026-09-13) — FLUID KPI AMOUNTS: a number never runs out of its cell again. The
                         amounts were drawn at a fixed 30px (.kpi), 26px (.stat) and 28px
                         (.lead) whatever the cell was wide, so a 31-day Order Hub range —
                         $101351.93 next to $84704.93 — painted straight over the neighbouring
                         cell, and a value carrying a unit ($37106.77 36.6%) pushed the unit
                         out of the panel. Each cell is now a size container
                         (`container-type: inline-size`) and its `.v` is sized in cqw OF THAT
                         CELL: `clamp(16px, 11cqw, 30px)` for .kpi, and the same idea with
                         their own maxima for .stat (26px), .lead (28px) and .clock .time
                         (26px). The type gives way before the box does. `min-width: 0`,
                         `white-space: nowrap` and `font-variant-numeric: tabular-nums` go with
                         it, plus a last-resort `overflow: hidden; text-overflow: ellipsis`
                         that is a tripwire, not a layout: at the narrowest cell the clamp
                         floor still draws a 12-character amount inside the content box, so it
                         never runs (audit H proves it). The floor is 16px and not the 18px
                         the first draft used: at the 170px cell a 12-digit count is 135.5px of
                         the 136px content box at 18px — inside, but by half a pixel, which is
                         not a guarantee. At 16px it is 120px in 136, and the longest amount in
                         reference.html ($1,234,567.89) 112px. The `.unit` is `flex: none` and the
                         `.v` wraps, so a unit with no room left on the line drops UNDER the
                         number instead of out of the cell. `.kpi-strip` columns are
                         `minmax(170px, 1fr)`: a seventh cell wraps to a second row rather than
                         squeezing the six above it. The 640px media query caps the MAX rather
                         than replacing the clamp — a fixed `font-size: 24px` there would have
                         taken the whole fix back on a phone, which is where the cells are
                         narrowest. Audit H (tools/audit-overflow.js) is the gate: at 1920,
                         1440, 1280 and 400px, dark and light, every `.kpi .v`, `.stat .v`,
                         `.lead .v` and `.clock .time` has `scrollWidth <= clientWidth` and its
                         rect inside its cell.
   v1.3.6 (2026-09-12) — the selected label is CENTRED in the closed <select>. Base appearance
                         (1.3.5) makes the control a flex box and clones the selected option
                         into it, and at the UA's `align-items: normal` that clone was
                         stretched to the whole content box with its text on the first line of
                         it: the label sat 1 px under the top edge with every spare pixel left
                         underneath — 16 px of it in a 36 px field, 12 px in the compact 32 px
                         table variant, and 18 px in a 40 px `.in`. The control now declares
                         `display: inline-flex; align-items: center` in its own right, plus
                         `line-height: 1` so the centring is the font's own box and not a line
                         box that moves with the page's body line-height, and
                         `justify-content: flex-start` so a control stretched to fill a cell
                         keeps its label at the left edge and away from the arrow, which is
                         unmoved — still the --chevron background image pinned right. `.in` is
                         named explicitly: its `font: inherit` SHORTHAND writes line-height too
                         and at (0,1,1) would take it straight back. An author
                         `selectedcontent` is centred and held to the control's full height for
                         the pages that write their own `<button><selectedcontent>`; Chrome 152
                         builds the default clone as an `-internal-select-inner-element` no
                         author selector reaches, which is why audit E measures this one with a
                         ruler in the UA shadow tree (tools/measure-centring.js) instead of
                         reading a computed value — the clone's BOX was centred the whole time
                         it was broken, because it filled the control. The list boxes are
                         excluded from all of it by name: `select[multiple]` is not a flex
                         button, and 1.3.5's `overflow: clip` was taking its scrollbar away.
   v1.3.5 (2026-09-11) — themed <select> PICKER: the open options menu is no longer the
                         operating system's popup. `appearance: base-select` on the control and
                         on `::picker(select)` (Chrome / Edge 135+, all of it behind @supports)
                         moves the list into the page, where it takes --surface-solid, a
                         --stroke-2 hairline, --r-sm, the theme shadow and a 6px gap under the
                         field over the theme's own --blur, fades in over 4px (honouring
                         prefers-reduced-motion) and flips
                         above the control when the viewport runs out. Rows are Inter 13.5,
                         --well-2 on hover, --glass-2 when selected, with the checkbox tick
                         re-used as a --violet-2 ::checkmark at the END of the row; optgroup
                         labels are 11.5px --text-3. The closed control keeps its 1.3.3 look
                         exactly, which takes work: base appearance clones the selected option
                         into a box in the UA shadow tree that no author rule can style, so a
                         long label wrapped and broke the control's height. `white-space`
                         inherits, so the label is held to one line and clipped at the CONTENT
                         box (`overflow-clip-margin`), which also means the arrow has to stay
                         the --chevron background image rather than the ::picker-icon
                         pseudo-element the clip would remove; --chevron-up is swapped in while
                         `select:open`. Safari and Firefox keep the native picker and the
                         unchanged 1.3.3 control. Audit G (tools/audit-select.js).
   v1.3.4 (2026-09-11) — themed tooltips: any element with `data-tip` renders its text as a
                         themed ::after (surface card, Inter 12/500, hairline border, theme
                         shadow) that fades in after 120 ms on hover and focus-visible, instead
                         of the browser's black system `title` box. Place with `data-tip-pos`
                         (top is the default, `.rail` items default to `right`, `.icon-btn`
                         to `bottom` because the top bar sits at the top of the viewport);
                         `data-tip-wide` wraps a long sentence instead of keeping it on one
                         line. Tooltips are off below 640 px, where the rail scrolls. Native
                         `title=` is now banned on icon controls — DESIGN.md 2h, audit F.
                         `.rail` and `.topbar` take a z-index so their tooltips are not painted
                         over: every .glass panel has a backdrop-filter, which is a stacking
                         context, and the panels come later in the DOM. Fixed while
                         screenshotting the hover state: `.rail .on:hover` keeps the violet
                         fill — in light mode the active item was turning white-on-white.
   v1.3.3 (2026-09-10) — every form control is themed at ELEMENT level: an <input>, <select>,
                         <textarea>, checkbox, radio or <button> with no class at all now looks
                         like .in / .btn instead of a native browser control. .in and .btn stay
                         as the explicit sizing variants; controls inside table.tbl td / .tbl-edit
                         are compact. New --chevron token so the select arrow follows --violet-2
                         in both modes, and color-scheme so native pickers match. .rail and
                         .icon-btn buttons take font: inherit too — the base rule skips them by
                         name, and without it they fell back to the UA's Arial. `.in` no longer
                         captures a checkbox/radio that happens to carry it: .in is the text
                         field, the box below is the box.
   v1.3.2 (2026-09-10) — light mode: .overlay drops its blur too (paper & ink has no blur;
                         only .glass was being reset, so every modal backdrop stayed frosted)
   v1.3.1 (2026-09-10) — light mode: .btn.primary and .btn.danger keep their fill (the
                         html[data-theme] .btn rule outranked them, so a primary button was
                         white on white); .btn.danger gets the soft error tint
   v1.3.0 (2026-09-11) — light mode rebuilt as "paper & ink": neutral off-white canvas, flat
                         white cards with hairline borders and a whisper of shadow, no blur,
                         borderless wells, ink-first numbers with desaturated accents, calm rail
   v1.2.2 (2026-09-11) — vertical rhythm: every direct child section of .main is separated by
                         --gap automatically (no more touching panels); .kpi-strip is a section too
   v1.2.1 (2026-09-11) — light mode redesigned: deeper accents, white cards with tinted
                         shadows, lavender wells, visible tracks; new --track / --bar-alt tokens
   v1.2.0 (2026-09-11) — type scale + icon sizing rules; icons.js is the only icon source
   v1.1.0 (2026-09-11) — full-width layout, real light mode (html[data-theme="light"]),
   KPI strip + world-clock components, numbers always in Inter (never monospace), no emoji icons
   v1.0.0 (2026-09-10) — initial
   Shared by hub, Order Hub, Comment Hub, post-hub, BIS, SEO, Idea Hub.
   Rules: Inter font, dark violet backdrop, frosted-glass panels,
   Chart.js visuals via charts.js. Never use raw blue link colors.
   ============================================================ */

:root {
  /* palette */
  --violet: #8b7cf6;
  --violet-2: #a99bff;
  --mint: #5fe3a1;
  --coral: #ff8a80;
  --sky: #7cc4ff;
  --gold: #ffc766;

  /* text */
  --text: #f3f1fb;
  --text-2: rgba(243, 241, 251, .72);
  --text-3: rgba(243, 241, 251, .48);
  --text-4: rgba(243, 241, 251, .30);

  /* glass */
  --glass: rgba(255, 255, 255, .06);
  --glass-2: rgba(255, 255, 255, .10);
  --glass-3: rgba(255, 255, 255, .16);
  --well: rgba(0, 0, 0, .18);
  --well-2: rgba(0, 0, 0, .26);
  --stroke: rgba(255, 255, 255, .12);
  --stroke-2: rgba(255, 255, 255, .22);
  --blur: blur(24px);
  --surface-solid: rgba(30, 18, 55, .92);   /* sticky table heads, tooltips, dropdown options */
  --badge-ring: #23163f;                    /* ring around notification dots */
  --bar-muted: rgba(255, 255, 255, .55);    /* non-highlighted bars */
  --track: rgba(255, 255, 255, .14);        /* progress-bar track */
  --bar-alt: rgba(255, 255, 255, .75);      /* second segment of split bars */
  --overlay: rgba(12, 6, 24, .45);
  --chart-grid: rgba(255, 255, 255, .08);
  --chart-tick: rgba(243, 241, 251, .55);

  /* shape */
  --r: 18px;
  --r-sm: 12px;
  --r-xs: 8px;
  --shadow: 0 20px 50px rgba(0, 0, 0, .22), inset 0 1px 0 rgba(255, 255, 255, .10);

  /* layout */
  --rail-w: 72px;
  --max-w: 100%;            /* full width; set 1320px on a page that should stay narrow */
  --gap: 16px;

  --font: Inter, system-ui, -apple-system, "Segoe UI", sans-serif;

  /* the <select> arrow. A data URI cannot read a custom property, so the stroke colour is
     written out; %23a99bff IS --violet-2 for this mode, and light mode redefines the token
     with its own --violet-2 (%234747c0). Change one, change the other. */
  --chevron: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23a99bff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M6 9l6 6 6-6'/></svg>");
  /* the same arrow the other way up, swapped in while the list is open (1.3.5). It is a
     second image rather than a rotation because the arrow has to be a BACKGROUND here — see
     the picker block for why a pseudo-element cannot survive the closed control's clip. */
  --chevron-up: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23a99bff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M18 15l-6-6-6 6'/></svg>");
  /* the white tick / dot drawn inside a checked checkbox and radio */
  --check: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M5 12.5l4.5 4.5L19 7'/></svg>");
  --radio-dot: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24'><circle cx='12' cy='12' r='5' fill='%23ffffff'/></svg>");
  --dash: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3' stroke-linecap='round'><path d='M6 12h12'/></svg>");

  /* native pickers (date, time, colour, the file button, scrollbars) follow the mode */
  color-scheme: dark;
}


/* ---------- light mode: <html data-theme="light"> — "paper & ink" ---------- */
html[data-theme="light"] {
  --violet: #5b5bd6;
  --violet-2: #4747c0;
  --mint: #0f7b4f;
  --coral: #c0392b;
  --sky: #1b62c0;
  --gold: #946300;

  --text: #0f1220;
  --text-2: #3a3f52;
  --text-3: #667085;
  --text-4: #98a2b3;

  --glass: #ffffff;
  --glass-2: #f9fafc;
  --glass-3: #f2f4f8;
  --well: #f6f7fa;
  --well-2: #eef0f5;
  --stroke: #e6e8ef;
  --stroke-2: #d0d5dd;
  --shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 8px 24px rgba(16, 24, 40, .05);
  --blur: none;

  --surface-solid: #ffffff;
  --badge-ring: #ffffff;
  --bar-muted: #dfe1f3;
  --track: #eef0f5;
  --bar-alt: #b9bce8;
  --overlay: rgba(16, 24, 40, .38);
  --chart-grid: #eef0f5;
  --chart-tick: #667085;

  /* %234747c0 is this mode's --violet-2 (see the token in :root) */
  --chevron: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%234747c0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M6 9l6 6 6-6'/></svg>");
  --chevron-up: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%234747c0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M18 15l-6-6-6 6'/></svg>");

  color-scheme: light;
}
html[data-theme="light"] { background: #f4f5f9; }
html[data-theme="light"] body {
  background:
    radial-gradient(900px 420px at 50% -120px, rgba(91, 91, 214, .10), transparent 70%),
    linear-gradient(180deg, #f7f8fb 0%, #f3f4f8 100%);
}
html[data-theme="light"] body::before, html[data-theme="light"] body::after { display: none; }

/* surfaces: flat white cards, hairline borders, borderless wells */
html[data-theme="light"] .glass { backdrop-filter: none; -webkit-backdrop-filter: none; }
/* light mode is "no blur" everywhere: .overlay carries its own blur, so it needs its own reset */
html[data-theme="light"] .overlay { backdrop-filter: none; -webkit-backdrop-filter: none; }
html[data-theme="light"] .well, html[data-theme="light"] .row, html[data-theme="light"] .stat, html[data-theme="light"] .kpi { border-color: transparent; }
html[data-theme="light"] .rail { box-shadow: 0 1px 2px rgba(16, 24, 40, .04); }
html[data-theme="light"] .rail .on { box-shadow: none; }
html[data-theme="light"] .rail .on, html[data-theme="light"] .btn.primary { color: #fff; }
html[data-theme="light"] .btn { background: #fff; }
html[data-theme="light"] .btn:hover { background: var(--glass-2); }
html[data-theme="light"] .btn.ghost { background: transparent; }
/* .primary and .danger keep their own fill: "html[data-theme] .btn" outranks ".btn.primary",
   so without these two the primary button would be white text on a white background. */
html[data-theme="light"] .btn.primary { background: var(--violet); box-shadow: none; }
html[data-theme="light"] .btn.primary:hover { background: var(--violet-2); }
html[data-theme="light"] .btn.danger { background: #fbe9e7; border-color: transparent; }
html[data-theme="light"] .btn.danger:hover { background: #f7ddd9; }
html[data-theme="light"] .chip, html[data-theme="light"] .icon-btn, html[data-theme="light"] .search, html[data-theme="light"] .user { background: #fff; }
html[data-theme="light"] .chip:hover, html[data-theme="light"] .chip.on { background: var(--glass-2); }
html[data-theme="light"] .seg { background: var(--well-2); border-color: transparent; }
html[data-theme="light"] .seg button.on { background: #fff; box-shadow: 0 1px 2px rgba(16, 24, 40, .08); }
html[data-theme="light"] .tabs button.on { border-bottom-color: var(--violet); }
/* controls are white on paper, with the hairline --stroke (#e6e8ef) doing the work the dark
   mode's --well fill does. The unclassed selectors sit at the same weight as their .in twins
   minus the class, so an explicit .in still wins where the two disagree. */
html[data-theme="light"] input.in:where(:not([type="checkbox"], [type="radio"])), html[data-theme="light"] select.in, html[data-theme="light"] textarea.in,
html[data-theme="light"] input:where(:not([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"])),
html[data-theme="light"] select, html[data-theme="light"] textarea { background-color: #fff; }
/* unchecked boxes are white too. Scoped to :not(:checked) and written as background-COLOR:
   "html[data-theme] input" outranks "input:checked", and the shorthand would wipe the tick. */
html[data-theme="light"] input:where([type="checkbox"], [type="radio"]):not(:checked):not(:indeterminate) { background-color: #fff; }
html[data-theme="light"] button:where(:not(.btn, .rail *, .seg *, .tabs *, .icon-btn)), html[data-theme="light"] input:where([type="submit"], [type="reset"]) { background-color: #fff; }
html[data-theme="light"] button:where(:not(.btn, .rail *, .seg *, .tabs *, .icon-btn)):hover, html[data-theme="light"] input:where([type="submit"], [type="reset"]):hover { background-color: var(--glass-2); }
/* same trap as .btn.primary in 1.3.1: the rule above outranks the variant, so say it again */
html[data-theme="light"] button.primary, html[data-theme="light"] button[data-primary],
html[data-theme="light"] input[type="submit"].primary { background-color: var(--violet); box-shadow: none; color: #fff; }
html[data-theme="light"] button.primary:hover, html[data-theme="light"] button[data-primary]:hover,
html[data-theme="light"] input[type="submit"].primary:hover { background-color: var(--violet-2); }
html[data-theme="light"] button.danger { background: #fbe9e7; border-color: transparent; }
html[data-theme="light"] .card-head .ic { background: var(--well); border-color: transparent; }
html[data-theme="light"] .table-wrap { background: #fff; }
html[data-theme="light"] table.tbl th { background: #fff; border-bottom-color: var(--stroke); }
html[data-theme="light"] table.tbl td { border-bottom-color: #f0f2f6; }
html[data-theme="light"] table.tbl tbody tr:hover td { background: var(--well); }
html[data-theme="light"] table.tbl tr.selected td { background: #eef0fb; }
html[data-theme="light"] a.card:hover, html[data-theme="light"] .card.clickable:hover { background: #fff; border-color: var(--stroke-2); }

/* colour is for meaning, not decoration: badges and deltas as soft tints */
html[data-theme="light"] .badge { background: var(--well-2); border-color: transparent; }
html[data-theme="light"] .badge.ok, html[data-theme="light"] .badge.success, html[data-theme="light"] .badge.delivered, html[data-theme="light"] .badge.fulfilled { background: #e6f5ee; }
html[data-theme="light"] .badge.info, html[data-theme="light"] .badge.transit, html[data-theme="light"] .badge.processing, html[data-theme="light"] .badge.shipped { background: #e8f0fb; }
html[data-theme="light"] .badge.warn, html[data-theme="light"] .badge.pending, html[data-theme="light"] .badge.exception, html[data-theme="light"] .badge.unfulfilled { background: #fbf1dc; }
html[data-theme="light"] .badge.err, html[data-theme="light"] .badge.failed, html[data-theme="light"] .badge.cancelled, html[data-theme="light"] .badge.error { background: #fbe9e7; }
html[data-theme="light"] .badge.violet { background: #eceefb; }
html[data-theme="light"] .delta { background: #e6f5ee; }
html[data-theme="light"] .delta.neg { background: #fbe9e7; }
html[data-theme="light"] .delta.flat { background: var(--well-2); }
html[data-theme="light"] .count { background: var(--well-2); }
html[data-theme="light"] .dot { box-shadow: none; }
html[data-theme="light"] .dot.warn, html[data-theme="light"] .dot.err { box-shadow: none; }
html[data-theme="light"] .toast { box-shadow: var(--shadow); }
html[data-theme="light"] ::selection { background: rgba(91, 91, 214, .18); }
html[data-theme="light"] * { scrollbar-color: #d0d5dd transparent; }
html[data-theme="light"] *::-webkit-scrollbar-thumb { background: #d0d5dd; }
html { transition: background .25s; }
body, .glass, .btn, .in, .badge { transition: background-color .25s, border-color .25s, color .25s; }

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { background: #1a0f2e; }
body {
  margin: 0;
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.45;
  color: var(--text);
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  background:
    radial-gradient(1200px 700px at 18% 12%, rgba(139, 124, 246, .42), transparent 60%),
    radial-gradient(900px 600px at 88% 20%, rgba(255, 138, 128, .16), transparent 60%),
    radial-gradient(1000px 800px at 70% 100%, rgba(95, 227, 161, .12), transparent 60%),
    linear-gradient(160deg, #2b1a4d 0%, #1d1136 45%, #150b28 100%);
  background-attachment: fixed;
}
body::before, body::after {
  content: ""; position: fixed; border-radius: 50%; filter: blur(60px); pointer-events: none; z-index: 0;
}
body::before { width: 520px; height: 520px; left: -160px; top: 20%; background: rgba(139, 124, 246, .28); }
body::after { width: 420px; height: 420px; right: -120px; bottom: -80px; background: rgba(255, 199, 102, .14); }

/* no control ever falls back to the UA's Arial: the element-level rules further down set
   this too, but range, colour and file inputs are outside them and a page should never have
   to say it (1.3.3) */
input, select, textarea, button { font: inherit; }
a, a:visited, a:hover, a:active { color: inherit; text-decoration: none; }
a.link { color: var(--violet-2); }
a.link:hover { text-decoration: underline; }
h1, h2, h3, h4 { margin: 0; font-weight: 600; color: var(--text); letter-spacing: -.01em; }
h1 { font-size: 22px; } h2 { font-size: 15px; font-weight: 500; } h3 { font-size: 14px; font-weight: 500; }
p { margin: 0; }
small, .muted { color: var(--text-3); }
code, .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; }
/* numbers, prices, clocks and stats are ALWAYS Inter with tabular figures — never monospace */
.num, .stat .v, .lead .v, .kpi .v, .clock .time, .row .val, table.tbl td { font-family: var(--font); font-variant-numeric: tabular-nums; }
::selection { background: rgba(139, 124, 246, .45); }
* { scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, .18) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .18); border-radius: 8px; }

/* ---------- type scale (the ONLY sizes/weights allowed) ----------
   11.5px  meta, table heads, badges        400/500
   12px    labels, captions                 400/500
   13px    body in dense UI, buttons, chips 400/500
   14px    default body, table cells        400/500
   15px    panel titles (h2)                500
   22px    page title (h1)                  600
   26–30px stats / KPI numbers              600
   Weights: 400, 500, 600 only. Letter-spacing only on .cc and headings (negative).
   Icons: .ico 18px (default), .ico-sm 16px, .ico-lg 20px; stroke = currentColor.
   ------------------------------------------------------------------ */
.ico { width: 18px; height: 18px; flex: none; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; vertical-align: -3px; }
.ico-sm { width: 16px; height: 16px; vertical-align: -2px; }
.ico-lg { width: 20px; height: 20px; vertical-align: -4px; }
.btn .ico, .chip .ico { width: 16px; height: 16px; vertical-align: 0; }
.rail .ico, .icon-btn .ico { width: 20px; height: 20px; vertical-align: 0; }
.panel-head .ico { color: var(--text-2); }
.card-head .ic .ico { width: 17px; height: 17px; }
b, strong { font-weight: 600; }
.t-11 { font-size: 11.5px; } .t-12 { font-size: 12px; } .t-13 { font-size: 13px; } .t-15 { font-size: 15px; }
.w-500 { font-weight: 500; } .w-600 { font-weight: 600; }

/* ---------- shell layout ---------- */
.shell {
  position: relative; z-index: 1;
  max-width: var(--max-w); margin: 0 auto;
  padding: 22px 22px 40px;
  display: grid; grid-template-columns: var(--rail-w) 1fr; gap: 18px;
}
.shell.no-rail { grid-template-columns: 1fr; }
.main { min-width: 0; }
/* vertical rhythm: sections inside .main never touch */
.main > * + * { margin-top: var(--gap); }
.main > .title-row { margin-top: 22px; }
.section + .section, .panel + .panel, .panel + .kpi-strip, .kpi-strip + .panel, .clocks + .panel, .panel + .clocks, .grid + .grid, .panel + .grid, .grid + .panel { margin-top: var(--gap); }

/* ---------- glass surface ---------- */
.glass {
  background: var(--glass);
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  border: 1px solid var(--stroke);
  border-radius: var(--r);
  box-shadow: var(--shadow);
}
.panel { padding: 18px 20px 16px; }
.panel-head { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; flex-wrap: wrap; }
.panel-head svg { width: 18px; height: 18px; stroke: var(--text-2); fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.panel-head .right { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.well { background: var(--well); border: 1px solid var(--stroke); border-radius: 14px; }

/* ---------- icon rail ---------- */
.rail {
  /* z-index, not just `sticky`: every .glass panel carries a backdrop-filter, which makes it a
     stacking context painted like a z-index:0 element — so the search bar and the panels of
     .main, which come later in the DOM, painted straight over the rail's tooltips. */
  position: sticky; top: 22px; z-index: 20; align-self: start;
  background: var(--glass); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  border: 1px solid var(--stroke); border-radius: 26px; padding: 14px 0;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  box-shadow: var(--shadow);
}
.rail a, .rail button {
  font: inherit;   /* a <button> the base rule skips still must not fall back to the UA's Arial */
  width: 44px; height: 44px; border-radius: 14px; border: 0; background: transparent; cursor: pointer;
  display: grid; place-items: center; color: var(--text-2); position: relative;
  transition: background .18s, color .18s;
}
.rail a:hover, .rail button:hover { background: var(--glass-2); color: var(--text); }
.rail .on { background: var(--violet); color: #fff; box-shadow: 0 8px 24px rgba(139, 124, 246, .45); }
/* the current item keeps its fill under the pointer. Without this the generic hover above wins
   on background while light mode's `.on { color: #fff }` wins on colour — a white icon on a
   near-white box, i.e. the active item disappeared the moment you hovered it. */
.rail .on:hover { background: var(--violet); color: #fff; }
.rail svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.rail .sep { width: 28px; height: 1px; background: var(--stroke); margin: 6px 0; }
.rail .badge-dot { position: absolute; top: 6px; right: 6px; width: 8px; height: 8px; border-radius: 50%; background: var(--coral); border: 2px solid var(--badge-ring); }

/* ---------- tooltips (data-tip) ---------- */
/* The browser's native `title` tooltip is a black system box in a system font: it ignores every
   token in here, and on an icon-only control it is the only label there is. `data-tip` carries
   the same text and paints it with the theme (pair it with `aria-label` — a ::after is not an
   accessible name). See DESIGN.md 2h; audit F fails a `title=` on an icon control. */
[data-tip] { position: relative; }
[data-tip]::after {
  content: attr(data-tip);
  position: absolute; z-index: 80;
  font-family: var(--font); font-size: 12px; font-weight: 500; line-height: 1.35; letter-spacing: 0;
  white-space: nowrap;
  padding: 6px 10px; border-radius: 8px;
  background: var(--surface-solid); color: var(--text);
  border: 1px solid var(--stroke-2); box-shadow: var(--shadow);
  pointer-events: none;   /* never let the tooltip eat the hover that opened it */
  opacity: 0;
  /* --tip-t* is where the tooltip rests, --tip-d* the few px it slides in from */
  transform: translate(calc(var(--tip-tx) + var(--tip-dx)), calc(var(--tip-ty) + var(--tip-dy)));
  transition: opacity .16s ease, transform .16s ease;
  /* top (the default everywhere but the rail and the top bar) */
  left: 50%; right: auto; bottom: 100%; top: auto; margin: 0 0 8px 0;
  --tip-tx: -50%; --tip-ty: 0px; --tip-dx: 0px; --tip-dy: 4px;
}
[data-tip]:hover::after, [data-tip]:focus-visible::after {
  opacity: 1;
  transform: translate(var(--tip-tx), var(--tip-ty));
  transition-delay: .12s;   /* a tooltip that appears while the pointer is passing through is noise */
}
[data-tip=""]::after { display: none; }   /* an empty tip renders nothing; a disabled control still explains itself */
/* a whole sentence (a table row action, a badge explaining a state) wraps instead of running
   off the viewport on one line */
[data-tip][data-tip-wide]::after { white-space: normal; width: max-content; max-width: 280px; }

/* container defaults: the rail sits on the left edge, the top bar at the top of the page, so
   neither can put a tooltip above itself */
.rail [data-tip]::after {
  left: 100%; right: auto; top: 50%; bottom: auto; margin: 0 0 0 10px;
  --tip-tx: 0px; --tip-ty: -50%; --tip-dx: -4px; --tip-dy: 0px;
}
.icon-btn[data-tip]::after {
  left: 50%; right: auto; top: 100%; bottom: auto; margin: 8px 0 0 0;
  --tip-tx: -50%; --tip-ty: 0px; --tip-dx: 0px; --tip-dy: -4px;
}

/* explicit placement always wins over the container default — these come last on purpose */
[data-tip][data-tip-pos="top"]::after {
  left: 50%; right: auto; bottom: 100%; top: auto; margin: 0 0 8px 0;
  --tip-tx: -50%; --tip-ty: 0px; --tip-dx: 0px; --tip-dy: 4px;
}
[data-tip][data-tip-pos="bottom"]::after {
  left: 50%; right: auto; top: 100%; bottom: auto; margin: 8px 0 0 0;
  --tip-tx: -50%; --tip-ty: 0px; --tip-dx: 0px; --tip-dy: -4px;
}
[data-tip][data-tip-pos="right"]::after {
  left: 100%; right: auto; top: 50%; bottom: auto; margin: 0 0 0 10px;
  --tip-tx: 0px; --tip-ty: -50%; --tip-dx: -4px; --tip-dy: 0px;
}
[data-tip][data-tip-pos="left"]::after {
  right: 100%; left: auto; top: 50%; bottom: auto; margin: 0 10px 0 0;
  --tip-tx: 0px; --tip-ty: -50%; --tip-dx: 4px; --tip-dy: 0px;
}

/* ---------- top bar ---------- */
/* z-index for the same reason as .rail: the icon buttons hang their tooltips below
   themselves, over the first panel of the page. */
.topbar { position: relative; z-index: 10; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.search {
  flex: 1; min-width: 220px; display: flex; align-items: center; gap: 10px;
  padding: 0 16px; height: 48px; color: var(--text-3); font-size: 14px;
}
.search input { flex: 1; background: transparent; border: 0; outline: 0; color: var(--text); font: inherit; min-width: 0; }
.search input::placeholder { color: var(--text-3); }
.search svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; }
.icon-btn {
  font: inherit;
  width: 48px; height: 48px; display: grid; place-items: center; color: var(--text-2);
  position: relative; cursor: pointer; border: 1px solid var(--stroke);
}
.icon-btn svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.icon-btn .badge-dot { position: absolute; top: 12px; right: 13px; width: 8px; height: 8px; border-radius: 50%; background: var(--coral); }
.user { display: flex; align-items: center; gap: 10px; height: 48px; padding: 0 14px 0 8px; font-size: 13px; }
.user .av { width: 32px; height: 32px; border-radius: 50%; background: linear-gradient(135deg, var(--violet), var(--mint)); display: grid; place-items: center; font-weight: 600; font-size: 13px; color: #fff; }
.user small { display: block; color: var(--text-3); font-size: 11.5px; }

.title-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 22px; }
.title-row .sub { color: var(--text-3); font-size: 13px; }
.title-row .right { margin-left: auto; display: flex; gap: 10px; flex-wrap: wrap; }

/* ---------- grids ---------- */
.grid { display: grid; gap: var(--gap); margin-top: var(--gap); }
.grid.cols-2 { grid-template-columns: 1fr 1fr; }
.grid.cols-3 { grid-template-columns: 1fr 1fr 1fr; }
.grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
.grid.main-side { grid-template-columns: 1.9fr 1fr; }
.grid.auto { grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); }
.stack { display: grid; gap: var(--gap); }

/* ---------- stats ---------- */
.stat { padding: 14px 14px 12px; min-height: 104px; display: flex; flex-direction: column; justify-content: space-between; container-type: inline-size; }
.stat .k { font-size: 12.5px; color: var(--text-2); }
.stat .v {
  min-width: 0; font-size: clamp(16px, 10cqw, 26px); font-weight: 600; letter-spacing: -.02em;
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; color: var(--text);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.stat .v .delta, .stat .v .unit { flex: none; }
.delta { font-size: 11px; font-weight: 500; padding: 3px 7px; border-radius: 7px; background: rgba(95, 227, 161, .16); color: var(--mint); white-space: nowrap; }
.delta.neg { background: rgba(255, 138, 128, .16); color: var(--coral); }
.delta.flat { background: var(--glass-2); color: var(--text-2); }

/* ---------- chips / segmented / tabs ---------- */
.chip {
  display: inline-flex; align-items: center; gap: 8px; height: 40px; padding: 0 14px;
  border-radius: var(--r-sm); font-size: 13px; color: var(--text-2); cursor: pointer;
  border: 1px solid var(--stroke); background: var(--glass); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
}
.chip svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.chip:hover, .chip.on { background: var(--glass-2); color: var(--text); }
.seg { display: inline-flex; padding: 4px; border-radius: 12px; background: var(--well-2); border: 1px solid var(--stroke); }
.seg button { font: inherit; font-size: 12.5px; color: var(--text-2); background: transparent; border: 0; padding: 7px 14px; border-radius: 9px; cursor: pointer; }
.seg button.on { background: var(--glass-3); color: var(--text); }
.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--stroke); margin-bottom: 14px; }
.tabs button { font: inherit; font-size: 13px; color: var(--text-3); background: transparent; border: 0; padding: 10px 14px; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.tabs button.on { color: var(--text); border-bottom-color: var(--violet); }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 38px; padding: 0 16px; border-radius: var(--r-sm); font: inherit; font-size: 13px; font-weight: 500;
  color: var(--text); background: var(--glass-2); border: 1px solid var(--stroke); cursor: pointer;
  transition: background .18s, border-color .18s, transform .1s;
}
.btn:hover { background: var(--glass-3); border-color: var(--stroke-2); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn.primary { background: var(--violet); border-color: transparent; color: #fff; box-shadow: 0 8px 24px rgba(139, 124, 246, .35); }
.btn.primary:hover { background: var(--violet-2); }
.btn.danger { background: rgba(255, 138, 128, .18); border-color: rgba(255, 138, 128, .35); color: var(--coral); }
.btn.ghost { background: transparent; border-color: transparent; color: var(--text-2); }
.btn.sm { height: 30px; padding: 0 10px; font-size: 12px; border-radius: var(--r-xs); }
.btn svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* ---------- forms ---------- */
.field { display: grid; gap: 6px; }
.field label, label.lbl { font-size: 12px; color: var(--text-2); }
/* .in is the 40px TEXT field. A checkbox or radio that also carries it (several dashboards
   put .in on every control they render) must not be sized like one — it keeps the element-level
   box below. :where() keeps the weight identical to the plain `input.in` it replaces. */
input.in:where(:not([type="checkbox"], [type="radio"])), select.in, textarea.in {
  width: 100%; font: inherit; font-size: 13.5px; color: var(--text);
  background: var(--well); border: 1px solid var(--stroke); border-radius: var(--r-sm);
  padding: 0 12px; height: 40px; outline: 0; transition: border-color .18s, box-shadow .18s;
}
textarea.in { height: auto; min-height: 90px; padding: 10px 12px; resize: vertical; }
input.in:where(:not([type="checkbox"], [type="radio"])):focus, select.in:focus, textarea.in:focus { border-color: var(--violet); box-shadow: 0 0 0 3px rgba(139, 124, 246, .25); }
input.in::placeholder, textarea.in::placeholder { color: var(--text-4); }
select.in { appearance: none; -webkit-appearance: none; padding-right: 34px; background-image: var(--chevron); background-repeat: no-repeat; background-position: right 12px center; }
select.in option { background: var(--surface-solid); color: var(--text); }
.check { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-2); cursor: pointer; }
.check input { width: 16px; height: 16px; }
.toolbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }


/* ---------- form controls at ELEMENT level (1.3.3) ----------
   Everything below styles a control that carries NO class, because a page that builds its
   table rows in JavaScript almost never remembers to add one — and a native white box on a
   dark panel is the one defect no dashboard should be able to reproduce.

   Weight: every exclusion list is wrapped in :where(), so each of these rules weighs exactly
   one element selector (0,0,1). `.in` (0,1,1) and `.btn` (0,1,0) therefore still win wherever
   the two disagree, and they stay what they have always been: the explicit sizing variants.
   Anything a page sets on its own class also still wins — which is what audit E looks for. */

input:where(:not([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"], [type="submit"], [type="reset"], [type="button"], [type="image"], [type="hidden"])),
select, textarea {
  font: inherit; font-size: 13.5px; color: var(--text);
  background: var(--well); border: 1px solid var(--stroke); border-radius: var(--r-sm);
  height: 36px; padding: 0 10px; outline: 0; max-width: 100%;
  color-scheme: inherit;
  transition: border-color .18s, box-shadow .18s, background-color .25s;
}
textarea { height: auto; min-height: 80px; padding: 8px 10px; resize: vertical; line-height: 1.45; }
input:where(:not([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"], [type="submit"], [type="reset"], [type="button"], [type="image"], [type="hidden"])):focus,
select:focus, textarea:focus { border-color: var(--violet); box-shadow: 0 0 0 3px rgba(139, 124, 246, .25); }
:is(input, textarea)::placeholder { color: var(--text-4); }
:is(input, select, textarea, button):disabled { opacity: .45; cursor: not-allowed; }

/* numbers line up: right-aligned, tabular figures */
input:where([type="number"], [inputmode="decimal"], [inputmode="numeric"], .num, .number) {
  text-align: right; font-variant-numeric: tabular-nums;
}

select {
  appearance: none; -webkit-appearance: none; cursor: pointer;
  padding-right: 30px;
  background-image: var(--chevron); background-repeat: no-repeat; background-position: right 10px center;
}
select option, select optgroup { background: var(--surface-solid); color: var(--text); }
select[multiple], select[size]:not([size="1"]) { height: auto; padding: 6px 10px; background-image: none; }

/* ---------- the <select> PICKER (1.3.5) ----------
   1.3.3 themed the closed control. The open list stayed the operating system's own popup —
   system font, OS colours, a native tick — because until recently nothing in CSS could reach
   it: the popup is painted by the platform, not by the page. Chrome / Edge 135+ ship the
   customizable select, where `appearance: base-select` on BOTH the control and its
   `::picker(select)` moves that popup into the page and the theme's tokens finally apply.

   Everything lives inside @supports on purpose. A browser without it (Safari and Firefox
   today) keeps the native picker AND the untouched 1.3.3 closed control — the popup is the
   one part of a control that must never be left half-styled, so it is all or nothing.

   Two things change shape in base appearance. The control becomes a flex button whose label
   is a `selectedcontent` box and whose arrow is a real pseudo-element (`::picker-icon`)
   instead of a background image — which is why the chevron can finally rotate while the list
   is open. Both are still drawn from the same --chevron token, so the arrow follows
   --violet-2 in each mode exactly as the background image did. */
@supports (appearance: base-select) {
  /* `select.in` is named explicitly: the 1.3.3 sizing variant sets `appearance: none` at
     (0,1,1) and would otherwise outrank a bare `select` here and keep the OS popup on every
     dashboard that puts .in on its drop-downs. */
  select, select.in, ::picker(select) { appearance: base-select; }
  /* a list box is not a drop-down: it has no picker, and base appearance has nothing to say
     about it, so it keeps the 1.3.3 rendering */
  select[multiple], select[size]:not([size="1"]) { appearance: none; -webkit-appearance: none; }
  /* 1.3.3 painted every row --surface-solid so the NATIVE popup would not be a white box on a
     dark page. The picker draws that surface itself now, and a row repeating it is an opaque
     block sitting inside a translucent card — so it has to be undone by name: those rules
     weigh two element selectors and outrank a bare `option`. */
  select option, select optgroup, select.in option { background: transparent; }

  /* --- the closed control: the 1.3.3 look, and that takes work ---
     In base appearance the browser clones the selected option into a `selectedcontent` box.
     When the page does not write that box itself the browser writes it into the UA shadow
     tree, where an author `selectedcontent` rule never reaches it: it cannot be told to
     shrink, to clip or to ellipsize. Left alone, a label longer than the field WRAPS and the
     control's height breaks — comment-hub's post filter, whose options are whole post titles
     in a 260 px field, is the case that found this.

     `white-space` does reach it, because it inherits. So the label is held to one line and
     cut off at the CONTENT box — `overflow-clip-margin: content-box`, not plain
     `overflow: hidden`, which clips at the padding box and would let the text run under the
     arrow. That same clip is why the arrow cannot be the `::picker-icon` pseudo-element: it
     lives in the padding strip the clip removes. It stays the 1.3.3 background image, drawn
     from --chevron in exactly the place 1.3.3 drew it, and while the list is open the
     --chevron-up token is swapped in. A background image cannot be transitioned, so this is
     a flip and not the 180° turn a pseudo-element would have animated — the honest price of
     a label that stays on one line.

     1.3.6 adds the other half of that: WHERE the label sits. A base-appearance control is a
     flex box, and at the UA's `align-items: normal` the clone stretches to the whole content
     box and its text is drawn on the FIRST line of it — 1 px under the top edge, with all the
     spare height left underneath. That is 16 px of it in a 36 px field and 12 px in the 32 px
     table variant, which is what a label riding high in the control looks like. The control is
     therefore declared `inline-flex` with `align-items: center` in its own right, so the clone
     is centred rather than stretched, and `line-height: 1` takes the inherited leading back out
     of it — otherwise the centring is a line box whose height comes from whatever body
     line-height the page happens to set, and it moves when that does. `justify-content:
     flex-start` keeps the label at the left edge of a control that was stretched to fill a
     cell, so it can never drift over the arrow; the arrow itself is unmoved, still the
     --chevron background image pinned `right 10px center` in the padding strip the
     padding-right reserves for it.

     The list boxes are excluded by name. `select[multiple]` keeps 1.3.3's rendering (it has no
     picker to theme), and a rows-tall box is not a flex button: `inline-flex` would lay its
     options out in a row, and the `overflow: clip` above would take its scrollbar away. The
     `:where()` keeps all of this at one element selector. */
  select:where(:not([multiple], [size]:not([size="1"]))),
  select.in:where(:not([multiple], [size]:not([size="1"]))) {
    display: inline-flex; align-items: center; justify-content: flex-start;
    line-height: 1;
    white-space: nowrap;
    overflow: clip; overflow-clip-margin: content-box;
    text-align: left;
  }
  /* `.in` is named for the same reason it is named on the `appearance` line above, and for a
     different declaration: the 1.3.3 sizing variant sets the `font` SHORTHAND (`font:
     inherit`) at (0,1,1), and a shorthand writes every longhand in it — including
     line-height, which it would take back from the page's body. Same weight, later in the
     file, so this one wins. */

  /* A page that writes its own `<button><selectedcontent></selectedcontent></button>` into the
     select gets a real element in the light DOM instead of the UA's clone, and that element is
     a block that would put its text back at the top. It is centred here as well, and held to
     the full height of the control so the row it centres in is the control's own. Chrome 152
     builds the default clone as an `-internal-select-inner-element` in the UA shadow tree,
     which no author selector reaches — for that one the `align-items: center` above is what
     does the centring, and this rule is for the pages that write the button themselves. */
  selectedcontent { display: flex; align-items: center; min-height: 100%; }
  select:open { background-image: var(--chevron-up); }
  /* the UA's own arrow, replaced by the background image above */
  select::picker-icon { display: none; }

  /* --- the popup --- */
  ::picker(select) {
    box-sizing: border-box;
    /* never narrower than the field it belongs to */
    min-width: anchor-size(width);
    max-height: 320px; overflow-y: auto;
    padding: 6px;
    background: var(--surface-solid);
    /* --surface-solid is 92% opaque in dark mode, which is fine behind a tooltip and not fine
       behind a menu: a list this size sitting on a table let the rows underneath read through
       it. The blur is what the sticky table head already does with the same token, and light
       mode sets --blur: none over an opaque white, so it costs nothing there. */
    backdrop-filter: var(--blur);
    -webkit-backdrop-filter: var(--blur);
    border: 1px solid var(--stroke-2);
    border-radius: var(--r-sm);
    box-shadow: var(--shadow);
    color: var(--text);
    color-scheme: inherit;
    /* anchored under the control; flip-block lifts it above instead when the viewport runs
       out below, which is why the 6px gap is set on both sides rather than just the top */
    position-area: block-end span-inline-end;
    position-try-fallbacks: flip-block;
    margin-top: 6px;
    margin-bottom: 6px;
    opacity: 0; translate: 0 -4px;
    /* display and overlay are discrete properties: without allow-discrete the popup would
       pop out of the top layer at frame 0 and the fade-out would never be seen */
    transition: opacity .16s ease, translate .16s ease,
                display .16s allow-discrete, overlay .16s allow-discrete;
  }
  select:open::picker(select) { opacity: 1; translate: 0 0; }
  @starting-style { select:open::picker(select) { opacity: 0; translate: 0 -4px; } }

  /* --- the rows --- */
  option {
    display: flex; align-items: center; gap: 8px;
    font-family: var(--font); font-size: 13.5px; font-weight: 400; line-height: 1.35;
    color: var(--text); background: transparent;
    padding: 8px 10px; border-radius: var(--r-xs);
    cursor: pointer; outline: 0;
    transition: background-color .12s;
  }
  /* :checked first so hovering the current row still gives feedback */
  option:checked { background: var(--glass-2); }
  option:hover:not(:disabled), option:focus-visible { background: var(--well-2); }
  option:disabled { opacity: .45; cursor: not-allowed; }
  /* the tick. --check is a white data URI, and a data URI cannot read a custom property, so
     it is used as a MASK and the violet is painted underneath — same shape as the checkbox
     tick, this mode's --violet-2. `order` moves it past the label: the browser reserves the
     checkmark box at the start of the row, and a check belongs at the end of it. */
  option::checkmark {
    content: ""; order: 1; flex: none; margin-left: auto;
    width: 14px; height: 14px;
    background-color: var(--violet-2);
    -webkit-mask: var(--check) center / 14px 14px no-repeat;
            mask: var(--check) center / 14px 14px no-repeat;
  }
  option:not(:checked)::checkmark { visibility: hidden; }
  /* a group heading is a label, not a row: smaller, quieter, and left as written.
     `select optgroup` rather than `optgroup` for the same reason as the reset above. */
  select optgroup {
    padding: 6px 10px 2px;
    font-family: var(--font); font-size: 11.5px; font-weight: 500; line-height: 1.35;
    color: var(--text-3); background: transparent;
    text-transform: none; letter-spacing: 0;
  }
  select optgroup option { padding-left: 10px; }

  @media (prefers-reduced-motion: reduce) {
    ::picker(select), option { transition: none; }
  }
}

/* checkboxes and radios: no native blue anywhere */
input:where([type="checkbox"], [type="radio"]) {
  appearance: none; -webkit-appearance: none;
  width: 16px; height: 16px; flex: none; margin: 0; padding: 0;
  border: 1px solid var(--stroke-2); border-radius: 4px;
  background: var(--well) no-repeat center; background-size: 14px 14px;
  cursor: pointer; vertical-align: -3px; outline: 0;
  font: inherit; color: inherit;
  accent-color: var(--violet);   /* fallback if appearance:none is ever unavailable */
  transition: background-color .15s, border-color .15s, box-shadow .15s;
}
input:where([type="radio"]) { border-radius: 50%; }
input:where([type="checkbox"], [type="radio"]):hover:not(:disabled) { border-color: var(--violet-2); }
input:where([type="checkbox"]):checked { background-color: var(--violet); background-image: var(--check); border-color: transparent; }
input:where([type="checkbox"]):indeterminate { background-color: var(--violet); background-image: var(--dash); border-color: transparent; }
input:where([type="radio"]):checked { background-color: var(--violet); background-image: var(--radio-dot); border-color: transparent; }
input:where([type="checkbox"], [type="radio"]):focus-visible { box-shadow: 0 0 0 3px rgba(139, 124, 246, .25); }

/* range and colour keep their native mechanics but not their native colour */
input[type="range"] { accent-color: var(--violet); }
input[type="color"] { width: 36px; height: 36px; padding: 2px; border: 1px solid var(--stroke); border-radius: var(--r-xs); background: var(--well); cursor: pointer; }
input[type="file"] { font: inherit; font-size: 13px; color: var(--text-2); max-width: 100%; }
input[type="file"]::file-selector-button {
  font: inherit; font-size: 12px; height: 30px; padding: 0 10px; margin-right: 10px;
  color: var(--text); background: var(--glass-2); border: 1px solid var(--stroke);
  border-radius: var(--r-xs); cursor: pointer;
}
input[type="file"]::file-selector-button:hover { background: var(--glass-3); border-color: var(--stroke-2); }

/* an unclassed <button> is a .btn; the rail, the segmented control and the tab strip own
   their own buttons and are excluded by name */
button:where(:not(.btn, .rail *, .seg *, .tabs *, .icon-btn)), input:where([type="submit"], [type="reset"], [type="button"]) {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 38px; padding: 0 16px; border-radius: var(--r-sm); font: inherit; font-size: 13px; font-weight: 500;
  color: var(--text); background: var(--glass-2); border: 1px solid var(--stroke); cursor: pointer;
  transition: background .18s, border-color .18s, transform .1s;
}
button:where(:not(.btn, .rail *, .seg *, .tabs *, .icon-btn)):hover:not(:disabled), input:where([type="submit"], [type="reset"], [type="button"]):hover:not(:disabled) { background: var(--glass-3); border-color: var(--stroke-2); }
button:where(:not(.btn, .rail *, .seg *, .tabs *, .icon-btn)):active:not(:disabled), input:where([type="submit"], [type="reset"], [type="button"]):active:not(:disabled) { transform: translateY(1px); }
button.primary, button[data-primary], input[type="submit"].primary { background: var(--violet); border-color: transparent; color: #fff; box-shadow: 0 8px 24px rgba(139, 124, 246, .35); }
button.primary:hover, button[data-primary]:hover, input[type="submit"].primary:hover { background: var(--violet-2); }
button.danger { background: rgba(255, 138, 128, .18); border-color: rgba(255, 138, 128, .35); color: var(--coral); }
button.ghost { background: transparent; border-color: transparent; color: var(--text-2); }
button:where(:not(.btn, .rail *, .seg *, .tabs *, .icon-btn)) svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* editable grids: a control in a table cell is compact and fills the cell */
table.tbl td :where(input, select, textarea), .tbl-edit :where(input, select, textarea) {
  height: 32px; border-radius: var(--r-xs);
}
/* "fill the cell" is a DEFAULT, not a law: at zero weight a page's own column-width class
   (.w-code, .w-num …) still decides how wide its column is. */
:where(table.tbl td, .tbl-edit) :where(input:not([type="checkbox"], [type="radio"], [type="file"]), select, textarea) { width: 100%; }
table.tbl td textarea, .tbl-edit textarea { height: auto; min-height: 32px; }
table.tbl td input:where([type="checkbox"], [type="radio"]), .tbl-edit input:where([type="checkbox"], [type="radio"]) {
  width: 16px; height: 16px; border-radius: 4px;
}
table.tbl td input:where([type="radio"]), .tbl-edit input:where([type="radio"]) { border-radius: 50%; }
/* height only — padding stays whatever the button already had, so a bare icon button in the
   row-tools column does not suddenly need 32 px of side padding */
table.tbl td button:where(:not(.btn, .icon-btn)), .tbl-edit button:where(:not(.btn, .icon-btn)) {
  height: 30px; font-size: 12px; border-radius: var(--r-xs);
}
table.tbl td select, .tbl-edit select { padding-right: 26px; background-position: right 8px center; }
/* header cells stay labels, never inherit the control colour */
table.tbl th { color: var(--text-3); }
.tbl-edit { display: grid; gap: 8px; align-items: center; }

/* ---------- badges ---------- */
.badge {
  display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 9px;
  border-radius: 7px; font-size: 11.5px; font-weight: 500; white-space: nowrap;
  background: var(--glass-2); color: var(--text-2); border: 1px solid var(--stroke);
}
.badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.badge.ok, .badge.success, .badge.delivered, .badge.fulfilled { background: rgba(95, 227, 161, .14); color: var(--mint); border-color: rgba(95, 227, 161, .25); }
.badge.info, .badge.transit, .badge.processing, .badge.shipped { background: rgba(124, 196, 255, .14); color: var(--sky); border-color: rgba(124, 196, 255, .25); }
.badge.warn, .badge.pending, .badge.exception, .badge.unfulfilled { background: rgba(255, 199, 102, .14); color: var(--gold); border-color: rgba(255, 199, 102, .25); }
.badge.err, .badge.failed, .badge.cancelled, .badge.error { background: rgba(255, 138, 128, .14); color: var(--coral); border-color: rgba(255, 138, 128, .25); }
.badge.violet { background: rgba(139, 124, 246, .18); color: var(--violet-2); border-color: rgba(139, 124, 246, .3); }
.count { display: inline-block; background: var(--glass-3); color: var(--text); padding: 2px 7px; border-radius: 6px; font-weight: 500; font-size: 11.5px; margin-left: 6px; }
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--mint); box-shadow: 0 0 8px var(--mint); }
.dot.warn { background: var(--gold); box-shadow: 0 0 8px var(--gold); }
.dot.err { background: var(--coral); box-shadow: 0 0 8px var(--coral); }
.dot.off { background: var(--text-4); box-shadow: none; }

/* ---------- data tables (real <table>) ---------- */
.table-wrap { overflow: auto; border-radius: 14px; border: 1px solid var(--stroke); background: var(--well); }
table.tbl { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; }
table.tbl th { text-align: left; font-weight: 500; font-size: 11.5px; color: var(--text-3); padding: 10px 12px; border-bottom: 1px solid var(--stroke); white-space: nowrap; position: sticky; top: 0; background: var(--surface-solid); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur); z-index: 1; }
table.tbl td { padding: 11px 12px; border-bottom: 1px solid rgba(255, 255, 255, .06); vertical-align: middle; }
table.tbl tr:last-child td { border-bottom: 0; }
table.tbl tbody tr:hover td { background: rgba(255, 255, 255, .04); }
table.tbl tr.selected td { background: rgba(139, 124, 246, .14); }
table.tbl td .sub { display: block; font-size: 11.5px; color: var(--text-3); }
table.tbl .num { text-align: right; font-variant-numeric: tabular-nums; }

/* ---------- row list (progress rows, reference "Restock demand") ---------- */
.rows { display: grid; gap: 8px; }
.rows .th { display: grid; grid-template-columns: 1fr 1fr auto; font-size: 11.5px; color: var(--text-3); padding: 0 12px 4px; }
.row { display: grid; grid-template-columns: 1.1fr 1fr auto; align-items: center; gap: 14px; padding: 10px 12px; border-radius: 12px; background: var(--well); border: 1px solid var(--stroke); }
.row .n { font-size: 13px; font-weight: 500; color: var(--text); }
.row .n small { display: block; font-size: 11px; color: var(--text-3); font-weight: 400; }
.row .val { font-size: 12px; color: var(--text-2); min-width: 52px; text-align: right; }
.bar { height: 4px; border-radius: 4px; background: var(--track); position: relative; overflow: hidden; }
.bar span { position: absolute; top: 0; bottom: 0; left: 0; border-radius: 4px; background: var(--violet); }
.bar span.b { background: var(--bar-alt); }

/* ---------- system / summary cards ---------- */
.card { display: block; padding: 16px 16px 14px; transition: transform .18s, background .18s; }
a.card:hover, .card.clickable:hover { background: var(--glass-2); transform: translateY(-2px); cursor: pointer; }
.card-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.card-head .ic { width: 34px; height: 34px; border-radius: 10px; background: var(--glass-2); border: 1px solid var(--stroke); display: grid; place-items: center; }
.card-head svg { width: 17px; height: 17px; stroke: var(--text); fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.card-head small { display: block; font-size: 11.5px; color: var(--text-3); }
.card-head .dot { margin-left: auto; }
.lead { display: flex; align-items: flex-end; gap: 10px; container-type: inline-size; }
.lead .v {
  min-width: 0; font-size: clamp(16px, 10cqw, 28px); font-weight: 600; letter-spacing: -.02em;
  line-height: 1; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.lead .k { font-size: 12px; color: var(--text-3); margin-top: 3px; }
.lead .d { margin-left: auto; font-size: 11.5px; font-weight: 500; color: var(--mint); }
.lead .d.neg { color: var(--coral); }
.card-foot { display: flex; justify-content: space-between; margin-top: 8px; font-size: 11.5px; color: var(--text-3); }
.card-foot .open { color: var(--violet-2); font-weight: 500; }

/* ---------- KPI strip (overview numbers) ---------- */
/* An amount is as long as the data makes it, and the cell is as wide as the strip makes it, so
   the TYPE gives way: every cell is a size container and its .v is sized in cqw of that cell.
   `minmax(170px, 1fr)` is the narrowest a cell gets — a seventh cell wraps to a second row
   rather than squeezing the six above it — and at that width the clamp floor still draws a
   12-character amount inside the content box, so the last-resort ellipsis below never runs. */
.kpi-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 12px; }
.kpi-strip + .kpi-strip { margin-top: 12px; }
.kpi { padding: 14px 16px 12px; min-height: 0; container-type: inline-size; }
.kpi .k { font-size: 12px; color: var(--text-3); margin-bottom: 8px; }
.kpi .v {
  min-width: 0; font-size: clamp(16px, 11cqw, 30px); font-weight: 600; letter-spacing: -.02em;
  line-height: 1.1; color: var(--text); display: flex; align-items: baseline; gap: 8px;
  flex-wrap: wrap; font-variant-numeric: tabular-nums; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
/* the unit keeps its own size and never shrinks; when the number leaves it no room on the
   line it drops UNDER the number (the .v wraps) instead of running out of the cell */
.kpi .v .unit { flex: none; font-size: 14px; font-weight: 500; color: var(--text-3); letter-spacing: 0; }
.kpi .v.mint { color: var(--mint); } .kpi .v.coral { color: var(--coral); } .kpi .v.sky { color: var(--sky); }
.kpi .v.gold { color: var(--gold); } .kpi .v.violet { color: var(--violet-2); }
.kpi .foot { font-size: 11.5px; color: var(--text-3); margin-top: 6px; }

/* ---------- world clocks ---------- */
.clocks { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; }
.clock { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px; container-type: inline-size; }
.clock .city { font-size: 14px; font-weight: 500; color: var(--text); display: flex; align-items: center; gap: 8px; }
.clock .city .cc { font-size: 10.5px; font-weight: 600; letter-spacing: .06em; color: var(--text-3); text-transform: uppercase; }
.clock .date { font-size: 12px; color: var(--text-3); margin-top: 2px; }
.clock .time { min-width: 0; font-size: clamp(16px, 12cqw, 26px); font-weight: 600; letter-spacing: -.01em; line-height: 1; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---------- charts ---------- */
.chart { position: relative; width: 100%; }
.chart.h-56 { height: 56px; } .chart.h-120 { height: 120px; } .chart.h-150 { height: 150px; }
.chart.h-236 { height: 236px; } .chart.h-300 { height: 300px; }
.legend { display: grid; gap: 9px; font-size: 12.5px; }
.legend div { display: flex; align-items: center; gap: 8px; color: var(--text-2); }
.legend i { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.legend b { margin-left: auto; font-weight: 500; color: var(--text); }
.donut-row { display: grid; grid-template-columns: 1fr 120px; gap: 8px; align-items: center; }

/* ---------- modal / drawer / toast ---------- */
.overlay { position: fixed; inset: 0; z-index: 50; background: var(--overlay); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); display: none; align-items: center; justify-content: center; padding: 20px; }
.overlay.open { display: flex; }
.modal { width: min(720px, 100%); max-height: 90vh; overflow: auto; padding: 22px 24px; }
.modal-head { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
.modal-head .close { margin-left: auto; }
.drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(520px, 100%); z-index: 60; transform: translateX(100%); transition: transform .22s; border-radius: var(--r) 0 0 var(--r); padding: 22px 24px; overflow: auto; }
.drawer.open { transform: none; }
.toasts { position: fixed; right: 20px; bottom: 20px; z-index: 70; display: grid; gap: 8px; }
.toast { padding: 12px 16px; font-size: 13px; border-left: 3px solid var(--violet); min-width: 240px; }
.toast.ok { border-left-color: var(--mint); } .toast.err { border-left-color: var(--coral); }
.empty { text-align: center; color: var(--text-3); padding: 36px 12px; font-size: 13px; }
.spinner { width: 18px; height: 18px; border: 2px solid var(--stroke-2); border-top-color: var(--violet); border-radius: 50%; animation: spin .8s linear infinite; display: inline-block; }
@keyframes spin { to { transform: rotate(360deg); } }
.divider { height: 1px; background: var(--stroke); margin: 14px 0; }

/* ---------- utilities ---------- */
.flex { display: flex; align-items: center; gap: 10px; }
.between { justify-content: space-between; }
.wrap { flex-wrap: wrap; }
.ml-auto { margin-left: auto; }
.mt-8 { margin-top: 8px; } .mt-16 { margin-top: 16px; } .mb-8 { margin-bottom: 8px; }
.right { text-align: right; }
.nowrap { white-space: nowrap; }
.hide { display: none !important; }
.t-mint { color: var(--mint); } .t-coral { color: var(--coral); } .t-sky { color: var(--sky); } .t-gold { color: var(--gold); } .t-violet { color: var(--violet-2); }

/* ---------- responsive ---------- */
@media (max-width: 980px) {
  .grid.cols-2, .grid.cols-3, .grid.main-side { grid-template-columns: 1fr; }
  .grid.cols-4 { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 640px) {
  .shell { grid-template-columns: 1fr; padding: 14px 14px 32px; }
  .rail { position: static; flex-direction: row; justify-content: space-around; border-radius: 20px; padding: 8px; overflow-x: auto; }
  .rail .sep { display: none; }
  /* no hover on a phone, and the rail scrolls here — a tooltip would be clipped by that
     overflow. The `aria-label` next to every `data-tip` is what remains, which is the part
     that mattered. */
  [data-tip]::after { display: none; }
  .user span { display: none; }
  .seg { width: 100%; } .seg button { flex: 1; }
  .donut-row { grid-template-columns: 1fr; }
  .grid.cols-4 { grid-template-columns: 1fr; }
  /* a phone caps the MAX; the cqw floor underneath is what keeps a long amount inside */
  .kpi .v { font-size: clamp(16px, 11cqw, 24px); } .clock .time { font-size: clamp(16px, 12cqw, 22px); }
  .modal { padding: 16px; }
  .panel { padding: 14px 14px 12px; }
}
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
  /* the rule above already removes the fade and its 120 ms delay; this removes the slide, so a
     tooltip simply appears where it belongs instead of arriving from 4 px away */
  [data-tip]::after { transform: translate(var(--tip-tx), var(--tip-ty)); }
}
