/**
 * BWOOD design tokens — from DESIGN.md.
 *
 * Light by default, warm-neutral, paper-adjacent. Dark is a genuine re-tint,
 * not an inversion. Every neutral is tinted toward hue 55–80, the cream and
 * walnut axis; there is no pure #000 or #fff anywhere.
 *
 * Colour is authored in oklch so that lightness steps are perceptually even
 * and the dark variant can drop chroma as lightness rises without the hue
 * shifting underneath it.
 */

/*
 * 23 Sep 2026 — the putty refinement.
 *
 * The owner approved a warmer, softer neutral family (BWOOD UPGRADING UI /
 * BWOOD_Refinement_Kit). The ten roles the kit names by hex are written here
 * verbatim, in hex, exactly as the kit states them; everything derived from
 * them stays in oklch as before. The two idioms sit side by side on purpose —
 * a value that came from the owner should be greppable as the number they
 * approved, not as a conversion of it.
 *
 *   Canvas #F4F2EE · Surface #FFFEFC · Raised #E8E5DF · Sidebar #EDE9E2
 *   Ink #302B27 · Secondary #6B625A · Primary #9E4F32 · Sage #E1E5DA
 *   Divider #DDD6CD
 *
 * Two of the ten did not land where the kit put them, and both are argued at
 * the token: the kit's secondary #6B625A sits on `--ink-faint` rather than
 * `--ink-muted`, and `--line` is decorative only with a separate
 * `--line-strong` doing the interactive work. Both moves are the kit's own
 * instruction — "check computed contrast and adjust within the same family".
 *
 * The previous cream/walnut values are in git history. What did NOT change:
 * the focus hue (blue, for the reason set out at --focus), the meanings and
 * hues of the five states (their QUIET fills were re-tinted warm — a mint
 * chip on putty was the one thing that read as borrowed), the type scale, and
 * the spacing scale — the kit's 4/8/12/16/24/32/48 was already this scale.
 */
:root {
  /* --- Colour roles ------------------------------------------------- */

  /*
   * `--chrome` is the INVERSE surface: a thing that floats over the page and
   * must read as "not the page" — tooltips (Clarify), strong avatars. It is
   * dark in BOTH themes, and `--on-chrome` light in both. That is load-
   * bearing: Clarify.css says so in a comment and relies on it.
   *
   * It is NOT the sidebar any more. The sidebar is `--nav` below, because the
   * approved refinement makes the light-mode rail putty rather than espresso,
   * and flipping `--chrome` to do that would have turned every tooltip in
   * four applications into light-on-light.
   */
  --chrome: oklch(0.29 0.032 32);        /* walnut: tooltips, strong avatars */
  --chrome-2: oklch(0.245 0.03 32);      /* deeper walnut for nested chrome  */
  --on-chrome: oklch(0.94 0.012 70);
  --on-chrome-muted: oklch(0.76 0.02 45);

  /* The navigation rail. Putty in light, warm graphite in dark — the shell
     reads only these four, so an application restyles its rail by restating
     them and nothing else moves. */
  --nav: #EDE9E2;
  --nav-ink: #302B27;
  --nav-ink-muted: #6B625A;
  --nav-line: #DDD6CD;

  --page: #F4F2EE;                       /* warm putty: app background       */
  --surface: #FFFEFC;                    /* cards, panels, table bodies      */
  --inset: #E8E5DF;                      /* table headers, wells, columns    */

  --ink: #302B27;                        /* the kit's main text */
  /* BWD-U17: --ink-faint was 0.635, which is 3.0-3.4:1 against page, surface
     and inset — below AA for the hints, counts and eyebrows it is used for
     in 76 places. Both muted and faint move so the three levels stay
     visibly distinct; the gap from --ink was 14.5:1 to 5.3:1 and had room. */
  /*
   * 18 Sep 2026 — both levels moved down (darker) again, for the reason the
   * owner gave rather than for a failing audit.
   *
   * At 0.535, `--ink-faint` computes to about 4.5:1 on `--inset` — over the
   * AA line by roughly three hundredths. That is a pass, and it is also
   * exactly the state that reads as washed out: a hair above the threshold,
   * at the smallest size in the product, on a tinted ground. An audit that
   * only asks "does it pass" cannot see it; the person looking at it all day
   * can. 0.495 and 0.425 compute to roughly 5.4:1 and 7.3:1 on `--inset`,
   * with the 0.07 lightness gap between the two levels preserved so the three
   * ink weights stay visibly distinct.
   *
   * These ratios are computed from the oklch lightness, not measured off a
   * rendered page as the 18 Sep dark-mode note below was — treat them as the
   * direction and the margin, not as four significant figures.
   */
  /*
   * 23 Sep 2026 — the three ink levels, and why the kit's secondary hex ended
   * up on the FAINT level rather than the muted one.
   *
   * `--inset` is now #E8E5DF. That is a genuinely darker raised surface than
   * the old #F5F0EA, and it is the ground `--ink-faint` is written on in 76
   * places — hints, counts, eyebrows, at the smallest size in the product.
   * Computed against it:
   *
   *   #6B625A on #E8E5DF  4.75:1   (the kit's stated secondary)
   *   #776D64 on #E8E5DF  4.02:1   FAILS
   *
   * So a third level LIGHTER than the kit's secondary cannot clear AA on the
   * kit's own raised surface — there is 0.25 of headroom above the line and a
   * lighter step needs more than that. Rather than ship 76 failing sites, the
   * two levels shift down one notch each: the kit's #6B625A becomes the faint
   * level, and muted goes a step darker to keep the three weights visibly
   * distinct. The kit anticipates exactly this — "check computed contrast and
   * adjust within the same family if necessary; they are not a claim that
   * every pairing already passes".
   *
   *   #302B27 on page 12.9:1 · surface 14.2:1 · inset 11.6:1
   *   #5E564E on page  6.4:1 · surface  7.1:1 · inset  5.7:1
   *   #6B625A on page  5.6:1 · surface  6.2:1 · inset  4.8:1
   */
  --ink-muted: #5E564E;
  --ink-faint: #6B625A;
  --line: #DDD6CD;                       /* DECORATIVE dividers only         */
  /*
   * The interactive one: input borders, control outlines, the dashed edge of
   * a drop zone — anything that has to clear the 3:1 non-text floor.
   *
   * The binding ground is `--inset`, not `--surface`, because a field inside
   * a well is the darkest thing this border is ever drawn on. #7E756B is
   * 3.6:1 there and 4.5:1 on --surface. The first derivation (#988E83) was
   * chosen against white, measured 3.2:1 on surface, and was 2.6:1 on inset —
   * a border that passes on the page and vanishes in the panel.
   *
   * `--line` cannot do this job and must not be asked to: it is 1.3:1 on
   * surface, which is correct for a decorative divider and nothing else.
   */
  --line-strong: #7E756B;

  --accent: #9E4F32;                     /* wood/copper: PRIMARY ACTION only */
  --accent-hover: #8A4429;
  /*
   * The text weight of terracotta, and the only accent that belongs in a
   * `color:` declaration.
   *
   * `--accent` is a FILL. Measured against the surfaces it is written on it is
   * 4.32:1 on --page and 3.98:1 on --accent-quiet — below the 4.5:1 body text
   * needs, which is why accent-coloured labels, quiet accent chips and link
   * text have been drifting into hand-picked hex in the apps. This is the same
   * shape, and the same fix, as --warn-ink beside it.
   *
   * 23 Sep 2026: the kit's #9E4F32 is darker than the terracotta it replaces
   * and measures 5.8:1 on --surface, so the fill would now pass as text on
   * white on its own. --accent-ink stays a separate, darker token anyway,
   * because the grounds it is actually written on are --accent-quiet and
   * --inset, not white, and those are where the old drift started.
   */
  --accent-ink: #8A4429;
  --accent-quiet: #F6E3D8;
  --accent-soft: #F1D9CB;  /* the layer behind the copper card */
  --on-accent: #FFFFFF;

  /* Sage: a restrained grouping surface — the third card tone beside surface
     and accent-quiet. Never a status, never a CTA. */
  --sage: #E1E5DA;
  --sage-ink: #3E4A36;

  /* The veil behind a modal, drawer or narrow-screen sheet. Shared, so a
     component that draws one never depends on the app having defined it. */
  --scrim: rgb(48 43 39 / 0.38);
  /* The light laid over a card's fill to raise its status tab (SculptedCard). */
  --tab-veil: rgb(255 255 255 / 0.62);

  /*
   * The five states, re-tinted onto putty (23 Sep 2026).
   *
   * The hues and the fill/ink split are unchanged — these are the same five
   * meanings with the same rules. What moved is the QUIET fills: they were
   * mixed for a cream ground at oklch lightness 0.955, and on #F4F2EE a mint
   * or an ice-blue chip is the one thing in the product that reads as
   * borrowed from somewhere else. Each quiet fill now carries the page's own
   * warmth. Measured ink-on-quiet is 5.1–5.7:1 across all five.
   */
  --good: #3E6B44;                       /* olive: HEALTHY, never a CTA      */
  --good-quiet: #E4EBDF;
  --warn: #B4801C;                       /* amber: decision needed, at risk  */
  /* BWD-U17: amber at full lightness is 2.84:1 on --surface — it is a fill
     colour, not a text colour. This is the text weight of it, and the only
     one that should ever appear in a `color:` declaration. */
  --warn-ink: #7E5806;
  --warn-quiet: #F7EBD4;
  --crit: #A8372B;                       /* blocked, failed, critical        */
  --crit-quiet: #F6E0DC;
  --info: #3A5E78;                       /* slate: in progress, neutral fact */
  --info-quiet: #E5EAEE;

  /*
   * The ink that goes ON each status fill, as against the fill itself.
   *
   * Without these a caller reaches for `--ink`, which flips with the theme
   * while the fill does not: dark ink on the amber fill measures 1.65:1 in
   * dark mode. Amber and olive stay light in both themes so their ink is dark
   * in both; terracotta, slate and the crit red carry light text in both.
   */
  --on-good: oklch(0.985 0.006 128);
  --on-warn: oklch(0.24 0.035 76);
  --on-crit: oklch(0.985 0.006 40);
  --on-info: oklch(0.985 0.005 232);

  /* Keyboard focus, and NOTHING else.
   *
   * This used to be `var(--accent)`, and the UI review of 6 September caught
   * what that meant on screen: terracotta at hue 40 and `--crit` at hue 28
   * are twelve degrees apart, so a focused percentage field, an open filter,
   * a selected journal row, the active sidebar item and the current tab all
   * read as validation failures. Five findings, one alias.
   *
   * Blue is the one hue in this palette that can never be mistaken for a
   * state: the status colours run olive-amber-terracotta-slate, and nothing
   * in the product means "blue". It is also not `--info` (hue 232) — thirty
   * degrees further round and more saturated, because a focus ring is chrome
   * and an info pill is a status, and the two must not trade places.
   *
   * The three treatments the review found collapsed into each other stay
   * apart from here on: SELECTION is a tinted background, FOCUS is this
   * ring, ERROR is a `--crit` border. */
  --focus: oklch(0.55 0.19 265);
  --focus-quiet: oklch(0.955 0.03 265);

  /* The background a selected row or card carries. Deliberately a fill and
     never a border: a border competes with the focus ring that may be on the
     same element at the same time. */
  --selected: oklch(0.955 0.019 265);

  /* Categorical ramp for data visualisation, in this order. Never encode a
     category by lightness alone. */
  --cat-1: var(--good);
  --cat-2: var(--warn);
  --cat-3: var(--accent);
  --cat-4: oklch(0.42 0.045 42);
  --cat-5: var(--info);

  /* --- Type ---------------------------------------------------------
     Both stacks led with a Windows font and named no Linux one, so every
     platform but Windows fell past the specific names to the generic
     `sans-serif` / `monospace` — which on Ubuntu is DejaVu, around a tenth
     wider than Segoe at the same size. The design had therefore never been
     laid out in the font most of its readers see: macOS, Linux, and CI.

     That is not cosmetic. BWD-R43 spent a day on a swallowed click that
     turned out to be a customer name wrapping to a second line on a Linux
     runner and nowhere else, which grew a card until a full-width button sat
     across its middle. Every width-sensitive layout in OS and Go carried the
     same latent difference.

     `system-ui` leads both now. On Windows it resolves to Segoe UI, so
     nothing changes there and the screenshot baselines still hold; elsewhere
     it resolves to the font the platform actually uses — SF on macOS,
     Cantarell or Ubuntu on GNOME — instead of falling through to a generic.
     The names after it are fallbacks for browsers that do not support
     `system-ui`, ordered per platform, and they end on metric-compatible
     faces rather than on whatever the system happens to alias. */
  --font-sans:
    system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
    'Noto Sans', 'Liberation Sans', Arial, sans-serif;
  /* References, quantities and codes are ALWAYS mono. It is how the eye
     finds PRJ-2416 in a dense table — and a table of them is exactly where a
     fallback with different advances shows up as columns that do not line
     up, so the Linux and macOS names are spelled out here too. */
  --font-mono:
    ui-monospace, 'Cascadia Mono', 'Segoe UI Mono', Consolas, 'SF Mono', Menlo,
    'DejaVu Sans Mono', 'Liberation Mono', monospace;

  /*
   * 18 Sep 2026 — the scale moved up a step at the bottom.
   *
   * The owner's report was that the system is "overwhelming", and specifically
   * "the text vs the background, the number vs the background". The palette
   * turned out not to be the cause. The type scale was: `--text-small` is used
   * **734 times** across BWOOD_OS against **78** uses of `--text-body`, so
   * roughly nine tenths of the product was set *below its own body size* — at
   * 12.5px, frequently in `--ink-faint`, and for numbers in a mono face whose
   * thinner stems read lighter again at the same nominal size.
   *
   * Raising the two bottom steps here lifts all 734 sites at once, which is
   * the only way to do this without editing 734 sites and getting it wrong in
   * some of them. The steps above are unchanged: this is a floor being raised,
   * not the whole scale inflating, because density is still the point.
   */
  --text-eyebrow: 0.75rem;    /* 12   (was 11)   */
  --text-small: 0.84375rem;   /* 13.5 (was 12.5) */
  --text-body: 0.9375rem;     /* 15   (was 14)   */
  --text-card: 1.0625rem;     /* 17   */
  --text-screen: 1.3125rem;   /* 21   */
  --text-display: 1.75rem;    /* 28   */
  --text-hero: 2.5rem;        /* 40   */

  --weight-body: 400;
  --weight-emphasis: 550;
  --weight-heading: 650;
  --weight-display: 750;

  /* --- Space -------------------------------------------------------- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;
  --space-8: 32px;
  --space-12: 48px;

  /*
   * 23 Sep 2026 — the kit's radii. Rounder than the crisp direction of
   * 1 Sep, which is the change the owner approved: cards 24, prominent
   * object cards 24–28, controls 12–16, pills genuinely round.
   *
   * `--radius-card` moving 12 → 24 is the single largest visual lever in the
   * file: it reaches every Panel, Modal, Drawer and table shell in four
   * applications at once. Anything that must stay crisp — a table cell, a
   * grid line, a dense line-entry row — uses `--radius-inset`, and that is
   * unchanged at 8.
   */
  --radius-control: 12px;
  --radius-card: 24px;
  --radius-object: 28px;  /* the sculpted project/attention/task card only */
  --radius-mobile: 20px;
  --radius-pill: 999px;
  /*
   * The genre tranche's three shapes (20 Sep), merged 24 Sep onto the
   * approved radii above rather than its own 16px card: a stat tile and an
   * entity card are large and mostly empty, so they take the object radius;
   * a sheet slides up over the page and draws only the two corners facing
   * into the screen; a circle is a pill that stays
   * round when its label makes it 2px wider than tall.
   */
  --radius-tile: var(--radius-object);
  --radius-sheet: 28px;
  --radius-circle: 999px;

  --sidebar-width: 236px;
  /** Icon+label rail (Shell.tsx). Narrower than the old grouped sidebar —
      there is no section heading to make room for any more. */
  --rail-width: 208px;
  --topbar-height: 60px;
  --content-max: 1320px;
  --touch-target: 44px;

  /*
   * Elevation, as it was until 20 Sep 2026: one shadow for genuine layering,
   * and flat cards separated by a hairline rather than by a shadow.
   *
   * The genre tranche reverses the second half of that sentence — see
   * `--elev-resting` below — but these three names stay exactly as they are
   * and keep their jobs. `--shadow-popover` in particular is not an
   * elevation step: it is what a menu, a combobox list or a Clarify bubble
   * uses to say "I am floating over the document", and nothing about cards
   * changes that.
   */
  --shadow-raised: 0 1px 2px oklch(0.29 0.032 32 / 0.07),
                   0 12px 32px oklch(0.29 0.032 32 / 0.10);
  /* BWD-U17: four stylesheets fell back to `rgb(0 0 0 / 0.16)` for this,
     because the token they named was never defined. Tinted walnut like the
     one above — DESIGN.md has no pure black anywhere. */
  --shadow-popover: 0 8px 24px oklch(0.29 0.032 32 / 0.16);
  /* Three stylesheets each reinvented a hover shadow because this never
     existed. Defined once, for the one legitimate case — an interactive
     card or row lifting under the pointer. */
  --shadow-hover: 0 1px 3px oklch(0.29 0.032 32 / 0.06),
                  0 4px 14px oklch(0.29 0.032 32 / 0.09);
  /* The inset cue on a scroll container's clipped edge (TableScroll).
     Its own token, and the dark value is a *lighter* wash rather than a
     darker one — which is the whole reason it cannot reuse the three above.
     `--surface` in dark mode is already near-black (L 0.245), so a black
     overlay has nowhere to go: even at full opacity it reaches about 1.29:1
     against that surface, i.e. invisible. Lifting the edge instead has all
     the headroom, and reads at roughly 2.9:1. Light mode keeps the tinted
     walnut, where darkening does work. */
  --shadow-edge: oklch(0.29 0.032 32 / 0.28);

  /* --- Genre: the two elevations -------------------------------------
   *
   * 20 Sep 2026. The visual language asks for cards that float on a tinted
   * page, separated by real space rather than hairlines, with "one elevation
   * for resting cards, one for raised", and states the rule that governs
   * both: never a hard border AND a shadow on the same edge.
   *
   * So a card's edge is drawn exactly once, by one of these. A component
   * that takes `--elev-resting` removes its `border`; a component that keeps
   * a border — the dashed `EmptyState`, a table's own frame — takes no
   * elevation. Both is the failure this pair exists to prevent.
   *
   * WHY DARK IS NOT THE LIGHT VALUE WITH A BLACKER SHADOW.
   *
   * Measured before writing, because the Analytics agent's finding was that
   * light steps fail on the dark surface and the honest default is to assume
   * mine did too. They did, and worse than expected:
   *
   *   --surface against --page is 1.07:1 in light and 1.11:1 in dark. In
   *   BOTH themes the card's fill is invisible on its own and the edge is
   *   whatever the shadow draws.
   *
   *   In light the shadow can draw it: walnut at 0.07 alpha over the cream
   *   page composites to about 1.07:1, and the blur spreads that over enough
   *   pixels for the eye to integrate it into a soft edge. That is the look
   *   the reference has and it is what light mode is good at.
   *
   *   In dark it cannot. Black over `--page` (oklch 0.20) reaches 1.03:1 at
   *   0.2 alpha, 1.06:1 at 0.4, and 1.14:1 at 0.9 — a fully opaque black
   *   shadow on this page is still very nearly invisible. `--shadow-raised`'s
   *   dark value is 0.4/0.45 black, i.e. about 1.06:1, which means the raised
   *   cards in this product have had no visible elevation in dark mode at all.
   *   It looked like a shadow in the stylesheet and was one nowhere else.
   *
   * The fix is the one `--shadow-edge` already found for the same reason:
   * on a dark ground you lift the edge instead of darkening it. A 1px
   * light-wash ring at 0.09 alpha composites to 2.4:1 against the page, and
   * at 0.14 to about 3.2:1 — visible, and the raised step clears the 3:1 that
   * WCAG 1.4.11 asks of a non-text boundary. It is drawn with `box-shadow`,
   * follows `border-radius`, and is the ONLY thing drawing that edge, so the
   * no-border-and-shadow rule is kept rather than dodged. The blurred black
   * layers stay underneath: they do little against `--page` but they do seat
   * a raised card against the lighter `--surface` it may be sitting on.
   */
  --elev-resting: 0 1px 2px oklch(0.29 0.032 32 / 0.05),
                  0 4px 12px oklch(0.29 0.032 32 / 0.07);
  --elev-raised: 0 2px 6px oklch(0.29 0.032 32 / 0.07),
                 0 14px 34px oklch(0.29 0.032 32 / 0.13);

  /* --- Genre: rhythm --------------------------------------------------
   *
   * "The reference breathes; today's screens do not." These are the numbers
   * that make that true in one place instead of four. A screen sets its
   * spacing from these, never from `--space-*` directly, so that a change of
   * mind about how much a card breathes is one line and not an audit.
   */
  --card-pad: var(--space-6);         /* 24 — a card with prose or tiles in it */
  --card-pad-tight: var(--space-4);   /* 16 — a card wrapping a dense register */
  --card-gap: var(--space-4);         /* 16 — between cards inside one section */
  --section-gap: var(--space-8);      /* 32 — between sections down a page     */
  /* The page's side gutter. Narrowed to 16px under the phone breakpoint in
     genre.css — §5 asks for a 16px gutter and no horizontal scrolling, and
     24px each side of a 360px phone is 13% of the screen. */
  --page-gutter: var(--space-6);
  /* AUD-DS-06: how tall a table region grows before it scrolls inside its
     card (the head stays pinned). `dvh` so a phone's toolbar does not count. */
  --table-max-h: min(70dvh, 720px);

  /* --- Genre: component metrics ---------------------------------------
     The three sizes the new components share. Named because an EntityCard's
     circular affordance and a BottomBar's centre action are the same
     gesture at two scales and must not drift apart. */
  --affordance-size: 40px;            /* the circle in an EntityCard footer */
  --affordance-size-lg: 56px;         /* the bottom bar's centre action     */
  --avatar-size: 28px;
  --avatar-size-sm: 22px;
  /* The bar's own height, without the safe area. A phone layout reserves
     `calc(var(--bottombar-height) + env(safe-area-inset-bottom, 0px))` at
     the foot of its scroll container — see BottomBar.css. */
  --bottombar-height: 64px;

  /* --- Aliases -------------------------------------------------------
     Seventeen names are reached for across the screens and were defined
     nowhere. A `var()` with no fallback and no definition makes the whole
     declaration invalid at computed-value time, so these were not
     near-misses — they were rules that silently did not apply.
     `boq.css`'s `border: 1px solid var(--rule)` is why the BOQ line editor
     has no border today, and its `var(--space-5)` indent is why the card
     sits flush against the row.

     Aliases rather than copies, so they re-tint with the dark palette on
     their own: `var()` resolves at the point of use, not here. New code
     should prefer the canonical name on the right. */
  --rule: var(--line);
  /* `--focus` is NOT an alias any more — see its definition in the palette
     above. Aliasing it to `--accent` is what made focus look like an error. */
  --brand: var(--accent);
  --space-5: 20px;
  --surface-sunken: var(--inset);
  --surface-muted: var(--inset);
  --surface-quiet: var(--inset);
  --ink-quiet: var(--ink-faint);
  --crit-ink: var(--crit);
  --info-ink: var(--info);
  --warn-line: var(--warn);
  --warn-surface: var(--warn-quiet);
  /* A soft accent-tinted border for hovered interactive rows — resolved
     per-theme through the mix, so dark needs no second definition. */
  --accent-line: color-mix(in oklch, var(--accent) 50%, var(--line));
  --crit-line: var(--crit);
  --crit-surface: var(--crit-quiet);
  --font-body: var(--font-sans);
  --text-page: var(--text-screen);
  --text-section: var(--text-card);
  --weight-normal: var(--weight-body);
  --weight-medium: var(--weight-emphasis);
  --weight-label: var(--weight-emphasis);

  /* --- Motion ------------------------------------------------------- */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-hover: 120ms;
  --dur-panel: 180ms;
  --dur-drawer: 240ms;

  /* --- Added for the shared layout and planning components (OS revamp,
     22 Sep 2026). Defaults only: an application that never renders
     AppShell, WeekCalendar, Roadmap or StageTracker sees no change, and
     BWOOD OS restates every one of these in its own theme. ----------- */
  /* --sage / --sage-ink: declared once, with the kit's values, in the colour
     roles above. This block used to restate them in oklch AFTER that, so the
     legacy value silently won (found by the Analytics pass, 23 Sep 2026). */
  --radius-inset: 8px;
  --radius-surface: var(--radius-card);
  --rail-collapsed: 76px;
  --shadow-card: 0 1px 2px oklch(0.29 0.032 32 / 0.05);
  --dur-enter: 180ms;
}

/**
 * Dark. Evening shift, and OLED phones on the shop floor. Re-tinted rather
 * than inverted: the walnut gets deeper, the cream becomes a warm charcoal,
 * and the accents gain lightness to hold contrast against it.
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --chrome: oklch(0.22 0.026 32);
    --chrome-2: oklch(0.185 0.022 32);
    --on-chrome: oklch(0.92 0.012 70);
    --on-chrome-muted: oklch(0.70 0.018 45);

    /* Layered warm graphite: the rail sits BETWEEN canvas and surface in
       lightness, so the shell reads as one object lit from the content side
       rather than as a black gutter beside a grey page. */
    --nav: #24221F;
    --nav-ink: #F0EBE3;
    --nav-ink-muted: #BDB2A6;
    --nav-line: #3A352F;

    --page: #1E1D1A;
    --surface: #2C2A26;
    --inset: #35322D;

    --ink: #F0EBE3;
    --ink-muted: #BDB2A6;
    /* BWD-U17 raised this from 0.60 to 0.63 for being 4.11:1 on the dark
       surface. Re-measured on 18 Sep 2026 against a rendered page: 0.63 is
       *also* 4.11:1 there, so the move was real but too small to reach the
       4.5:1 the hints, counts and eyebrows using it need. 0.66 measures
       4.6:1 on --surface.

       Raised again the same day, to 0.70, for the reason recorded against the
       light theme: 4.6:1 clears AA and still reads faint at 12.5px, and the
       type scale it is set in has now moved up a step too. --ink-muted moves
       with it, keeping the gap that makes the two levels tell apart. */
    /* Dark has the headroom light did not, so both levels keep the kit's
       stated secondary and a genuinely lighter third above it:
         #BDB2A6 on surface 8.0:1 · on inset 6.1:1
         #ACA294 on surface 6.6:1 · on inset 5.1:1
       (#A2978B, the first derivation, was 4.46:1 on --inset — under the line
       by four hundredths, which is exactly the failure a spot check misses.) */
    --ink-faint: #ACA294;
    --line: #49423B;                     /* decorative only */
    --line-strong: #928980;              /* 3.2:1 on --surface: input borders */

    --accent: #D7986F;
    --accent-hover: #E3A87F;
    /* Lighter than the fill here, for the same reason it is darker in light:
       it is read against the page, not printed on the accent. */
    --accent-ink: #E3A87F;
    --accent-quiet: #40291D;
    --accent-soft: #EDC6A8;
    --on-accent: #201C18;

    --sage: #3D4438;
    --sage-ink: #C4D1B7;
    --scrim: rgb(0 0 0 / 0.55);
    --tab-veil: rgb(255 255 255 / 0.08);

    --good: #9BC49F;
    --good-quiet: #2B3A2C;
    --warn: #E0B368;
    --warn-ink: #EAC489;
    --warn-quiet: #3D3320;
    /* BWD-U17: 0.645 was 3.93:1 on --crit-quiet. */
    --crit: #E89183;
    --crit-quiet: #402722;
    --info: #93B2C9;
    --info-quiet: #27323A;
    --on-good: oklch(0.20 0.032 128);
    --on-crit: oklch(0.20 0.032 28);
    --on-info: oklch(0.20 0.032 232);

    /* Lifted for the dark ground; same hue, so focus reads identically. */
    --focus: oklch(0.74 0.15 265);
    --focus-quiet: oklch(0.29 0.055 265);
    --selected: oklch(0.285 0.035 265);

    --cat-4: oklch(0.62 0.05 42);

    --shadow-raised: 0 1px 2px oklch(0 0 0 / 0.4), 0 12px 32px oklch(0 0 0 / 0.45);
    --shadow-popover: 0 8px 24px oklch(0 0 0 / 0.5);
    --shadow-hover: 0 1px 3px oklch(0 0 0 / 0.3), 0 4px 16px oklch(0 0 0 / 0.32);
    --shadow-edge: oklch(0.97 0.01 78 / 0.16);

    /* Re-validated against the dark surface, not flipped. The ring is what
       draws the edge here; see the long note beside the light values. */
    --elev-resting: 0 0 0 1px oklch(0.97 0.01 78 / 0.09),
                    0 2px 6px oklch(0 0 0 / 0.36);
    --elev-raised: 0 0 0 1px oklch(0.97 0.01 78 / 0.14),
                   0 4px 10px oklch(0 0 0 / 0.42),
                   0 16px 36px oklch(0 0 0 / 0.5);
  }
}

/* The theme toggle stamps data-theme on the root; it must win both ways. */
:root[data-theme='dark'] {
  --chrome: oklch(0.22 0.026 32);
  --chrome-2: oklch(0.185 0.022 32);
  --on-chrome: oklch(0.92 0.012 70);
  --on-chrome-muted: oklch(0.70 0.018 45);
  --nav: #24221F;
  --nav-ink: #F0EBE3;
  --nav-ink-muted: #BDB2A6;
  --nav-line: #3A352F;
  --page: #1E1D1A;
  --surface: #2C2A26;
  --inset: #35322D;
  --ink: #F0EBE3;
  --ink-muted: #BDB2A6;
  /* See the note in the media-query block above: 0.63 measured 4.11:1, not
     the 4.5:1 this is used at, and 0.66 cleared it by too little to read
     comfortably. This block must carry the same values as that one — a token
     that moves in only one of the two is how a theme ends up half-applied. */
  --ink-faint: #ACA294;
  --line: #49423B;
  --line-strong: #928980;
  --accent: #D7986F;
  --accent-hover: #E3A87F;
  --accent-ink: #E3A87F;
  --accent-quiet: #40291D;
  --accent-soft: #EDC6A8;
  --on-accent: #201C18;
  --sage: #3D4438;
  --sage-ink: #C4D1B7;
  --scrim: rgb(0 0 0 / 0.55);
  --tab-veil: rgb(255 255 255 / 0.08);
  --good: #9BC49F;
  --good-quiet: #2B3A2C;
  --warn: #E0B368;
  --warn-ink: #EAC489;
  --warn-quiet: #3D3320;
  /* BWD-U17: 0.645 was 3.93:1 on --crit-quiet. */
  --crit: #E89183;
  --crit-quiet: #402722;
  --info: #93B2C9;
  --info-quiet: #27323A;
  --on-good: oklch(0.20 0.032 128);
  --on-crit: oklch(0.20 0.032 28);
  --on-info: oklch(0.20 0.032 232);
  /* Lifted for the dark ground; same hue, so focus reads identically. */
  --focus: oklch(0.74 0.15 265);
  --focus-quiet: oklch(0.29 0.055 265);
  --selected: oklch(0.285 0.035 265);
  --cat-4: oklch(0.62 0.05 42);
  --shadow-raised: 0 1px 2px oklch(0 0 0 / 0.4), 0 12px 32px oklch(0 0 0 / 0.45);
  --shadow-popover: 0 8px 24px oklch(0 0 0 / 0.5);
  --shadow-hover: 0 1px 3px oklch(0 0 0 / 0.3), 0 4px 16px oklch(0 0 0 / 0.32);
  --shadow-edge: oklch(0.97 0.01 78 / 0.16);
  /* Must carry the same values as the media-query block above — a token
     that moves in only one of the two is how a theme ends up half-applied,
     and `genre.test.ts` now fails the build when they diverge. */
  --elev-resting: 0 0 0 1px oklch(0.97 0.01 78 / 0.09),
                  0 2px 6px oklch(0 0 0 / 0.36);
  --elev-raised: 0 0 0 1px oklch(0.97 0.01 78 / 0.14),
                 0 4px 10px oklch(0 0 0 / 0.42),
                 0 16px 36px oklch(0 0 0 / 0.5);
}

/* --- Base ----------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  min-height: 100%;
}

body {
  background: var(--page);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-body);
  font-weight: var(--weight-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* A column of figures must align. Anywhere one can occur, numerals are
   tabular — this is not optional decoration. */
table,
.num,
[data-numeric] {
  font-variant-numeric: tabular-nums;
}

code,
kbd,
samp,
.ref {
  font-family: var(--font-mono);
}

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-control);
}

/* Body copy caps at 70ch; longer lines lose the reader on the return sweep. */
.prose {
  max-width: 70ch;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Visually hidden but read aloud — announced to a screen reader, hidden from
   sighted users. Clip-based rather than display:none or visibility:hidden,
   which screen readers skip entirely.

   A global utility, so it belongs in the base. It used to live in
   primitives.css, which meant Skeleton and Toast could only get it by
   depending on another component's stylesheet — the cross-import this
   tranche exists to stop. */
.sr-only {
  position: absolute;
  /*
   * Pinned, not left at its static position — 13 Sep 2026.
   *
   * The tester, on a project: *"bug in projects I can scroll that much which
   * is wrong."* Measured on the setup-wizard tab: the window scrolled 446px
   * past the bottom of an application whose shell is exactly `100dvh`, into
   * nothing. `document.body.scrollHeight` was 720 and
   * `documentElement.scrollHeight` was 1166, which is the signature of a box
   * positioned against the initial containing block rather than against
   * anything in the page.
   *
   * These were that box. `position: absolute` with no offsets leaves an
   * element at its *static* position — where it would have been in flow —
   * and when the nearest positioned ancestor is the document (which it is,
   * for a label deep inside a pane that scrolls but is not `position:
   * relative`), a 1px box four screens down the pane lands four screens down
   * the DOCUMENT, and the document grows to hold it.
   *
   * `Clarify.css` already carries this finding for its own shut bubble —
   * "a mark in the sixth lane of a sideways-scrolling board put a 1px
   * absolute box at x≈1500 relative to the page, and the page grew to
   * 1794px to hold it" — and fixed it for that one component by giving the
   * mark a containing block. Every other `.sr-only` in the product still had
   * it. Two offsets fix all of them at once: the box goes to the top-left of
   * whatever contains it and contributes nothing to any scroll area.
   *
   * Safe because this element is never seen. It is clipped to nothing and
   * exists only to be read aloud, and a screen reader takes its place in the
   * reading order from the DOM, not from the layout.
   */
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
  /*
   * Read as written, not as the heading above it is drawn.
   *
   * `Clarify`'s bubble is `.sr-only` while it is shut, and a good many of the
   * headings it sits beside are `text-transform: uppercase` — a section
   * eyebrow, a fieldset legend. The transform is inherited, so the sentence
   * announced to a screen reader is the uppercase one, and several readers
   * spell an all-caps run out letter by letter. The bubble resets this when
   * it OPENS (Clarify.css); shut, nothing did.
   *
   * Nothing visible changes: this element is clipped to a single pixel.
   */
  text-transform: none;
}

/**
 * Three densities (§6.2d, BWD-U14 / BWD-U22).
 *
 * The tokens for these already existed; nothing applied them, so every
 * surface in the product sat at one density — a record header as tight as a
 * table row, and a guided step as tight as both. These are container
 * classes, not new tokens: a screen declares which of the three it is and
 * the type scale, control height and measure follow.
 *
 *  - **Summary** — record headers, KPI rows, overview cards. Generous, 17px
 *    body against a 21px heading. What you read once and act on.
 *  - **Register** — tables and lists. The current density, unchanged; a
 *    register earns its density by being scanned, not read.
 *  - **Flow** — guided steps. One column, 44px targets (--touch-target),
 *    a ~640px measure. What you fill in.
 */
.bw-density-summary {
  --density-gap: var(--space-6);
  --density-control-height: 38px;
  font-size: var(--text-card);
  line-height: 1.55;
}

.bw-density-summary :is(h1, h2) {
  font-size: var(--text-screen);
}

.bw-density-flow {
  --density-gap: var(--space-4);
  --density-control-height: var(--touch-target);
  font-size: var(--text-body);
}

/* A flow is one column at a measure that stays readable. Prose only — a
   table inside a step must be allowed its full width, which is the bug
   §12.2 of the blueprint records being fixed once already. */
.bw-density-flow > p,
.bw-density-flow > ul,
.bw-density-flow > ol {
  max-width: 640px;
}

.bw-density-flow :is(input, select, textarea, .bw-button) {
  min-height: var(--density-control-height);
}

/* Inline error and loading lines. These were `customers__error` and
   `customers__loading` — defined in Customers.css and used by 26 and 14
   other screens respectively, so every one of them imported Customers'
   stylesheet to get them. Global here, because that is what they always
   were. */
.bw-error {
  margin: var(--space-2) 0;
  color: var(--crit);
  font-size: var(--text-small);
}

.bw-loading {
  margin: var(--space-4) 0;
  color: var(--ink-muted);
}

/* --------------------------------------------------------------------
   Native controls — select, file input, tick boxes, tab focus.
   Ported from ControlsTheme.css (1 Sep 2026), tokens only, no hex and no
   !important: with the override themes retired there is nothing left to
   out-rank. The :not() guards leave LineGrid's cell editors and
   FileField's hidden input alone — both style themselves.
   -------------------------------------------------------------------- */

select:not(.bw-grid__editor) {
  appearance: none;
  -webkit-appearance: none;
  /* 44px, the kit's control height — the component-class controls moved to
     it on 23 Sep 2026, and a bare native control left at 38 sits visibly
     shorter beside them in the same row. */
  min-height: var(--touch-target, 44px);
  padding: 8px 32px 8px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-control);
  background:
    var(--surface)
    url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23625b55' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
    no-repeat right 10px center;
  color: var(--ink);
  font: inherit;
  font-size: var(--text-small);
  cursor: pointer;
  transition: border-color var(--dur-hover) var(--ease), background-color var(--dur-hover) var(--ease);
}

/*
 * A chevron is a data URI, so its stroke cannot take a token and has to be
 * restated per theme — and until 18 Sep 2026 only the explicit half was.
 * Every reader on a dark machine who had chosen nothing, which is the default,
 * got the pale-surface chevron drawn on a dark field. Both halves now, in the
 * order the rest of this file uses: the system default first, the explicit
 * choice after it so it wins in both directions.
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) select:not(.bw-grid__editor) {
    background:
      var(--surface)
      url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23c2b7ac' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
      no-repeat right 10px center;
  }
}

:root[data-theme='dark'] select:not(.bw-grid__editor) {
  background:
    var(--surface)
    url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23c2b7ac' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
    no-repeat right 10px center;
}

.bw-density-flow :is(input, select, textarea):not(.bw-grid__editor) {
  min-height: var(--touch-target);
}
.bw-density-flow .bw-grid :is(input, select, textarea) { min-height: 0; }

select:hover:not(:disabled) { border-color: var(--accent); }
select:disabled { opacity: 0.55; cursor: not-allowed; background-color: var(--inset); }
select option { background: var(--surface); color: var(--ink); }

/* --------------------------------------------------------------------
   A date field's opener, drawn like a select's.

   Owner, 17 Sep 2026, on the Job's route steps: *"adjust drop down arrow
   for the date tab in routes section"*. `Station` and `Named person`
   there are selects carrying the 14px chevron above; `Planned date` sat
   beside them showing the browser's own calendar glyph — a different
   icon, a different size, in near-black rather than `--ink-faint`, and
   pinned hard against the border. Three controls in one row, one of them
   visibly not of the same family.

   So the indicator takes the select's chevron: same 14px mask, same
   inset, same muted colour. It is a background-image on the shadow
   pseudo-element rather than a mask, because `-webkit-mask` on
   `::-webkit-calendar-picker-indicator` is not honoured in every Chromium
   version this runs on. `opacity: 1` is needed — the default indicator is
   translucent, which would wash the chevron out next to a select's.

   Chromium only: Firefox exposes no pseudo-element for this control, so it
   keeps its own calendar icon. The field still works and still reads as a
   field — it is the icon that differs — and there is no standard selector to
   fix it with, so it is left alone rather than papered over with an overlaid
   element that would sit on top of a real button.
   -------------------------------------------------------------------- */
input[type='date']:where(:not(.bw-grid__editor))::-webkit-calendar-picker-indicator,
input[type='datetime-local']:where(:not(.bw-grid__editor))::-webkit-calendar-picker-indicator,
input[type='month']:where(:not(.bw-grid__editor))::-webkit-calendar-picker-indicator,
input[type='time']:where(:not(.bw-grid__editor))::-webkit-calendar-picker-indicator {
  width: 14px;
  height: 14px;
  margin: 0;
  padding: 0;
  opacity: 1;
  cursor: pointer;
  background:
    url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23625b55' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
    no-repeat center / 14px 14px;
}

/* The same one-sided gap as the chevron above, for the same reason. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) input[type='date']:where(:not(.bw-grid__editor))::-webkit-calendar-picker-indicator,
  :root:not([data-theme='light']) input[type='datetime-local']:where(:not(.bw-grid__editor))::-webkit-calendar-picker-indicator,
  :root:not([data-theme='light']) input[type='month']:where(:not(.bw-grid__editor))::-webkit-calendar-picker-indicator,
  :root:not([data-theme='light']) input[type='time']:where(:not(.bw-grid__editor))::-webkit-calendar-picker-indicator {
    background:
      url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23c2b7ac' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
      no-repeat center / 14px 14px;
  }
}

:root[data-theme='dark'] input[type='date']:where(:not(.bw-grid__editor))::-webkit-calendar-picker-indicator,
:root[data-theme='dark'] input[type='datetime-local']:where(:not(.bw-grid__editor))::-webkit-calendar-picker-indicator,
:root[data-theme='dark'] input[type='month']:where(:not(.bw-grid__editor))::-webkit-calendar-picker-indicator,
:root[data-theme='dark'] input[type='time']:where(:not(.bw-grid__editor))::-webkit-calendar-picker-indicator {
  background:
    url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23c2b7ac' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
    no-repeat center / 14px 14px;
}

/* The chevron sits where a select's does — 10px in from the edge — so a
   date field and a select in the same row line their openers up. */
input[type='date']:where(:not(.bw-grid__editor)),
input[type='datetime-local']:where(:not(.bw-grid__editor)),
input[type='month']:where(:not(.bw-grid__editor)),
input[type='time']:where(:not(.bw-grid__editor)) {
  padding-right: 10px;
}

/* --------------------------------------------------------------------
   Text entry — the floor every screen builds on.

   `select` has been styled globally here since the port from
   ControlsTheme, and `input[type=file]`, the tick boxes and search each
   have a rule below. Free-text entry never got one. The consequence was
   not cosmetic drift, it was fields that are not visibly fields:
   `Engineering.css`'s `.eng__detail textarea` declares `resize: vertical`
   and nothing else, so "Setup instruction", "Expected output" and a BOM
   line's "Material specification" render as bare text on the card with no
   edge at all — findable only by clicking where one guesses, or by
   noticing the resize grip. Around twenty screens hand-rolled the same
   six declarations to avoid that, each free to drift, and the ones that
   forgot are the bug.

   Written with `:where()`, which contributes NO specificity: the whole
   selector weighs (0,0,1), so every existing class rule — including
   `.bw-combo__input`, `.pj__note-field textarea` and LineGrid's
   `.bw-grid__editor--combo .bw-combo__input { border: 0 }` — still wins
   without touching a `!important`. This is a floor, not an override, and
   a screen that wants a chrome-less control keeps it by saying so.

   The exclusion list is by TYPE rather than an opt-in class because a
   text input usually declares no `type` at all: of ~300 inputs in
   BWOOD_OS only two say `type="text"`. So everything that is not a
   button, a tick box, a file picker or a slider is text entry, and the
   list below is what that leaves out. `[type=file]` and the hidden
   FileField input are excluded because both style themselves below.
   -------------------------------------------------------------------- */
input:where(:not(
  [type='button'], [type='submit'], [type='reset'], [type='image'],
  [type='checkbox'], [type='radio'], [type='file'],
  [type='range'], [type='color'],
  .bw-grid__editor, .bw-filefield__input
)),
textarea:where(:not(.bw-grid__editor)) {
  min-height: var(--touch-target, 44px);
  padding: 8px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: var(--text-small);
  transition: border-color var(--dur-hover) var(--ease);
}

/* A textarea is a block of prose, not a one-line field: it needs room to
   start with and a grip that only travels one way, so dragging it cannot
   break the column it sits in. */
textarea:where(:not(.bw-grid__editor)) {
  min-height: 72px;
  resize: vertical;
  line-height: 1.45;
}

/* The same two states `select` gets, for the same reason. The disabled one
   is not decoration: several screens render a read-only field as a
   disabled input, and without this it is pixel-identical to an editable
   one — so a person types into it and nothing happens. */
:is(input, textarea):where(:not(
  [type='button'], [type='submit'], [type='reset'], [type='image'],
  [type='checkbox'], [type='radio'], [type='file'],
  [type='range'], [type='color'],
  .bw-grid__editor, .bw-filefield__input
)):hover:not(:disabled, :read-only) {
  border-color: var(--accent);
}

:is(input, textarea):where(:not(
  [type='button'], [type='submit'], [type='reset'], [type='image'],
  [type='checkbox'], [type='radio'], [type='file'],
  [type='range'], [type='color'],
  .bw-grid__editor, .bw-filefield__input
)):disabled {
  opacity: 0.55;
  cursor: not-allowed;
  background-color: var(--inset);
}

input[type='file']:not(.bw-filefield__input) {
  display: block;
  width: 100%;
  padding: 7px 10px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink-muted);
  font: inherit;
  font-size: var(--text-small);
  cursor: pointer;
  transition: border-color var(--dur-hover) var(--ease), background var(--dur-hover) var(--ease);
}
input[type='file']:not(.bw-filefield__input):hover {
  border-color: var(--accent);
  background: var(--accent-quiet);
}
input[type='file']:not(.bw-filefield__input)::file-selector-button {
  margin-right: var(--space-3);
  padding: 6px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: var(--text-small);
  font-weight: var(--weight-emphasis);
  cursor: pointer;
}

input[type='checkbox'],
input[type='radio'] {
  accent-color: var(--accent);
  width: 16px;
  height: 16px;
  cursor: pointer;
}

[role='tablist'] > button[role='tab']:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -2px;
  border-radius: var(--radius-control);
}

input[type='checkbox']:disabled,
input[type='radio']:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

/* A select that sits alone in a filter bar needs a floor of its own, or it
   reads as a stray field. */
.bw-filterbar select {
  min-width: 168px;
}

input[type='search'] {
  appearance: none;
  -webkit-appearance: none;
}

/* Safari draws its own clear button, which lands on top of ours. */
input[type='search']::-webkit-search-decoration,
input[type='search']::-webkit-search-cancel-button {
  -webkit-appearance: none;
}

@media (prefers-reduced-motion: reduce) {
  .bw-button,
  select,
  input[type='file'] {
    transition: none;
  }
}
