/* ══ FINNA · COMPONENTS ════════════════════════════════════════════════════════════
   The shared component layer, one level above tokens.css. Every surface links tokens.css
   and then this file; the values here are tokens only, nothing is hardcoded. Hand-kept
   (not generated) so it stays easy to read and change — source.py remains the home for the
   token values themselves.

   Brand Guide v1.5 · first slice — the keys, link, input and selection controls, plus the
   text patterns and the form group they compose into. Components read ROLE tokens
   (--bg, --text, --border, --primary …), so a theme that swaps those tokens re-colours every
   component here for free — see the theme note in the preview.

   WHAT A PAGE MUST LOAD (REV 35 — verified on a bare page with nothing else):
     1. chrome/fonts.css   — Lexend. Without it the components fall back to the system
                             sans named in --font-sans; nothing breaks, the face is wrong.
     2. brand/tokens.css   — every value here is a var() from it. REQUIRED, and first.
     3. this file          — after tokens.css, before any page CSS that adjusts them.
   No global reset is required: the baseline rule below gives the components border-box
   sizing themselves. THEMES: the light ground is the default and needs nothing. On a dark
   ground, give the scope the four semantic hooks — --focus, --text-link, --heading,
   --text-error — plus dark values for the role tokens; every hook falls back to its
   light-ground colour, so a page that sets none simply renders the light components.
   ════════════════════════════════════════════════════════════════════════════════════ 
   DARK MODE (REV 42, 25 Sep 2026): the panel and the data table face --bg-raised, and the
   green chips write in --text-selected, so both modes read. On white every value is unchanged.
   */

/* ── BASELINE — the components size their borders inward whatever the page does. The brand
   page has a global border-box reset; a bare page does not, and .input{width:100%} overflows
   its row without this. Scoped to the components so a host page's own model is untouched. */
.key,.link,.input,.choice,.choice *,.switch,.switch *,.intro,.intro *,.prose,.prose *,
.cta,.cta *,.form-row,.form-row *,.panel,.panel *,.actions,.actions *{ box-sizing:border-box }

/* ── ELEMENT · KEY — the one button (canonical, promoted from the site) ───────────────── */
.key{
  --kh:var(--key-height-m); --kr:var(--key-radius-m); --ko:var(--key-offset-m);
  --klabel:var(--key-label-m); --kicon:var(--key-icon-m);
  display:inline-flex; align-items:center; justify-content:center; gap:var(--space-2);
  height:var(--kh); min-width:calc(var(--kh) * 2.25); padding:0 calc(var(--kh) / 2);
  border:0; border-radius:var(--kr); cursor:pointer; text-decoration:none;
  font:var(--weight-label) var(--klabel)/1 var(--font-sans); letter-spacing:.01em;
  background-color:var(--primary); color:var(--white);
  background-image:var(--key-sheen);
  box-shadow:0 var(--ko) 0 var(--primary-pressed), var(--key-edge);
  transition:background-color var(--t-state) var(--ease,ease),
             box-shadow var(--t-state) var(--ease,ease),
             transform var(--t-state) var(--ease,ease);
}
.key:hover{ background-color:var(--primary-hover);
  transform:translateY(calc(var(--ko) / 2)); box-shadow:0 calc(var(--ko) / 2) 0 var(--primary-pressed), var(--key-edge) }
.key:active{ transform:translateY(var(--ko)); background-color:var(--primary-pressed);
  box-shadow:0 0 0 var(--primary-pressed), var(--key-edge) }
/* THE FOCUS RING HOLDS THE SHELF (Rose, 29 Sep 2026: "it looks off"). An outline hugs the face
   only, so the shelf crowded the ring's bottom edge while the top had air. The ring is drawn
   round face AND shelf, with the same gap all round. */
.key:focus-visible{ outline:none }
.key:focus-visible::before{ --kfo:3px; content:""; position:absolute; pointer-events:none;
  inset:calc(-1 * var(--kfo)) calc(-1 * var(--kfo)) calc(-1 * (var(--kfo) + var(--ko))) calc(-1 * var(--kfo));
  border:2px solid var(--focus, var(--primary)); border-radius:calc(var(--kr) + var(--kfo)) }
.key[disabled],.key:disabled{ background-color:var(--bg-sunken); color:var(--text-secondary);
  background-image:none; box-shadow:inset 0 0 0 1px var(--border); cursor:not-allowed }
/* The guide's rule, now the code's: "the label is still information — it is never faded
   into the disabled colour" (§Buttons). text-secondary reads at 7.5:1 on the sunken face's
   ground; the flat face, no offset and no motion are what say "inactive". */
.key svg{ width:var(--kicon); height:var(--kicon); flex:none }
.key-s{ --kh:var(--key-height-s);--kr:var(--key-radius-s);--ko:var(--key-offset-s);--klabel:var(--key-label-s);--kicon:var(--key-icon-s) }
.key-l{ --kh:var(--key-height-l);--kr:var(--key-radius-l);--ko:var(--key-offset-l);--klabel:var(--key-label-l);--kicon:var(--key-icon-l) }
.key-xl{ --kh:var(--key-height-xl);--kr:var(--key-radius-xl);--ko:var(--key-offset-xl);--klabel:var(--key-label-xl);--kicon:var(--key-icon-xl) }

/* SECONDARY — the second action beside a primary: outlined, the offset alone, no face.
   ON A COLOURED OR TINTED SURFACE IT IS DRAWN IN BLACK (white on the dark ground — it follows
   --heading): black words, a black outline and a black shelf (Rose, 29 Sep 2026: the grey outline
   and green words were too weak on a tint; a filled key was tried and turned down — it must still
   read as the second action). The green words and grey outline are for white backgrounds only.
   A tinted surface opts in with .on-tint, or sets the three --key-secondary-* values itself. */
/* THE OUTLINE IS A REAL BORDER, NOT AN INSET SHADOW (Rose, 29 Sep 2026: "the corners seem
   jagged"). An inset shadow and the shelf under it are drawn as two separate curves and meet
   in steps at the bottom corners; a border and the shelf share one edge, so the curve is
   clean. The key is border-box with a fixed height, so the border costs no size. */
/* The shelf is stacked (½px, 1px, half, whole): a single shadow and the border each soften
   their own edge, and a hairline of the ground showed between them at the corners. */
.key-secondary{ --kse:var(--key-secondary-edge, var(--border-strong));
  background-color:transparent; background-image:none; border:var(--line-object) solid var(--kse);
  color:var(--key-secondary-text, var(--text-link, var(--primary)));
  box-shadow:0 .5px 0 var(--kse), 0 1px 0 var(--kse), 0 calc(var(--ko) / 2) 0 var(--kse), 0 var(--ko) 0 var(--kse) }
.key-secondary:hover{ background-color:var(--key-secondary-wash, var(--primary-12)); color:var(--key-secondary-text, var(--text-link, var(--primary)));
  transform:translateY(calc(var(--ko) / 2)); box-shadow:0 .5px 0 var(--kse), 0 calc(var(--ko) / 2) 0 var(--kse) }
.key-secondary:active{ transform:translateY(var(--ko)); background-color:var(--key-secondary-wash, var(--primary-12));
  box-shadow:0 0 0 var(--kse) }
.key-secondary:disabled,.key-secondary[disabled]{ border-color:var(--border); box-shadow:none }
.on-tint{ --key-secondary-text:var(--heading); --key-secondary-edge:var(--heading);
  --key-secondary-wash:color-mix(in srgb, var(--heading) 6%, transparent) }

/* WHITE — the same key on the dark ground, where green on green would vanish.
   DEPARTURE (for review): the site colours this label var(--text); here it is var(--black),
   because the face stays white in every theme so the label must not swap with the theme.
   --black is the fixed 21:1 rung ("headings and section icons on white").
   The throw is white at 30% (Rose, 18 Sep) — the green throw belongs to green keys; on the
   dark ground it read as a smear. Hover/active keep the face fixed and light in every theme. */
.key-white{ --kwt:color-mix(in srgb, var(--white) 30%, transparent);
  background-color:var(--white); color:var(--button-white-text); background-image:var(--key-sheen);
  box-shadow:0 var(--ko) 0 var(--kwt), inset 0 1px 0 rgba(255,255,255,.6) }
.key-white:hover{ background-color:color-mix(in srgb, var(--white) 92%, var(--black));
  transform:translateY(calc(var(--ko) / 2)); box-shadow:0 calc(var(--ko) / 2) 0 var(--kwt), inset 0 1px 0 rgba(255,255,255,.6) }
.key-white:active{ transform:translateY(var(--ko));
  background-color:color-mix(in srgb, var(--white) 88%, var(--black));
  box-shadow:0 0 0 var(--kwt), inset 0 1px 0 rgba(255,255,255,.6) }

/* BULLION — the attention key: the one action on a thing that needs you (27 Sep 2026). The same
   key recoloured, never reshaped: a Champagne face on a Bullion shelf, black words — fixed, so
   they hold on the dark ground. Hover warms the face toward Bullion; pressed is Bullion.
   A COLOURED KEY'S SHELF IS ITS COLOUR DARKENED (Rose, 29 Sep 2026: the bright Bullion shelf was
   "too colourful"): Bullion with a quarter black, the way green keys stand on pressed green. */
.key-bullion{ --kbs:color-mix(in srgb, var(--gold) 75%, var(--black));
  background-color:var(--gold-light); color:var(--black);
  box-shadow:0 var(--ko) 0 var(--kbs), var(--key-edge) }
.key-bullion:hover{ background-color:color-mix(in srgb, var(--gold-light) 80%, var(--gold));
  transform:translateY(calc(var(--ko) / 2)); box-shadow:0 calc(var(--ko) / 2) 0 var(--kbs), var(--key-edge) }
.key-bullion:active{ transform:translateY(var(--ko)); background-color:var(--gold);
  box-shadow:0 0 0 var(--kbs), var(--key-edge) }
.key-bullion:focus-visible::before{ border-color:var(--gold) }

/* SELECTED — a key that stays on: a filter, a view switch, the current page in a row of
   section keys. Promoted from the brand page's drawing (§Buttons, "Selected") in REV 44. The
   face is the pressed colour with no shelf, so it sits in rather than standing up: the shape
   says "this one is chosen" — pressed, not just tinted. Mark it aria-pressed="true" for a
   toggle, aria-current="page" for the page you are on. */
.key[aria-pressed="true"],.key[aria-current="page"],
.key[aria-pressed="true"]:hover,.key[aria-current="page"]:hover,
.key[aria-pressed="true"]:active,.key[aria-current="page"]:active{
  background-color:var(--primary-pressed); background-image:none; color:var(--white);
  box-shadow:0 0 0 var(--primary-pressed), inset 0 0 0 1px color-mix(in srgb, var(--white) 22%, transparent);
  transform:translateY(var(--ko)) }

/* QUIET — the third action, or one inside a card (KEY_VARIANTS). Text only: no face, no shelf,
   no lift. Hover lays a Primary 12% wash under the words — the tint the selected row uses. On a
   coloured or tinted surface it reads --key-secondary-text and -wash, the same hooks .on-tint
   sets, so a quiet key and the outlined key beside it always agree about the ground. */
.key-quiet{ background-color:transparent; background-image:none; box-shadow:none;
  color:var(--key-secondary-text, var(--text-link, var(--primary))) }
.key-quiet:hover{ background-color:var(--key-secondary-wash, var(--primary-12));
  color:var(--key-secondary-text, var(--text-link, var(--primary)));
  transform:none; box-shadow:none }
.key-quiet:active{ background-color:var(--key-secondary-wash, var(--primary-12));
  transform:none; box-shadow:none }

/* DESTRUCTIVE — deleting or removing something that cannot be got back (KEY_VARIANTS). Never a
   surface's default, and always beside a quiet way out. Fiery face, white words; the shelf is
   Night Bordeaux, which is the token's own stated job ("the pressed face of a destructive key")
   and keeps the rule that a coloured key's shelf is its face darkened. Hover lightens the face
   a step; pressed is Night Bordeaux.
   MEASURED, NOT ESTIMATED: white on Fiery is 3.9:1 — under the 4.5:1 this estate holds itself
   to. Built to the written spec and flagged rather than quietly redrawn; making the face
   --warning-dark instead takes it to 11.9:1. Rose's call (30 Sep 2026). */
.key-destructive{ --kds:var(--warning-dark);
  background-color:var(--warning); color:var(--white); background-image:var(--key-sheen);
  box-shadow:0 var(--ko) 0 var(--kds), var(--key-edge) }
.key-destructive:hover{ background-color:color-mix(in srgb, var(--warning) 88%, var(--white));
  transform:translateY(calc(var(--ko) / 2)); box-shadow:0 calc(var(--ko) / 2) 0 var(--kds), var(--key-edge) }
.key-destructive:active{ background-color:var(--warning-dark); transform:translateY(var(--ko));
  box-shadow:0 0 0 var(--kds), var(--key-edge) }
.key-destructive:focus-visible::before{ border-color:var(--warning) }

/* FULL WIDTH — the phone, and the last row of a form (KEY_VARIANTS). Any variant's colours,
   stretched to its container. Width only: height, radius, shelf and label stay the size's. */
.key-full{ width:100% }

/* ICON ONLY — a toolbar, or the corner of a card, where the symbol is unmistakable
   (KEY_VARIANTS). Any variant's colours, square: the width equals the face's height, so the
   icon sits centred with the same air all round. It always carries a spoken label (aria-label).
   "With an icon" needs no class — .key svg already takes the size's own --kicon. */
.key-icon-only{ width:var(--kh); min-width:var(--kh); padding:0 }

/* DISABLED WINS. The variant hover/active rules above share the disabled rule's
   specificity and come later in the file, so without this a disabled secondary or white
   key still lit up under the pointer. Stated once, for every variant and pointer state. */
.key:disabled,.key[disabled],
.key:disabled:hover,.key[disabled]:hover,
.key:disabled:active,.key[disabled]:active{
  background-color:var(--bg-sunken); color:var(--text-secondary); background-image:none;
  box-shadow:inset 0 0 0 1px var(--border); transform:none; cursor:not-allowed }

/* ── ELEMENT · LINK — text that navigates ─────────────────────────────────────────────────
   Restored to the site's approved rule (source/site.css .link): heading weight at the reading
   rung, a 1.5px underline in the link's own colour, hover eases the whole link back.
   --text-link lets a theme give links their on-dark colour (--primary-24 exists for this). */
.link{ display:inline-flex; align-items:center; gap:9px;
  font-weight:var(--weight-heading); font-size:var(--text-reading);
  color:var(--text-link, var(--primary)); text-decoration:none;
  border-bottom:1.5px solid currentColor; padding-bottom:3px;
  transition:opacity var(--t-state) var(--ease,ease) }
.link:hover{ opacity:.75 }
.link:focus-visible{ outline:2px solid var(--focus, var(--primary)); outline-offset:3px; border-radius:2px }
.link .ic{ width:16px; height:16px; flex:none }

/* ── ELEMENT · INPUT — the text field ─────────────────────────────────────────────────────
   Follows the estate's one existing form (the waitlist): the input pairs with the key ladder —
   height and label from the same rung as the key beside it (M here; mobile bumps follow the
   tokens automatically). Surface per the tokens' own roles: --bg-sunken "input fields",
   --r-card "cards, fields, panes". */
.input{ --kh:var(--input-height); --klabel:var(--key-label-m);
  display:block; width:100%; height:var(--kh);
  padding:0 var(--space-4); border:1px solid var(--text-tertiary); border-radius:var(--r-card);  /* was --border-strong, 1.61:1 — see the boundary note at .choice */
  background:var(--bg-sunken); color:var(--text);
  font:var(--weight-reading) var(--klabel)/1.4 var(--font-sans);
  transition:border-color var(--t-state) var(--ease,ease), box-shadow var(--t-state) var(--ease,ease) }
.input::placeholder{ color:var(--text-tertiary) }
.input:hover{ border-color:var(--text-secondary) }
.input:focus-visible{ outline:0; border-color:var(--focus, var(--primary));
  box-shadow:0 0 0 3px var(--primary-12) }
.input[aria-invalid="true"]{ border-color:var(--warning);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--warning) 18%, transparent) }
.input:disabled{ background:var(--bg-sunken); border-color:var(--border);
  color:var(--text-disabled); cursor:not-allowed }
textarea.input{ height:auto; min-height:calc(var(--input-height) * 2); padding:var(--space-4);
  line-height:1.55; resize:vertical }

/* ── ELEMENT · SELECTION CONTROLS — one family, from the key's language ───────────────── */
/* Option and switch labels take --weight-label: "labels, the interface, subheads —
   anything small that must hold its own" (tokens.css). */
.choice{ display:flex; align-items:flex-start; gap:var(--space-3); cursor:pointer;
  font:var(--weight-label) var(--text-reading)/1.4 var(--font-sans); color:var(--text) }
.choice input[type="checkbox"],.choice input[type="radio"]{
  appearance:none; -webkit-appearance:none; margin:0; flex:none; cursor:pointer;
  width:20px; height:20px; margin-top:1px;
  /* PROPOSED (REV 35, for design sign-off): the unchecked boundary was --border-strong,
     #CCCCCC — 1.61:1 on white, under the 3:1 WCAG needs for a control's boundary. The
     nearest token that clears it is --text-tertiary, 4.74:1. Same change on the input
     and the switch's off track, so the controls stay one family. */
  border:1px solid var(--text-tertiary); background:var(--bg);
  display:grid; place-content:center;
  transition:background-color var(--t-state) var(--ease,ease), border-color var(--t-state) var(--ease,ease) }
.choice input[type="checkbox"]{ border-radius:6px }
.choice input[type="radio"]{ border-radius:50% }
.choice input:checked{ background-color:var(--primary); border-color:var(--primary) }
.choice input[type="checkbox"]:checked::after{ content:""; width:10px; height:10px;
  background:var(--white); clip-path:polygon(14% 44%,0 60%,40% 100%,100% 22%,86% 8%,38% 66%) }
.choice input[type="radio"]:checked::after{ content:""; width:8px; height:8px; border-radius:50%; background:var(--white) }
.choice input:focus-visible{ outline:2px solid var(--focus, var(--primary)); outline-offset:2px }
.choice input:disabled{ background:var(--bg-sunken); border-color:var(--border-strong); cursor:not-allowed }
.choice input:disabled:checked{ background:var(--text-disabled); border-color:var(--text-disabled) }
.choice:has(input:disabled){ color:var(--text-disabled); cursor:not-allowed }
.choice-body{ display:flex; flex-direction:column; gap:2px; min-width:0 }
.choice .choice-note{ color:var(--text-tertiary); font-size:var(--text-label); line-height:1.45 }

/* SWITCH — the one pill in the system (--r-pill).
   RULE: a switch takes effect the moment it is flipped. Inside a save/cancel form use a
   checkbox instead — deferred settings are choices, not switches. */
.switch{ display:inline-flex; align-items:center; gap:var(--space-3); cursor:pointer;
  font:var(--weight-label) var(--text-reading)/1.4 var(--font-sans); color:var(--text) }
.switch input{ appearance:none; -webkit-appearance:none; margin:0; cursor:pointer;
  width:44px; height:26px; flex:none; border-radius:var(--r-pill);
  background:var(--text-tertiary); position:relative;  /* off track: the state indicator — was --border-strong, 1.61:1 */
  transition:background-color var(--t-state) var(--ease,ease) }
.switch input::after{ content:""; position:absolute; top:3px; left:3px; width:20px; height:20px;
  border-radius:50%; background:var(--white); box-shadow:0 1px 2px rgba(0,0,0,.3);
  transition:transform var(--t-state) var(--ease,ease) }
.switch input:checked{ background:var(--primary) }
.switch input:checked::after{ transform:translateX(18px) }
.switch input:focus-visible{ outline:2px solid var(--focus, var(--primary)); outline-offset:2px }
.switch input:disabled{ background:var(--border-strong); cursor:not-allowed }
.switch input:disabled:checked{ background:var(--text-disabled) }
.switch:has(input:disabled){ color:var(--text-disabled); cursor:not-allowed }

/* ══ PATTERNS — elements arranged. These are what a page or product screen composes. ═════ */

/* ── PATTERN · SECTION INTRODUCTION — eyebrow, title, lead ────────────────────────────── */
.intro{ display:flex; flex-direction:column; gap:var(--space-3); max-width:64ch }
.intro .eyebrow{ margin:0; font:var(--weight-heading) var(--text-label)/1 var(--font-sans);
  letter-spacing:.12em; text-transform:uppercase; color:var(--text-tertiary) }
.intro .title{ font:var(--weight-heading) var(--text-title)/1.1 var(--font-sans);
  letter-spacing:-.01em; color:var(--heading, var(--black)); margin:0 }
.intro .lead{ font:var(--weight-reading) var(--text-subhead)/1.5 var(--font-sans); color:var(--text-secondary); margin:0 }

/* ── PATTERN · PROSE — a reading block at a measured width ────────────────────────────── */
.prose{ max-width:66ch; font:var(--weight-reading) var(--text-reading)/1.6 var(--font-sans); color:var(--text) }
.prose > * + *{ margin-top:var(--space-5) }
.prose h3{ font:var(--weight-heading) var(--text-subhead)/1.25 var(--font-sans); color:var(--text) }
.prose strong{ font-weight:var(--weight-heading) }
/* PROPOSED — prose links had no approved rule; this mirrors .link minus the flex.
   For visual review before it counts as part of the system. */
.prose a{ color:var(--text-link, var(--primary)); text-decoration:none;
  font-weight:var(--weight-heading); border-bottom:1.5px solid currentColor; padding-bottom:1px;
  transition:opacity var(--t-state) var(--ease,ease) }
.prose a:hover{ opacity:.75 }

/* ── PATTERN · TEXT WITH AN ACTION — a line of text and the one key that follows it ────── */
.cta{ display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
  gap:var(--space-5); padding:var(--space-6);
  border:1px solid var(--border); border-radius:var(--r-card); background:var(--bg-sunken) }
.cta .cta-text{ display:flex; flex-direction:column; gap:var(--space-1); min-width:0 }
.cta .cta-text b{ font:var(--weight-heading) var(--text-subhead)/1.2 var(--font-sans); color:var(--text) }
.cta .cta-text span{ color:var(--text-secondary); font-size:var(--text-reading) }

/* ── PATTERN · FORM ROW — label, control, help, and the error that replaces the help ──────
   A related group of controls is a <fieldset class="form-row"> with a <legend class="label">
   and its options in a .options wrapper (fieldset keeps its own layout rules, so the wrapper
   carries the spacing). Labels take --weight-label, the interface weight. */
.form-row{ display:flex; flex-direction:column; gap:var(--space-2) }
fieldset.form-row{ display:block; border:0; padding:0; margin:0; min-width:0 }
fieldset.form-row > legend{ padding:0; margin-bottom:var(--space-2) }
.form-row .options{ display:flex; flex-direction:column; gap:var(--space-2) }
.form-row .label{ font:var(--weight-label) var(--text-label)/1.2 var(--font-sans); color:var(--text) }
.form-row .label .opt{ color:var(--text-tertiary); font-weight:var(--weight-reading); letter-spacing:0 }
.help{ font-size:var(--text-label); line-height:1.5; color:var(--text-tertiary) }
.form-row .error{ font-size:var(--text-label); line-height:1.5;
  color:var(--text-error, var(--warning-dark));
  display:none; align-items:center; gap:var(--space-1) }
.form-row[data-invalid="true"] .help{ display:none }
.form-row[data-invalid="true"] .error{ display:flex }

/* ── PATTERN · PANEL — a card that holds a group of rows ──────────────────────────────── */
.panel{ background:var(--bg-raised); border:1px solid var(--border); border-radius:var(--r-card);
  padding:var(--space-6); display:flex; flex-direction:column; gap:var(--space-6);
  box-shadow:0 1px 2px rgba(0,0,0,.04) }
.panel-body{ display:flex; flex-direction:column; gap:var(--space-5) }

/* ── PATTERN · ACTIONS — the button row that closes a panel or form ───────────────────── */
.actions{ display:flex; flex-wrap:wrap; gap:var(--space-3); justify-content:flex-end;
  padding-top:var(--space-5); border-top:1px solid var(--border) }
@media (max-width:520px){ .actions{ flex-direction:column-reverse } .actions .key{ width:100% } }

/* ══ PRODUCT · DATA & COLLECTIONS — batch 1 (REV 36) ═══════════════════════════════════════
   The parts an asset screen is made of: the page head, status labels, label/value pairs,
   the record row, the data table, the search-and-filter toolbar, the pager and the
   collection states. Drawings promoted from the site where one existed (the status label is
   the site's chip; the pairs row is the site's .rec) — values unchanged, only made shared.
   Behaviour lives in brand/components.js, loaded after this file. Everything composes:
   a toolbar above a .data-table above a .pager is the whole list screen. */

/* ── PATTERN · PAGE HEAD — the intro pattern with the page's actions beside it ─────────── */
.page-head{ display:flex; align-items:flex-end; justify-content:space-between;
  gap:var(--space-5); flex-wrap:wrap }
.page-head .intro{ flex:1 1 38ch }
.page-head-actions{ display:flex; gap:var(--space-3); flex-wrap:wrap }
@media (max-width:520px){ .page-head-actions{ width:100% } .page-head-actions .key{ flex:1 } }

/* ── ELEMENT · STATUS LABEL — the site's chip, promoted (source/site.css .chip) ──────────
   Text carries the meaning; colour supports it. Approved roles only: ok = the green pair,
   attention = the warning pair (12% tint ground, measured on the site 13 Sep), neutral =
   the sunken ground. No new hues, and never a financial figure in a label. */
.status-label{ display:inline-flex; align-items:center; gap:6px; white-space:nowrap;
  font:var(--weight-heading) var(--text-label)/1 var(--font-sans);
  padding:7px 14px; border-radius:var(--r-pill) }
.status-label.ok{ background:var(--primary-12); color:var(--text-selected) }
/* ATTENTION IS BULLION (Rose, 27 Sep 2026: the Fiery version read as an error). Champagne face,
   black words — fixed, not --text, so the label holds on the dark ground too. */
.status-label.attention{ background:var(--gold-light); color:var(--black) }
.status-label.neutral{ background:var(--bg-sunken); color:var(--text-secondary) }

/* ── PATTERN · LABEL / VALUE PAIRS — the site's .rec drawing, as a definition list ───────
   For a record's detail: amounts, ownership, dates, source. An UNKNOWN value is an em dash
   with .unknown — never a zero, which is a statement. .pair-note under a value carries
   source / updated-at metadata. */
dl.pairs{ margin:0 }
.pairs > div{ display:flex; justify-content:space-between; align-items:baseline;
  gap:var(--space-5); padding:14px 0; border-bottom:1px solid var(--border);
  font-size:var(--text-reading) }
.pairs > div:last-child{ border-bottom:0 }
.pairs dt{ color:var(--text-secondary); font-weight:var(--weight-reading) }
.pairs dd{ margin:0; text-align:right; color:var(--text); min-width:0 }
.pairs .num{ font-variant-numeric:tabular-nums lining-nums }
.pair-note{ display:block; font-size:var(--text-label); color:var(--text-tertiary); margin-top:2px }
.unknown{ color:var(--text-tertiary) }
/* ON A PHONE A LONG VALUE GOES UNDER ITS LABEL (REV 53, phone pass 29 Sep 2026). Side by side, a
   long value squeezed its label into a column of single words ("What / enough / means") and ran
   down the right edge a word a line. Now a pair stays side by side while both fit on one line;
   when they don't, the value drops under the label and reads from the left, as a sentence.
   A FIGURE NEVER DROPS: its row stays side by side on the right edge, so a column of figures
   still lines up. The figure takes at most 60% of the row — a long note under it wraps inside
   that — and the label keeps the rest ("The / gap" no more). */
@media (max-width:520px){
  .pairs > div{ flex-wrap:wrap; row-gap:var(--space-1) }
  .pairs dt{ flex:1 1 auto }
  .pairs dd:not(.num){ text-align:left }
  .pairs > div:has(> dd.num){ display:grid; grid-template-columns:minmax(0,1fr) fit-content(60%) }
}

/* ── PATTERN · RECORD ROW — a list row: identity, metadata, a trailing value or action ─── */
.record{ display:flex; align-items:center; gap:var(--space-4);
  padding:var(--space-4) var(--space-2); border-bottom:1px solid var(--border);
  color:var(--text); text-decoration:none }
a.record{ transition:background-color var(--t-state) var(--ease,ease) }
a.record:hover{ background:var(--bg-sunken) }
a.record:focus-visible{ outline:2px solid var(--focus, var(--primary)); outline-offset:-2px }
.record .ic{ width:20px; height:20px; flex:none; color:var(--text-secondary) }
.record-main{ flex:1; min-width:0 }
.record-title{ font-weight:var(--weight-label); display:block;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.record-sub{ display:block; font-size:var(--text-label); color:var(--text-tertiary);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.record-meta{ text-align:right; flex:none }
.record.is-selected{ background:var(--primary-12) }
.record.is-unavailable{ color:var(--text-disabled) }
.record.is-unavailable .record-sub,.record.is-unavailable .record-meta{ color:var(--text-disabled) }
/* ON A PHONE THE ROW WRAPS INSTEAD OF CUTTING (REV 53, phone pass 29 Sep 2026): at 390 the ellipsis
   took the end of every title and line under it — the reason a line drifted, an adviser's email,
   who a policy pays. The row grows a line instead; an address with no spaces breaks where it must.
   The words keep at least half the row: a trailing part wider than the rest (a long status
   label) drops under them, to the right, instead of squeezing them into a column of single words. */
@media (max-width:520px){
  .record{ flex-wrap:wrap; row-gap:var(--space-2) }
  .record-main{ flex:1 1 50% }
  .record-meta{ margin-left:auto }
  .record-title,.record-sub{ white-space:normal; overflow-wrap:anywhere }
}

/* ── PATTERN · DATA TABLE — semantic table in a scrolling card ────────────────────────────
   The wrapper owns the card edge and the horizontal scroll a phone needs (tabindex=0 and a
   label on the wrapper make that scroll keyboard-reachable). Numbers are right-aligned and
   tabular. Row actions sit in the last cell. Loading / empty / error replace the BODY of
   the card, never the whole card — see .collection-state. */
.data-table{ overflow-x:auto; border:1px solid var(--border); border-radius:var(--r-card);
  background:var(--bg-raised) }
.data-table:focus-visible{ outline:2px solid var(--focus, var(--primary)); outline-offset:2px }
.data-table table{ width:100%; border-collapse:collapse; min-width:640px }
.data-table th{ font:var(--weight-label) var(--text-label)/1.2 var(--font-sans);
  text-align:left; color:var(--text-secondary); padding:12px var(--space-4);
  border-bottom:1px solid var(--border-strong); white-space:nowrap }
.data-table td{ padding:14px var(--space-4); border-bottom:1px solid var(--border);
  font-size:var(--text-reading); vertical-align:top }
.data-table tbody tr:last-child td{ border-bottom:0 }
.data-table tbody tr{ transition:background-color var(--t-state) var(--ease,ease) }
.data-table tbody tr:hover{ background:var(--bg-sunken) }
.data-table .num{ text-align:right; font-variant-numeric:tabular-nums lining-nums }
.data-table .cell-sub{ display:block; font-size:var(--text-label); color:var(--text-tertiary) }
.data-table td a{ color:var(--text); font-weight:var(--weight-label); text-decoration:none }
.data-table td a:hover{ color:var(--text-link, var(--primary)) }
.data-table td a:focus-visible{ outline:2px solid var(--focus, var(--primary)); outline-offset:2px; border-radius:2px }

/* ── PATTERN · TOOLBAR — search, filters, active-filter tags, result count ────────────────
   Sits above a table or list. The search is the shared .input with a clear action that
   appears once there is something to clear. Filters are NATIVE selects wearing .input —
   short fixed lists need nothing richer. Active choices echo as removable tags, the count
   says what the list is showing (aria-live, so a reader hears it change). Results update
   as the controls change — there is no Apply. */
.toolbar{ display:flex; align-items:center; gap:var(--space-3); flex-wrap:wrap }
.search{ position:relative; flex:1 1 220px; max-width:400px }
.search .input{ padding-right:46px }
.search-clear{ position:absolute; right:7px; top:50%; transform:translateY(-50%);
  display:none; align-items:center; justify-content:center; width:32px; height:32px;
  border:0; border-radius:8px; background:none; color:var(--text-tertiary); cursor:pointer }
.search-clear:hover{ background:var(--bg-sunken); color:var(--text) }
.search-clear:focus-visible{ outline:2px solid var(--focus, var(--primary)); outline-offset:1px }
.search-clear svg{ width:16px; height:16px }
.search[data-filled] .search-clear{ display:inline-flex }
select.input{ width:auto; padding-right:42px; cursor:pointer;
  appearance:none; -webkit-appearance:none;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23555555' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 14px center; background-size:16px }
.filter-tags{ display:flex; gap:var(--space-2); flex-wrap:wrap }
.filter-tag{ display:inline-flex; align-items:center; gap:8px; cursor:pointer; border:0;
  font:var(--weight-label) var(--text-label)/1 var(--font-sans);
  padding:7px 11px 7px 14px; border-radius:var(--r-pill);
  background:var(--primary-12); color:var(--text-selected);
  transition:background-color var(--t-state) var(--ease,ease) }
.filter-tag:hover{ background:color-mix(in srgb, var(--primary-12) 70%, var(--primary-24, #BFD8CE)) }
.filter-tag:focus-visible{ outline:2px solid var(--focus, var(--primary)); outline-offset:2px }
.filter-tag svg{ width:13px; height:13px }
.result-count{ margin-left:auto; font-size:var(--text-label); color:var(--text-secondary); white-space:nowrap }
@media (max-width:520px){ .search{ max-width:none } .result-count{ margin-left:0; width:100% } }

/* ── PATTERN · PAGER — previous / next, with the range stated ─────────────────────────────
   Secondary S keys, disabled at the limits (a disabled key already reads inactive). The
   range is aria-live so the page change is announced. */
.pager{ display:flex; align-items:center; justify-content:flex-end; gap:var(--space-3);
  padding-top:var(--space-4) }
.pager-range{ font-size:var(--text-label); color:var(--text-secondary); margin-right:auto }

/* ── PATTERN · COLLECTION STATES — what a list shows when it has no rows to show ──────────
   One family, four occasions: first-use empty, filtered no-results, failure with retry, and
   unavailable. It REPLACES the rows, never the card or the toolbar — the person keeps their
   bearings. Loading is the skeleton below. */
/* the hidden attribute must WIN over these display rules — without this line a wired
   collection shows its no-results state under the live rows */
.collection-state[hidden],.pager[hidden]{ display:none }
.collection-state{ display:grid; justify-items:center; gap:var(--space-3);
  padding:var(--space-7) var(--space-6); text-align:center; color:var(--text-secondary) }
.collection-state .ic{ width:26px; height:26px; color:var(--text-tertiary) }
.collection-state b{ font:var(--weight-heading) var(--text-subhead)/1.3 var(--font-sans); color:var(--heading, var(--black)) }
.collection-state p{ margin:0; max-width:46ch; font-size:var(--text-reading); line-height:1.5 }
.collection-state .key{ margin-top:var(--space-2) }

/* SKELETON — loading rows. Announce loading on the container (aria-busy), not per bar.
   The shimmer rests under prefers-reduced-motion. */
.skeleton{ display:grid; gap:var(--space-4); padding:var(--space-5) var(--space-4) }
.skeleton i{ display:block; height:14px; border-radius:7px; background:var(--bg-sunken);
  background-image:linear-gradient(90deg, var(--bg-sunken) 30%,
    color-mix(in srgb, var(--bg-sunken) 45%, var(--white)) 50%, var(--bg-sunken) 70%);
  background-size:300% 100%; animation:fin-skeleton 1.4s ease-in-out infinite }
.skeleton i:nth-child(2n){ width:82% } .skeleton i:nth-child(3n){ width:64% }
@keyframes fin-skeleton{ from{ background-position:100% 0 } to{ background-position:0 0 } }
@media (prefers-reduced-motion:reduce){ .skeleton i{ animation:none } }

/* ── TEMPLATE · SCREEN — the page container, promoted from lab/asset-overview.html (REV 37)
   The estate's measure and gutter with a stacked flow. A list screen is
   .screen > .page-head > the Collection organism; a settings screen is .screen > .panel.
   Compose downward-only: a screen places organisms, it never re-draws them. */
.screen{ max-width:1060px; margin-inline:auto;
  padding:40px clamp(20px,5vw,64px) 96px;
  display:flex; flex-direction:column; gap:var(--space-5) }

/* ── ELEMENT · SLIDER — consolidated from the §Controls drawing (REV 39): the 4px track,
   the primary fill, the 22px round handle on a 1.5px primary ring. Native <input
   type="range">, so dragging and the arrow keys come from the browser. Markup:
   <div class="slider-row"><label class="label">…</label>
     <input class="slider" type="range" data-unit="%"><output>…</output></div>
   components.js keeps the fill and the <output> in step with the value. */
.slider-row{ display:flex; align-items:center; gap:var(--space-4) }
.slider-row output{ min-width:4ch; text-align:right;
  font:var(--weight-label) var(--text-reading)/1 var(--font-sans);
  font-variant-numeric:tabular-nums; color:var(--text) }
.slider{ appearance:none; -webkit-appearance:none; flex:1; height:24px; margin:0;
  background:none; cursor:pointer }
.slider::-webkit-slider-runnable-track{ height:4px; border-radius:2px;
  background:linear-gradient(var(--primary), var(--primary)) 0 / var(--fill, 50%) 100% no-repeat var(--border) }
.slider::-webkit-slider-thumb{ -webkit-appearance:none; width:22px; height:22px; margin-top:-9px;
  border-radius:50%; background:var(--bg); border:1.5px solid var(--primary);
  box-shadow:0 1px 3px rgba(0,0,0,.28) }
.slider::-moz-range-track{ height:4px; border-radius:2px; background:var(--border) }
.slider::-moz-range-progress{ height:4px; border-radius:2px; background:var(--primary) }
.slider::-moz-range-thumb{ width:22px; height:22px; border-radius:50%; background:var(--bg);
  border:1.5px solid var(--primary); box-shadow:0 1px 3px rgba(0,0,0,.28) }
.slider:focus-visible{ outline:0 }
.slider:focus-visible::-webkit-slider-thumb{ outline:2px solid var(--focus, var(--primary)); outline-offset:3px }
.slider:focus-visible::-moz-range-thumb{ outline:2px solid var(--focus, var(--primary)); outline-offset:3px }
.slider:disabled{ cursor:not-allowed }
.slider:disabled::-webkit-slider-runnable-track{ background:var(--border) }
.slider:disabled::-webkit-slider-thumb{ border-color:var(--border-strong); background:var(--bg-sunken); box-shadow:none }
.slider:disabled::-moz-range-thumb{ border-color:var(--border-strong); background:var(--bg-sunken); box-shadow:none }

/* ── TOUCH TARGETS (REV 40) — on a coarse pointer every control's HIT AREA clears the
   48px touch rule; the drawings stay their size. Rows grow by padding; the small round
   actions grow by an invisible halo. */
@media (pointer:coarse){
  .choice{ min-height:48px; padding-block:4px }
  .switch{ min-height:48px }
  .filter-tag{ position:relative }
  .filter-tag::after{ content:""; position:absolute; inset:-11px -2px }  /* tags sit 8px apart sideways, 12px from their neighbours above and below */
  .search-clear::after{ content:""; position:absolute; inset:-8px -6px -8px 0 }  /* never over the input’s text */
  .link{ position:relative }
  .link::after{ content:""; position:absolute; inset:-10px -4px }  /* a standalone link reaches the touch target (27 Sep 2026) */
  .pager .key{ min-height:48px }
  .slider{ height:32px }
}

/* Compact faces retain a separate mobile hit area. Allow clearance between rows. */
.key{position:relative;white-space:nowrap}
@media(max-width:768px), (pointer:coarse){
 .key::after{content:"";position:absolute;inset:min(0px,calc((var(--kh) - var(--key-touch-target))/2)) 0}
}

/* ══ PRODUCT · SCREENS — batch 2 (REV 54, 30 Sep 2026) ════════════════════════════════════
   The screen parts the app was handed over with (29 Sep 2026), promoted so the brand holds
   them: the scene (a page's opener in its desk's tint), the fact tiles, Freya's summary box,
   the desk card, the note, the boundary, the coming-next list and the detail sheet. Class
   names are the app's own, so the app's markup works with this file unchanged. Sizes come
   from tokens.css (--card-icon, --list-mark, --sheet-w). Dark values follow the tokens'
   own opt-in: data-mode="dark" on <html>, or "auto" on a dark device. */

/* ── PATTERN · SCENE — the opener at the top of a page, in its desk's tint ───────────── */
.scene{ --scene:color-mix(in srgb,var(--primary-48) 30%,var(--bg-raised));
  container-type:inline-size; position:relative; border-radius:var(--r-card); background:var(--scene);
  padding:clamp(var(--space-4),3.2vw,var(--space-7)) }
/* ONE DESK COLOUR (Rose, 30 Sep 2026: "we just want one color — same as on Succession").
   Every desk's opener takes the Succession tint; the desk classes stay as hooks. The plain
   .scene above keeps its green for an opener that is not a desk (Home's summary). */
:is(.scene-records,.scene-policy,.scene-investment,.scene-protection,.scene-succession){ --scene:color-mix(in srgb,var(--l-property) 13%,var(--bg-raised)) }
:is(.scene,.hm-desk) .eyebrow{ font:var(--weight-heading) var(--text-label)/1 var(--font-sans);
  letter-spacing:.12em; text-transform:uppercase; color:var(--text-link) }
.sc-words{ min-width:0 }
.sc-answer{ margin:var(--space-3) 0 0; font:var(--weight-heading) var(--text-title)/1.18 var(--font-sans);
  letter-spacing:var(--track-heading); color:var(--heading); max-width:22ch }
.sc-answer b,.sc-sub b{ font-weight:inherit; white-space:nowrap }
.sc-sub{ margin:var(--space-3) 0 0; font-size:var(--text-reading); line-height:var(--lh-reading);
  color:var(--text-secondary); max-width:48ch }
.sc-sub b{ color:var(--text); font-weight:var(--weight-label) }

/* ── ON THE WASH — a scene at the very top of a page may take the header wash (Home's
   summary, 29 Sep 2026). It sets the dark ground's hooks: white words, marks and outlined
   keys, Primary 24% for the eyebrow and focus. The Champagne key keeps its face. */
.scene.on-wash{ background:var(--wash-hero); color:var(--white);
  --heading:var(--white); --text:var(--white); --text-link:var(--primary-24); --focus:var(--primary-24);
  --text-secondary:color-mix(in srgb,var(--white) 82%,transparent);
  --text-tertiary:color-mix(in srgb,var(--white) 64%,transparent);
  --border:color-mix(in srgb,var(--white) 16%,transparent);
  --border-strong:color-mix(in srgb,var(--white) 32%,transparent); --bg:var(--bg-dark) }

/* ── PATTERN · FACT TILES — the value large, what it is small, the name and an icon on
   top. Never narrower than 200px; one to a row on a phone. The one that needs you is the
   Needs-you card in miniature: Champagne, a Bullion hairline, its status label. */
.sc-facts{ list-style:none; margin:var(--space-5) 0 0; padding:0; display:grid; gap:var(--space-2) }
.sc-facts.hl{ grid-template-columns:repeat(auto-fit,minmax(min(200px,100%),1fr)); gap:var(--space-3); container-type:inline-size }
.sc-facts.hl li{ position:relative; display:flex; flex-direction:column; gap:var(--space-2); min-width:0;
  padding:var(--space-5); container-type:inline-size; border-radius:var(--r-tile);
  background:color-mix(in srgb,var(--bg-raised) 76%,transparent) }
@container (min-width:272px) and (max-width:411px){ .sc-facts.hl li:last-child:nth-child(odd){ grid-column:1 / -1 } }
@media (max-width:560px){ .sc-facts.hl{ gap:var(--space-2) } .sc-facts.hl li{ padding:var(--space-4); gap:var(--space-1) } }
.hl-k{ display:flex; align-items:center; gap:var(--space-2); min-height:22px; font-size:var(--text-label);
  font-weight:var(--weight-label); color:var(--text-secondary) }
.hl-kt{ margin-right:auto; white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
.hl-k .ic{ flex:none; width:var(--card-icon); height:var(--card-icon); color:var(--heading) }
.hl-k .status-label{ white-space:nowrap; margin-right:auto }
.sc-facts.hl .hl-v{ font:var(--weight-heading) min(var(--text-title),19cqi)/1.1 var(--font-sans);
  letter-spacing:var(--track-heading); white-space:nowrap; color:var(--heading) }
.sc-facts.hl .hl-l{ font-size:var(--text-label); line-height:1.4; color:var(--text-secondary) }
.sc-facts.hl li.att{ background:color-mix(in srgb,var(--gold-light) 30%,var(--bg-raised)); box-shadow:inset 0 0 0 1px var(--gold) }
.sc-facts.hl li.att .hl-kt{ display:none }
.sc-facts.hl li:not(.att) .hl-k .status-label{ display:none }

/* ── ORGANISM · FREYA'S SUMMARY — a scene in Freya's name: her mark, "Freya · your AI chief
   of staff", the place and the day, an Ask Freya key (gone on a phone, where her bar is),
   the answer in a sentence, a line under it, then up to four boxes in order of importance:
   For you, Noticed, Coming up, Opens next — a figure, a line and one key each. */
.brief .sc-words{ display:flex; flex-direction:column }
.brief .sc-answer{ max-width:28ch; text-wrap:balance }
.brief .sc-sub{ max-width:58ch }
.br-head{ display:flex; align-items:center; gap:var(--space-3); flex-wrap:wrap }
.br-head > svg{ width:40px; height:40px; flex:none; color:var(--heading) }
.br-who{ display:flex; flex-direction:column; gap:var(--space-1); min-width:0; margin-right:auto }
.br-when{ font-size:var(--text-label); color:var(--text-secondary) }
.br-ask{ flex:none }
.br-boxes{ list-style:none; margin:var(--space-6) 0 0; padding:0; display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--space-3) }
.br-boxes > li:first-child:nth-last-child(odd){ grid-column:1 / -1 }
@container (min-width:1040px){ .br-boxes{ grid-template-columns:repeat(var(--n,4),minmax(0,1fr)) }
  .br-boxes > li:first-child:nth-last-child(odd){ grid-column:auto } }
@container (max-width:519px){ .br-boxes{ grid-template-columns:minmax(0,1fr) } }
.br-box{ display:flex; flex-direction:column; gap:var(--space-2); min-width:0; padding:var(--space-5);
  border-radius:var(--r-tile); background:color-mix(in srgb,var(--bg-raised) 78%,transparent); container-type:inline-size }
.br-box.att{ background:color-mix(in srgb,var(--gold-light) 30%,var(--bg-raised)); box-shadow:inset 0 0 0 1px var(--gold) }
.br-k{ display:flex; align-items:center; gap:var(--space-2); min-height:24px }
.br-k .status-label{ margin-right:auto; white-space:nowrap }
.br-k .ic{ flex:none; width:var(--card-icon); height:var(--card-icon); color:var(--heading) }
/* THE BOX FOR YOU (Rose, 30 Sep 2026: "not this triangle warning — something much more friendly, a
   notification"). It was Needs you, under a warning triangle. Its icon is now the bell, with Freya's
   has-something dot on it: Fiery, but a dot, so it reads as new and not as an error. Its label is a
   calm chip, so the gold card and its one bullion key keep the colour. The dot sits on the corner of
   the row's last element, the icon, ringed in the card's own tint. */
.status-label.notify{ background:var(--bg-raised); color:var(--heading) }
.br-box.att .br-k{ position:relative }
.br-box.att .br-k::after{ content:""; position:absolute; top:-3px; right:-3px; width:10px; height:10px;
  border-radius:50%; background:var(--warning); box-shadow:0 0 0 2px color-mix(in srgb,var(--gold-light) 30%,var(--bg-raised)) }
.br-v{ margin-top:var(--space-2); font:var(--weight-heading) min(var(--text-title),14cqi)/1.15 var(--font-sans);
  letter-spacing:var(--track-heading); color:var(--heading); overflow-wrap:anywhere }
.br-l{ font-size:var(--text-reading); line-height:var(--lh-reading); color:var(--text-secondary) }
.br-l b{ font-weight:var(--weight-label); color:var(--text) }
.br-act{ margin-top:auto; padding-top:var(--space-3) }
.br-keys{ display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-2) var(--space-3); margin-top:var(--space-5) }
@media (max-width:640px){
  .br-head > svg{ width:32px; height:32px }
  .br-ask{ display:none }
  .br-box{ padding:var(--space-4) }
  .br-l{ font-size:var(--text-label); line-height:1.5 }
}

/* ── MOLECULE · DESK CARD — one card a desk: its name and plate, a status label, one figure,
   one line and one small picture, all drawn on the same 6px line. The whole card is the
   link. Quiet labels sit on the page colour so they read on the tint. ONE COLOUR (Rose, 30 Sep 2026:
   "make these green bg's same color as on the desks"): every card, Learn's too, takes the desk
   colour the desk openers have (REV 57), so Home and the desks read as one family. */
.hm-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(min(272px,100%),1fr)); gap:var(--space-3) }
.hm-desk{ --scene:color-mix(in srgb,var(--l-property) 13%,var(--bg-raised)); position:relative; display:flex;
  flex-direction:column; gap:var(--space-2); min-width:0; padding:var(--space-5); border-radius:var(--r-card);
  background:var(--scene); color:var(--text); text-decoration:none;
  box-shadow:inset 0 0 0 1px transparent; transition:box-shadow var(--t-state) var(--ease) }
.hm-desk:hover{ box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--heading) 22%,transparent) }
.hm-desk:focus-visible{ outline:2px solid var(--heading); outline-offset:2px }
.hm-dh{ display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-2); min-height:28px;
  padding-right:calc(var(--list-mark) + var(--space-2)) }
.hm-dh > svg{ position:absolute; top:var(--space-4); right:var(--space-4); width:var(--list-mark); height:var(--list-mark); color:var(--heading) }
.hm-dh b{ font:var(--weight-heading) var(--text-reading)/1.2 var(--font-sans); color:var(--heading) }
.hm-st{ align-self:flex-start; white-space:nowrap }
.hm-st:is(.neutral,.ok){ background:var(--bg) }
.hm-v{ margin-top:var(--space-1); font:var(--weight-heading) var(--text-title)/1.1 var(--font-sans);
  letter-spacing:var(--track-heading); color:var(--heading); font-variant-numeric:tabular-nums }
.hm-l{ font-size:var(--text-label); line-height:1.45; color:var(--text-secondary) }
.hm-l b{ font-weight:var(--weight-label); color:var(--text) }
.hm-vis{ display:flex; flex-direction:column; gap:6px; margin-top:auto; padding-top:var(--space-4) }
.hm-cap{ display:flex; justify-content:space-between; gap:var(--space-3); font-size:var(--text-label); line-height:1.3; color:var(--text-secondary) }
.hm-bar,.hm-mix,.hm-band,.hm-year,.hm-will,.hm-path{ position:relative; display:flex; height:6px; border-radius:3px;
  background:color-mix(in srgb,var(--heading) 10%,transparent) }
.hm-bar,.hm-mix{ gap:2px; background:none }
.hm-bar i,.hm-mix i{ display:block; height:100%; border-radius:3px }
.hm-bar i{ background:var(--primary-pressed) }
.hm-bar i.owe{ background:var(--l-owed) }
.hm-mix i{ min-width:3px }
.hm-band i{ position:absolute; top:0; bottom:0 }
.hm-band .rng{ border-radius:3px; background:var(--primary-48) }
.hm-band .tgt{ width:2px; margin-left:-1px; top:-3px; bottom:-3px; background:var(--heading) }
.hm-band .now{ width:12px; height:12px; margin:-3px 0 0 -6px; border-radius:50%; background:var(--heading); box-shadow:0 0 0 2px var(--scene) }
.hm-band .now.out{ background:var(--gold) }
.hm-year i{ position:absolute; top:-3px; width:12px; height:12px; margin-left:-6px; border-radius:50%;
  background:var(--primary-pressed); box-shadow:0 0 0 2px var(--scene) }
.hm-year i.soon{ background:var(--gold) }
.hm-will .lived{ position:absolute; inset:0 auto 0 0; border-radius:3px; background:var(--primary-pressed) }
.hm-will .today{ position:absolute; top:-3px; width:12px; height:12px; margin-left:-6px; border-radius:50%;
  background:var(--heading); box-shadow:0 0 0 2px var(--scene) }
.hm-path{ gap:3px; background:none }
.hm-path i{ flex:1; height:100%; border-radius:3px; background:color-mix(in srgb,var(--heading) 12%,transparent) }
.hm-path i.done{ background:var(--primary-pressed) }
.hm-path i.now{ background:var(--primary-48) }
@media (max-width:640px){ .hm-desk{ padding:var(--space-4) } .hm-v{ font-size:var(--text-subhead) } }

/* ── ELEMENTS · NOTE, BOUNDARY, COMING NEXT ─────────────────────────────────────────────
   A note is an aside in a hairline card. A boundary says what Finna does not do, on Mint
   Cream, with a draft tag while counsel writes the words. Coming next is a dashed card:
   something promised, not built. */
.note{ border:1px solid var(--border); border-radius:var(--r-card); padding:var(--space-4) var(--space-5);
  font-size:var(--text-reading); color:var(--text-secondary); background:var(--bg) }
.note b{ color:var(--text); font-weight:var(--weight-label) }
.boundary{ border-radius:var(--r-card); background:var(--gold-dark); padding:var(--space-5) }
.boundary h3{ margin:0 0 var(--space-2); font:var(--weight-heading) var(--text-subhead)/var(--lh-subhead) var(--font-sans); color:var(--heading) }
.boundary ul{ margin:var(--space-2) 0 0; padding-left:var(--space-5) }
.boundary li{ margin-bottom:var(--space-1) }
.draft-tag{ display:inline-block; font:var(--weight-heading) var(--text-label)/1 var(--font-sans); letter-spacing:.06em;
  text-transform:uppercase; color:var(--text-secondary); background:var(--bg-sunken); border-radius:var(--r-chip);
  padding:var(--space-1) var(--space-2); margin-left:var(--space-2); vertical-align:2px }
@media (max-width:560px){ .draft-tag{ display:table; margin:var(--space-2) 0 0; line-height:1.3 } }
.next-list{ display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:var(--space-3) }
.next-item{ border:1px dashed var(--border-strong); border-radius:var(--r-card); padding:var(--space-4);
  color:var(--text-secondary); font-size:var(--text-label); line-height:1.5 }
.next-item b{ display:block; color:var(--text); font-size:var(--text-reading); font-weight:var(--weight-label); margin-bottom:var(--space-1) }

/* ── ORGANISM · DETAIL SHEET — a record's detail and its forms, sliding in over the work
   from the right, over a scrim. The head holds the title and the close key; the body scrolls. */
.scrim{ position:fixed; inset:0; background:color-mix(in srgb,var(--black) 28%,transparent); z-index:40;
  opacity:0; transition:opacity var(--t-move) var(--ease) }
.scrim.on{ opacity:1 }
.sheet{ position:fixed; top:0; right:0; height:100%; width:min(var(--sheet-w),100%); z-index:41; background:var(--bg-raised);
  border-left:1px solid var(--border-strong); display:flex; flex-direction:column;
  transform:translateX(100%); transition:transform var(--t-move) var(--ease) }
.sheet.on{ transform:none }
.sheet-head{ display:flex; align-items:flex-start; justify-content:space-between; gap:var(--space-4);
  padding:var(--space-5) var(--space-5) var(--space-4); border-bottom:1px solid var(--border) }
.sheet-body{ flex:1; overflow:auto; padding:var(--space-5); display:flex; flex-direction:column; gap:var(--space-6) }

/* ── dark values (the tokens' opt-in) ── */
:root[data-mode="dark"] .scene{ --scene:color-mix(in srgb,var(--primary-48) 12%,var(--bg-raised)) }
:root[data-mode="dark"] :is(.scene-records,.scene-policy,.scene-investment,.scene-protection,.scene-succession){ --scene:color-mix(in srgb,var(--l-property) 18%,var(--bg-raised)) }
:root[data-mode="dark"] .br-box{ background:color-mix(in srgb,var(--bg-raised) 64%,transparent) }
:root[data-mode="dark"] .br-box.att{ background:color-mix(in srgb,var(--gold) 14%,var(--bg-raised)) }
:root[data-mode="dark"] .br-box.att .br-k::after{ box-shadow:0 0 0 2px color-mix(in srgb,var(--gold) 14%,var(--bg-raised)) }
:root[data-mode="dark"] .hm-desk{ --scene:color-mix(in srgb,var(--l-property) 18%,var(--bg-raised)) }
:root[data-mode="dark"] :is(.hm-bar i,.hm-year i,.hm-will .lived,.hm-path i.done){ background:var(--primary-72) }
:root[data-mode="dark"] .hm-bar i.owe{ background:var(--l-owed-2) }
:root[data-mode="dark"] :is(.hm-year i.soon,.hm-band .now.out){ background:var(--gold) }
:root[data-mode="dark"] .boundary{ background:color-mix(in srgb,var(--gold-dark) 10%,var(--bg-raised)) }
/* the status label on the dark ground: a clear step up from its card (29 Sep 2026: "the chips are too weak") */
:root[data-mode="dark"] .status-label.neutral{ background:color-mix(in srgb,var(--white) 10%,transparent) }
:root[data-mode="dark"] .status-label.ok{ background:color-mix(in srgb,var(--primary) 50%,transparent); color:var(--white) }
@media (prefers-color-scheme:dark){
  :root[data-mode="auto"] .scene{ --scene:color-mix(in srgb,var(--primary-48) 12%,var(--bg-raised)) }
  :root[data-mode="auto"] :is(.scene-records,.scene-policy,.scene-investment,.scene-protection,.scene-succession){ --scene:color-mix(in srgb,var(--l-property) 18%,var(--bg-raised)) }
  :root[data-mode="auto"] .br-box{ background:color-mix(in srgb,var(--bg-raised) 64%,transparent) }
  :root[data-mode="auto"] .br-box.att{ background:color-mix(in srgb,var(--gold) 14%,var(--bg-raised)) }
  :root[data-mode="auto"] .br-box.att .br-k::after{ box-shadow:0 0 0 2px color-mix(in srgb,var(--gold) 14%,var(--bg-raised)) }
  :root[data-mode="auto"] .hm-desk{ --scene:color-mix(in srgb,var(--l-property) 18%,var(--bg-raised)) }
  :root[data-mode="auto"] :is(.hm-bar i,.hm-year i,.hm-will .lived,.hm-path i.done){ background:var(--primary-72) }
  :root[data-mode="auto"] .hm-bar i.owe{ background:var(--l-owed-2) }
  :root[data-mode="auto"] :is(.hm-year i.soon,.hm-band .now.out){ background:var(--gold) }
  :root[data-mode="auto"] .boundary{ background:color-mix(in srgb,var(--gold-dark) 10%,var(--bg-raised)) }
  :root[data-mode="auto"] .status-label.neutral{ background:color-mix(in srgb,var(--white) 10%,transparent) }
  :root[data-mode="auto"] .status-label.ok{ background:color-mix(in srgb,var(--primary) 50%,transparent); color:var(--white) }
}
