/* Winloom design tokens — brand foundation v1.4 §10, "Black, orange, and nothing
   else". Replaces the v1.0 "brass on ink" system wholesale; this was a change of
   identity, not a change of shade, so the old token NAMES are gone rather than
   repointed. See the note on --danger below for the one name that could not be
   carried over safely.

   Orange appears on ONE element per view — the thing you want read or clicked.
   No second accent, no gradient, no brand blue, and no woven texture anywhere:
   the weave belongs in the mark, once. */
/* ONE brand family, loaded as a VARIABLE font — house style sheet 02.

   The previous import asked for `Schibsted+Grotesk:wght@400;500;700` plus
   Archivo plus DM Mono. Three faults in one line:

   1. Archivo and DM Mono are RETIRED. Sheet 02 names them: they belong to the
      v1.0 brass-on-ink identity and carry no role here.
   2. The weight list had no 600 and no 650, which the type scale requires for
      H3 and for H1/H2. A requested weight that is not loaded does not error —
      it snaps to the nearest one present. So every H1 and H2 on this site has
      been rendering at 700 while the stylesheet asked for 650, silently, with
      nothing anywhere reporting it. Sheet 02 calls this out by name.
   3. Two extra families are two extra fetches for faces nothing should use.

   `wght@400..700` requests the variable axis, so 600 and 650 are real
   interpolations rather than the nearest static cut. VERIFIED against the
   Google Fonts API before this was committed, because the failure mode here is
   silent by construction: this URL returns `font-weight: 400 700` (a range),
   where the old spelling returned separate @font-face blocks at fixed weights.
   Both return HTTP 200 and a stylesheet that works; only the range gives 650.

   SELF-HOSTED, NOT @import'ed FROM GOOGLE
   ---------------------------------------
   Two reasons, and the second is the one that made this non-optional.

   1. Every visitor to a Swiss-hosted product would otherwise have their IP
      handed to Google on page load, before consenting to anything and without
      it appearing anywhere in the product's own data flows. There is no
      trade — the font is SIL OFL and the files are ours to serve.

   2. `@import` of a remote stylesheet is a blocking fetch of a THIRD-PARTY
      document that must complete before the local rules below it are applied.
      Google being slow, blocked (CN), or DNS-poisoned does not degrade the
      type — it delays or discards the whole of this file. Self-hosting turns
      a dependency on another company's uptime into a request to the same
      origin that already served the page.

   THE FILES ARE THE FILES GOOGLE SERVES, byte for byte: sha256
   e3b56e90…f3a7 (latin) and f5df746d…54db (latin-ext), verified against
   fonts.gstatic.com on 19 Sep 2026 before they were installed. Downloading
   "a Schibsted Grotesk" and assuming it is variable is exactly the defect in
   fault 2 above wearing different clothes — a static cut also returns 200,
   also renders, and also snaps 650 to 700. Both files were additionally
   decoded and confirmed to carry `fvar`/`gvar`, i.e. a real weight axis.

   The unicode-ranges below are Google's own, copied from the stylesheet that
   URL returns rather than typed from memory: get one wrong and the affected
   glyphs fall back to system-ui mid-word, which is subtle enough to ship.

   `font-weight: 400 700` is a RANGE, not a pair. That is what makes 650 an
   interpolation instead of a rounding. */

/* latin-ext */
@font-face {
  font-family: 'Schibsted Grotesk';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/brand/fonts/schibsted-grotesk-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Schibsted Grotesk';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/brand/fonts/schibsted-grotesk-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --ink:         #0A0A0A;  /* true black, not a tinted near-black */
  --orange:      #FF4A1C;  /* "Signal Orange" — the only accent */
  --paper:       #F7F7F5;  /* light ground, a hair warm so it doesn't fight the orange */
  --panel:       #EDEDEA;  /* blocked-out areas ON PAPER: table stripes, quiet cards, insets */
  --orange-text: #C93A12;  /* "Deep Orange" in the book. Orange fails as small text; links use this */
  --slate:       #5E5E5B;  /* secondary text. a true neutral; the palette carries no third hue */

  /* --panel is the sixth base colour and it was missing. Sheet 04 lists the
     palette as three core (Ink, Orange, Paper) plus three support (Panel,
     Slate, Deep Orange), and sheet 07 fixes that set. This file had five of
     the six and used #FFFFFF for the job Panel exists to do — see --surface.

     "One step down from Paper, never a second background colour." Panel is not
     a ground you build a page on; it is how you block out a region that is
     already on Paper. Ink on Panel measures 16.88:1. */

  /* ── PRODUCT-UI STATE COLOURS — NOT BRAND, AND NOT YET DECIDED ─────────────
     These two are deliberately separated from the six above by this comment,
     because they are a different KIND of thing and the separation is the only
     thing stopping them being read as palette.

     House style sheet 07 lists "Product UI semantic palette" as an open
     question: owner "product + design", trigger "when the product needs
     states", priority MEDIUM. Sheet 04 is blunter — "Don't introduce a second
     accent for semantics. Success, warning and error states are a product-UI
     palette and will be specified against the product, not borrowed from the
     brand accent. Until then, use Ink, Paper and weight — not a green and a
     red invented on the spot."

     We have a green invented on the spot. It is live on .pill.ok, .flash,
     .s-done, .ok and six places in the Forge brief, and it predates the book.

     Deleting it tonight would be the wrong call in both directions: it would
     change the meaning of every completed row in projectloom on the authority
     of a book that explicitly says this is not its decision to make, and it
     would replace a working state system with nothing. So it stays, it works,
     and it is labelled — the trigger in sheet 07 has fired, and the item is
     now waiting on Thomas and Seb rather than on nobody having noticed.

     --verified is NOT in the book's palette. Do not add it to the six.
     --danger is a darkened orange, not a red, for the reason below.

     The name --signal was deliberately not reused: in v1.0 it meant
     risk-and-loss, in v1.4 "Signal Orange" is the house accent. A straight
     carry-over would have made every blocked row read as a call to action. */
  --verified:    #17916A;
  --danger:      var(--orange-text);

  --ground: var(--paper);
  --figure: var(--ink);
  --accent: var(--orange);

  /* Derived surfaces. These exist as tokens because every consumer that needed
     them previously invented its own literal — projectloom had #fff cards and
     #dfe3dc borders, the main app had color-mix() spelled out at 14 call sites,
     and `--hairline` was *referenced* by .addbox while being defined nowhere at
     all, so that border has been falling back to a translucent WHITE on a light
     ground since it shipped: invisible, and invisible in a way no colour sweep
     reports because the value it renders is not written down anywhere.

     --surface was #FFFFFF. Sheet 04 says Paper is "warm white — pure #FFFFFF
     next to Ink is glary and reads as an unstyled default", and gives Panel as
     the blocked-out surface on Paper. A pure-white card on a warm-white page
     is the exact glare the palette is built to avoid, so --surface now points
     at the token that owns that job. On Ink it stays --surface-ink. */
  --surface:  var(--panel);
  --hairline: color-mix(in srgb, var(--figure) 12%, transparent);
  --tint:     color-mix(in srgb, var(--figure) 5%, transparent);
  /* A dashed border needs more weight than a solid one to read as the same
     line: the gaps eat roughly half of it. --hairline on Ink is 12% Paper,
     which renders as an edge when solid and as nothing when dashed. */
  --hairline-strong: color-mix(in srgb, var(--figure) 32%, transparent);
  /* Raised, not tinted. --tint is the figure colour at 5% over whatever is
     BEHIND the element, which on a dark ground composites to roughly the
     card colour it was supposed to lift off - a hover state that changes
     nothing visible. Mixing INTO --surface instead moves away from the card
     in whichever direction the theme makes lighter. */
  --surface-raised: color-mix(in srgb, var(--figure) 7%, var(--surface));

  /* Fixed constants, not theme-dependent: the accent warmed for a black ground
     (#FF4A1C goes muddy on true black) and the card surface on that ground.
     Named because the same two values were written out five and two times
     respectively across the dark-mode blocks and the projectloom sign-in page,
     and a palette with the accent hardcoded in six places is one that drifts on
     the next tweak rather than the next rebrand. */
  --accent-ink:  #FF5A2E;
  --surface-ink: #141413;

  /* ── TYPE ────────────────────────────────────────────────────────────────
     One family. --display and --ui were two names for what sheet 02 says is
     one decision, and they had drifted to two different faces. Both are now
     --font; the old names are gone rather than aliased, because an alias is
     how a second face gets reintroduced by someone reading the name as a spec.

     --mono is NOT a brand face and is deliberately a platform stack. Sheet 02
     answers this directly: code, hashes, IDs and paths are "technical strings,
     not brand surface", and shipping a licensed mono to render a commit hash
     buys nothing and adds a font to load. For FIGURES that must align in
     columns the answer is not mono at all — it is this same face with
     font-variant-numeric: tabular-nums, which is already in the file. */
  --font: 'Schibsted Grotesk', system-ui, -apple-system, sans-serif;
  --mono: ui-monospace, Menlo, Consolas, monospace;

  /* ── TYPE SCALE — sheet 02. Eight steps, and these numbers are fixed. ─────
     Sizes in px at 1x. Tracking is in px, tightening as size grows and opening
     as it shrinks; the eyebrow is the only positive value in the system.
     Weights 650 and 600 are why the variable axis is loaded — see the @import. */
  --t-display: 700 56px/1.02;   --tr-display: -1.6px;
  --t-h1:      650 34px/1.12;   --tr-h1:      -0.8px;
  --t-h2:      650 22px/1.2;    --tr-h2:      -0.4px;
  --t-h3:      600 16px/1.3;    --tr-h3:      -0.1px;
  --t-body:    400 15px/1.55;   --tr-body:     0;
  --t-small:   400 13px/1.5;    --tr-small:    0;
  --t-caption: 400 11px/1.45;   --tr-caption:  0.2px;
  --t-eyebrow: 700 10.5px/1.2;  --tr-eyebrow:  1.7px;

  /* Per-slice opacity for the spend pies, overridden inline on each slice. */
  --slice-a:   1;

  /* ── GRID AND SPACING — sheet 05. One base unit of 4px and one derivation.
     Content 1128 = 12 columns of 72 with gutters of 24. The column is 72
     because it is the only candidate that divides 1128 to a whole number AND
     sits on the 4-unit base; 20/28/32 all give fractional columns.

     --measure is 8 columns (8x72 + 7x24 = 744), not 12. Sheet 05: "Text sits
     in 8 columns, not 12" — a full-width paragraph at this page size runs past
     a comfortable measure, and sheet 02 caps body copy at 72 characters. */
  --content: 1128px;
  --col:       72px;
  --gutter:    24px;
  --base:       4px;
  --measure:  744px;

  /* The spacing scale. Multiples of 4, thinning out as they grow so the
     choices stay few. Nothing between 64 and 96 — if you want 80, you want 96. */
  --s1:  4px;  --s2:  8px;  --s3: 12px;  --s4: 16px;  --s6: 24px;
  --s8: 32px;  --s12: 48px; --s16: 64px; --s24: 96px; --s32: 128px;

  /* ── TINTS — sheet 04. The accent at reduced opacity over a ground.
     Which ground decides whether you get a tint or a different colour, so the
     two ramps below are NOT interchangeable.

     Orange on Paper gives tints: the hue holds all the way down. Use for
     highlight fills, chart series, hover states, quiet callouts.

     Orange on INK gives BROWNS, not tints — 13% composites to #2A120C, which
     is not in the palette and reads as a printing fault. That ramp is
     deliberately absent from this file. To go quiet on a dark ground, reduce
     PAPER on Ink instead; --paper-on-ink-* below is that ramp, and it stays
     neutral (13% gives a true grey). */
  --tint-o07: #F8EBE6;  --tint-o13: #F8E1D9;  --tint-o20: #F9D4CA;
  --tint-o40: #FAB29E;  --tint-o60: #FC8F73;  --tint-o80: #FD6D47;

  --quiet-i07: #1B1B1A;  --quiet-i13: #292929;  --quiet-i20: #393939;
  --quiet-i40: #696968;  --quiet-i60: #989897;  --quiet-i80: #C8C8C6;
}
/* Dark mode inverts ground and text and warms the accent so it holds on black.
   --surface must invert too: a white card on a black ground is a torch. */
@media (prefers-color-scheme: dark) {
  :root {
    --ground: var(--ink); --figure: var(--paper);
    --accent: var(--accent-ink); --danger: var(--accent-ink);
    --surface: var(--surface-ink);
  }
}
[data-theme="dark"] {
  --ground: var(--ink); --figure: var(--paper);
  --accent: var(--accent-ink); --danger: var(--accent-ink);
  --surface: var(--surface-ink);
}

* { box-sizing: border-box; }
/* Body is 15/1.55, not 16/1.55. Sheet 02 sets the Body step at 15px and the
   whole scale is derived against that; leaving the base at 16 would put every
   step one notch off its specified relationship to running text. */
body {
  margin: 0; background: var(--ground); color: var(--figure);
  font: var(--t-body) var(--font); letter-spacing: var(--tr-body);
  -webkit-font-smoothing: antialiased;
}

/* The warp-hairline layer that used to live here is DELETED, not disabled.
   v1.0 listed it as a Do; v1.4 §10 names it as the single change that breaks the
   identity — "no woven textures, no thread patterns, no fabric imagery anywhere"
   — because threads rendered thin and many read as heritage craft, which is
   where this identity started and why it was rejected. It went out with its
   `position: relative` anchor on <body> and the `body > *` stacking rule that
   existed only to sit content above it; a disabled-but-present texture is the
   thing somebody re-enables in six months. */
/* The type scale, applied. Sheet 02 gives eight steps with a size, a line
   height, a tracking value and a weight each; previously this file had three
   headings sharing one weight (700) and one tracking (-0.025em), with h1 on a
   viewport clamp that landed between the Display and H1 steps at most widths
   and matched neither. The clamp is gone: Display and H1 are different steps
   with different jobs, and a heading that changes size with the window is not
   on the scale at any width.

   Each rule sets `font:` (weight/size/line-height/family) and letter-spacing
   separately, because tracking is not part of the font shorthand — and the
   shorthand resets line-height, so the two must travel together or a later
   font-size declaration silently drops the leading. */
h1, h2, h3 { font-family: var(--font); margin: 0 0 .5em; }
.t-display { font: var(--t-display) var(--font); letter-spacing: var(--tr-display); }
h1         { font: var(--t-h1)      var(--font); letter-spacing: var(--tr-h1); }
h2         { font: var(--t-h2)      var(--font); letter-spacing: var(--tr-h2); }
h3         { font: var(--t-h3)      var(--font); letter-spacing: var(--tr-h3); }
.t-small   { font: var(--t-small)   var(--font); letter-spacing: var(--tr-small); }
.t-caption { font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption); }

/* Eyebrow: Slate, never Ink — sheet 02 states the colour as part of the step.
   It was 500-weight DM Mono at .72rem/.14em; it is now the brand face at the
   specified 700 / 10.5px / +1.7px. */
.eyebrow {
  font: var(--t-eyebrow) var(--font); letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase; color: var(--slate);
}

/* Figures that must align in columns are NOT mono — sheet 02 answers this
   explicitly. Same face, tabular figures. .mono stays for genuine technical
   strings (hashes, IDs, paths) and resolves to the platform stack. */
.num, td.num { font-variant-numeric: tabular-nums; }
.mono { font-family: var(--mono); }
a { color: var(--figure); text-decoration-color: color-mix(in srgb, var(--figure) 35%, transparent); }
.muted { color: var(--slate); }

/* Content width is the grid's, not a round rem number. 1128 = 12 columns of 72
   with 11 gutters of 24; 68rem (1088) is not on the grid at any column count,
   so nothing inside it could land on a column edge even in principle. Padding
   and margins come off the 4-unit spacing scale. */
.wrap { max-width: var(--content); margin: 0 auto; padding: var(--s8) var(--s6) var(--s16); }

/* FULL-BLEED, opt-in per page. Seb, 2026-09-20 05:42: "I would like the
   app.winloom.ai platform section to use the whole width of the screen."

   Opt-IN and not a change to .wrap, because .wrap is the container for every
   page on this layout — profile, the sign-in flows, deal-brief, the tenant
   and user FORMS. Widening it globally would have set a column of text
   fields 2000px wide on an ultrawide and called it a feature. The platform
   admin page is the one that earns the width: it is a fixed rail plus a pane
   of tables and charts, all of which get more useful as they get wider,
   and none of which is running text.

   Prose is unaffected by construction: .sub, .panel-sub and .tile-blurb
   carry their own max-widths off the measure, so the reading column stays at
   8 columns no matter how wide its container gets — sheet 05. The header bar
   was ALREADY full-bleed, so this also removes the seam where a full-width
   dark bar sat on top of a 1128px body. */
.wrap-wide { max-width: none; }

/* Running text sits in 8 columns, not 12 — sheet 05. Apply to prose blocks;
   headings, tables and images may go the full 12. */
.measure { max-width: var(--measure); }
.card {
  background: color-mix(in srgb, var(--ground) 92%, var(--figure));
  border: 1px solid color-mix(in srgb, var(--figure) 12%, transparent);
  border-radius: 10px; padding: 1.5rem;
}

/* Orange is the accent and it is spent ONCE per view — here, the primary action. */
.btn {
  display: inline-block; font: 500 .95rem/1 var(--font); padding: .85rem 1.4rem;
  border: 1px solid var(--accent); border-radius: 8px; cursor: pointer;
  background: var(--accent); color: var(--ink); text-decoration: none;
}
.btn:hover { filter: brightness(1.07); }
.btn-quiet {
  background: transparent; color: var(--figure);
  border-color: color-mix(in srgb, var(--figure) 25%, transparent);
}
label { display: block; font: 500 .8rem/1 var(--font); margin: 0 0 .4rem; color: var(--slate); }
input[type=email], input[type=password], input[type=text] {
  width: 100%; padding: .8rem .9rem; font: 400 1rem var(--font);
  color: var(--figure); background: color-mix(in srgb, var(--ground) 88%, var(--figure));
  border: 1px solid color-mix(in srgb, var(--figure) 18%, transparent); border-radius: 8px;
}
input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.field { margin-bottom: 1.1rem; }
.err {
  border-left: 3px solid var(--danger); padding: .7rem .9rem; margin-bottom: 1.2rem;
  background: color-mix(in srgb, var(--danger) 9%, transparent); font-size: .92rem;
}

/* Login: single centred column on ink. */
.auth { min-height: 100vh; display: grid; place-items: center; padding: 2rem; }
.auth .panel { width: 100%; max-width: 25rem; }
.brandrow { display: flex; align-items: center; gap: .8rem; margin-bottom: 2.5rem; }

/* ── THE LOCKUP IS A PLACED ASSET, AND THAT IS WHY THESE RULES SIZE ON HEIGHT ─
   Sheet 02: "'Winloom' is never typed into a layout as live text — it is placed
   as the lockup asset." Until now all five document heads did exactly that: a
   tile <img> beside a <span class="word">winloom</span> set in the webfont.
   That is not a near-miss of the rule, it is the failure mode the rule names —
   the wordmark's letterfit, tracking and weight came from whatever the browser
   resolved, so a font that failed to load rendered the logo in the fallback at
   the wrong widths, silently and only for the visitor it happened to.

   Self-hosting the webfont does NOT fix it and is explicitly not an exception:
   it removes the visitor-supplies-the-font failure and leaves the other one,
   because the font still loads separately from the markup.

   The asset carries real outlines at wght=650 with the -0.018em tracking baked
   in, so none of that is resolved at render time any more.

   SIZED BY HEIGHT, NOT WIDTH, because the lockup's aspect is fixed at
   924.04:256 and sheet 02's floor is a MINIMUM WIDTH of 96px. Constraining the
   height makes the width a derived value that can be checked once per call
   site: 28px -> 101px, 30px -> 108px, 44px -> 159px, 46px -> 166px. All four
   clear 96. Set a width here instead and the floor becomes something you have
   to remember rather than something the arithmetic hands you.

   The old `.brandrow img` and `header.top img` element rules are gone rather
   than amended: `width: 46px` on an element selector would have caught the
   lockup and squashed it to 46px WIDE — 13px tall, a quarter of the minimum —
   and `header.top img` also reached the nav avatar, which only escaped because
   the partial writes an inline width. Both are now class-scoped. */
.lockup { display: block; width: auto; }
.brandrow .lockup { height: 46px; }

/* App shell */
header.top {
  display: flex; align-items: center; gap: 1.25rem; padding: .9rem 1.5rem;
  border-bottom: 1px solid color-mix(in srgb, var(--figure) 12%, transparent);
}
header.top .lockup { height: 30px; }
header.top nav { margin-left: auto; display: flex; gap: 1.25rem; font-size: .92rem; }
.grid { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
table { width: 100%; border-collapse: collapse; font-size: .92rem; }
/* A column header is an uppercase section label, which is the Eyebrow step —
   not a technical string, so not mono. Same for .pill below. */
th {
  text-align: left; font: var(--t-eyebrow) var(--font);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase; color: var(--slate); padding: .6rem .5rem;
  border-bottom: 1px solid color-mix(in srgb, var(--figure) 14%, transparent);
}
td { padding: .65rem .5rem; border-bottom: 1px solid color-mix(in srgb, var(--figure) 8%, transparent); }
.pill {
  font: var(--t-eyebrow) var(--font); letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  padding: .3rem .55rem; border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--figure) 20%, transparent); color: var(--slate);
}
.pill.ok { color: var(--verified); border-color: color-mix(in srgb, var(--verified) 45%, transparent); }

/* winloom-06 — dashboard action surface */
.stats{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,220px));
       gap:1rem;margin:2rem 0}
.stats .card p{margin:0}
.stats .stat{font-size:2.1rem;line-height:1.1;margin:.15rem 0}
.stats .muted{font-size:.82rem}
.addbox{border:1px solid var(--hairline);border-radius:12px;
        padding:1.5rem 1.5rem 1.75rem;margin:2.5rem 0}
.addbox h2{margin:0 0 .25rem}
.addbox .muted{margin:0 0 1.25rem;font-size:.9rem}
.addform{display:flex;gap:1rem;align-items:flex-end;flex-wrap:wrap}
.addform .field{flex:1 1 220px;margin:0}
.addform .btn{flex:0 0 auto}
/* No `var(--x, #fallback)` anywhere in this file, deliberately. A fallback is
   where a retired colour survives a rebrand: the token name reads as current,
   the rendered pixel is the old palette, and a sweep for old hex finds it only
   if the sweep reads its own stylesheet. Two of these (#35786A, #AF4835) were
   still sitting in my v1.4 draft for exactly that reason. If a token is
   missing, the right outcome is a visible break, not a quiet 2026 revival. */
.flash{border-left:2px solid var(--verified);padding:.65rem 1rem;
       margin:1.5rem 0;font-size:.92rem}
.errs{list-style:none;padding:0;margin:1rem 0 0;font-size:.88rem;
      color:var(--danger)}

/* winloom-07 — an <input> with no type attribute is still a text input.
   input[type=text] does not match it; :not() on the types that style
   themselves differently does. */
input:not([type=hidden]):not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=button]) {
  background: color-mix(in srgb, var(--ground) 88%, var(--figure) 12%);
  color: var(--figure);
  border: 1px solid color-mix(in srgb, var(--ground) 70%, var(--figure) 30%);
}
input::placeholder { color: color-mix(in srgb, var(--ground) 45%, var(--figure) 55%); }

/* winloom-09 — the account page. Citations are part of the type hierarchy,
   not a footnote: a claim and its provenance are one block. */
.jobline{display:flex;gap:.75rem;align-items:center;margin:1.25rem 0 0;flex-wrap:wrap}
.jobline .err{color:var(--danger);font-size:.8rem}
.claims{margin:1.5rem 0 0}
.claim{padding:1.1rem 0;border-top:1px solid color-mix(in srgb,var(--ground) 88%,var(--figure) 12%)}
.claim dt{font-size:.78rem;letter-spacing:.04em;opacity:.65;margin:0 0 .35rem}
.claim dd{margin:0;font-size:1.02rem}
.claim .vals{margin:0;padding-left:1.1rem}
.cite{font-size:.72rem;opacity:.6;margin:.5rem 0 0}
.cite a{color:inherit}

/* winloom-14 — profile, avatars, admin user editor. */

/* Panels are scoped to .wrap on purpose. The auth pages (/sign-in,
   /set-password) also use .panel, but they sit in .auth, not .wrap — an
   unscoped rule here would put a card border around the login box, which is a
   design change to a page nobody asked me to touch. */
.wrap .panel {
  border: 1px solid color-mix(in srgb, var(--figure) 12%, transparent);
  border-radius: 12px;
  padding: 1.5rem 1.5rem 1.75rem;
  margin: 1.75rem 0;
}
.panel-h  { margin: 0 0 .25rem; font-size: 1.2rem; }
.panel-sub{ margin: 0 0 1.25rem; font-size: .9rem; max-width: 46rem; }
.panel-sub:last-child { margin-bottom: 0; }

/* Forms that are a column of fields, not a layout. Full-width inputs on a
   68rem page put the label a screen away from the value. */
.narrow { max-width: 26rem; }
.rowbtns { display: flex; gap: .75rem; align-items: center; flex-wrap: wrap; }
.rowbtns input[type=file] { flex: 1 1 14rem; font-size: .88rem; }

.btn-sm { padding: .5rem .9rem; font-size: .85rem; }

/* Avatar. One rule for both the image and the initials fallback so the two
   cannot drift apart — the fallback is what every user sees on day one. */
.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px; object-fit: cover; flex: 0 0 auto;
  background: color-mix(in srgb, var(--ground) 82%, var(--figure));
  border: 1px solid color-mix(in srgb, var(--figure) 14%, transparent);
}
.avatar-initials {
  font: 500 1rem/1 var(--font); letter-spacing: .02em;
  color: var(--slate); user-select: none;
}

.profile-head { display: flex; align-items: center; gap: 1.25rem; margin: .5rem 0 2rem; }
.avatar-form  { display: flex; align-items: flex-start; gap: 1.25rem; flex-wrap: wrap; }
.avatar-form-controls { flex: 1 1 18rem; min-width: 0; }

/* The nav entry is the avatar itself. It doubles as the "am I signed in as the
   right person" indicator, which is the question a shared laptop raises. */
header.top nav .navme {
  display: inline-flex; align-items: center; gap: .5rem; text-decoration: none;
}
header.top nav .navme .avatar { width: 26px; height: 26px; font-size: 10px; }

/* Tables of passkeys wrap badly on a phone; let them scroll rather than
   squeeze the action column to nothing. */
@media (max-width: 640px) {
  .wrap .panel { padding: 1.25rem 1rem 1.5rem; }
  .profile-head { gap: 1rem; }
}

/* ───────────────────────────────────────────────────────────────────────────
   projectloom — brand v1.4

   This section exists because projectloom did not link this file at all. It
   re-declared the entire palette inline in pl/layout.blade.php, and its sign-in
   page declared a THIRD copy property-by-property. Three independent palettes,
   so last night's identity change would have moved the main app to v1.4 and
   left all twelve projectloom pages on brass-on-ink — the surface Seb and
   Thomas actually read, diverging from the one they don't.

   That is the same defect as the timestamp drift fixed in 5209ecc: a second
   surface quietly re-implementing a global, then aging away from it. The fix is
   the same shape too — one definition, consumed everywhere, with a test that
   fails the build if a second copy appears.

   projectloom keeps its own LAYOUT (denser, tabular, built for reply-by-number)
   and takes its COLOUR from the tokens above. Nothing below is a literal hex.
   ─────────────────────────────────────────────────────────────────────────── */

.pl-body { background: var(--ground); color: var(--figure); font: 400 15px/1.55 var(--font); margin: 0; }
.pl-body a { color: var(--orange-text); }

.pl-head {
  background: var(--ink); color: var(--paper); padding: 14px 22px;
  display: flex; align-items: center; gap: 26px; flex-wrap: wrap;
}
/* The wordmark is NOT the accent. v1.4 spends orange on the thing you want
   clicked, and a logo in every header on every page is the one element
   guaranteed not to be that. It was brass here, which is why it reads as a
   demotion rather than a recolour — it is.

   28px and not the 21px the live text used: the lockup adds the tile, so
   matching the old cap height would have put the whole asset at 21px tall and
   76px wide — under sheet 02's 96px floor. The wordmark inside it still lands
   at 15px, close to where it was. */
.pl-head .lockup { height: 28px; }
.pl-head nav a { color: var(--paper); text-decoration: none; opacity: .78; font-weight: 500; }
.pl-head nav a:hover, .pl-head nav a.on {
  opacity: 1; border-bottom: 2px solid var(--accent); padding-bottom: 3px;
}
.pl-head .sp { margin-left: auto; font-size: 13px; opacity: .72; }
.pl-head .sp button { background: none; color: var(--accent); padding: 0 0 0 10px; border: 0; cursor: pointer; font: inherit; }

/* 1060/26/22/70 were none of them on the grid or the 4-unit base. projectloom
   stays denser than the app — that is explicitly the designer's call under
   sheet 07 — but density is bought with SPACING, which is open to judgement,
   not by inventing type sizes, which is not. The headings below were 27px and
   19px: two steps that exist on no scale, between H1 (34) and H2 (22) and
   between H2 (22) and H3 (16) respectively. They now sit on the scale, and the
   page gets its density back from the tighter margins around them. */
.pl-main { max-width: var(--content); margin: 0 auto; padding: var(--s6) var(--s6) var(--s16); }
.pl-body h1 { font: var(--t-h1) var(--font); letter-spacing: var(--tr-h1); margin: 0 0 var(--s1); }
.pl-body h2 { font: var(--t-h2) var(--font); letter-spacing: var(--tr-h2); margin: var(--s6) 0 var(--s3); }
.sub { color: var(--slate); font: var(--t-small) var(--font); margin: 0 0 var(--s6); max-width: var(--measure); }

.pl-body .card {
  background: var(--surface); border: 1px solid var(--hairline);
  border-radius: 9px; padding: 16px 18px; margin-bottom: 12px;
}
.pl-body table {
  width: 100%; border-collapse: collapse; background: var(--surface);
  border: 1px solid var(--hairline); border-radius: 9px; overflow: hidden;
}
.pl-body th {
  background: var(--ink); color: var(--paper); text-align: left;
  padding: var(--s2) var(--s3);
  font: var(--t-eyebrow) var(--font); letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
}
.pl-body td { padding: 10px 12px; border-top: 1px solid var(--hairline); vertical-align: top; }
.ref { font: 500 12px var(--mono); color: var(--orange-text); }

.pl-body .pill {
  display: inline-block; font-size: 11px; font-weight: 600; padding: 2px 9px;
  border-radius: 11px; text-transform: uppercase; letter-spacing: .4px;
}
/* Status colour, re-derived rather than recoloured.

   v1.4 gives green ONE meaning — Verified: sourced, confirmed, complete — and
   the old palette spent it on `in_progress` while `done` got an unrelated
   hardcoded green found nowhere in either brand. So the two states that most
   need to differ were both green, and the one that had actually earned the
   semantic positive was the one not using it. `done` takes Verified now;
   `in_progress` is neutral-with-an-accent-edge, which is what "started, not
   finished" should look like.

   `blocked` uses --danger, and that is the rename this palette forced: v1.0's
   --signal meant risk-and-loss, v1.4's "Signal Orange" is the house accent. A
   straight name carry-over would have made every blocked row look like a
   call-to-action. */
.s-open        { background: var(--tint);    color: var(--slate); }
.s-in_progress { background: var(--tint);    color: var(--figure); box-shadow: inset 2px 0 0 var(--accent); }
.s-blocked     { background: color-mix(in srgb, var(--danger) 10%, transparent);   color: var(--danger); }
.s-done        { background: color-mix(in srgb, var(--verified) 12%, transparent); color: var(--verified); }
.s-parked      { background: var(--tint);    color: var(--slate); opacity: .8; }

.pl-body input, .pl-body select, .pl-body textarea {
  font: 400 14px var(--font); padding: 8px 10px; border: 1px solid var(--hairline);
  border-radius: 6px; background: var(--surface); color: var(--figure); width: 100%;
}
.pl-body textarea { min-height: 74px; resize: vertical; }
.pl-body label { display: block; font-size: 12px; font-weight: 600; color: var(--slate); margin: 10px 0 4px; }

/* Default button is the quiet one; .go is the single accent action per view.
   Renamed from .brass — a class called .brass in a palette with no brass is
   how the old identity gets reintroduced by someone reading the class name as
   a spec. The rename is enforced by NoRetiredBrandTest. */
.pl-body button, .pl-body .btn {
  font: 600 14px var(--font); padding: 9px 17px; border: 0; border-radius: 6px;
  background: var(--ink); color: var(--paper); cursor: pointer; width: auto;
}
.pl-body button.go { background: var(--accent); color: var(--ink); }
.pl-body button.link { background: none; color: var(--orange-text); padding: 0 0 0 10px; }
/* Destructive text-buttons (Delete an attachment, a document). These were
   #9a3b3b inline — a dark red belonging to neither palette, invented at the two
   call sites that needed to look dangerous. --danger is a darkened orange in
   v1.4, so a delete no longer reads as "red = stop" but as "spent accent =
   consequence"; it is the only negative the brand gives us. Flagged for
   Thomas alongside the --danger question. */
.pl-body button.danger { background: none; color: var(--danger); padding: 9px 4px; }
/* Semantic positive, text form. Verified is the ONE meaning green carries. */
.ok { color: var(--verified); font-weight: 600; }

.row { display: flex; gap: 12px; flex-wrap: wrap; }
.row > * { flex: 1; min-width: 150px; }
.note { border-left: 3px solid var(--hairline); padding: 8px 0 8px 13px; margin: 12px 0; }
.note.status { border-left-color: var(--accent); }
.who { font-size: 12px; color: var(--slate); margin-bottom: 3px; }
.who .statusflag { color: var(--orange-text); }
.new {
  background: var(--accent); color: var(--ink); font-size: 11px; font-weight: 700;
  padding: 2px 8px; border-radius: 11px;
}
.pl-body code { font: 400 13px var(--mono); background: var(--tint); padding: 1px 5px; border-radius: 3px; }
/* Decision log. `decided` takes Verified: a decision on the record is the
   sourced, confirmed state green is reserved for. `superseded` is slate and
   the row is dimmed but NOT hidden or struck through, because a superseded
   decision is still history someone needs to be able to read. */
.s-decided     { background: color-mix(in srgb, var(--verified) 12%, transparent); color: var(--verified); }
.s-superseded  { background: var(--tint); color: var(--slate); }
.pl-body tr.superseded td { opacity: .62; }
.pl-body .superseded-notice { border-left: 3px solid var(--slate); }
.pl-body blockquote { margin: var(--s2) 0 0; padding-left: 13px; border-left: 3px solid var(--hairline); color: var(--slate); }
.pl-body .err {
  background: color-mix(in srgb, var(--danger) 8%, transparent); color: var(--danger);
  padding: 9px 12px; border-radius: 6px; margin-bottom: 14px; font-size: 13px;
  border-left: 3px solid var(--danger);
}
.quiet { color: var(--slate); }

/* projectloom sign-in. Single centred column on true black. */
.pl-auth {
  margin: 0; min-height: 100vh; display: grid; place-items: center;
  background: var(--ground); color: var(--figure); font: 400 15px var(--font);
}
.pl-auth .b { width: 360px; text-align: center; }
/* `.b` is text-align:center, which centres inline content — the lockup is
   display:block, so it needs auto margins or it sits hard left under centred
   copy. That difference is invisible in the markup and only shows on render. */
.pl-auth .lockup { height: 44px; margin: 0 auto 6px; }
.pl-auth p { color: color-mix(in srgb, var(--paper) 62%, var(--ink)); font-size: 13px; line-height: 1.6; }
.pl-auth button {
  font: 600 15px var(--font); padding: 12px 22px; border: 0; border-radius: 7px;
  background: var(--accent); color: var(--ink); cursor: pointer; width: 100%; margin-top: 18px;
}
.pl-auth #pk-err { color: var(--accent-ink); font-size: 13px; margin-top: 12px; display: none; }

/* ---------------------------------------------------------------------------
   Support-session bar — the deliberate exception to "orange once per view".

   Seb's ruling, 2026-09-16: keep it loud, and treat safety as the exception to
   the brand rule rather than the brand rule as the exception to safety. So an
   engineer looking at a customer's live data gets orange on this bar AND on
   whatever the page's own accent element is. That is a real cost — the page
   briefly has two orange things and the hierarchy §10 buys goes soft — and it
   is the right trade, because the failure it prevents is editing the wrong
   tenant's data while believing you are in your own account.

   The two modes must not look alike; the old amber pair (#ff9d3c read-write,
   #ffd84d read-only) did that with two hues, which v1.4 does not have. They are
   now separated by GROUND instead:

     rw (editing, changes are live)  orange field, ink text   — the alarm
     ro (viewing, read-only)         ink field, orange text   — present, calm

   Both are unmistakably the support bar; only one is an alarm. Same move as the
   brief's three bands — hierarchy, not another colour.

   NOT theme-reactive, deliberately: --ground/--figure are not used anywhere
   below. This is safety chrome, and safety chrome that changes colour with the
   user's OS preference is one OS update away from being quiet. It reads
   identically in light and dark.

   Contrast measured, not eyeballed, at 14px/600 where AA needs 4.5:
   ink-on-orange 5.89, paper-on-ink 18.46, accent-ink-on-ink 6.37. Note these
   are LOWER than the pale amber they replace (9.12 / 13.65) — a saturated
   ground cannot also be a high-contrast one. Still AA on every pair; flagged
   because "louder" and "higher contrast" are not the same axis and the bar got
   louder while getting numerically closer. --------------------------------- */
.supportbar {
  position: sticky; top: 0; z-index: 9999;
  padding: .6rem 1rem; font: 600 14px/1.4 var(--font);
  display: flex; gap: 1rem; align-items: center;
  justify-content: space-between; flex-wrap: wrap;
}
.supportbar[data-mode="rw"] { background: var(--orange); color: var(--ink); }
.supportbar[data-mode="ro"] {
  background: var(--ink); color: var(--paper);
  box-shadow: inset 0 -2px 0 var(--accent-ink);
}
/* The mode word is the one thing to read first, so it is the accent in the
   calm state. In the alarm state the whole bar is already the accent, and
   re-accenting inside it would leave the loudest element invisible. */
.supportbar .mode { letter-spacing: .02em; }
.supportbar[data-mode="ro"] .mode { color: var(--accent-ink); }

.supportbar .who { display: flex; gap: .5rem; align-items: center; }
.supportbar form { display: flex; gap: .4rem; align-items: center; margin: 0; }

/* Controls inherit the bar's own text colour rather than carrying a literal.
   currentColor is what makes one rule work on both grounds: on the orange
   field these read as ink, on the ink field as paper, with no second block and
   nothing to keep in sync. */
.supportbar input,
.supportbar button {
  font: inherit; font-size: 13px; padding: .3rem .7rem; border-radius: 4px;
  background: transparent; color: inherit;
  border: 1px solid color-mix(in srgb, currentColor 34%, transparent);
}
.supportbar input { padding: .3rem .5rem; width: 15rem; }
.supportbar input::placeholder { color: color-mix(in srgb, currentColor 62%, transparent); }
.supportbar button { cursor: pointer; }
.supportbar button:hover { background: color-mix(in srgb, currentColor 12%, transparent); }
/* Exit is the way out of a mode that is costing you safety margin, so it is
   the solid one — filled with the bar's text colour, knocked out in its
   ground. Works on both modes for the same currentColor reason. */
.supportbar button.exit {
  font-weight: 700; background: currentColor; border-color: currentColor;
}
.supportbar[data-mode="rw"] button.exit { color: var(--orange); background: var(--ink); }
.supportbar[data-mode="ro"] button.exit { color: var(--ink); background: var(--paper); }

/* ── PLATFORM SECTION TILES ────────────────────────────────────────────────
   A fixed rail of section tiles on the left, and the open section in the
   pane beside it. The rail is the same width, in the same place, with the
   same tiles in the same order, whether a section is open or not.

   THAT IS THE WHOLE POINT, and it is why this is not a grid. The first
   version put the open section into the grid as a full-width cell, which
   reads well in a screenshot and uses badly: the cell pushed every tile
   after it to a new position, so opening Tenants moved Users and API
   tokens, and the second click was never where the first one taught you it
   would be. A control that moves when you use it has to be re-found every
   time. Reflow is a cost paid by the person clicking, not by the layout.

   Colour comes entirely from tokens, so the dark block at the top of this
   file themes all of this with no rules of its own. */
.admin-split {
  display: grid;
  grid-template-columns: 264px minmax(0, 1fr);
  gap: calc(var(--base) * 8);
  align-items: start;
  margin-top: calc(var(--base) * 10);
}

/* The rail. A flex column rather than a grid so a tile is exactly as tall as
   its own content and the rail does not grow a row of equal-height cells. */
.tiles { display: flex; flex-direction: column; gap: calc(var(--base) * 3); }

.tile {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 0 calc(var(--base) * 3);
  padding: calc(var(--base) * 4) calc(var(--base) * 5);
  border: 1px solid var(--hairline);
  border-left: 3px solid transparent;
  border-radius: 8px;
  background: var(--surface);
  color: inherit;
  text-decoration: none;
  /* No transform. The hover lift was the same complaint one notch smaller:
     the thing you are aiming at moved while you aimed at it. Colour changes
     do not move anything. */
  transition: border-color .12s ease, background .12s ease;
}
.tile .tile-title { font: var(--t-h3) var(--font); letter-spacing: var(--tr-h3); }
/* Tabular figures so the counts line up down the rail — sheet 02's answer
   for numbers in a column, which is exactly what this is. */
.tile .tile-count {
  font: var(--t-h3) var(--font); letter-spacing: var(--tr-h3);
  font-variant-numeric: tabular-nums; color: var(--slate);
}
.tile .tile-blurb {
  grid-column: 1 / -1;
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
  color: var(--slate); margin: var(--s1) 0 0;
}

a.tile:hover { border-color: var(--hairline); background: var(--surface-raised); }
a.tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The open one. The accent lives on the left edge, which is already 3px of
   transparent border on every tile, so marking a tile current changes its
   colour and not its box: no reflow, and the rail cannot jitter by a pixel
   as you move down it. */
.tile-current, a.tile-current:hover {
  border-left-color: var(--accent);
  background: var(--surface-raised);
}

/* Not built yet, and saying so is the point of drawing it at all. A dashed
   border needs more weight than a solid one to read as the same line — the
   gaps eat about half of it — which is why this uses the stronger hairline. */
.tile-stub {
  border-style: dashed; border-left-color: var(--hairline-strong);
  border-color: var(--hairline-strong); background: transparent;
}

/* The pane. min-width:0 on the grid column above is what stops a wide table
   here from forcing the rail narrower — without it the 264px is a request. */
.pane { min-width: 0; }
.pane-head { margin-bottom: calc(var(--base) * 5); }
.pane-head h2 { font: var(--t-h2) var(--font); letter-spacing: var(--tr-h2); margin: 0; }
.pane-head .tile-blurb { font: var(--t-small) var(--font); letter-spacing: var(--tr-small); color: var(--slate); margin: var(--s2) 0 0; }
.pane-empty { color: var(--slate); }
.pane table { margin: 0; }
/* Technical strings — bucket keys, hashes, paths — are long and unspaced, and
   the default table algorithm sizes a column to its widest UNBREAKABLE token.
   Measured at 390px on /admin/ai-spend: the <table> box reported a
   well-behaved 342px while its rows ran to 850 and pushed the whole page
   sideways. The table looks innocent to anything that measures the <table>
   element, so this was only visible by measuring every row. */
.pane td.mono { overflow-wrap: anywhere; }
.pane .tile-actions { display: flex; justify-content: flex-end; margin: 0 0 calc(var(--base) * 3); }
/* The pane is narrower than the old full-width grid cell, so button labels
   that never wrapped before now can. A control that reflows to two lines
   changes its own hit area as the table grows. */
.pane .btn, .pane button { white-space: nowrap; }
/* A table wider than the pane scrolls inside the pane. The alternative is the
   whole page growing a horizontal scrollbar, which moves the rail off-screen
   — the one thing this layout exists to prevent. */
.pane .table-wrap, .pane > table { display: block; overflow-x: auto; }

/* One breakpoint, at the width where 264 + 24 + a usable pane stops fitting.
   The rail goes back to a row of tiles above the pane; it still does not move
   when you open one, because the pane is still below it either way. */
@media (max-width: 880px) {
  .admin-split { grid-template-columns: minmax(0, 1fr); }
  .tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
}

@media (prefers-reduced-motion: reduce) {
  .tile { transition: none; }
}

/* ── PLATFORM ADMIN: SEARCH, COUNT, PAGER ─────────────────────────────────
   Added 2026-09-19 for "tenants, users and other tiles should have a search
   function when activated — now there are a few, there could be hundreds."

   Everything here is tokens only, so the dark block at the top of this file
   themes it with no rules of its own. Two of tonight's three CSS defects came
   from rules that declared their own colour and from a theme attribute on the
   wrong element; the cheapest defence against both is to declare neither. */

/* Visually hidden, still read aloud. The search input has a placeholder and
   no visible label, and a placeholder is not a label — it disappears the
   moment you type, which is precisely when you would want to re-read it. */
.vh {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; border: 0; overflow: hidden; white-space: nowrap;
  clip-path: inset(50%);
}

.pane-search {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: calc(var(--base) * 2);
  margin: calc(var(--base) * 4) 0 0;
}
/* The input takes the slack and the buttons keep their size, so the row does
   not re-lay-out when the Clear button appears and disappears. Without the
   min-width the flex item can be squeezed below a usable width on the
   narrow-pane breakpoint instead of wrapping. */
.pane-search input[type=search] {
  flex: 1 1 14rem; min-width: 12rem;
  padding: .5rem .75rem; border-radius: 6px;
  font: var(--t-small) var(--font); letter-spacing: var(--tr-small);
}
/* Safari draws a decoration inside type=search that sits on top of the text
   and does not inherit the theme. The field is a plain box in every browser
   or it is a different control in one of them. */
.pane-search input[type=search]::-webkit-search-decoration,
.pane-search input[type=search]::-webkit-search-cancel-button {
  -webkit-appearance: none; appearance: none;
}

/* How many rows, and out of how many. Sits between the search and the table
   because that is the reading order of the question it answers. */
.pane-count {
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
  color: var(--slate);
  margin: calc(var(--base) * 3) 0 0;
  font-variant-numeric: tabular-nums;
}

.pager {
  display: flex; align-items: center; justify-content: flex-end;
  gap: calc(var(--base) * 3);
  margin: calc(var(--base) * 4) 0 0;
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
}
.pager-where { color: var(--slate); font-variant-numeric: tabular-nums; }
.pager-btn {
  padding: .4rem .8rem; border-radius: 6px;
  border: 1px solid var(--hairline);
  color: inherit; text-decoration: none; white-space: nowrap;
}
a.pager-btn:hover { background: var(--surface-raised); }
a.pager-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* An end of the list, not a disabled control you should keep trying. Border
   removed rather than dimmed: a greyed-out box still reads as a button. */
.pager-btn.is-off { color: var(--slate); border-color: transparent; }

/* A stub tile has no count — it has a state. Sharing .tile-count's grid cell
   keeps the title baseline identical to a real tile's, so the rail does not
   develop two kinds of row. */
.tile-soon {
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
  text-transform: uppercase; color: var(--slate);
}
/* What it is waiting for. Dimmer than the blurb, because the blurb says what
   the section will be and this says why it is not that yet. */
.tile-stub-note {
  grid-column: 1 / -1;
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
  color: color-mix(in srgb, var(--slate) 75%, var(--ground) 25%);
  margin: var(--s1) 0 0;
}

/* ── PLATFORM ADMIN: AI-SPEND CHARTS ──────────────────────────────────────
   Added 2026-09-20 for "the cost dashboard should have piecharts that are
   clickable ... clicking on a pie opens up that data section."

   Tokens only, like the block above it, so the dark theme at the top of this
   file themes the charts with no rules of its own.

   THE SLICES ARE ONE HUE ON PURPOSE. Sheet 04: "Don't introduce a second
   accent for semantics." A categorical chart palette is the largest version
   of that mistake — eight invented colours, each implying a meaning it does
   not carry. Slices are one ramp of the accent ordered by size, so position
   in the ramp encodes rank and nothing else, and what separates two adjacent
   slices is the hairline between them rather than a colour difference. That
   also survives the dark theme, which a hand-picked categorical set would
   not: every slice is the same token at a different opacity. */

/* auto-FILL, not auto-fit. The difference only shows once the pane is wider
   than the cards need — which is exactly what .wrap-wide just made possible.
   auto-fit COLLAPSES the empty tracks, so two cards in a 2000px pane become
   two 1000px cards each holding an 11rem pie adrift in whitespace. auto-fill
   keeps the empty tracks, so the cards stay a readable width and the spare
   room stays spare. Identical behaviour at the old 1128px, where the tracks
   were full anyway. */
/* ── AI SPEND: TOTALS, SCOPE, COVERAGE ───────────────────────────────────
   Added 2026-09-20 06:10. These classes were in the markup from the start and
   had NO rules at all — the totals rendered as a run of plain text, which is
   part of how USD 3.77 came to be read as an org figure: nothing about it
   looked like a headline number with a scope attached.

   Tokens only, so the dark block at the top of this file themes all of it. */

.spend-summary {
  display: flex; flex-wrap: wrap;
  gap: calc(var(--base) * 3);
  margin: calc(var(--base) * 4) 0 0;
}
.spend-card {
  display: flex; flex-direction: column; gap: var(--s1);
  border: 1px solid var(--hairline); border-radius: 8px;
  padding: calc(var(--base) * 3) calc(var(--base) * 4);
  min-width: 13rem;
}
.spend-provider {
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
  text-transform: uppercase; color: var(--slate);
}
/* Tabular figures: these sit one above another across cards and the decimal
   points have to line up — sheet 02's answer for numbers in a column. */
.spend-amount { font: var(--t-h2) var(--font); letter-spacing: var(--tr-h2); font-variant-numeric: tabular-nums; }
.spend-meta { font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption); color: var(--slate); }

/* THE SCOPE BAR. The selected scope and the OTHER scope's total, side by side
   and always both present. A toggle on its own would not have prevented the
   misreading: you have to already suspect a number to go looking for the
   control that would have corrected it. The contrast figure is the fix; the
   button is a convenience. */
.spend-scopebar {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: calc(var(--base) * 3);
  margin: calc(var(--base) * 4) 0 0;
  padding: calc(var(--base) * 2) calc(var(--base) * 3);
  border: 1px solid var(--hairline); border-radius: 8px;
  background: var(--surface);
}
.spend-scope-now {
  font: var(--t-small) var(--font); font-weight: 600;
  /* The accent marks which question the page is answering — the one thing on
     this bar you must not have to read twice. */
  border-left: 3px solid var(--accent); padding-left: calc(var(--base) * 2);
}
.spend-scope-other {
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
  color: var(--slate); font-variant-numeric: tabular-nums;
}
.spend-scopebar .btn { margin-left: auto; }

.spend-scope, .spend-coverage {
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
  max-width: var(--measure); margin: calc(var(--base) * 3) 0 0;
}
/* A gap inside the stored range is a fact about the data, not an error state,
   so it is marked and NOT coloured as a danger. It is also not hidden, which
   is the only other option and the wrong one: an absent day that says nothing
   is indistinguishable from a day that cost nothing. */
.spend-gap { color: var(--figure); border-bottom: 1px dashed var(--accent); }

.spend-charts {
  display: grid;
  /* auto-FIT with no max-width: the charts fill the pane exactly like the
     tables above and below them.

     This was auto-fill with `max-width: 72rem` for about twenty minutes. I
     capped it on the judgement that two pies get no better past 72rem — true
     as far as it goes, and not my call to make silently. Seb asked for the
     platform section to use the whole width and then had to say so a second
     time, because the charts stopped at 1152px inside a 2216px pane while
     everything around them ran full width. An instruction applied to most of
     a page reads as a bug, not as taste.

     The fit/fill distinction is the whole mechanism. auto-FILL keeps empty
     tracks, so at 2216px it laid five 430px columns and the two cards
     occupied the first two — narrow cards, dead space, and (measured) too
     narrow to trigger the 30rem container query below, so the pie/legend
     never went side by side either. auto-FIT collapses the empty tracks and
     hands their space to the real cards: two cards, ~1100px each, query
     fires, pie stays 14rem and the legend takes the rest. The 26rem floor
     still matters at narrow widths, where min(100%, …) drops it to one
     column. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr));
  gap: calc(var(--base) * 4);
  margin: calc(var(--base) * 5) 0;
}

.chart-card {
  border: 1px solid var(--hairline);
  border-radius: 8px;
  padding: calc(var(--base) * 4);
  min-width: 0;
}

.chart-head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: calc(var(--base) * 2);
  color: inherit; text-decoration: none;
}
a.chart-head:hover .chart-title { text-decoration: underline; }
a.chart-head:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.chart-title { font: var(--t-h3) var(--font); }
/* The +/- is the affordance, so it must not look like a decoration. It sits
   in the same cell whether the card is open or closed, or the heading moves
   under the cursor between the click and the next one. */
.chart-toggle {
  font-variant-numeric: tabular-nums; color: var(--slate);
  min-width: 1.2em; text-align: right;
}
/* A dimension with no data behind it has no count — it has a state. Same
   treatment as .tile-soon, because it is the same kind of thing. */
.chart-soon {
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
  text-transform: uppercase; color: var(--slate);
}
.chart-blurb {
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
  color: var(--slate); margin: var(--s1) 0 0;
}
/* Why the chart beside it is empty. Dimmer than the blurb, because the blurb
   says what the chart would show and this says why it does not yet. */
.chart-unsourced {
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
  color: color-mix(in srgb, var(--slate) 75%, var(--ground) 25%);
  margin: calc(var(--base) * 3) 0 0;
}

.pie-figure { margin: calc(var(--base) * 4) 0 0; }
.pie-caption {
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
  font-variant-numeric: tabular-nums;
  margin-bottom: calc(var(--base) * 2);
}
.pie { display: block; width: 100%; max-width: 11rem; height: auto; margin: 0 auto; }

/* Once the card is wide enough, the legend sits BESIDE the pie instead of
   under it. Added with .wrap-wide (Seb, 2026-09-20): full width only pays
   off if something uses it, and the alternative was two 380px cards adrift
   in 1576px of pane. The pie stops growing at 14rem — past that it is a
   bigger circle carrying the same information, and the legend is the part
   that gains from the room, because a model id like
   Qwen/Qwen3.5-397B-A17B-FP8 stops wrapping mid-token.

   Keyed off the CARD, not the viewport: @container asks the question the
   layout actually has ("is this card wide?"), where a media query asks one
   about the window that is merely correlated with it — and the 264px rail
   beside the pane is exactly what makes the two disagree. Baseline since
   2023; a browser without it keeps the stacked layout, which is the
   current working one.

   THIS BLOCK MUST STAY BELOW `.pie` ABOVE. Same specificity, so the later
   rule wins: written above it, `max-width: 14rem` loses to the 11rem and
   the pie silently never grows — a container query that fires correctly and
   changes nothing you can see. Measured: 176px at every width. */
.chart-card { container-type: inline-size; }

@container (min-width: 30rem) {
  .pie-figure {
    display: grid;
    grid-template-columns: minmax(0, auto) minmax(0, 1fr);
    gap: calc(var(--base) * 5);
    align-items: start;
  }
  .pie-caption { grid-column: 1 / -1; }
  .pie { max-width: 14rem; margin: 0; }
  .pie-legend { margin-top: 0; }
}

/* stroke is the ground colour, so the gap between slices is the page showing
   through rather than a line drawn in some other colour. At 1.2 it separates
   adjacent steps of the ramp without eating a thin slice. */
.slice {
  fill: var(--accent);
  fill-opacity: var(--slice-a);
  stroke: var(--ground);
  stroke-width: 1.2;
}
.pie a:hover .slice,
.pie a:focus-visible .slice { fill-opacity: 1; stroke-width: 2; }
.pie a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The legend is the readable copy of the chart, not a key beside it. A slice
   worth 0.4% is a sliver nobody can click; this row is the hit area that
   always works, and it carries the figure the slice can only imply. */
.pie-legend { list-style: none; margin: calc(var(--base) * 3) 0 0; padding: 0; }
.pie-legend li + li { margin-top: var(--s1); }
.pie-legend a, .pie-legend .legend-flat {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: baseline;
  gap: calc(var(--base) * 2);
  padding: .15rem .3rem; border-radius: 4px;
  color: inherit; text-decoration: none;
}
.pie-legend a:hover { background: var(--surface-raised); }
.pie-legend a:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.legend-swatch {
  width: .7rem; height: .7rem; border-radius: 2px;
  background: var(--accent); opacity: var(--slice-a);
  align-self: center;
}
/* Model ids are long, unspaced and full of slashes — overflow-wrap:anywhere
   is what stops Qwen/Qwen3.5-397B-A17B-FP8 from pushing the figures off the
   card instead of wrapping. */
.legend-label {
  min-width: 0; overflow-wrap: anywhere;
  font: var(--t-small) var(--font); letter-spacing: var(--tr-small);
}
.legend-share, .legend-amount {
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
  color: var(--slate); font-variant-numeric: tabular-nums;
}

/* table-layout: fixed, because the model column holds long unspaced ids and
   the default algorithm sizes columns to their widest unbreakable token.
   Measured at 390px: the table BOX reported a well-behaved 330px while its
   rows ran to 850 and pushed the page sideways — the table itself looks
   innocent in any check that measures the <table>, which is why this needed
   measuring per row rather than per element. */
.chart-data { margin: calc(var(--base) * 4) 0 0; width: 100%; table-layout: fixed; }
.chart-data td, .chart-data th { font: var(--t-caption) var(--font); }
.chart-data td:first-child { overflow-wrap: anywhere; }
/* The three figure columns are as wide as their content and no wider, so the
   model name gets whatever is left rather than the four columns splitting
   the width evenly — which is what table-layout: fixed does by default. */
.chart-data th:not(:first-child), .chart-data td:not(:first-child) { width: 5.5rem; }

/* An active chart filter, removable one at a time. With two narrowings on,
   what you want is almost always to drop one and keep looking — a single
   "clear all" is a different control answering a different question. */
.pane-filters {
  display: flex; flex-wrap: wrap;
  gap: calc(var(--base) * 2);
  margin: calc(var(--base) * 3) 0 0;
}
.filter-chip {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .25rem .65rem; border-radius: 999px;
  border: 1px solid var(--hairline);
  font: var(--t-caption) var(--font); letter-spacing: var(--tr-caption);
  color: inherit; text-decoration: none;
}
.filter-chip:hover { background: var(--surface-raised); }
.filter-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A group heading in the admin tile rail ("Users" over Platform users and
   Tenant users). Full row in the narrow-screen grid so it sits above its
   tiles instead of taking a tile's cell. */
.tiles-group { margin: calc(var(--base) * 2) 0 calc(var(--base) * -1); grid-column: 1 / -1; }

/* Infrastructure (2026-09-27). Three states a row can be in besides fine:
   not reporting (danger), a collector disagreeing with a correction (accent),
   and corrected by hand (a left rule on the value, so a corrected value reads
   differently from a collected one without needing a legend). */
.pill.bad  { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, transparent); }
.pill.warn { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
/* /admin/infrastructure, redrawn 2026-10-11. Seb: "looks very messy ... doesn't
   read well". The page now answers in order: is anything wrong (status box),
   how it fits together (an HTML diagram: real text that wraps and follows the
   theme; the SVG one drew white chips with theme-coloured text, which went
   white-on-white in dark mode), then everything by group. Four tones only:
   ok = --verified, bad = --danger, warn = --accent, manual = a quiet dashed
   ring. "Entered by hand" is not a fault, so it no longer wears orange. */
.ix { --ix-ok: var(--verified); --ix-bad: var(--danger); --ix-warn: var(--accent);
      --ix-line: color-mix(in srgb, var(--figure) 14%, transparent);
      --ix-card: color-mix(in srgb, var(--figure) 4%, var(--ground)); }
.ix-lede { max-width: 70ch; font-size: 1rem; }
.ix-h2 { margin: 2.6rem 0 .3rem; }
.ix-sub { max-width: 80ch; margin: 0 0 1rem; }
.ix-dot { display: inline-block; width: .7rem; height: .7rem; border-radius: 50%; flex: none;
          background: var(--ix-ok); vertical-align: 1px; }
.ix-dot.bad { background: var(--ix-bad); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ix-bad) 25%, transparent); }
.ix-dot.warn { background: var(--ix-warn); }
.ix-dot.manual, .ix-dot.empty { background: transparent; border: 1.5px dashed var(--slate); }
.ix-tone { font-size: .82rem; font-weight: 600; color: var(--ix-ok); white-space: nowrap; }
.ix-tone.bad { color: var(--ix-bad); } .ix-tone.warn { color: var(--ix-warn); }
.ix-tone.manual, .ix-tone.empty { color: var(--slate); font-weight: 500; }

/* 1. status */
.ix-status { margin: 1.4rem 0 0; padding: 1rem 1.2rem; border-radius: 12px; border: 1px solid var(--ix-line);
             border-left: 4px solid var(--ix-ok); background: var(--ix-card); max-width: 980px; }
.ix-status-bad { border-left-color: var(--ix-bad); }
.ix-status-head { display: flex; align-items: center; gap: .7rem; font-size: 1.1rem; }
.ix-problems { margin: .7rem 0 0; padding: 0 0 0 1.4rem; display: grid; gap: .35rem; }
.ix-problems li { display: flex; flex-wrap: wrap; gap: .3rem .8rem; align-items: baseline; }
.ix-problems a { font-weight: 600; }
.ix-status-foot { margin: .7rem 0 0; font-size: .85rem; }

/* 2. diagram */
.ixd { display: grid; grid-template-columns: minmax(0, 1fr) 180px minmax(0, 1fr);
       grid-template-areas: "a1 . c1" "a2 . c2" "a3 b3 c3" "a4 . c4" "a5 b5 c5";
       column-gap: 0; row-gap: 0; margin: 1rem 0 0; max-width: 1360px; }
.ixd-a1 { grid-area: a1; } .ixd-c1 { grid-area: c1; } .ixd-a2 { grid-area: a2; } .ixd-c2 { grid-area: c2; }
.ixd-a3 { grid-area: a3; } .ixd-b3 { grid-area: b3; } .ixd-c3 { grid-area: c3; }
.ixd-a4 { grid-area: a4; } .ixd-c4 { grid-area: c4; } .ixd-a5 { grid-area: a5; } .ixd-b5 { grid-area: b5; } .ixd-c5 { grid-area: c5; }
.ixd-ext { justify-self: center; width: min(100%, 300px); text-align: center; padding: .7rem 1rem; border-radius: 10px;
           border: 1px solid var(--ix-line); background: var(--ix-card); display: grid; gap: .15rem; }
.ixd-ext strong { font-size: .95rem; } .ixd-ext span { font-size: .8rem; color: var(--slate); }
.ixd-b5 { width: min(100%, 220px); align-self: start; }
/* vertical connectors: a line with an arrowhead, label beside it */
.ixd-v { position: relative; height: 64px; display: flex; justify-content: center; }
.ixd-line { position: relative; width: 2px; height: 100%; background: var(--slate); }
.ixd-line::after, .ixd-line.both::before { content: ""; position: absolute; left: 50%; transform: translateX(-50%);
           border: 6px solid transparent; }
.ixd-line.down::after, .ixd-line.both::after { bottom: -2px; border-top: 8px solid var(--slate); border-bottom: 0; }
.ixd-line.up::after { top: -2px; border-bottom: 8px solid var(--slate); border-top: 0; }
.ixd-line.both::before { top: -2px; border-bottom: 8px solid var(--slate); border-top: 0; }
.ixd-lbl { position: absolute; left: calc(50% + 12px); top: 50%; transform: translateY(-50%);
           font-size: .78rem; line-height: 1.35; color: var(--slate); white-space: nowrap; }
/* the planes */
.ixd-plane { border: 1.5px solid var(--hairline-strong); border-radius: 14px; padding: 1.1rem 1.1rem 1rem;
             background: var(--ix-card); display: flex; flex-direction: column; gap: .9rem; }
.ixd-plane-nuc { border-color: color-mix(in srgb, var(--ix-ok) 55%, transparent); }
.ixd-plane header .eyebrow { margin: 0; }
.ixd-plane h3 { margin: .15rem 0 .2rem; font: var(--t-h2) var(--font); letter-spacing: var(--tr-h2); }
.ixd-plane h3 a { color: inherit; text-decoration: none; }
.ixd-tagline { margin: 0; font-size: .9rem; }
.ixd-host { margin: .5rem 0 0; font-size: .75rem; color: var(--slate); }
.ixd-disk { display: grid; grid-template-columns: 7.5rem 1fr 7rem; align-items: center; gap: .6rem; margin-top: .35rem; font-size: .75rem; }
.ixd-bar { height: 6px; border-radius: 3px; background: var(--ix-line); overflow: hidden; }
.ixd-fill { display: block; height: 100%; background: var(--ix-ok); }
.ixd-fill.warn { background: var(--ix-warn); } .ixd-fill.bad { background: var(--ix-bad); }
.ixd-tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .6rem; }
.ixd-tile { display: grid; gap: .2rem; padding: .7rem .8rem; border-radius: 10px; text-decoration: none; color: var(--figure);
            background: var(--ground); border: 1px solid var(--ix-line); transition: border-color .15s; }
.ixd-tile:hover { border-color: var(--figure); }
.ixd-tile.bad { border-color: var(--ix-bad); border-width: 1.5px; }
.ixd-tile.warn { border-color: var(--ix-warn); }
.ixd-tile.manual, .ixd-tile.empty { border-style: dashed; }
.ixd-tile-top { display: flex; align-items: center; gap: .5rem; font-size: .95rem; }
.ixd-tile-st { font-size: .8rem; font-weight: 600; color: var(--ix-ok); }
.ixd-tile.bad .ixd-tile-st { color: var(--ix-bad); } .ixd-tile.warn .ixd-tile-st { color: var(--ix-warn); }
.ixd-tile.manual .ixd-tile-st, .ixd-tile.empty .ixd-tile-st { color: var(--slate); font-weight: 500; }
.ixd-tile-pv { font-size: .76rem; color: var(--slate); line-height: 1.35; }
.ixd-note { margin: 0; font-size: .8rem; color: var(--slate); }
/* the crossing */
.ixd-pull { align-self: center; display: grid; justify-items: center; gap: .4rem; padding: 0 .6rem; text-align: center; }
.ixd-pull-t { font-weight: 700; font-size: .85rem; letter-spacing: .04em; color: var(--ix-warn); text-transform: uppercase; }
.ixd-pull-s { font-size: .76rem; line-height: 1.4; color: var(--slate); }
.ixd-arrow-left { position: relative; width: calc(100% + 1.2rem); height: 3px; background: var(--ix-warn); }
.ixd-arrow-left::before { content: ""; position: absolute; left: -2px; top: 50%; transform: translateY(-50%);
                          border: 8px solid transparent; border-right: 12px solid var(--ix-warn); border-left: 0; }
.ixd-legend { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: .4rem 1.4rem; margin-top: 1.2rem; font-size: .82rem; align-items: center; }
.ixd-legend > span { display: inline-flex; align-items: center; gap: .4rem; }
@media (max-width: 960px) {
  .ixd { grid-template-columns: 1fr; grid-template-areas: "a1" "a2" "a3" "b3" "c3" "c2" "c1" "a4" "a5" "b5" "c4" "c5"; row-gap: .2rem; }
  .ixd-pull { padding: 1rem 0; } .ixd-arrow-left { width: 3px; height: 40px; }
  .ixd-arrow-left::before { left: 50%; top: -2px; transform: translateX(-50%); border: 8px solid transparent; border-bottom: 12px solid var(--ix-warn); border-top: 0; }
  .ixd-c2 .ixd-line.up::after { top: auto; bottom: -2px; border-top: 8px solid var(--slate); border-bottom: 0; }
  .ixd-tiles { grid-template-columns: 1fr; }
}

/* 3. inventory */
.ix-plane { margin-top: 2rem; }
.ix-plane-head h3 { margin: .1rem 0 .2rem; font: var(--t-h2) var(--font); }
.ix-plane-head p { margin: 0; font-size: .88rem; }
.ix-host { font-size: .8rem; font-weight: 400; margin-left: .5rem; }
.ix-groups { display: grid; grid-template-columns: repeat(auto-fill, minmax(520px, 1fr)); gap: 1rem; margin-top: 1rem; align-items: start; }
.ix-group { border: 1px solid var(--ix-line); border-radius: 12px; background: var(--ix-card); overflow: hidden; }
.ix-group:target { outline: 2px solid var(--ix-warn); outline-offset: 2px; }
.ix-group > header { display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: .1rem 1rem;
                     padding: .85rem 1rem .7rem; border-bottom: 1px solid var(--ix-line); }
.ix-group h4 { margin: 0; font: var(--t-h3) var(--font); }
.ix-group > header p { grid-column: 1 / -1; margin: 0; font-size: .8rem; }
.ix-count { font-size: .78rem; font-weight: 500; color: var(--slate); margin-left: .3rem; }
.ix-empty { padding: .8rem 1rem; margin: 0; font-size: .85rem; }
.ix-rows { list-style: none; margin: 0; padding: 0; }
.ix-row { display: grid; grid-template-columns: auto 1fr auto; gap: .2rem .8rem; padding: .65rem 1rem; align-items: start;
          border-top: 1px solid color-mix(in srgb, var(--figure) 6%, transparent); }
.ix-row:first-child { border-top: 0; }
.ix-row > .ix-dot { margin-top: .4rem; }
/* A row with nothing to say but its name and status (most web addresses) takes one line. */
.ix-row.ix-compact { padding-top: .4rem; padding-bottom: .4rem; align-items: center; }
.ix-row.ix-compact > .ix-dot { margin-top: 0; }
.ix-row.ix-compact .ix-state { grid-auto-flow: column; gap: .8rem; align-items: baseline; }
.ix-row:target { background: color-mix(in srgb, var(--ix-warn) 10%, transparent); }
.ix-row.ix-bad { background: color-mix(in srgb, var(--ix-bad) 7%, transparent); }
.ix-name { font-weight: 600; font-size: .93rem; line-height: 1.35; }
.ix-tag { display: inline-block; font-size: .7rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
          color: var(--slate); border: 1px solid var(--ix-line); border-radius: 4px; padding: 0 .35rem; margin-right: .35rem; vertical-align: 1px; }
.ix-about { font-size: .84rem; color: var(--slate); margin-top: .1rem; }
.ix-meta { display: flex; flex-wrap: wrap; gap: .1rem .9rem; margin-top: .25rem; font-size: .75rem; }
.ix-meta .mono { color: var(--slate); }
.ix-fact.corrected { border-left: 2px solid var(--ix-warn); padding-left: .35rem; }
.ix-prov { font-size: .75rem; margin-top: .25rem; }
.ix-disagree { color: var(--ix-bad); font-size: .8rem; }
.ix-state { display: grid; justify-items: end; gap: .1rem; text-align: right; font-size: .75rem; }
.ix-fix summary { cursor: pointer; color: var(--slate); font-size: .75rem; list-style: none; }
.ix-fix summary::-webkit-details-marker { display: none; }
.ix-fix summary:hover { color: var(--figure); text-decoration: underline; }
.ix-fix[open] { grid-column: 1 / -1; }
.ix-fix form, .ix-add { display: grid; gap: .35rem; min-width: 300px; margin-top: .5rem; text-align: left; }
.ix-fix textarea, .ix-add textarea, .ix-fix select, .ix-fix input, .ix-add input {
  width: 100%; padding: .5rem .6rem; font: 400 .85rem var(--font); color: var(--figure);
  background: var(--ground); border: 1px solid var(--hairline-strong); border-radius: 6px; }
.ix-fix textarea, .ix-add textarea { font-family: var(--mono); font-size: .78rem; }
.ix-add-row { display: grid; grid-template-columns: 1fr 1fr; gap: .8rem; }
.ix-plane-foot { display: flex; flex-wrap: wrap; gap: .5rem 2rem; margin-top: .9rem; }
.ix-more summary { cursor: pointer; color: var(--slate); font-size: .88rem; }
.ix-more summary:hover { color: var(--figure); }
.ix-history { margin-top: 2.6rem; }
@media (max-width: 640px) {
  .ix-groups { grid-template-columns: 1fr; }
  .ix-row { grid-template-columns: auto 1fr; }
  .ix-state { grid-column: 2; justify-items: start; text-align: left; }
}

/* Align › Our company (2026-10-06). Its forms use textareas, selects and
   number/date inputs, which the global input rules above never reach: the
   page rendered browser-white boxes on the dark ground. Scoped to the form
   class so no other page's controls change. */
.co-form textarea, .co-form select, .co-form input[type=number], .co-form input[type=date],
.co-form input:not([type]) {
  width: 100%; padding: .7rem .8rem; font: 400 .95rem var(--font);
  color: var(--figure); background: color-mix(in srgb, var(--ground) 88%, var(--figure) 12%);
  border: 1px solid color-mix(in srgb, var(--ground) 70%, var(--figure) 30%); border-radius: 8px;
}
.co-form textarea { resize: vertical; min-height: 4.5rem; }
.co-form textarea:focus, .co-form select:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.co-form .co-row { display: grid; grid-template-columns: 5rem repeat(3, 1fr); gap: .5rem; }
.co-form .co-row-2 { grid-template-columns: 5rem 1fr; }
.co-form .co-check { display: flex; gap: .5rem; align-items: center; font-size: .9rem; color: var(--figure); }
.co-form fieldset:disabled textarea, .co-form fieldset:disabled input { opacity: .75; }

/* Account tabs (2026-10-08). One bar under the account header on Overview,
   Fit, Brief and Concierge, replacing the three "→" links that each opened a
   separate page. The current tab is marked the way .pl-head marks its nav:
   a 2px accent underline. The underline sits on a border that every tab
   already has (transparent), so switching tabs moves nothing. On a narrow
   screen the bar scrolls sideways instead of wrapping onto two lines. */
.acct-tabs {
  display: flex; gap: var(--s6);
  margin: var(--s6) 0 var(--s6);
  border-bottom: 1px solid var(--hairline);
  overflow-x: auto; scrollbar-width: none;
}
.acct-tabs a {
  flex: none; padding: var(--s2) 0;
  margin-bottom: -1px; border-bottom: 2px solid transparent;
  color: var(--slate); text-decoration: none; font-weight: 500;
  transition: color .12s ease, border-color .12s ease;
}
.acct-tabs a:hover { color: var(--figure); border-bottom-color: var(--hairline-strong); }
.acct-tabs a.on { color: var(--figure); border-bottom-color: var(--accent); }
.acct-tabs a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Account journey + "Research a new account" wizard (WL-4, Seb 2026-10-10:
   "a wizard like experience ... with proper graphics and status indicators.
   When requirements are not met, info popups explaining").

   States are told apart by ICON AND WORD first and by colour last, because the
   palette has one hue: done = solid figure, running = accent ring, queued and
   waiting = slate, needs attention = --danger edge. The accent is still spent
   once per view: on the step to look at next, and the page's primary button
   stays the only filled orange thing. */
.acct-journey { margin: var(--s6) 0; }
.journey {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s3);
}
.jstep {
  position: relative; display: flex; align-items: stretch;
  border: 1px solid var(--hairline); border-radius: 10px; background: var(--surface);
  transition: border-color .12s ease, background .12s ease;
}
.jstep:hover { border-color: var(--hairline-strong); background: var(--surface-raised); }
.jstep.is-here { border-color: var(--figure); }
.jstep.is-next:not(.is-here) { border-color: var(--accent); }
.jstep-main {
  flex: 1; min-width: 0; display: flex; gap: var(--s3); align-items: flex-start;
  padding: var(--s3) var(--s4); color: var(--figure); text-decoration: none;
}
.jstep-main:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 10px; }
.jstep-ico {
  flex: none; display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%;
  border: 1.5px solid var(--hairline-strong); color: var(--slate);
}
.js-done .jstep-ico { background: var(--figure); border-color: var(--figure); color: var(--ground); }
.js-running .jstep-ico, .js-ready .jstep-ico { border-color: var(--figure); color: var(--figure); }
.js-running .jstep-ico { border-style: dashed; animation: jspin 3s linear infinite; }
.js-running .jstep-ico .ico { animation: jspin 3s linear infinite reverse; }
.js-attention .jstep-ico { border-color: var(--danger); color: var(--danger); }
.js-waiting .jstep-ico, .js-queued .jstep-ico { border-style: dashed; }
@keyframes jspin { to { transform: rotate(360deg); } }
.jstep-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.jstep-label { font: var(--t-h3) var(--font); letter-spacing: var(--tr-h3); }
.jstep-state { display: flex; flex-direction: column; gap: 2px; font: var(--t-small) var(--font); color: var(--slate); }
.jbadge {
  display: inline-flex; align-items: center; gap: 4px; width: fit-content;
  font: var(--t-eyebrow) var(--font); letter-spacing: var(--tr-eyebrow); text-transform: uppercase;
}
.js-done .jbadge { color: var(--figure); }
.js-attention .jbadge { color: var(--danger); }
.jstep-sum { overflow-wrap: anywhere; }

/* The (i) button and its popover. Popover = top layer: no clipping, Esc and
   click-outside close it, no script. */
.info-btn {
  flex: none; align-self: flex-start; margin: var(--s2) var(--s2) 0 0;
  display: grid; place-items: center; width: 32px; height: 32px; padding: 0;
  border: 0; border-radius: 50%; background: transparent; color: var(--slate); cursor: pointer;
}
.info-btn:hover { color: var(--figure); background: var(--surface-raised); }
.info-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.info-inline { display: inline-grid; width: 24px; height: 24px; margin: 0 0 0 4px; vertical-align: middle; }
.infopop {
  max-width: min(420px, calc(100vw - 32px)); padding: var(--s4) var(--s6);
  border: 1px solid var(--hairline-strong); border-radius: 12px;
  background: var(--surface); color: var(--figure);
  box-shadow: 0 12px 40px rgba(0,0,0,.28);
}
.infopop::backdrop { background: rgba(0,0,0,.25); }
.infopop p { margin: 0 0 var(--s3); font: var(--t-small) var(--font); }
.infopop .infopop-h { display: flex; gap: var(--s2); align-items: center; font: var(--t-h3) var(--font); }
.infopop .infopop-act { display: flex; gap: var(--s2); justify-content: flex-end; margin: var(--s4) 0 0; }

/* A build button that would only be refused: shown, disabled, with its reason. */
.btn[disabled], .btn[aria-disabled="true"] {
  background: transparent; color: var(--slate); border-color: var(--hairline-strong);
  border-style: dashed; cursor: not-allowed; filter: none;
}
.gated { display: inline-flex; align-items: center; gap: var(--s1); }
.gated .info-btn { margin: 0; }
.gated-why { font: var(--t-small) var(--font); color: var(--slate); margin: var(--s2) 0 0; }

/* ── Wizard ─────────────────────────────────────────────────────────────── */
.lede { max-width: var(--measure); }
.wizard { max-width: var(--measure); margin-top: var(--s8); }
.wsteps {
  list-style: none; margin: 0 0 var(--s8); padding: 0;
  display: flex; gap: var(--s2); counter-reset: none;
}
.wsteps li {
  flex: 1; display: flex; align-items: center; gap: var(--s2);
  padding-bottom: var(--s3); border-bottom: 2px solid var(--hairline);
  color: var(--slate); font-weight: 500;
}
.wnum {
  display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%;
  border: 1.5px solid var(--hairline-strong); font: 600 13px/1 var(--font);
}
.wsteps li.is-done { color: var(--figure); border-bottom-color: var(--figure); }
.wsteps li.is-done .wnum { background: var(--figure); border-color: var(--figure); color: var(--ground); }
.wsteps li.is-on { color: var(--figure); border-bottom-color: var(--accent); }
.wsteps li.is-on .wnum { border-color: var(--figure); }
.wizard:not(.is-stepped) .wsteps { display: none; }
.wizard:not(.is-stepped) .wpanel + .wpanel { margin-top: var(--s8); }
.wizard:not(.is-stepped) [data-next] { display: none; }
.wpanel {
  border: 1px solid var(--hairline); border-radius: 12px; background: var(--surface);
  padding: var(--s8);
}
.wpanel-h { display: flex; gap: var(--s4); align-items: flex-start; margin-bottom: var(--s6); }
.wpanel-h h2 { margin: 0 0 var(--s1); }
.wpanel-h p { margin: 0; }
.wpanel-ico {
  flex: none; display: grid; place-items: center; width: 56px; height: 56px; border-radius: 14px;
  background: var(--figure); color: var(--ground);
}
.wfield label { display: flex; align-items: center; }
.wcheck { min-height: 1.3em; margin: var(--s1) 0 0; font: var(--t-small) var(--font); color: var(--slate); }
.wfield.is-ok .wcheck { color: var(--figure); }
.wfield.is-ok .wcheck:not(:empty)::before { content: "✓ "; font-weight: 700; }
.wfield.is-bad .wcheck { color: var(--danger); }
.wfield.is-bad input { border-color: var(--danger); }
.wnav { display: flex; justify-content: space-between; gap: var(--s3); margin-top: var(--s8); }
.wnav .btn { display: inline-flex; align-items: center; gap: var(--s2); }

.readiness { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s3); }
.rd {
  display: flex; gap: var(--s3); align-items: flex-start;
  padding: var(--s3) var(--s4); border: 1px solid var(--hairline); border-radius: 10px;
}
.rd-ico {
  flex: none; display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%;
  border: 1.5px dashed var(--hairline-strong); color: var(--slate);
}
.rd.js-done .rd-ico { background: var(--figure); border: 0; color: var(--ground); }
.rd.js-attention .rd-ico { border: 1.5px solid var(--danger); color: var(--danger); }
.rd-text { flex: 1; display: flex; flex-direction: column; gap: 2px; }
.rd .info-btn { margin: 0; }

.wsummary { padding: var(--s4) 0 var(--s6); border-bottom: 1px solid var(--hairline); margin-bottom: var(--s6); }
.wsummary p { margin: 0; }
.wsummary-name { font: var(--t-h2) var(--font); letter-spacing: var(--tr-h2); margin-top: var(--s1) !important; }
.flow {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s4);
}
.flow-step { position: relative; display: flex; flex-direction: column; gap: var(--s1); font-size: .9rem; }
.flow-step:not(:last-child)::after {
  content: ""; position: absolute; top: 26px; left: 60px; right: calc(-1 * var(--s4) + 4px);
  border-top: 1.5px dashed var(--hairline-strong);
}
.flow-ico {
  display: grid; place-items: center; width: 52px; height: 52px; border-radius: 50%;
  border: 1.5px solid var(--figure); margin-bottom: var(--s2);
}
.flow-step.js-waiting .flow-ico { border-style: dashed; border-color: var(--hairline-strong); color: var(--slate); }
.flow-step:first-child .flow-ico { background: var(--figure); color: var(--ground); }

@media (max-width: 880px) {
  .journey { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .journey { grid-template-columns: 1fr; }
  .wpanel { padding: var(--s6) var(--s4); }
  .wsteps li span:last-child { display: none; }
  .wsteps li.is-on span:last-child { display: inline; }
  .flow { grid-template-columns: 1fr; }
  .flow-step { flex-direction: row; flex-wrap: wrap; align-items: center; column-gap: var(--s3); }
  .flow-step .muted { flex-basis: 100%; padding-left: 64px; margin-top: calc(-1 * var(--s3)); }
  .flow-step:not(:last-child)::after { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .js-running .jstep-ico, .js-running .jstep-ico .ico { animation: none; }
}
.btn-icon { display: inline-flex; align-items: center; gap: var(--s2); }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* Phone header (Seb 2026-10-10, with WL-4: "the top menu is too wide on
   phones"). At 390px the menu ran past the screen edge and pushed the page
   sideways. Below 640px the menu moves to its own row under the logo and
   scrolls sideways if it still does not fit; the profile link shows only the
   avatar, its name kept for screen readers. */
@media (max-width: 640px) {
  header.top { flex-wrap: wrap; row-gap: .6rem; padding: .75rem 1rem; }
  header.top nav {
    margin-left: 0; width: 100%; align-items: center; gap: .8rem;
    overflow-x: auto; scrollbar-width: none; white-space: nowrap;
  }
  header.top nav > * { flex: none; }
  header.top nav .navme { margin-left: auto; }
  header.top nav form .btn { padding: .35rem .65rem !important; }
  header.top nav .navme > span:not(.avatar) {
    position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap;
  }
}

/* ── WL-7 account "What we know" (2026-10-10) ───────────────────────────────
   Seb: "scrolling, scrolling ... looks like a data dump". Tabs (each its own
   URL), cards on Overview, quotes folded behind an (i). Tokens only; the accent
   is not spent here (the page's primary action already owns it). */
.know-head { display: flex; justify-content: space-between; align-items: flex-end; gap: var(--s4); margin: var(--s6) 0 var(--s3); flex-wrap: wrap; }
.know-head h2 { margin: 0 0 .25rem; }
.know-head p { margin: 0; }
.know-act { margin: 0; }

.ktabs { display: flex; gap: .25rem; flex-wrap: wrap; border-bottom: 1px solid var(--hairline); margin: 0 0 var(--s6); }
.ktabs a { padding: .6rem .9rem; color: var(--slate); text-decoration: none; border-bottom: 2px solid transparent; margin-bottom: -1px; font-size: .95rem; }
.ktabs a:hover { color: var(--figure); }
.ktabs a.is-here { color: var(--figure); border-bottom-color: var(--figure); font-weight: 600; }
.kcount { display: inline-block; min-width: 1.4em; padding: 0 .35em; border-radius: 999px; background: var(--tint); color: var(--slate); font-size: .78rem; text-align: center; font-variant-numeric: tabular-nums; }

.kgrid { display: grid; gap: var(--s4); grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); align-items: start; }
.kgrid--2 { grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr)); }
.kcard { background: var(--surface); border: 1px solid var(--hairline); border-radius: 10px; padding: var(--s4) var(--s6); }
.kcard--wide { grid-column: 1 / -1; }
.kh { font-size: 1rem; margin: 0 0 var(--s3); display: flex; align-items: center; gap: .5rem; }
.kh a { color: inherit; text-decoration: none; }
.kh a:hover { text-decoration: underline; }
.kcard .eyebrow { margin: var(--s3) 0 .35rem; }

.kglance { display: grid; grid-template-columns: max-content 1fr; gap: .55rem var(--s6); margin: 0; }
.kglance dt { color: var(--slate); font-size: .9rem; }
.kglance dd { margin: 0; }

.kperson { margin: 0 0 .45rem; display: flex; flex-direction: column; }
.kperson .muted { font-size: .88rem; }
.kpeople { display: grid; gap: var(--s3); grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }
.kcard--person { padding: var(--s3) var(--s4); }
.kcard--person p { margin: 0; }
.kname { font-weight: 600; }

.kchips { display: flex; flex-wrap: wrap; gap: .35rem; margin: 0; }
.kchip { padding: .25rem .6rem; border-radius: 999px; border: 1px solid var(--hairline); background: var(--ground); font-size: .88rem; }

.knews { margin: 0 0 .5rem; font-size: .93rem; }
.kdate { color: var(--slate); font-variant-numeric: tabular-nums; font-size: .85rem; min-width: 6.5em; display: inline-block; }

.kblock { margin: 0 0 var(--s6); }
.klist { list-style: none; padding: 0; margin: 0; columns: 2 18rem; column-gap: var(--s6); }
.klist li { break-inside: avoid; padding: .4rem 0; border-bottom: 1px solid var(--hairline); }
.klist--inline { columns: auto; display: flex; flex-wrap: wrap; gap: .35rem; }
.klist--inline li { border: 1px solid var(--hairline); border-radius: 999px; padding: .2rem .6rem; }

.ktimeline { list-style: none; padding: 0; margin: 0; }
.ktimeline li { display: flex; gap: var(--s3); padding: .45rem 0; border-bottom: 1px solid var(--hairline); }

.ksources td { vertical-align: top; }
.ksources .num { text-align: right; font-variant-numeric: tabular-nums; }

/* The source of one fact, folded. */
.ksrc { display: inline-block; position: relative; vertical-align: middle; }
.ksrc > summary { display: inline-flex; gap: .3rem; align-items: center; cursor: pointer; list-style: none; vertical-align: middle; }
.ksrc > summary::-webkit-details-marker { display: none; }
.ksrc-i { display: inline-grid; place-items: center; width: 1.05rem; height: 1.05rem; border-radius: 50%; border: 1px solid var(--hairline-strong); color: var(--slate); font: 600 .68rem/1 var(--font); font-style: italic; }
.ksrc[open] .ksrc-i, .ksrc > summary:hover .ksrc-i { border-color: var(--figure); color: var(--figure); }
.ksrc-i--inline { vertical-align: middle; }
.ksrc-pop { position: absolute; z-index: 5; left: 0; top: 1.5rem; width: min(28rem, 80vw); background: var(--surface-raised); border: 1px solid var(--hairline-strong); border-radius: 8px; padding: .7rem .85rem; font-size: .86rem; box-shadow: 0 6px 24px color-mix(in srgb, var(--ink) 14%, transparent); }
.ksrc-pop q { display: block; margin: 0 0 .4rem; }
.kout { font: var(--t-eyebrow) var(--font); letter-spacing: var(--tr-eyebrow); text-transform: uppercase; color: var(--slate); border: 1px dashed var(--hairline-strong); border-radius: 999px; padding: .1rem .4rem; }
@media (max-width: 640px) { .kglance { grid-template-columns: 1fr; } .kglance dt { margin-top: .4rem; } }
.kmore { font-size: .85rem; margin-top: .3rem; }

/* WL-7 polish (2026-10-11, Seb: "more visually pleasing ... more graphics").
   Monograms instead of logos (no outside logo service: which accounts a tenant
   researches stays inside Winloom), line icons per tab and card, key figures as
   tiles, and two small charts drawn in CSS. Theme tokens only, light and dark. */
.ktabs a { display: inline-flex; align-items: center; gap: .4rem; padding-left: .7rem; padding-right: .7rem; }
.kicon { flex: none; opacity: .85; }
.kh .kicon { color: var(--slate); }
.kmono { --h: 210; display: inline-grid; place-items: center; flex: none; width: 2.1rem; height: 2.1rem; border-radius: 50%;
         background: hsl(var(--h) 42% 40%); color: #fff; font: 700 .78rem/1 var(--font); letter-spacing: .02em; }
.kmono--lg { width: 2.8rem; height: 2.8rem; font-size: .95rem; }
.kmono--xl { width: 4.2rem; height: 4.2rem; font-size: 1.4rem; border-radius: 14px; }
.kmono--sq { border-radius: 8px; width: 2.3rem; height: 2.3rem; }
.ksmall { font-size: .82rem; margin: .5rem 0 0; }
.kmore-link { margin: .6rem 0 0; font-size: .9rem; }

/* hero */
.khero { display: flex; gap: var(--s6); align-items: flex-start; padding: var(--s6); margin: 0 0 var(--s4);
         border: 1px solid var(--hairline); border-radius: 14px;
         background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 9%, var(--surface)), var(--surface) 55%); }
.khero-body { min-width: 0; flex: 1; }
.khero-name { margin: 0 0 .35rem; font: var(--t-h2) var(--font); display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem .8rem; }
.khero-dom { font-size: .85rem; font-weight: 400; color: var(--slate); text-decoration: none; }
.khero-dom:hover { color: var(--figure); }
.khero-lead { font-size: 1.08rem; line-height: 1.5; margin: 0 0 .5rem; max-width: 70ch; }
.khero-sells { margin: 0 0 .8rem; font-size: .93rem; }
.khero-sells-l { display: inline-flex; align-items: center; gap: .3rem; color: var(--slate); margin-right: .35rem; }
.kfacts { display: flex; flex-wrap: wrap; gap: .45rem; }
.kfact { display: inline-flex; align-items: center; gap: .35rem; padding: .3rem .65rem; border-radius: 8px;
         background: var(--ground); border: 1px solid var(--hairline); font-size: .86rem; }
.kfact .kicon { color: var(--slate); }
.kfact-l { color: var(--slate); font-size: .78rem; }

/* key figures */
.ktiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr)); gap: var(--s3); margin: 0 0 var(--s4); }
.ktile { display: grid; grid-template-columns: auto 1fr; grid-template-rows: auto auto; column-gap: .7rem; align-items: center;
         padding: .85rem 1rem; border-radius: 12px; border: 1px solid var(--hairline); background: var(--surface);
         color: var(--figure); text-decoration: none; transition: border-color .15s, transform .15s; }
.ktile:hover { border-color: var(--figure); transform: translateY(-1px); }
.ktile-ic { grid-row: 1 / 3; display: grid; place-items: center; width: 2.4rem; height: 2.4rem; border-radius: 10px;
            background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); }
.ktile-n { font: 700 1.5rem/1.1 var(--font); font-variant-numeric: tabular-nums; }
.ktile-l { color: var(--slate); font-size: .82rem; }
.ktile--zero { opacity: .55; }
.ktile--zero .ktile-ic { background: var(--tint); color: var(--slate); }

/* bar chart: news per year */
.kbars { display: flex; align-items: flex-end; gap: .5rem; height: 9rem; padding: .25rem 0 0; }
.kbars--wide { height: 7rem; margin: 0 0 var(--s4); }
.kbar { flex: 1; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: .25rem; min-width: 0; }
.kbar-b { width: 100%; max-width: 3rem; border-radius: 6px 6px 2px 2px; background: linear-gradient(to top, var(--accent), color-mix(in srgb, var(--accent) 55%, transparent)); }
.kbar-n { font-size: .78rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.kbar-y { font-size: .75rem; color: var(--slate); font-variant-numeric: tabular-nums; }

/* split bar: own site vs outside */
.ksplit { display: flex; height: .9rem; border-radius: 999px; overflow: hidden; background: var(--tint); margin: .4rem 0 .7rem; }
.ksplit--wide { height: .7rem; max-width: 40rem; }
.ksplit-own { background: var(--verified); }
.ksplit-out { background: var(--accent); }
.klegend { display: flex; flex-wrap: wrap; gap: .4rem 1.4rem; margin: 0; font-size: .88rem; }
.klegend > span { display: inline-flex; align-items: center; gap: .35rem; }
.kdot { display: inline-block; width: .7rem; height: .7rem; border-radius: 3px; }
.kdot--own { background: var(--verified); } .kdot--out { background: var(--accent); }

/* people */
.kperson-row { display: flex; align-items: center; gap: .7rem; margin: 0 0 .65rem; }
.kperson-row > span:last-child { display: flex; flex-direction: column; line-height: 1.3; }
.kperson-row .muted { font-size: .85rem; }
.kgroup-h { margin-top: var(--s6); }
.kgroup-h:first-of-type { margin-top: 0; }
.kcard--person { display: flex; gap: .8rem; align-items: flex-start; }

/* customers & partners as logo tiles */
.klogos { display: grid; grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr)); gap: .5rem; }
.klogos--wide { grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr)); }
.klogo { display: flex; flex-direction: column; align-items: center; text-align: center; gap: .3rem; padding: .7rem .5rem;
         border-radius: 10px; background: var(--ground); border: 1px solid var(--hairline); min-width: 0; }
.klogo-n { font-size: .84rem; font-weight: 600; line-height: 1.25; overflow-wrap: anywhere; }
.klogo-k { font-size: .7rem; color: var(--slate); text-transform: uppercase; letter-spacing: .06em; }
.klogo--card { flex-direction: row; text-align: left; padding: .6rem .75rem; gap: .65rem; }

/* timeline with dots */
.kline { list-style: none; margin: 0; padding: 0 0 0 1.1rem; border-left: 2px solid var(--hairline); }
.kline li { position: relative; display: flex; gap: .7rem; padding: .35rem 0 .55rem; font-size: .93rem; }
.kline li::before { content: ""; position: absolute; left: calc(-1.1rem - 6px); top: .7rem; width: 10px; height: 10px; border-radius: 50%;
                    background: var(--ground); border: 2px solid var(--accent); }
.kline .kdate { min-width: 5.5em; }
.kyear { margin: var(--s4) 0 .3rem; font: 600 .95rem var(--font); color: var(--slate); }
.kroles { list-style: none; padding: 0; margin: 0; display: grid; gap: .45rem; }
.kroles li { display: flex; align-items: center; gap: .55rem; padding: .55rem .75rem; border: 1px solid var(--hairline); border-radius: 10px; background: var(--surface); }
.kroles .kicon { color: var(--accent); }

@media (max-width: 640px) {
  .khero { flex-direction: column; padding: var(--s4); }
  .kmono--xl { width: 3.2rem; height: 3.2rem; font-size: 1.1rem; }
}

.korgs { display: grid; grid-template-columns: 1fr 1fr; gap: .45rem; }
.korg { display: flex; align-items: center; gap: .55rem; padding: .45rem .55rem; border-radius: 10px; background: var(--ground); border: 1px solid var(--hairline); min-width: 0; }
.korg > span:last-child { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.korg .klogo-n { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
