/* ════════════════════════════════════════════════════════════════════════
 * Build Informed · bi.css — the complete design system in ONE file.
 *
 * Merged from: colors · typography · structure · base · addendum · app
 * Kept as a single file on purpose: no subfolders means nothing breaks when
 * the files sit together in one directory.
 *
 * Every screen loads exactly this, then app.js.
 * ════════════════════════════════════════════════════════════════════════ */
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600&family=DM+Serif+Display:ital@0;1&display=swap');



/* ══ tokens/colors.css ══════════════════════════════════════════════ */

/* Build Informed — colour tokens (Design Language v2, July 2026).
   Backgrounds are NEVER tinted: the page is always --paper, all text is --ink.
   Tint washes from v1 are RETIRED. */
:root{
--paper:#FCFBF9;--ink:#211F1C;
--ink-mid:#5C564D;--ink-soft:rgba(33,31,28,.5);
--rule:rgba(90,78,45,.22);
--highlight-marigold:#fdf4ae;--underline:rgba(90,78,45,.32);
/* Safety signals. A set apart: these are the only colours in the app that
   carry meaning rather than wayfinding, and they must never be used as a
   module or decoration colour. Green and red carry text (7.7:1 and 8.6:1 on
   paper); amber is swatch-only at 3.5:1, exactly as Marigold is. */
--signal-known:#1F5B45;--signal-estimated:#B07D0C;--signal-stop:#8E1F14;
--on-dark:rgba(252,251,249,.92);--on-dark-mid:rgba(252,251,249,.72);--on-dark-soft:rgba(252,251,249,.55);--on-dark-rule:rgba(252,251,249,.18);
/* Module fills + line (headline) variants */
--m-sh-fill:#E8A104;--m-sh-line:#E8A104;
--m-01-fill:#A73F21;--m-01-line:#A73F21;
--m-nri-fill:#01466D;--m-nri-line:#015788;
--m-02-fill:#8F6525;--m-02-line:#8F6525;
--m-03-fill:#2E3A4A;--m-03-line:#43546B;
--m-04-fill:#600F07;--m-04-line:#93170B;
--m-05-fill:#2D3722;--m-05-line:#51623D;
--m-06-fill:#6F7127;--m-06-line:#6F7127;
--m-07-fill:#7D6A55;--m-07-line:#7D6A55;
--m-08-fill:#4C2F2D;--m-08-line:#734744;
--m-09-fill:#8288BC;--m-09-line:#8288BC;
--m-10-fill:#7A9367;--m-10-line:#7A9367;
--m-vastu-fill:#874b2d;--m-vastu-line:#874b2d;
/* Text on fills: white on the nine body-text-safe colours. SH/09/10 (light-colour rule) NEVER carry text, not ink, not paper; their --on-fill is a fallback only and chips flip to swatch mode. */
--m-sh-on:#211F1C;--m-01-on:#fff;--m-nri-on:#fff;--m-02-on:#fff;--m-03-on:#fff;--m-04-on:#fff;--m-05-on:#fff;--m-06-on:#fff;--m-07-on:#fff;--m-08-on:#fff;--m-09-on:#211F1C;--m-10-on:#211F1C;--m-vastu-on:#fff;
/* Verdict colours (tools: go / hold / stop) */
--verdict-go:#5C7A4A;--verdict-hold:#D9A93D;--verdict-stop:#A64020;
/* Default module scope (01 Terracotta, as in v2 tokens.css) */
--fill:var(--m-01-fill);--line:var(--m-01-line);--on-fill:var(--m-01-on);--label:var(--m-01-fill);--accent:var(--m-01-line) /* @kind color */;--on-dark-fill:#C9784B;--chip-bg:var(--fill);--chip-fg:var(--on-fill);--chip-border:none /* @kind other */;--chip-swatch:none /* @kind other */;--fill-text:var(--fill);--hero-bg:var(--fill);--hero-fg:var(--on-fill);--hero-bar:transparent;
/* Legacy v1 aliases (kept so older cards don't break; prefer v2 names) */
--ink-faint:var(--ink-soft);--ink-ghost:var(--ink-soft);--border-hairline:var(--rule);--border-faint:rgba(90,78,45,.16);--track:rgba(90,78,45,.14);
}
/* Dark-surface context (footer, mobile tab bar) */
[data-surface="dark"]{background:var(--ink);color:var(--on-dark);--rule:var(--on-dark-rule);--ink-mid:var(--on-dark-mid);--ink-soft:var(--on-dark-soft);--fill:var(--on-dark-fill);--line:var(--on-dark-fill)}


/* ══ tokens/typography.css ══════════════════════════════════════════════ */

/* Fonts per Design Language v2: DM Serif Display (display/accent — ONE weight, 400; the serif is NEVER bold) + DM Sans (body, UI, and all labels at weight 500 uppercase wide-tracked). JetBrains Mono is retired; --font-mono resolves to DM Sans. Loaded from Google Fonts (no binaries provided). */
/* font import hoisted to top of file (CSS requires @import first) */
:root{
--font-serif:'DM Serif Display',Georgia,'Times New Roman',serif;
--font-sans:'DM Sans',system-ui,-apple-system,sans-serif;
--font-mono:'DM Sans',system-ui,-apple-system,sans-serif;
--t-display:80px;--t-h2:48px;--t-h3:30px;--t-h4:22px;--t-lede:21px;--t-body:17px;--t-body-sm:15px;--t-meta:14px;--t-label:11px;--t-label-sm:10px;
--lh-tight:1.08; /* @kind other */
--lh-snug:1.25; /* @kind other */
--lh-body:1.65; /* @kind other */
--ls-label:.22em;--ls-tight:-.012em;
--fw-label:500; /* @kind font */
--fw-serif:400; /* @kind font */
/* Legacy v1 aliases */
--text-display:var(--t-display);--text-h1:var(--t-h2);--text-h2:var(--t-h3);--text-h3:var(--t-h4);--text-body:var(--t-body);--text-small:var(--t-body-sm);--text-meta:var(--t-label);--text-micro:var(--t-label-sm);
--leading-tight:var(--lh-tight);--leading-heading:var(--lh-snug);--leading-body:var(--lh-body);
--tracking-label:var(--ls-label);
}


/* ══ tokens/structure.css ══════════════════════════════════════════════ */

/* Spacing, layout, lines, radius, motion — Design Language v2. Square corners everywhere; pills only for actions. */
:root{
--PAD:24px;
--space-1:6px;--space-2:12px;--space-3:18px;--space-4:24px;--space-5:32px;--space-6:48px;--space-7:80px;--space-8:120px;
--hair:.5px;--line-w:1px;--line-bold:2px;
--radius-pill:999px;--radius-0:0;
--radius-square:8%; /* colour-filled squares round like the logo tile; rectangles stay sharp */
--ease:cubic-bezier(.2,.7,.2,1); /* @kind other */
--dur-fast:140ms; /* @kind other */
--dur:180ms; /* @kind other */
--dur-slow:280ms; /* @kind other */
--touch:44px;
/* v1 structural constants still used by components */
--bar-h:5px;--rule-h:2.5px;--hairline:.5px;--underline-w:2px;--radius-chip:3px;
}
/* --PAD drove the pill bar's side padding off its own 980 and 1600
   breakpoints, which are not the system's. Between 981 and 1023 the bar sat
   on 72px of padding while the layout was still in its tablet state. The pill
   bar only exists below 1024, so one value covers it. --INDENT went with it:
   it was referenced by nothing. */


/* ══ tokens/base.css ══════════════════════════════════════════════ */

/* Base element styles — v2. The page is always paper, all text is always ink. */
body{background:var(--paper);color:var(--ink);font-family:var(--font-sans);font-weight:400;font-size:16px;line-height:var(--lh-body);margin:0;-webkit-font-smoothing:antialiased}
a{color:var(--ink);text-decoration:underline;text-decoration-color:var(--line);text-decoration-thickness:var(--underline-w);text-underline-offset:3px}
a:hover{text-decoration-color:var(--ink)}
/* SERIF IS NEVER BOLD: DM Serif Display ships at 400 only; anything heavier is faux bold. */
h1,h2,h3,h4,h5,h6{font-family:var(--font-serif);font-weight:400;line-height:var(--lh-snug);color:var(--ink);font-synthesis-weight:none}
em,i{font-family:var(--font-serif);font-style:italic;font-weight:var(--fw-serif);font-synthesis-weight:none}
h1 em,h2 em,h3 em{color:var(--accent,var(--line))}
.accent{color:var(--accent,var(--fill))}
::selection{background:var(--rule)}


/* ══ addendum.css ══════════════════════════════════════════════ */

/* v2.1 addendum (identity + rule images, July 2026):
   1) Colour-filled SQUARES round their corners at 8% of size, like the logo square (tiny squares use 2px). Rectangles (bars, ribbons, plates) stay sharp.
   2) Progress bars: solid fill segment for the done portion, thin dashed rule segments for the remainder.
   3) No em-dashes anywhere in copy. */
.m-row__swatch,.tool-check__fill{border-radius:2px}
.m-progress__bar{background:repeating-linear-gradient(90deg,var(--rule) 0 10px,transparent 10px 17px) 0 50%/100% 1.5px no-repeat}
/* 4) Mobile nav (locked type, July 2026): floating pill tab bar + round action button. Replaces the dark m-tabbar. Track: soft ink wash (UI chrome, not a text background); tabs are ICON-ONLY, no labels, in fixed order: home, search, profile. Home is not a Lucide glyph but the brand square (logo construction: rounded at 8%, tiny sizes 2px), filled with the module colour of wherever the user is (var(--fill), so it recolours per module/tool scope). Active tab: paper CIRCLE, ink icon; inactive: ink-soft. Track hugs its three tabs (content width, centred), never stretched full-width. Action (+): quiet, not a focal point: soft ink-wash circle, ink-soft glyph (same wash as the track); tapping it expands a vertical stack of three labelled options above it: add a project note, attach a document, log a decision (paper pills, ink icon + label, hairline rule border; + rotates 45° while open). Flat, no shadows. */
.m-pillbar{display:flex;align-items:center;justify-content:center;gap:12px;padding:10px var(--PAD,16px);background:transparent}
.m-pillbar__tabs{display:flex;align-items:center;gap:12px;flex:none;background:#D7D3CF;border-radius:var(--radius-pill);padding:5px}
.m-pillbar__tab{display:inline-flex;align-items:center;justify-content:center;gap:7px;min-height:36px;padding:0 15px;border:none;border-radius:var(--radius-pill);background:transparent;font-family:var(--font-sans);font-weight:500;font-size:13px;color:var(--ink-soft);cursor:pointer;transition:color var(--dur) var(--ease),background var(--dur) var(--ease)}
.m-pillbar__tab .m-pillbar__icon{font-size:15px;line-height:1}
.m-pillbar__tab--icon{flex:none;width:44px;height:44px;padding:0}
.m-pillbar__home{width:16px;height:16px;flex:none;border-radius:2px;background:var(--fill);transition:background var(--dur) var(--ease)}
.m-pillbar__tab.is-active{background:var(--paper);color:var(--ink);border-radius:50%}
.m-pillbar__action{width:44px;height:44px;flex:none;border:none;border-radius:var(--radius-pill);background:var(--ink);color:var(--paper);font-size:22px;line-height:1;display:grid;place-items:center;cursor:pointer;transition:transform var(--dur) var(--ease),background var(--dur) var(--ease),color var(--dur) var(--ease)}
.m-pillbar__stack{position:relative}
.m-pillbar__stack.is-open .m-pillbar__action{transform:rotate(45deg) translateY(-2px);background:#3D3A35;color:var(--paper)}
.m-pillbar__menu{position:absolute;right:0;bottom:calc(100% + 10px);display:flex;flex-direction:column;align-items:flex-end;gap:8px;opacity:0;visibility:hidden;transform:translateY(6px);transition:opacity var(--dur) var(--ease),transform var(--dur) var(--ease),visibility var(--dur)}
.m-pillbar__stack.is-open .m-pillbar__menu{opacity:1;visibility:visible;transform:none}
.m-pillbar__option{display:inline-flex;align-items:center;gap:9px;min-height:44px;padding:0 16px;border:1px solid var(--rule);border-radius:var(--radius-pill);background:#FFFFFF;font-family:var(--font-sans);font-weight:500;font-size:13px;color:var(--ink);cursor:pointer;white-space:nowrap;max-width:calc(100vw - 32px)}
.m-pillbar__option .m-pillbar__icon{color:var(--ink-soft)}
/* 5) The module ribbon is RETIRED (July 2026): no ribbon in any design, desktop or mobile. */
.m-ribbon{display:none}


/* ══ app.css ══════════════════════════════════════════════ */

/* ════════════════════════════════════════════════════════════════════════
 * Build Informed · app.css — the application chrome
 *
 * Extracted from the approved Dashboard design so every screen rendered from
 * here matches it exactly. Nothing in this file invents a colour, size or
 * spacing value: everything resolves to the tokens in tokens/.
 *
 * The bottom navigation (.m-pillbar) already lives in addendum.css — it is
 * NOT redefined here. This file adds the screen scaffold, the app-specific
 * components, and the desktop/tablet behaviour that the phone designs imply.
 *
 * LOAD ORDER
 *   tokens/colors.css → tokens/typography.css → tokens/structure.css
 *   → tokens/base.css → patterns.css* → tools.css* → addendum.css → app.css
 *   (*only on pages that render module or tool content)
 * ════════════════════════════════════════════════════════════════════════ */

/* ── Screen scaffold ────────────────────────────────────────────────────
 * The approved design is a 390px column. On a phone it fills the screen; on
 * a larger display it stays a centred column with hairline edges, exactly as
 * the design shows, until the tablet breakpoint gives it a side rail. */
.screen {
  width: 100%;
  /* max-width is set once, from --spine, in the column geometry block. */
  margin: 0 auto;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  border-left: var(--hair) solid var(--rule);
  border-right: var(--hair) solid var(--rule);
  box-sizing: border-box;
}
/* Global, not scoped to .screen. It was `.screen *` for a long time, which
   covered almost everything and hid a real bug in the four components app.js
   appends to <body> instead: the sheet, the drawer, the scrim and the toast.
   Outside .screen they stayed content-box, so the capture sheet's `width:100%`
   (390px on a phone) had its 24px of padding added on each side and came out
   438px wide inside a 390px screen. The sheet scrolled sideways, and "Add a
   project note", "Attach a document" and "Log a decision" all inherited it
   because they are the same component.
   This file is only loaded by app screens (the tools take bi-tool-skin.css),
   so a global reset here cannot reach a tool's own layout. */
*, *::before, *::after { box-sizing: border-box; }

/* Standard horizontal padding for every section: 24px, per the design. */
.screen__pad { padding-left: var(--space-4); padding-right: var(--space-4); }

/* Pushes the bottom navigation to the foot of short screens. */
.screen__spacer { margin-top: auto; }

/* ── Top bar ────────────────────────────────────────────────────────────*/
.appbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px var(--space-4) 8px;
  border-bottom: var(--hair) solid var(--rule);
}
.appbar--plain { border-bottom: none; }

/* Groups multiple icon buttons on the appbar's right-hand end. .appbar's own
   justify-content:space-between expects exactly two children (brand, this);
   a third direct child would land in the middle instead of beside the menu
   button. Only the last icon in here should carry --flush. */
.appbar__actions { display: flex; align-items: center; gap: 4px; }

/* The brand lockup: 19px terracotta square + "Build Informed" in serif. */
.brandmark { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; }
.brandmark__tile {
  width: 19px; height: 19px; flex: none;
  background: var(--fill);
  border-radius: var(--radius-square);
}
.brandmark__name {
  font-family: var(--font-serif); font-weight: var(--fw-serif);
  font-size: 19px; line-height: 1; color: var(--ink);
  letter-spacing: var(--ls-tight);
}

/* Icon button — the standard 44px target used across the app. */
.iconbtn {
  width: var(--touch); height: var(--touch);
  display: grid; place-items: center;
  background: none; border: none; padding: 0;
  color: var(--ink); cursor: pointer;
}
.iconbtn--flush { margin-right: -12px; }
.iconbtn:focus-visible { outline: var(--line-bold) solid var(--fill); outline-offset: 2px; }

/* Screen title used on pushed screens: "YOUR LIBRARY". */
.screen-title {
  font-family: var(--font-sans);   /* it is a label, not a headline: h1 would
                                      otherwise inherit the serif from base */
  margin: 0;
  font-weight: var(--fw-label); font-size: var(--t-label);
  letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--ink); text-align: center; flex: 1;
}

/* ── Buttons ────────────────────────────────────────────────────────────
 * Pills for actions, exactly as the dashboard uses them. The two-line
 * variant carries a label above a quieter target line. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: var(--touch); padding: 14px 22px;
  font-family: var(--font-sans); font-weight: 500; font-size: var(--t-body-sm);
  border: none; border-radius: var(--radius-pill);
  background: var(--ink); color: var(--paper);
  cursor: pointer; text-decoration: none; text-align: center;
  transition: transform var(--dur) var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: var(--line-bold) solid var(--fill); outline-offset: 2px; }
.btn--block { display: flex; width: 100%; }
.btn--quiet { background: transparent; color: var(--ink); border: var(--line-w) solid var(--ink); }
.btn--module { background: var(--solid-bg); color: var(--solid-fg);
               border-color: var(--solid-border); }
.btn:disabled { opacity: .4; cursor: not-allowed; transform: none; }

/* Two-line action button (Continue reading / Resume tool). */
/* Both lines stay on one line, but they TRUNCATE rather than push the button
   wider than the space it was given. Without min-width:0 and the overflow
   pair, a long target like "Module 07 · Section 3" makes the button wider
   than its grid cell, and a grid item with the default min-width:auto refuses
   to shrink, so the grid grows and the page scrolls sideways. */
.btn--stack {
  flex-direction: column; gap: 2px; min-width: 0;
  min-height: 56px; padding: 8px 22px;
}
.btn--stack .btn__label,
.btn--stack .btn__sub {
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.btn--stack .btn__label { font-size: 15px; font-weight: 500; }
.btn--stack .btn__sub {
  font-size: 12px; letter-spacing: .04em;
  color: var(--on-dark-mid);
}
.btn--quiet.btn--stack { min-height: 52px; }
.btn--quiet.btn--stack .btn__label { font-size: 14px; }
.btn--quiet.btn--stack .btn__sub { color: var(--ink-soft); }

.textlink {
  background: none; border: none; padding: var(--space-2);
  font-family: var(--font-sans); font-size: var(--t-body-sm);
  color: var(--ink-mid); cursor: pointer;
  text-decoration: underline; text-decoration-color: var(--rule); text-underline-offset: 3px;
}
.textlink:hover { color: var(--ink); }

/* ── Score ring ─────────────────────────────────────────────────────────
 * Ring left, words right. Number and words always — never colour alone.
 * The arc uses pathLength="100" so the dash array is simply the score. */
.score-block { display: flex; align-items: center; gap: var(--space-4); }
.score-ring { position: relative; width: 132px; height: 132px; flex: none; }
.score-ring__track { stroke: rgba(167,63,33,.18); stroke-width: 11; fill: none; }
.score-ring__arc {
  stroke: var(--fill); stroke-width: 11; fill: none; stroke-linecap: round;
  transition: stroke-dasharray var(--dur-slow) var(--ease);
}
.score-ring__num {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-family: var(--font-serif); font-weight: var(--fw-serif);
  font-size: 46px; line-height: 1; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.score-words { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.score-words__title {
  font-family: var(--font-serif); font-weight: var(--fw-serif);
  font-size: 24px; line-height: 1.25; color: var(--ink); text-wrap: pretty;
}
.score-words__name { font-style: italic; color: var(--fill); }
.score-words__delta {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 15px; font-weight: 500; color: var(--fill);
  font-variant-numeric: tabular-nums;
}
.score-words__delta svg { transform: rotate(45deg); }

/* ── Eyebrow + next-step card ───────────────────────────────────────────*/
.eyebrow-label {
  font-size: var(--t-label); font-weight: var(--fw-label);
  letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--label);
}

/* Bordered action card: square corners, 1px ink border. */
.actioncard {
  width: 100%; display: flex; align-items: center; gap: var(--space-3);
  border: var(--line-w) solid var(--ink); border-radius: var(--radius-0);
  background: var(--paper); padding: 18px 16px 18px 20px;
  cursor: pointer; text-align: left; text-decoration: none;
  font-family: var(--font-sans);
  transition: transform var(--dur) var(--ease);
}
.actioncard:hover { transform: translateY(-1px); }
.actioncard:active { transform: translateY(0); }
.actioncard__body { flex: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.actioncard__title {
  font-family: var(--font-serif); font-weight: var(--fw-serif);
  font-size: 20px; line-height: 1.3; color: var(--ink); text-wrap: pretty;
}
.actioncard__note { font-size: 16px; line-height: 1.5; color: var(--ink-mid); text-wrap: pretty; }
.actioncard__chev { color: var(--ink-soft); flex: none; }

/* ── Library rows ───────────────────────────────────────────────────────
 * A filled colour tile carrying the module number, the title, a tool count,
 * and a right-hand state (chevron, progress, or tier tags). */
.libitem { border-bottom: var(--hair) solid var(--rule); }
/* The row is a strip holding two controls: a button that opens the module's
   tools in place, and a link that opens the module itself. */
.librow { display: flex; align-items: stretch; width: 100%;
  border-bottom: var(--hair) solid var(--rule); background: none; }
.librow__open { flex: 1; min-width: 0; display: flex; align-items: center;
  gap: var(--space-3); padding: var(--space-3) 0 var(--space-3) var(--space-4);
  background: none; border: none; text-align: left; cursor: pointer;
  font-family: var(--font-sans); color: var(--ink); }
/* No border here. It was a rendered hairline sitting exactly where
   .librow__right's text ends, so the fraction or the Read mark touched it.
   The 52px width alone is enough of a button boundary; the gap on
   .librow__right below keeps text clear of the edge with or without a
   line drawn. */
.librow__go { flex: none; width: 52px; display: grid; place-items: center;
  color: var(--ink-soft); text-decoration: none; }
.librow__go:hover { color: var(--ink); background: rgba(33,31,28,.04); }
.libitem.is-open .librow__open .librow__right { opacity: .6; }

.librow:focus-visible { outline: var(--line-bold) solid var(--fill); outline-offset: -2px; }
.libtile {
  width: 44px; height: 44px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--radius-square);
  font-family: var(--font-serif); font-weight: var(--fw-serif);
  font-size: 20px; line-height: 1;
}
.librow__body { flex: 1; min-width: 0; }
.librow__title {
  display: block; font-family: var(--font-serif); font-weight: var(--fw-serif);
  font-size: 19px; line-height: 1.2; color: var(--ink); text-wrap: pretty;
}
.librow__meta { display: block; font-size: 13px; color: var(--ink-mid); margin-top: 2px; }
.librow__right { flex: none; display: flex; align-items: center; gap: 10px;
  padding-right: var(--space-2); color: var(--ink-soft); }
.librow__pct { font-size: 12px; font-weight: 500; color: var(--fill-text); font-variant-numeric: tabular-nums; }
.librow__tags {
  /* 11px, the label floor. It was 10px, which is below the floor the tools
     are held to. One floor, everywhere. */
  font-size: 11px; font-weight: var(--fw-label); letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-soft); white-space: nowrap;
}
.librow__chev { transition: transform var(--dur) var(--ease); }
.librow[aria-expanded="true"] .librow__chev { transform: rotate(90deg); }

/* Inline tool list revealed when a module row expands. */
.libtools { padding: 0 var(--space-4) var(--space-3) 80px; }
.libtool {
  display: flex; align-items: center; gap: 10px;
  min-height: var(--touch); padding: 6px 0;
  border-bottom: var(--hair) solid var(--rule);
  font-size: 15px; color: var(--fill-text); text-decoration: none;
}
.libtool:last-child { border-bottom: none; }
.libtool__dash { color: var(--ink-soft); flex: none; }

/* ── Generic list row (records, outputs, settings, drawer) ──────────────*/
.row {
  display: flex; align-items: center; gap: var(--space-3);
  width: 100%; min-height: var(--touch);
  padding: var(--space-3) 0;
  background: none; border: none;
  border-bottom: var(--hair) solid var(--rule);
  text-align: left; cursor: pointer; color: var(--ink);
  font-family: var(--font-sans); font-size: var(--t-body);
  text-decoration: none;
}
.row__main { flex: 1; min-width: 0; }
.row__title { display: block; }
.row__sub { display: block; font-size: var(--t-meta); color: var(--ink-mid); }
.row__value { color: var(--ink); }
.row__chev { color: var(--ink-soft); flex: none; }
.row--static { cursor: default; }

/* Saved page. Elsewhere the whole .row is the link; here the row also holds a
   remove button, so the link is the .row__main inside it. That makes it a
   bare <a>, which picks up the global underline at the top of this file, so
   it is turned off here the same way .row turns it off. */
.row__main[href] { color: var(--ink); text-decoration: none; }
/* The module heading above each group. */
.savedgroup { display: block; color: var(--label); margin: var(--space-4) 0 var(--space-1); }
/* A highlight is the reader's own words off the page, so it is shown as the
   quotation it is rather than as a title. Two lines, then it stops: the row
   is a way back to the passage, not the passage itself. */
.savedhl {
  font-family: var(--font-serif); font-style: italic;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

.sec-head {
  font-weight: var(--fw-label); font-size: var(--t-label);
  letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--ink-soft);
  margin: var(--space-5) 0 var(--space-2);
}

/* ── Progress line ──────────────────────────────────────────────────────*/
.progress { display: flex; align-items: center; gap: var(--space-2); }
.progress__track { flex: 1; height: 2px; background: var(--track); position: relative; }
.progress__fill { position: absolute; inset: 0 auto 0 0; background: var(--fill); }
.progress__count {
  font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: .12em; color: var(--ink-soft); flex: none;
  font-variant-numeric: tabular-nums;
}

/* ── Sheets, scrim, drawer, toast ───────────────────────────────────────*/
.scrim {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(33,31,28,.45);
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur) var(--ease);
}
.scrim.is-open { opacity: 1; pointer-events: auto; }

.sheet {
  position: fixed; left: 50%; bottom: 0; z-index: 70;
  width: 100%; max-width: 390px;
  transform: translateX(-50%) translateY(100%);
  max-height: 88dvh; overflow-y: auto;
  background: var(--paper);
  border-top: var(--line-w) solid var(--ink);
  padding: var(--space-3) var(--space-4) calc(var(--space-6) + env(safe-area-inset-bottom, 0px));
  transition: transform var(--dur-slow) var(--ease);
}
.sheet.is-open { transform: translateX(-50%) translateY(0); }
.sheet__handle {
  width: 36px; height: 4px; margin: 0 auto var(--space-4);
  background: var(--rule); border-radius: var(--radius-pill);
}
.sheet__title {
  font-family: var(--font-serif); font-weight: var(--fw-serif);
  font-size: var(--t-h4); line-height: var(--lh-snug); margin: 0 0 var(--space-2);
}

.drawer {
  position: fixed; top: 0; bottom: 0; z-index: 70;
  left: 50%; margin-left: -195px;      /* aligned to the 390px column */
  width: min(320px, 86vw);
  transform: translateX(-110%);
  background: var(--paper);
  border-right: var(--line-w) solid var(--ink);
  padding: var(--space-4); overflow-y: auto;
  transition: transform var(--dur-slow) var(--ease);
}
.drawer.is-right { left: auto; right: 50%; margin: 0 -195px 0 0; transform: translateX(110%);
                   border-right: none; border-left: var(--line-w) solid var(--ink); }
.drawer.is-open { transform: translateX(0); }
.drawer__head { display: flex; justify-content: flex-end; margin-bottom: var(--space-3); }

.toast {
  position: fixed; z-index: 80; left: 50%; bottom: 96px;
  transform: translateX(-50%) translateY(12px);
  display: flex; align-items: center; gap: var(--space-3);
  max-width: 340px; padding: 12px 18px;
  background: var(--ink); color: var(--on-dark);
  font-size: var(--t-body-sm);
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.toast.is-open { opacity: 1; transform: translateX(-50%) translateY(0); pointer-events: auto; }
.toast__action {
  background: none; border: none; color: var(--on-dark-fill);
  font-family: var(--font-sans); font-size: var(--t-body-sm);
  text-decoration: underline; cursor: pointer; padding: 4px;
}

/* ── Safety signals ─────────────────────────────────────────────────────
 * Colour AND shape AND word. The hard stop is a SQUARE so it can never be
 * mistaken for a certainty dot, and it survives greyscale printing. */
/* An earlier signal component lived here: .signal__mark with its own
   #1E6B4E / #C27200 / #93170B. None of those three is the locked signal
   colour, it was referenced by nothing, and its .signal selector collided
   with the live one below. Removed. The signal set is --signal-known,
   --signal-estimated and --signal-stop, drawn by .dot in section 13.9. */

/* ── Empty state ────────────────────────────────────────────────────────*/
.empty { padding: var(--space-7) var(--space-4); text-align: center; }
.empty__title {
  font-family: var(--font-serif); font-weight: var(--fw-serif);
  font-size: var(--t-h4); margin-bottom: var(--space-2);
}
.empty__note { font-size: var(--t-body-sm); color: var(--ink-mid); }

/* ── Tablet & desktop ───────────────────────────────────────────────────
 * The phone design is the source of truth. On larger displays the column
 * widens moderately and the bottom pill bar becomes a side rail — the same
 * destinations, the same icons, relocated. Nothing else changes. */
@media (min-width: 1024px) {
  .screen__pad { padding-left: var(--space-6); padding-right: var(--space-6); }

  .sheet {
    left: 50%; top: 50%; bottom: auto;
    transform: translate(-50%, -45%) scale(.98); opacity: 0;
    width: min(520px, 92vw); max-width: 520px;
    border: var(--line-w) solid var(--ink);
    transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
  }
  .sheet.is-open { transform: translate(-50%, -50%) scale(1); opacity: 1; }

  .toast { bottom: var(--space-5); }
}

/* ── Accessibility ──────────────────────────────────────────────────────*/
.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;
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; scroll-behavior: auto !important;
  }
}


/* ══ v2.1 · fixed floating pill tab bar ═══════════════════════════════ */
.m-pillbar--fixed{
  position:fixed;left:50%;transform:translateX(-50%);
  bottom:0;z-index:45;width:100%;max-width:390px;
  padding:16px;padding-bottom:calc(16px + env(safe-area-inset-bottom,0px));
  background:transparent;pointer-events:none;
}
.m-pillbar--fixed .m-pillbar__tabs,
.m-pillbar--fixed .m-pillbar__stack{pointer-events:auto}
/* Above the scrim (60) only while the capture menu is open — see BI.openCapture.
   Both bars are position:fixed and so form their own stacking context, which a
   z-index on the menu inside cannot escape; the bar itself has to be lifted. */
.m-pillbar--fixed.is-capturing{z-index:65}
/* clears the fixed bar so content is never hidden behind it */
.screen--hasbar{padding-bottom:104px}



/* ══ module scoping ═══════════════════════════════════════════════════════
 * Design Language v2 §4 says: set data-module="07" on any container and
 * everything inside reads --fill / --line / --on-fill / --label / --accent /
 * --chip-*. The selectors that make that true were never in this file, so
 * only :root (module 01) resolved and every other scope silently stayed
 * terracotta. They are added here, generated from the --m-*- tokens above so
 * no colour is restated by hand.
 *
 * --fill-text carries the light-colour rule for TEXT: it is --fill on the
 * nine body-safe colours and --ink on Marigold, Periwinkle and Sage Green,
 * where the colour stays in swatches and fills but never in words. The one
 * exception is the module number chip, which fills solid and takes white
 * numerals via --chip-fg, per §2. */
[data-module="01"]{--fill:var(--m-01-fill);--line:var(--m-01-line);--on-fill:var(--m-01-on);--label:var(--fill);--accent:var(--line);--fill-text:var(--fill);--chip-bg:var(--fill);--chip-fg:var(--on-fill);--chip-border:none;--chip-swatch:none;--hero-bg:var(--fill);--hero-fg:var(--on-fill);--hero-bar:transparent}
[data-module="nri"]{--fill:var(--m-nri-fill);--line:var(--m-nri-line);--on-fill:var(--m-nri-on);--label:var(--fill);--accent:var(--line);--fill-text:var(--fill);--chip-bg:var(--fill);--chip-fg:var(--on-fill);--chip-border:none;--chip-swatch:none;--hero-bg:var(--fill);--hero-fg:var(--on-fill);--hero-bar:transparent}
[data-module="02"]{--fill:var(--m-02-fill);--line:var(--m-02-line);--on-fill:var(--m-02-on);--label:var(--fill);--accent:var(--line);--fill-text:var(--fill);--chip-bg:var(--fill);--chip-fg:var(--on-fill);--chip-border:none;--chip-swatch:none;--hero-bg:var(--fill);--hero-fg:var(--on-fill);--hero-bar:transparent}
[data-module="03"]{--fill:var(--m-03-fill);--line:var(--m-03-line);--on-fill:var(--m-03-on);--label:var(--fill);--accent:var(--line);--fill-text:var(--fill);--chip-bg:var(--fill);--chip-fg:var(--on-fill);--chip-border:none;--chip-swatch:none;--hero-bg:var(--fill);--hero-fg:var(--on-fill);--hero-bar:transparent}
[data-module="04"]{--fill:var(--m-04-fill);--line:var(--m-04-line);--on-fill:var(--m-04-on);--label:var(--fill);--accent:var(--line);--fill-text:var(--fill);--chip-bg:var(--fill);--chip-fg:var(--on-fill);--chip-border:none;--chip-swatch:none;--hero-bg:var(--fill);--hero-fg:var(--on-fill);--hero-bar:transparent}
[data-module="05"]{--fill:var(--m-05-fill);--line:var(--m-05-line);--on-fill:var(--m-05-on);--label:var(--fill);--accent:var(--line);--fill-text:var(--fill);--chip-bg:var(--fill);--chip-fg:var(--on-fill);--chip-border:none;--chip-swatch:none;--hero-bg:var(--fill);--hero-fg:var(--on-fill);--hero-bar:transparent}
[data-module="06"]{--fill:var(--m-06-fill);--line:var(--m-06-line);--on-fill:var(--m-06-on);--label:var(--fill);--accent:var(--line);--fill-text:var(--fill);--chip-bg:var(--fill);--chip-fg:var(--on-fill);--chip-border:none;--chip-swatch:none;--hero-bg:var(--fill);--hero-fg:var(--on-fill);--hero-bar:transparent}
[data-module="07"]{--fill:var(--m-07-fill);--line:var(--m-07-line);--on-fill:var(--m-07-on);--label:var(--fill);--accent:var(--line);--fill-text:var(--fill);--chip-bg:var(--fill);--chip-fg:var(--on-fill);--chip-border:none;--chip-swatch:none;--hero-bg:var(--fill);--hero-fg:var(--on-fill);--hero-bar:transparent}
[data-module="08"]{--fill:var(--m-08-fill);--line:var(--m-08-line);--on-fill:var(--m-08-on);--label:var(--fill);--accent:var(--line);--fill-text:var(--fill);--chip-bg:var(--fill);--chip-fg:var(--on-fill);--chip-border:none;--chip-swatch:none;--hero-bg:var(--fill);--hero-fg:var(--on-fill);--hero-bar:transparent}
[data-module="vastu"]{--fill:var(--m-vastu-fill);--line:var(--m-vastu-line);--on-fill:var(--m-vastu-on);--label:var(--fill);--accent:var(--line);--fill-text:var(--fill);--chip-bg:var(--fill);--chip-fg:var(--on-fill);--chip-border:none;--chip-swatch:none;--hero-bg:var(--fill);--hero-fg:var(--on-fill);--hero-bar:transparent}
[data-module="sh"]{--fill:var(--m-sh-fill);--line:var(--m-sh-line);--on-fill:var(--m-sh-on);--label:var(--ink);--accent:var(--ink);--fill-text:var(--ink);--chip-bg:var(--fill);--chip-fg:#fff;--chip-border:var(--line-w) solid var(--ink);--chip-swatch:var(--fill);--hero-bg:var(--paper);--hero-fg:var(--ink);--hero-bar:var(--fill)}
[data-module="09"]{--fill:var(--m-09-fill);--line:var(--m-09-line);--on-fill:var(--m-09-on);--label:var(--ink);--accent:var(--ink);--fill-text:var(--ink);--chip-bg:var(--fill);--chip-fg:#fff;--chip-border:var(--line-w) solid var(--ink);--chip-swatch:var(--fill);--hero-bg:var(--paper);--hero-fg:var(--ink);--hero-bar:var(--fill)}
[data-module="10"]{--fill:var(--m-10-fill);--line:var(--m-10-line);--on-fill:var(--m-10-on);--label:var(--ink);--accent:var(--ink);--fill-text:var(--ink);--chip-bg:var(--fill);--chip-fg:#fff;--chip-border:var(--line-w) solid var(--ink);--chip-swatch:var(--fill);--hero-bg:var(--paper);--hero-fg:var(--ink);--hero-bar:var(--fill)}

/* ── Filled controls and the light-colour rule ──────────────────────────
 * --fill-text (above) covers text painted IN the module colour. This covers
 * the opposite case: text painted ON it, which is where the rule was being
 * broken. --on-fill resolves to #211F1C on Marigold, Periwinkle and Sage, so
 * .btn--module, the dashboard buttons and the profile avatar were all
 * rendering near-black words on the colour. Ink on Periwinkle is exactly what
 * "these three never carry text" forbids.
 *
 * Measured, so the choice is not a guess (ratio against each fill):
 *              ink    paper
 *   Periwinkle 4.85   3.28
 *   Sage       4.85   3.28
 *   Marigold   7.46   2.13
 *
 * Periwinkle and Sage take PAPER on large controls: 3.28 clears the 3.0 that
 * large text needs, and it honours the rule. Marigold cannot carry paper
 * either (2.13 fails even the large-text floor), so it never fills a control
 * at all: it flips to swatch mode, which is the rule's own default, with the
 * colour in the border and the words left as ink on paper.
 *
 * Three tokens rather than one, because "what fills it" and "what sits on it"
 * move together and a control needs a border only in swatch mode. */
:root,
[data-module="01"], [data-module="02"], [data-module="03"], [data-module="04"],
[data-module="05"], [data-module="06"], [data-module="07"], [data-module="08"],
[data-module="nri"], [data-module="vastu"] {
  --solid-bg: var(--fill); --solid-fg: #fff; --solid-border: transparent;
}
[data-module="09"], [data-module="10"] {
  --solid-bg: var(--fill); --solid-fg: var(--paper); --solid-border: transparent;
}
[data-module="sh"] {
  --solid-bg: var(--paper); --solid-fg: var(--ink); --solid-border: var(--fill);
}

/* ── Library additions ──────────────────────────────────────────────────
 * .libtile had geometry but no fill: it takes the chip tokens, so the
 * number-chip exception on SH/09/10 arrives automatically. */
.libtile { background: var(--chip-bg); color: var(--chip-fg); }
/* Fallback for any row with no colour scope: paper tile, ink edge. */
.libtile--none { background: transparent; color: var(--ink); border: var(--line-w) solid var(--ink); }
/* Three-letter labels (NRI) need to breathe inside the 44px tile. */
.libtile--wide { font-size: 15px; letter-spacing: .01em; }

/* The quiet completion mark. Always paired with a word, never colour alone. */
/* Square, rounded at the same 8% corner as every tile in the system,
   rather than a round dot: one shape language for "complete", everywhere. */
.libdot { width: 8px; height: 8px; flex: none; border-radius: 2px;
  background: var(--fill); display: inline-block; }

/* Inline expansion under a module row: section count, reading time, action. */
.libpanel { display: none; padding: 0 var(--space-4) var(--space-4) 80px; }
.libpanel.is-open { display: block; }
.libpanel__meta { font-size: 13px; color: var(--ink-mid); margin: 0 0 var(--space-3); }

/* Tools grouped under module headers. The swatch carries the colour so the
 * header text can stay ink on every module, light ones included. */
.libgroup { padding: 0 var(--space-4); }
.libgroup__head { display: flex; align-items: center; gap: 10px; margin: var(--space-5) 0 var(--space-1); }
.libgroup__swatch { width: 12px; height: 12px; flex: none; border-radius: 2px; background: var(--fill); }
.libgroup__name {
  font-weight: var(--fw-label); font-size: var(--t-label);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--ink);
}
.libtool__body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.libtool__name { display: block; }
.libtool__sub { display: block; font-size: 13px; color: var(--ink-mid); line-height: 1.4; }
.libtool__chev { color: var(--ink-soft); flex: none; }

/* ── A module's tool, as a card ─────────────────────────────────────────
 * The dashed-row version read as one more line of the module's contents and
 * got skipped. A tool is the thing a reader came to DO, so it takes a bordered
 * card, a module-coloured edge, and an instruction as its first line. */
.libtoolcard {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3); margin-bottom: var(--space-2);
  border: var(--hair) solid var(--rule);
  border-left: 3px solid var(--fill);
  border-radius: var(--radius-sm, 6px);
  background: var(--paper);
  color: var(--ink); text-decoration: none;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.libtoolcard:last-child { margin-bottom: 0; }
.libtoolcard:hover { background: var(--paper-2, #F1ECE4); border-color: var(--ink-soft); border-left-color: var(--fill); }
.libtoolcard__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.libtoolcard__name { display: block; font-weight: 500; font-size: 15px; color: var(--fill-text); }
.libtoolcard__sub  { display: block; font-size: 13px; color: var(--ink-mid); line-height: 1.4; }
.libtoolcard__chev { color: var(--ink-soft); flex: none; }


/* ══ module overview screen ═══════════════════════════════════════════════
 * Built to the approved phone reference. The hero is the screen's one
 * saturated moment and it carries the video inside it: name, dek and poster
 * all sit on the module colour, and the paper page resumes below the band.
 * On Marigold, Periwinkle and Sage Green the --hero-* tokens flip the band
 * to paper with a colour bar, so the light-colour rule holds without a
 * modifier class. */
.mhero { background: var(--hero-bg); color: var(--hero-fg);
         padding: var(--space-5) var(--space-4) var(--space-5); border-radius: var(--radius-0); }
.mhero::before { content: ""; display: block; height: var(--bar-h); background: var(--hero-bar);
         margin: calc(var(--space-5) * -1) calc(var(--space-4) * -1) var(--space-4); }
.mhero__title {
  color: inherit;                  /* the base h1 rule paints ink; on a filled
                                      band the title must take the band's own
                                      foreground, which is paper */
  font-family: var(--font-serif); font-weight: var(--fw-serif);
  font-size: clamp(38px, 12vw, 54px); line-height: 1.02;
  letter-spacing: var(--ls-tight); margin: 0; text-wrap: balance;
}
.mhero__dek { font-family: var(--font-serif); font-style: italic; font-size: 19px;
  line-height: 1.35; margin: var(--space-2) 0 0; max-width: var(--measure); }

/* The poster sits on the band, so it is drawn in the band's foreground
   rather than in the module colour it is already sitting on. */
.mvideo { position: relative; width: 100%; aspect-ratio: 16 / 10; margin-top: var(--space-5);
  display: grid; place-items: center; gap: var(--space-3); padding: 0;
  background: transparent; color: inherit; cursor: pointer;
  border: var(--hair) solid currentColor; }
.mvideo__mark { width: 54px; height: 54px; border-radius: 50%;
  border: var(--hair) solid currentColor; display: grid; place-items: center; }
.mvideo__cap { font-weight: var(--fw-label); font-size: var(--t-label);
  letter-spacing: var(--ls-label); text-transform: uppercase; }
.mvideo video { width: 100%; height: 100%; display: block; background: var(--ink); }
.mvideo__fail { padding: var(--space-3) var(--space-4); }
.mvideo__fail p { margin: 0 0 var(--space-2); font-size: var(--t-body-sm); }

/* Progress, then the one action, then the reading figures. */
.mprog { display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-5) var(--space-4) 0; }
.mprog__count { flex: none; font-size: 13px; font-weight: 500; color: var(--ink);
  font-variant-numeric: tabular-nums; }
.mprog__track { flex: 1; height: 3px; background: var(--rule); }
.mprog__fill { height: 100%; background: var(--fill); }

.mbegin { padding: var(--space-4) var(--space-4) 0; }
.btn--module { background: var(--solid-bg); color: var(--solid-fg);
               border-color: var(--solid-border); min-height: 56px; font-size: 16px; }

.mfigures { padding: var(--space-4) var(--space-4) 0;
  font-weight: var(--fw-label); font-size: var(--t-label);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--fill-text); }

/* ── Where to start ──────────────────────────────────────────────────────
 * The author's shortlist, above the full contents. Cards rather than rows,
 * so it reads as a different kind of thing from the list below it and a
 * reader can tell at a glance that this is a choice, not an inventory. */
.mstart { padding: var(--space-5) var(--space-4) 0; }
.mstart__head { font-weight: var(--fw-label); font-size: var(--t-label);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--fill-text);
  padding-bottom: var(--space-3); border-bottom: var(--line-bold) solid var(--ink); }
.mstart__grid { display: grid; grid-template-columns: 1fr; }
.mstart__card { display: flex; align-items: baseline; gap: var(--space-3);
  min-width: 0; min-height: var(--touch); padding: var(--space-3) 0;
  border-bottom: var(--hair) solid var(--rule);
  color: var(--ink); text-decoration: none; }
.mstart__n { flex: none; min-width: 26px; font-family: var(--font-sans);
  font-size: 13px; font-weight: 500; color: var(--fill-text);
  font-variant-numeric: tabular-nums; }
.mstart__title { flex: 1; min-width: 0; font-family: var(--font-serif);
  font-weight: var(--fw-serif); font-size: 18px; line-height: 1.25; text-wrap: pretty; }
/* The second half of an authored title, set the way the reading surface sets
   a headline accent. This is what made the old contents read as typeset
   rather than as a list of strings. */
.mstart__title i, .msec__title i { font-style: italic; color: var(--fill-text); }
@media (min-width: 820px) {
  /* Two columns from tablet up. column-gap sits on the grid and the rule is
     drawn by each card, so the gutter never breaks a line the way a gap
     across a shared border would. */
  .mstart__grid { grid-template-columns: 1fr 1fr; column-gap: var(--space-5); }
}

/* ── The standfirst and the plate ────────────────────────────────────────
 * Both authored for every module and shown for none until now. */
.mhero__sub { max-width: var(--measure); margin: var(--space-3) 0 0;
  font-size: var(--t-body-sm); line-height: var(--lh-body); color: var(--on-fill); opacity: .85; }
.mhero__edition { margin: var(--space-2) 0 0; font-family: var(--font-sans);
  font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--on-fill); opacity: .6; }

/* The plate stands in for artwork that is not in the kit yet: plate.file
   names an SVG the screen does not have. The frame and the mark are what the
   previous edition showed around the artwork, so the page reads as finished
   rather than broken while the files are still coming. */
.mplate { margin: var(--space-5) 0 0; padding: 0;
  border: var(--hair) solid currentColor; color: var(--on-fill); }
.mplate__mark { display: flex; align-items: center; justify-content: center;
  min-height: 180px; font-family: var(--font-serif); font-style: italic;
  font-size: 56px; line-height: 1; color: var(--on-fill); opacity: .8; }
.mplate__cap { padding: var(--space-2) var(--space-3);
  border-top: var(--hair) solid currentColor;
  font-family: var(--font-sans); font-weight: var(--fw-label);
  font-size: var(--t-label-sm); letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--on-fill); opacity: .7; }

/* ── The architect notes index ───────────────────────────────────────────
 * 103 cards across six modules that were rendering with no way to find them. */
.mnotes { padding: var(--space-6) var(--space-4) 0; }
.mnotes__head { font-weight: var(--fw-label); font-size: var(--t-label);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--fill-text); }
.mnotes__dek { max-width: var(--measure); margin: var(--space-2) 0 var(--space-3);
  font-size: var(--t-body-sm); line-height: var(--lh-body); color: var(--ink-mid); }
.mnotes__grid { display: grid; grid-template-columns: 1fr;
  border-top: var(--line-bold) solid var(--ink); }
.mnotes__card { display: flex; align-items: baseline; gap: var(--space-3);
  min-width: 0; min-height: var(--touch); padding: var(--space-3) 0;
  border-bottom: var(--hair) solid var(--rule);
  color: var(--ink); text-decoration: none; }
.mnotes__n { flex: none; min-width: 26px; font-family: var(--font-sans);
  font-size: 13px; font-weight: 500; color: var(--fill-text);
  font-variant-numeric: tabular-nums; }
.mnotes__title { flex: 1; min-width: 0; font-family: var(--font-serif);
  font-weight: var(--fw-serif); font-size: 17px; line-height: 1.25; text-wrap: pretty; }
.mnotes__title i { font-style: italic; color: var(--fill-text); }
@media (min-width: 820px) {
  .mnotes__grid { grid-template-columns: 1fr 1fr; column-gap: var(--space-5); }
}

/* ── The module's tools ───────────────────────────────────────────────────
 * Rendered twice on a module page, near the top and at the foot. Same data,
 * one source (modules_nav.js -> module-NN.js). Cards carry a blurb, so they
 * are blockier than .mstart / .mnotes rows: a reader picking a tool wants to
 * know what it does before they leave the page.
 * The --fill-text colour is module-scoped, so each module's tools pick up that
 * module's own accent without anything per-module here. */
.mtools { padding: var(--space-5) var(--space-4) 0; }
.mtools--foot { padding-top: var(--space-6); }
.mtools__head { font-weight: var(--fw-label); font-size: var(--t-label);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--fill-text); }
.mtools__dek { max-width: var(--measure); margin: var(--space-2) 0 var(--space-3);
  font-size: var(--t-body-sm); line-height: var(--lh-body); color: var(--ink-mid); }
.mtools__grid { display: grid; grid-template-columns: 1fr; gap: var(--space-2);
  margin-top: var(--space-3); }
/* A card, not a list row. As underlined rows these read as more of the
   module's contents and were routinely skipped; the tool is the thing the
   reader came to DO, so it gets a border, a module-coloured edge and a
   chevron. */
.mtools__card { display: flex; align-items: center; gap: var(--space-3);
  min-width: 0; min-height: var(--touch); padding: var(--space-3);
  border: var(--hair) solid var(--rule);
  border-left: 3px solid var(--fill);
  border-radius: var(--radius-sm, 6px);
  background: var(--paper);
  color: var(--ink); text-decoration: none;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease); }
.mtools__card:hover { background: var(--paper-2, #F1ECE4); border-color: var(--ink-soft);
  border-left-color: var(--fill); }
.mtools__body { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; }
.mtools__chev { color: var(--ink-soft); flex: none; }
.mtools__phase { font-family: var(--font-sans); font-weight: var(--fw-label);
  font-size: 10px; letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--fill-text); }
.mtools__title { font-family: var(--font-serif); font-weight: var(--fw-serif);
  font-size: 17px; line-height: 1.25; text-wrap: pretty; color: var(--fill-text); }
.mtools__blurb { max-width: var(--measure); font-size: var(--t-body-sm);
  line-height: var(--lh-body); color: var(--ink-mid); text-wrap: pretty; }
/* The tool's real name, kept so a reader can match the card to the library. */
.mtools__name { font-size: 12px; color: var(--ink-soft); }
@media (min-width: 820px) {
  .mtools__grid { grid-template-columns: 1fr 1fr; column-gap: var(--space-5); }
}

/* A contents row that is an architect note says so. */
.msec__note { display: inline-block; margin-left: var(--space-2);
  font-family: var(--font-sans); font-weight: var(--fw-label);
  font-size: 10px; letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--fill-text); vertical-align: middle; }

/* ── Inserts on the reading surface ──────────────────────────────────────
 * The pullquote and the tools panel, placed where the author put them. */
.read__pull { max-width: var(--measure); margin: var(--space-6) 0 var(--space-5);
  padding: var(--space-5) 0; border-top: var(--line-bold) solid var(--ink);
  border-bottom: var(--hair) solid var(--rule); }
.read__pull blockquote { margin: 0; font-family: var(--font-serif);
  font-weight: var(--fw-serif); font-size: 26px; line-height: 1.25;
  text-wrap: pretty; }
.read__pull blockquote i { font-style: italic; color: var(--fill-text); }
.read__pull figcaption { margin-top: var(--space-3); font-family: var(--font-sans);
  font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--ink-soft); }

.read__sample { max-width: var(--measure); margin: var(--space-6) 0 var(--space-5);
  padding: var(--space-5); background: var(--wash);
  border-top: var(--line-bold) solid var(--fill); }
.read__sample-h { font-family: var(--font-serif); font-weight: var(--fw-serif);
  font-size: 24px; line-height: 1.2; margin: var(--space-2) 0 var(--space-3);
  text-wrap: pretty; }
.read__sample-h i { font-style: italic; color: var(--fill-text); }
.read__sample p { margin: 0 0 var(--space-3); font-size: var(--t-body-sm);
  line-height: var(--lh-body); color: var(--ink-mid); }
.read__sample p:last-child { margin-bottom: 0; }

/* ── The glossary page ───────────────────────────────────────────────────
 * The whole list, alphabetical, anchored by slug so a tooltip's "read more"
 * lands on the right entry. */
.gpage__dek { max-width: var(--measure); margin: var(--space-3) 0 var(--space-4);
  font-size: var(--t-body-sm); line-height: var(--lh-body); color: var(--ink-mid); }
.gpage__search { display: block; max-width: var(--measure); }
.gpage__search input { width: 100%; min-height: var(--touch);
  padding: var(--space-2) var(--space-3); font: inherit; font-size: 16px;
  color: var(--ink); background: var(--paper);
  border: var(--hair) solid var(--rule); border-radius: 0; }
.gpage__search input:focus { outline: 2px solid var(--fill); outline-offset: 1px; }
.gpage__count { margin: var(--space-2) 0 0; font-family: var(--font-sans);
  font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--ink-soft); }
.gpage__letter { padding: var(--space-5) var(--space-4) var(--space-2);
  font-family: var(--font-sans); font-weight: var(--fw-label); font-size: var(--t-label);
  letter-spacing: var(--ls-label); color: var(--fill-text);
  border-bottom: var(--line-bold) solid var(--ink); margin: 0 var(--space-4); }
.gterm { padding: var(--space-4); border-bottom: var(--hair) solid var(--rule);
  scroll-margin-top: 90px; }
.gterm.is-target { background: var(--wash);
  box-shadow: inset 3px 0 0 var(--fill); }
.gterm__t { margin: 0; font-family: var(--font-serif); font-weight: var(--fw-serif);
  font-size: 20px; line-height: 1.2; text-wrap: pretty; }
.gterm__meta { margin: var(--space-1) 0 var(--space-2); font-family: var(--font-sans);
  font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--ink-soft); }
.gterm__d { max-width: var(--measure); margin: 0; font-size: var(--t-body-sm);
  line-height: var(--lh-body); color: var(--ink-mid); }

/* ── Search results ──────────────────────────────────────────────────────
 * Grouped by type, capped per group with a "show all". The type label is a
 * heading rather than a colour, so a glossary hit and a build note are
 * distinguishable without relying on anyone seeing the difference. */
.sres { padding: var(--space-5) var(--space-4) 0; }
.sres__head { display: flex; align-items: baseline; gap: var(--space-2);
  font-family: var(--font-sans); font-weight: var(--fw-label); font-size: var(--t-label);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--fill-text);
  padding-bottom: var(--space-2); border-bottom: var(--line-bold) solid var(--ink); }
.sres__n { margin-left: auto; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.sres__row { display: block; padding: var(--space-3) 0;
  border-bottom: var(--hair) solid var(--rule); color: var(--ink); text-decoration: none; }
.sres__ctx { display: block; font-family: var(--font-sans); font-weight: var(--fw-label);
  font-size: var(--t-label-sm); letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--ink-soft); }
.sres__t { display: block; margin-top: 2px; font-family: var(--font-serif);
  font-weight: var(--fw-serif); font-size: 18px; line-height: 1.25; text-wrap: pretty; }
.sres__s { display: block; margin-top: var(--space-1); max-width: var(--measure);
  font-size: var(--t-body-sm); line-height: 1.45; color: var(--ink-mid); }
.sres__s mark { background: var(--wash); color: var(--ink);
  box-shadow: inset 0 -0.5em 0 var(--wash); }
.sres__more { display: block; width: 100%; min-height: var(--touch);
  margin-top: var(--space-2); background: none; border: 0; cursor: pointer;
  font-family: var(--font-sans); font-weight: var(--fw-label);
  font-size: var(--t-label-sm); letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--fill-text); text-align: left; }

/* Recent searches. Shown in place of results when the box is focused
 * empty. Reuses .sres/.sres__head/.sres__t; the row itself is a compact
 * flex line (icon, term, remove) rather than the title+snippet stack a
 * real result row uses. */
.sres__clear { margin-left: auto; background: none; border: 0; cursor: pointer;
  font-family: var(--font-sans); font-weight: var(--fw-label);
  font-size: var(--t-label-sm); letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--ink-soft); }
.sres__row--recent { display: flex; align-items: center; gap: var(--space-1);
  width: 100%; min-height: var(--touch);
  border-bottom: var(--hair) solid var(--rule); }
.sres__row-recent-btn { display: flex; align-items: center; gap: var(--space-2);
  flex: 1; min-width: 0; min-height: var(--touch); padding: var(--space-2) 0;
  background: none; border: 0; cursor: pointer; text-align: left; }
.sres__row-recent-btn .sres__t { flex: 1; min-width: 0; margin-top: 0; font-size: 16px; }
.sres__recent-icon { flex: none; color: var(--ink-soft); }
.sres__row-remove { flex: none; width: var(--touch); height: var(--touch);
  display: grid; place-items: center; background: none; border: 0; cursor: pointer;
  font-size: 18px; line-height: 1; color: var(--ink-soft); }

/* Chapters group the sections. Grouping, not gates. */
.mchapter { padding: var(--space-5) var(--space-4) var(--space-2);
  font-weight: var(--fw-label); font-size: var(--t-label);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--ink-soft); }

.msec { display: flex; align-items: center; gap: var(--space-3); width: 100%;
  min-height: var(--touch); padding: var(--space-2) var(--space-4);
  border-top: var(--hair) solid var(--rule); background: none; text-align: left;
  color: var(--ink); text-decoration: none; font-family: var(--font-sans); }
.msec__swatch { width: 13px; height: 13px; flex: none; border-radius: var(--radius-square);
  background: var(--fill); }
.msec__num { flex: none; font-size: 13px; font-weight: 500; color: var(--ink-soft);
  font-variant-numeric: tabular-nums; min-width: 26px; }
.msec__title { flex: 1; min-width: 0; font-family: var(--font-serif);
  font-weight: var(--fw-serif); font-size: 18px; line-height: 1.25; text-wrap: pretty; }
.msec__dot { width: 6px; height: 6px; flex: none; border-radius: 50%; background: var(--fill); }
.msec__chev { color: var(--ink-soft); flex: none; }

/* ══ column geometry and the shell ════════════════════════════════════════
 * Settled July 2026. --spine is the structural width of the content column:
 * 390 on a phone, 760 from tablet up. Prose inside it never exceeds
 * --measure (640); tables, video, plates and section rows may run the full
 * 760.
 *
 * The left rail is retired. Navigation is now the pill bar below 1024 and a
 * top bar at 1024 and up, following the studio site: brand at the left,
 * words at the right, the current one underlined. Between 820 and 1023 the
 * top bar carries only the brand and the menu, because the pill bar is still
 * doing the navigating.
 *
 * Both bars are built from BI.NAV, so a destination added in one place
 * appears in both. */
:root { --spine: 390px; --measure: 640px; --topbar-h: 0px; }
@media (min-width: 820px)  { :root { --spine: 760px; --topbar-h: 72px; } }
@media (min-width: 1024px) { :root { --topbar-h: 84px; } }

.screen { max-width: var(--spine); padding-top: var(--topbar-h); }

/* ── The bar ──────────────────────────────────────────────────────────── */
.topbar { display: none; }

@media (min-width: 820px) {
  .topbar {
    display: block; position: fixed; top: 0; left: 0; right: 0; z-index: 40;
    background: var(--paper); border-bottom: var(--hair) solid var(--rule);
  }
  /* Above the scrim (60) while the capture menu is open — see BI.openCapture. */
  .topbar.is-capturing { z-index: 65; }
  .topbar__inner {
    max-width: 1240px; margin: 0 auto; height: var(--topbar-h);
    padding: 0 var(--space-6);
    display: flex; align-items: center; justify-content: space-between; gap: var(--space-5);
  }
  .topbar__brand {
    display: flex; align-items: center; gap: var(--space-2);
    font-family: var(--font-serif); font-weight: var(--fw-serif);
    font-size: 21px; color: var(--ink); text-decoration: none; letter-spacing: var(--ls-tight);
  }
  /* The brand square, same idea as the pill bar's home swatch: it takes
     var(--fill) so it recolours with whatever module the screen is scoped to. */
  .topbar__tile { width: 20px; height: 20px; flex: none; border-radius: 2px;
                  background: var(--fill); }
  .topbar__nav { display: flex; align-items: center; gap: var(--space-2); }

  /* Words are hidden on tablet: the pill bar is still the navigation there,
     and repeating the same three destinations twice on one screen would
     break "each fact appears once". */
  .topbar__link { display: none; }

  .topbar__icon {
    width: var(--touch); height: var(--touch); flex: none; display: grid; place-items: center;
    border: none; background: none; color: var(--ink); cursor: pointer; border-radius: 50%;
  }
  .topbar__icon:hover { background: rgba(33,31,28,.06); }
  .topbar__stack { position: relative; display: none; }
  .topbar__menu {
    position: absolute; right: 0; top: calc(100% + 10px);
    display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
    opacity: 0; visibility: hidden; transform: translateY(-6px);
    transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility var(--dur);
  }
  .topbar__stack.is-open .topbar__menu { opacity: 1; visibility: visible; transform: none; }
  .topbar__stack.is-open .topbar__icon { background: var(--ink); color: var(--paper); }

  /* The screen-level bar keeps back arrows, locators and bookmarks. Where its
     only job was the brand lockup and the menu, the top bar has taken both
     and it collapses. */
  .appbar .brandmark,
  .appbar [data-bi="drawer-open"] { display: none; }
  .appbar:has(.brandmark) { display: none; }
}

@media (min-width: 1024px) {
  .topbar__link {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 6px 2px; margin: 0 var(--space-3);
    font-family: var(--font-sans); font-weight: 500; font-size: 15px;
    color: var(--ink); text-decoration: none;
    border-bottom: 1.5px solid transparent;
  }
  .topbar__glyph { display: inline-flex; color: var(--ink-soft); }
  .topbar__glyph svg { width: 16px; height: 16px; }
  .topbar__link:hover      { border-bottom-color: var(--rule); }
  .topbar__link.is-active  { border-bottom-color: var(--ink); }
  .topbar__stack { display: block; margin-left: var(--space-3); }

  /* One navigation at a time. */
  .m-pillbar--fixed { display: none; }
  .screen--hasbar   { padding-bottom: var(--space-6); }
}

/* Prose holds the measure wherever the spine is wider than it. */
/* Section rows split into two columns once the spine widens. Row-major, so
 * the sequence reads left to right then down. Chapter heads span both. */
@media (min-width: 820px) {
  .msecs { display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--space-6); }
  .msecs .msec { padding-left: 0; padding-right: 0; }
  .mchapter, .mprog, .mbegin, .mfigures, .screen__pad,
  .mhero { padding-left: var(--space-6); padding-right: var(--space-6); }
  .msecs { padding: 0 var(--space-6); }
}


/* ══ drawer: anchor it to the viewport, not the column ════════════════════
 * The closed drawer parks itself relative to the CENTRED COLUMN: right:50%,
 * pulled back by 195px, then slid away by translateX(110%) of its own 320px.
 * That only hides it while half the viewport minus 195 is under 352px, which
 * stops being true around 1094. The rule that compensated lived in the old
 * rail block and went with it, so at 1440 the whole panel was sitting on the
 * page looking like an open menu. Anchoring to the viewport edge removes the
 * arithmetic entirely: it cannot drift when the column changes again. */
@media (min-width: 820px) {
  .drawer          { left: 0;   right: auto; margin: 0; transform: translateX(-100%); }
  .drawer.is-right { left: auto; right: 0;   margin: 0; transform: translateX(100%); }
  .drawer.is-open  { transform: translateX(0); }
}


/* ══ dashboard: the opening grid ══════════════════════════════════════════
 * One grid at every width; only the spans change.
 *   phone      house | actions, then score across, then next step across
 *   820 and up the approved 2x2: house | actions over score | next step
 * Same four blocks either way. Nothing is added and nothing competes with
 * the score.
 *
 * The phone dashboard does not scroll: it is a standing screen, so the grid
 * takes the height left between the bars and spaces its three rows into it. */
.dash {
  display: grid; grid-template-columns: 158px 1fr;
  column-gap: var(--space-4); padding: var(--space-4);
  align-content: space-evenly;
}
.dash__cell { display: flex; flex-direction: column; justify-content: center;
  min-width: 0; }   /* or a long label inside makes the grid wider than the screen */
.dash__cell--score,
.dash__cell--next { grid-column: 1 / -1; }
.dash__cell--score { flex-direction: row; align-items: center; gap: var(--space-4); }
.dash__cell--actions { gap: var(--space-3); }
.dash__cell--next { gap: var(--space-2); }

@media (max-width: 819px) {
  /* The landing dashboard is a standing screen, so it is PINNED to the window
     rather than merely asked to be at least as tall as it. min-height lets the
     page grow when something inside overflows; height with overflow:hidden
     means a mistake shows as clipping, which is visible, rather than as a
     scrollbar, which is not. */
  .screen--dash:not(.screen--panes) { height: 100dvh; overflow: hidden; }
  .screen--dash:not(.screen--panes) .dash { min-height: 0; }

  /* Scoped with :not(.screen--panes) so the landing layout and the swipe
     layout never compete. Previously both sets of rules applied and the
     panes had to out-rank a column direction that was still in the cascade;
     now only one set is live at a time. */
  .screen--dash { min-height: 100dvh; display: flex; flex-direction: column; }
  .screen--dash:not(.screen--panes) .dash-wrap { flex: 1; display: flex; flex-direction: column; }
  .screen--dash:not(.screen--panes) .dash { flex: 1; }
}

@media (min-width: 820px) {
  /* No column gap: the rule above the score and the rule above the next step
     are two borders on two cells, and a gap between the columns leaves a
     visible break in the middle of what should read as one line. The gutter
     is made with padding inside the cells instead, so the borders meet. */
  .dash {
    grid-template-columns: 1fr 1fr;
    column-gap: 0; padding: var(--space-5) var(--space-6) 0;
    align-content: start;
  }
  .dash__cell--house,
  .dash__cell--score   { padding-right: var(--space-5); }
  .dash__cell--actions,
  .dash__cell--next    { padding-left: var(--space-5); }
  /* The delta line under the score runs long. Let it wrap inside its own
     cell rather than crowd the card in the next column. */
  .dash__cell--score .score-words { min-width: 0; }
  /* Longhand, not the padding shorthand: this rule sits after the gutter
     rules above and at the same weight, so `padding: x 0` was silently
     resetting their padding-left and padding-right back to zero. That is why
     the delta line kept touching the next-step card however far the gutter
     was pushed. */
  .dash__cell { padding-top: var(--space-5); padding-bottom: var(--space-5); }
  .dash__cell--house  { align-items: flex-start; }
  .dash__cell--score  { grid-column: 1; }
  .dash__cell--next   { grid-column: 2; flex-direction: column; }
  .dash__cell--score,
  .dash__cell--next   { border-top: var(--hair) solid var(--rule); }
}

/* ══ the You pane on the dashboard ════════════════════════════════════════
 * A continuation of the same column on tablet, marked by a 2px section top;
 * a companion beside the grid on desktop, marked by a hairline. Its own
 * region either way, so it never joins the 2x2. */
.dashyou { border-top: var(--line-w) solid var(--ink); margin-top: var(--space-6);
           padding: var(--space-5) var(--space-4) 0; }
.dashyou__group { margin-bottom: var(--space-5); }
.dashyou__head  { font-weight: var(--fw-label); font-size: var(--t-label-sm);
                  letter-spacing: var(--ls-label); text-transform: uppercase;
                  color: var(--ink-soft); margin-bottom: var(--space-2); }

.dashyou__id { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-5); }
.dashyou__avatar { width: 48px; height: 48px; flex: none; border-radius: 50%;
                   background: var(--solid-bg); color: var(--solid-fg);
                   border: var(--line-w) solid var(--solid-border);
                   display: grid; place-items: center;
                   font-family: var(--font-serif); font-size: 20px; }

/* The avatar is a button when a picture can be set. Same 48px circle, same
   letter underneath: the initial stays as the fallback rather than being
   replaced by a grey silhouette, so an account with no picture still looks
   like somebody's. */
.dashyou__avatar--btn { padding: 0; cursor: pointer; position: relative; overflow: hidden;
                   -webkit-appearance: none; appearance: none; }
.dashyou__avatar--btn img { position: absolute; inset: 0; width: 100%; height: 100%;
                   object-fit: cover; display: block; }
/* The hint only appears on hover or keyboard focus, so the circle stays a
   portrait rather than a control with a label sitting over someone's face. */
.dashyou__avatar--btn::after { content: "Change"; position: absolute; inset: auto 0 0 0;
                   padding: 3px 0 4px; font-family: var(--font-sans); font-size: 9px;
                   font-weight: var(--fw-label); letter-spacing: var(--ls-label);
                   text-transform: uppercase; text-align: center;
                   background: rgba(33,31,28, .68); color: #fff;
                   opacity: 0; transition: opacity var(--dur) var(--ease); }
.dashyou__avatar--btn:hover::after,
.dashyou__avatar--btn:focus-visible::after { opacity: 1; }
.dashyou__avatar--btn:focus-visible { outline: 2px solid var(--fill); outline-offset: 2px; }

/* Sits under the name, beside the project line, and only when a picture is
   set. Nothing to remove means nothing to offer. */
.dashyou__pic-clear { display: inline-block; margin-top: 2px; padding: 0;
                   background: none; border: 0; cursor: pointer;
                   font-family: var(--font-sans); font-size: 12px; color: var(--ink-soft);
                   text-decoration: underline; text-underline-offset: 2px; }
.dashyou__pic-clear:hover { color: var(--ink); }
.dashyou__name { font-family: var(--font-serif); font-size: 19px; line-height: 1.2; }
.dashyou__sub  { font-size: 13px; color: var(--ink-mid); }

.dashyou__row { display: flex; align-items: center; gap: var(--space-3);
                min-height: var(--touch); padding: var(--space-2) 0;
                border-bottom: var(--hair) solid var(--rule);
                color: var(--ink); text-decoration: none; font-size: 15px; }
.dashyou__label { flex: 1; min-width: 0; color: var(--ink-mid); }
.dashyou__value { text-align: right; }
.dashyou__chev  { color: var(--ink-soft); flex: none; }

/* Toggles read as on or off by position and by a written state, never by
   colour alone. */
.dashyou__toggle { width: 44px; height: 26px; flex: none; border-radius: 999px;
                   border: var(--hair) solid var(--rule); background: var(--rule);
                   position: relative; cursor: pointer; }
.dashyou__toggle::after { content: ""; position: absolute; top: 2px; left: 2px;
                   width: 20px; height: 20px; border-radius: 50%; background: var(--paper);
                   transition: transform var(--dur) var(--ease); }
.dashyou__toggle[aria-pressed="true"] { background: var(--fill); border-color: var(--fill); }
.dashyou__toggle[aria-pressed="true"]::after { transform: translateX(18px); }

.dashyou__hint { font-size: 14px; color: var(--ink-mid); margin: var(--space-4) 0 var(--space-5); }
.dashyou__hint a { color: var(--fill-text); }
.dashyou__footer { display: flex; gap: var(--space-4); padding-bottom: var(--space-6);
                   font-weight: var(--fw-label); font-size: var(--t-label-sm);
                   letter-spacing: var(--ls-label); text-transform: uppercase; }
.dashyou__footer a { color: var(--ink-soft); text-decoration: none; }

@media (min-width: 1024px) {
  /* Profile left, quadrants right, both on ROW ONE.
   *
   * Setting only grid-column put the quadrants at row 1 column 2, which left
   * the placement cursor past column 1; the profile then asked for column 1,
   * behind the cursor, so sparse auto-flow dropped it to row 2. Both items
   * now name their row as well as their column.
   *
   * One scrollbar, not two. Pinning the screen to the viewport and giving
   * the profile its own overflow produced an inner scrollbar sitting inside
   * the page's. Instead the page scrolls normally and the quadrants are
   * sticky: the profile is simply tall, the window scroll moves it, and the
   * quadrant block stays where it is. align-self: start matters, because a
   * stretched grid item fills the row and has nothing left to stick within. */
  .screen--dash {
    max-width: calc(var(--spine) + 400px + var(--space-6));
    padding-top: var(--topbar-h); padding-bottom: 0;
  }
  .screen--dash .dash-wrap {
    display: grid; grid-template-columns: 400px var(--spine);
    column-gap: var(--space-6); align-items: start;
  }

  /* The quadrants stand still: centred in the window height, held there by
     the page scroll rather than by a scroll container of their own. */
  .screen--dash .dash {
    grid-row: 1; grid-column: 2;
    align-self: start; position: sticky; top: var(--topbar-h);
    height: calc(100dvh - var(--topbar-h)); overflow: hidden;
    align-content: center;
    /* Symmetric padding, or align-content: center centres the content box
       while the uneven 80/48 padding pushes the block visibly low. */
    padding: var(--space-6);
  }

  /* The profile begins at the top of the page and runs as long as it needs.
     No overflow of its own: the window is its scroll. */
  .screen--dash .dashyou {
    grid-row: 1; grid-column: 1;
    border-top: none; border-left: none;
    border-right: var(--hair) solid var(--rule);
    margin-top: 0; overflow: visible;
    /* Left padding matters as much as right: the column sits against the
       screen's own edge rule, and without it the labels and the row
       underlines both start on the line. */
    padding: var(--space-5) var(--space-6);
  }
}


/* ══ bars that get out of the way ═════════════════════════════════════════
 * app.js adds .is-tucked while the reader is scrolling down and removes it
 * after sustained upward scrolling. The transforms live here so desktop can
 * opt out: at 1024 and up the top bar is the navigation, there is room for
 * it, and it never moves.
 *
 * The pill bar's base transform centres it, so the tuck has to compose with
 * translateX(-50%) rather than replace it. */
.m-pillbar--fixed, .topbar { transition: transform var(--dur) var(--ease); }
.m-pillbar--fixed.is-tucked { transform: translateX(-50%) translateY(calc(100% + var(--space-5))); }

@media (max-width: 1023px) {
  .topbar.is-tucked { transform: translateY(-100%); }
}
@media (min-width: 1024px) {
  .topbar.is-tucked { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .m-pillbar--fixed, .topbar { transition: none; }
}

/* The swipe hint belongs to phone pane mode and nowhere else. */
.dash-hint { display: none; }

/* ══ the phone's two dashboards ═══════════════════════════════════════════
 * Opening the app lands on the dashboard alone: a standing screen, no
 * scroll, no profile. Tapping the person icon opens the same screen in pane
 * mode, where the profile sits alongside and is reached by swiping sideways.
 * Both are dashboard.html; ?pane=you is the difference.
 *
 * These selectors carry both classes so they outrank the landing rules on
 * the same elements. The landing screen lays the wrapper out as a COLUMN,
 * and a panes rule of equal weight was leaving that direction in place, so
 * the two panes stacked and the whole thing became one long scroll. */
@media (max-width: 819px) {
  .dashyou { display: none; }

  /* The document itself must not scroll in pane mode. The screen alone was
     not enough: the two panes were widening the page and the BODY was
     scrolling, which is why the first pane moved vertically and a horizontal
     scrollbar appeared across the whole frame. html.is-panes is set by
     dashboard.html and locks both axes at the root. */
  html.is-panes, html.is-panes body { height: 100%; overflow: hidden; }
  .screen--dash.screen--panes { height: 100dvh; overflow: hidden; }

  .screen--dash.screen--panes .dash-wrap {
    flex: 1 1 auto; min-height: 0;
    width: 100%; max-width: 100%; min-width: 0;   /* the panes overflow INSIDE
                                                     this box, never past it */
    display: flex; flex-direction: row; flex-wrap: nowrap;
    overflow-x: auto; overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
  }
  .screen--dash.screen--panes .dash-wrap::-webkit-scrollbar { display: none; }

  .screen--dash.screen--panes .dash,
  .screen--dash.screen--panes .dashyou {
    flex: 0 0 100%; width: 100%; min-width: 100%; min-height: 0;
    scroll-snap-align: start;
  }

  /* Pane one stands still, exactly as the landing screen does. */
  .screen--dash.screen--panes .dash { position: relative; overflow: hidden; }

  /* Pane two is the only thing that scrolls, and only once swiped to. */
  .screen--dash.screen--panes .dashyou {
    display: block; border-top: none; margin-top: 0;
    overflow-y: auto; -webkit-overflow-scrolling: touch;
    padding: var(--space-5) var(--space-4) var(--space-6);
  }

  /* Just an arrow, in the bottom right corner of the pane.
   *
   * It is positioned against .dash, and .screen--hasbar already ends the
   * content 104px above the viewport to clear the pill bar. Measuring another
   * 104px up from there put the arrow 208px above the foot, on top of the
   * next-step card. Offsetting from the pane's own bottom edge is both
   * correct and independent of whatever the bar's height becomes. */
  .screen--dash.screen--panes .dash-hint {
    display: flex; position: absolute;
    left: auto; right: var(--space-4); bottom: var(--space-2);
    color: var(--ink-soft); pointer-events: none;
  }
  .screen--dash.screen--panes .dash-hint svg { animation: dash-nudge 2.4s var(--ease) infinite; }
}

@keyframes dash-nudge {
  0%, 62%, 100% { transform: translateX(0); }
  76%           { transform: translateX(4px); }
}
@media (prefers-reduced-motion: reduce) {
  .screen--dash.screen--panes .dash-hint svg { animation: none; }
}


/* The dashboard's main action takes the module colour at every width. Ink
 * read as the heaviest thing on the screen, which put the weight on the
 * button rather than on the score, and the colour is friendlier besides.
 * Resume tool stays quiet, so the two do not compete. */
.screen--dash .btn:not(.btn--quiet) { background: var(--solid-bg); color: var(--solid-fg);
                                      border-color: var(--solid-border); }


/* ══ micro-interactions ═══════════════════════════════════════════════════
 * The dashboard's two action pills lift on hover exactly as the next-step
 * card does, so the three things a reader can act on behave alike. Pointer
 * devices only: hover on a touchscreen fires on tap and leaves the element
 * stuck in its hovered state, which is why this is gated on the pointer
 * rather than on width. */
@media (hover: hover) and (pointer: fine) {
  .screen--dash .btn { transition: transform var(--dur) var(--ease); }
  .screen--dash .btn:hover  { transform: translateY(-1px); }
  .screen--dash .btn:active { transform: translateY(0); }
}


/* ══ reading surface ══════════════════════════════════════════════════════
 * Screen Spec 4. The spine holds the measure absolutely; the section index
 * becomes a COMPANION on the LEFT from 1024, matching where the dashboard
 * puts its companion. Paper background at every width: the long-form web
 * pages use a tinted ground, the app does not. */
.readbar { display: flex; align-items: center; gap: var(--space-2);
  padding: 10px var(--space-3); border-bottom: var(--hair) solid var(--rule);
  position: sticky; top: 0; background: var(--paper); z-index: 20; }
.readbar__where { flex: 1; min-width: 0; text-align: center;
  font-family: var(--font-sans); font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--ink-soft);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.readbar__prog { position: absolute; left: 0; bottom: -1px; height: 2px; background: var(--fill); }

/* Bookmark ribbon, on-state. The difference a reader has to see is FILLED
   versus outline, not one colour versus another: three module colours
   (Start Here, 09, 10) are light by the light-colour rule, so a state told
   in colour alone would be near-invisible in those three. Filling the shape
   reads at a glance in all thirteen, and aria-pressed carries it for anyone
   not going by sight. */
.readbar [data-bi="bookmark"].is-on { color: var(--line); }
.readbar [data-bi="bookmark"].is-on svg { fill: currentColor; }

.read { padding: var(--space-5) var(--space-4) var(--space-7); }
.read__chip { display: inline-block; padding: 4px 10px; border-radius: var(--radius-square);
  background: var(--chip-bg); color: var(--chip-fg);
  font-family: var(--font-sans); font-weight: 500; font-size: 13px; letter-spacing: .06em; }
.read__h2 { font-family: var(--font-serif); font-weight: var(--fw-serif);
  font-size: clamp(30px, 7vw, 40px); line-height: 1.1; letter-spacing: var(--ls-tight);
  margin: var(--space-3) 0 0; max-width: var(--measure); text-wrap: balance; }
.read__h2 i { font-style: italic; color: var(--fill-text); }
.read__dek { font-family: var(--font-serif); font-style: italic; font-size: 19px;
  line-height: 1.45; color: var(--ink-mid); margin: var(--space-3) 0 0;
  max-width: var(--measure); text-wrap: pretty; }

.read__spec { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-3);
  margin: var(--space-4) 0 0; padding-top: var(--space-3);
  border-top: var(--hair) solid var(--rule); max-width: var(--measure); }
.read__spec b { font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--fill-text); }
.read__spec span { font-size: 15px; color: var(--ink-mid); }

.read__body { max-width: var(--measure); margin-top: var(--space-5); }
.read__body p { font-size: var(--t-body); line-height: var(--lh-body);
  margin: 0 0 var(--space-4); text-wrap: pretty; }
/* ── the six prose treatments ────────────────────────────────────────────
 * These are the only ways emphasis is applied inside reading copy. Anything
 * else is a one-off and does not belong.
 *
 *   .lead-in     bold DM Sans opening a paragraph ("The brief.")
 *   .accent      italic in the module colour, for the turn in a sentence
 *   a            ink, underlined, offset clear of the descenders
 *   mark         a warm neutral band behind the words
 *   .takeaway    a left rule, no fill, for the one thing to remember
 *   .boxed       hairline box under a 2px module rule, with an eyebrow
 *   .pullquote   serif italic behind a quote glyph, with attribution
 *
 * The highlight band is a neutral, NOT marigold. --highlight-marigold is a
 * marker and swatch colour and was never meant to sit behind body copy. */
.read__body .lead-in { font-weight: 600; }

/* The accent is SERIF italic in the module colour. The reference shows serif
   letterforms breaking out of the sans body; italic sans is not the same
   thing and was wrong in the first build. */
.read__body .accent  { font-family: var(--font-serif); font-style: italic;
                       font-size: 1.06em; color: var(--fill-text); }

/* Link: a plain ink underline, close to the text. */
.read__body a { color: var(--ink); text-decoration: underline;
                text-decoration-thickness: 1px; text-underline-offset: 2px; }
.read__body a:hover { text-decoration-thickness: 2px; }
/* The author's highlight: the words go bold AND take a translucent stroke
   that sits against them, cutting the descenders rather than floating below.
   It is drawn as a gradient rather than a border so it can touch the text and
   carry transparency at once. Neutral, never the module colour, and never
   marigold: marigold is the reader's (see .hl). */
.read__body mark { background: linear-gradient(to top,
                     var(--underline) .34em, transparent .34em);
                   color: inherit; font-weight: 600; }

/* ── the reader's own highlights ─────────────────────────────────────────
 * The one place marigold sits behind body copy, and the only yellow in the
 * app. It is the reader's mark, not the author's, so it has to be instantly
 * findable when scanning back through a long module. */
.hl { background: var(--highlight-marigold); color: var(--ink);
      padding: 1px 0; box-decoration-break: clone; -webkit-box-decoration-break: clone;
      cursor: pointer; }

/* The little prompt that follows a selection. */
.hlbar { position: absolute; z-index: 65; display: flex; gap: 2px;
  background: var(--ink); border-radius: var(--radius-pill); padding: 4px; }
.hlbar button { min-height: 36px; padding: 0 14px; border: none; background: none;
  color: var(--paper); font-family: var(--font-sans); font-weight: 500; font-size: 14px;
  border-radius: var(--radius-pill); cursor: pointer; }
.hlbar button:hover { background: #3D3A35; }
.read__sub { font-family: var(--font-serif); font-size: 24px; line-height: 1.25;
  margin: var(--space-6) 0 var(--space-3); }

/* Takeaway: a thin left rule and nothing else. No fill, no box, no label.
   The reference rule is hairline-thin and neutral, not a 5px module bar. */
.read__takeaway { border-left: 2px solid var(--ink-soft);
  padding-left: var(--space-4); margin: var(--space-5) 0; }
.read__takeaway p { margin: 0; font-size: 19px; line-height: 1.55; }

/* Boxed paragraph: a hairline box sitting under a heavier module-colour top
   rule, with a quiet eyebrow naming what it is. */
.read__boxed { border: var(--hair) solid var(--rule); border-top: 2px solid var(--ink);
  padding: var(--space-4); margin: var(--space-5) 0; }
.read__boxed .eyebrow-label { margin-bottom: var(--space-2); }
.read__boxed p:last-child { margin-bottom: 0; }

/* Pull quote: the glyph, then serif italic, then the attribution. */
.read__pullquote { margin: var(--space-6) 0; }
.read__pullquote::before { content: "\201C"; display: block;
  font-family: var(--font-serif); font-size: 56px; line-height: .6;
  color: var(--rule); margin-bottom: var(--space-2); }
.read__pullquote p { font-family: var(--font-serif); font-style: italic;
  font-size: 23px; line-height: 1.4; margin: 0; }
.read__pullquote cite { display: block; margin-top: var(--space-3); font-style: normal;
  font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--ink-soft); }

/* Numbered items: principles, checklists, manifesto lines, conditionals. */
.read__item { display: flex; gap: var(--space-4); padding: var(--space-4) 0;
  border-top: var(--hair) solid var(--rule); max-width: var(--measure); }
.read__item-n { flex: none; min-width: 44px; font-family: var(--font-serif);
  font-style: italic; font-size: 30px; line-height: 1; color: var(--fill-text); }
.read__item-h { font-family: var(--font-serif); font-size: 20px; line-height: 1.3;
  margin: 0 0 var(--space-2); }
.read__item p { margin: 0; color: var(--ink-mid); line-height: var(--lh-body); }

/* Cards carry a group label above them and run in the spine, not beside it. */
.read__group { font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--ink-soft);
  margin: var(--space-6) 0 var(--space-2); }
.read__card { border: var(--hair) solid var(--rule); padding: var(--space-4);
  margin: 0 0 var(--space-3); max-width: var(--measure); }
.read__card h4 { font-family: var(--font-serif); font-size: 20px; margin: 0 0 var(--space-2); }

/* Inside a card, the spec rows sit tighter and read as a caption to the card
   rather than as a second header for the section. */
.read__spec--card { margin: 0 0 var(--space-3); padding: 0 0 var(--space-3);
  border-bottom: var(--hair) solid var(--rule); gap: var(--space-1) var(--space-3); }

/* The two lines that make an architect note worth having: who to raise it
   with, and how it goes wrong. Distinguished by weight and an eyebrow, never
   by colour alone, since the signal colours mean how sure a fact is. */
.read__raise, .read__pitfall { margin: var(--space-3) 0 0; font-size: var(--t-body-sm);
  line-height: var(--lh-body); color: var(--ink-mid); }
.read__raise .eyebrow-label, .read__pitfall .eyebrow-label { display: block;
  margin-bottom: 4px; color: var(--fill-text); }
.read__pitfall { padding-top: var(--space-3); border-top: var(--hair) solid var(--rule); }
.read__pitfall .eyebrow-label { color: var(--ink-soft); }
.read__pitfall--section { max-width: var(--measure); margin-top: var(--space-5); }
/* The section-level raise, same treatment as the section-level pitfall it sits
   above. Constrained to the measure so it reads as a note to the section
   rather than continuing the body column. */
.read__raise--section { max-width: var(--measure); margin-top: var(--space-5); }
.read__card p:last-child { margin-bottom: 0; }

/* ── the way back to the build notes ───────────────────────────────────
 * Every build note links forward to the one section that covers it at
 * length. This is the same relationship read backwards, and it is
 * deliberately the smallest thing on the page: a reader who wants it knows
 * what it is, and a reader who does not should not have to step over it. */
.read__notes { max-width: var(--measure); margin-top: var(--space-4); }
.read__notes-link {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 32px; padding: 0 13px;
  border: var(--hair) solid var(--rule); border-radius: var(--radius-pill);
  font-size: 13px; font-weight: 500; color: var(--ink-mid);
  text-decoration: none; background: transparent;
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.read__notes-link:hover { border-color: var(--fill); color: var(--fill-text); }
.read__notes-link b { font-weight: 600; font-variant-numeric: tabular-nums;
  color: var(--fill-text); }
.read__notes-link svg { width: 13px; height: 13px; flex: none; }

/* ── sequence items ────────────────────────────────────────────────────
 * A stage label instead of a number: "Month 1" over "Construction starts".
 * min-width: 0 on the prose column, or a long unbroken stage label makes the
 * row wider than the screen and the page scrolls sideways. */
.read__items { max-width: var(--measure); }
.read__item--stage { align-items: flex-start; }
.read__item--stage > div:last-child { min-width: 0; }
.read__stage { flex: none; width: 108px; }
.read__stage-n { display: block; font-family: var(--font-serif); font-size: 20px;
  line-height: 1.2; color: var(--fill-text); }
.read__stage-when { display: block; margin-top: 4px; font-weight: var(--fw-label);
  font-size: var(--t-label-sm); letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--ink-soft); }
.read__cost { margin: var(--space-2) 0 0; font-size: var(--t-body-sm); color: var(--ink); }
.read__cost span { font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--ink-soft);
  margin-right: var(--space-1); }
@media (max-width: 560px) {
  .read__item--stage { display: block; }
  .read__stage { width: auto; margin-bottom: var(--space-2); }
  .read__stage-n, .read__stage-when { display: inline; }
  .read__stage-when::before { content: " \00b7 "; }
}

/* ── conditional items ─────────────────────────────────────────────────
 * If on the left, Then on the right, from 700px. The labels are the section's
 * own ifLabel and thenLabel, which are not always the words If and Then. */
.read__cond { max-width: var(--measure); padding: var(--space-4) 0;
  border-bottom: var(--hair) solid var(--rule); }
.read__cond:last-child { border-bottom: 0; }
.read__cond-if p { margin: var(--space-1) 0 0; font-family: var(--font-serif);
  font-size: 19px; line-height: 1.35; color: var(--ink); }
.read__cond-then { min-width: 0; margin-top: var(--space-3); }
.read__cond-then p { margin: var(--space-1) 0 0; font-size: var(--t-body-sm);
  line-height: var(--lh-body); color: var(--ink-mid); }
.read__cond-note { font-style: italic; color: var(--ink-soft) !important; }
@media (min-width: 700px) {
  .read__cond { display: grid; grid-template-columns: 1fr 1.4fr; column-gap: var(--space-5); }
  .read__cond-if { min-width: 0; }
  .read__cond-then { margin-top: 0; }
}

/* ── datatable ─────────────────────────────────────────────────────────
 * Nineteen sections are a table and nothing else, so the table is the
 * reading, not an aside. The WRAPPER scrolls, never the table: `display:
 * block` on a <table> stops cells forming columns and any width on a cell
 * stops meaning anything. min-width on the table is what makes the wrapper
 * scroll rather than the columns crushing on a phone. */
.read__tablewrap { max-width: var(--measure); margin: var(--space-5) 0 var(--space-3);
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  border-top: var(--line-bold) solid var(--ink);
  border-bottom: var(--hair) solid var(--rule);
  /* A clipped table on a phone looks like a table that ends, not one that
     scrolls. These four layers are the CSS-only scroll shadow: the two `local`
     covers ride with the content and hide the two `scroll` fades except when
     there is more to see in that direction. No script, and it cannot get out
     of step with the actual scroll position. */
  background:
    linear-gradient(to right, var(--paper) 30%, rgba(252,251,249,0)) left center / 40px 100% no-repeat local,
    linear-gradient(to left,  var(--paper) 30%, rgba(252,251,249,0)) right center / 40px 100% no-repeat local,
    linear-gradient(to right, rgba(33,31,28,.16), rgba(33,31,28,0)) left center / 14px 100% no-repeat scroll,
    linear-gradient(to left,  rgba(33,31,28,.16), rgba(33,31,28,0)) right center / 14px 100% no-repeat scroll; }
.read__table { border-collapse: collapse; width: 100%; min-width: 460px;
  font-size: var(--t-body-sm); line-height: 1.45; }
.read__table th, .read__table td { text-align: left; vertical-align: top;
  padding: var(--space-3) var(--space-3) var(--space-3) 0;
  border-bottom: var(--hair) solid var(--rule); }
.read__table th:last-child, .read__table td:last-child { padding-right: 0; }
.read__table thead th { font-family: var(--font-sans); font-weight: var(--fw-label);
  font-size: var(--t-label-sm); letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--fill-text);
  padding-top: var(--space-2); padding-bottom: var(--space-2); }
.read__table tbody th { font-weight: 600; color: var(--ink); }
.read__table tbody td { color: var(--ink-mid); }
.read__table tbody tr:last-child th,
.read__table tbody tr:last-child td { border-bottom: 0; }
.read__tablefoot { max-width: var(--measure); font-size: var(--t-meta);
  line-height: 1.5; color: var(--ink-soft); margin: 0 0 var(--space-5); }

/* ── compare ───────────────────────────────────────────────────────────
 * Two labelled columns from 700px, two stacked blocks below it. min-width: 0
 * is what lets each column shrink; without it a long unbroken label makes the
 * grid wider than the screen and the whole page scrolls sideways. */
.read__compare { max-width: var(--measure); margin: var(--space-5) 0; }
.read__side { min-width: 0; }
.read__side + .read__side { margin-top: var(--space-5); }
.read__side-h { font-family: var(--font-sans); font-weight: var(--fw-label);
  font-size: var(--t-label-sm); letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--fill-text);
  margin: 0 0 var(--space-2); padding-bottom: var(--space-2);
  border-bottom: var(--line-bold) solid var(--ink); }
.read__side-item { padding: var(--space-3) 0;
  border-bottom: var(--hair) solid var(--rule); }
.read__side-item:last-child { border-bottom: 0; }
.read__side-item h4 { font-family: var(--font-serif); font-size: 19px;
  line-height: 1.3; margin: 0 0 var(--space-1); }
.read__side-item p { margin: 0; font-size: var(--t-body-sm);
  line-height: var(--lh-body); color: var(--ink-mid); }
@media (min-width: 700px) {
  .read__compare { display: grid; grid-template-columns: 1fr 1fr;
    column-gap: var(--space-5); }
  .read__side + .read__side { margin-top: 0; }
}

/* Glossary: a dotted underline, never colour alone, and the definition is
   reachable by keyboard as well as by pointer. */
.gloss { border-bottom: 1px dotted var(--fill); cursor: help; background: none;
  border-top: 0; border-left: 0; border-right: 0; padding: 0; font: inherit; color: inherit; }
.gloss__pop { position: absolute; z-index: 60; max-width: 300px;
  background: var(--paper); border: var(--line-w) solid var(--ink);
  padding: var(--space-3); font-size: 15px; line-height: 1.45; color: var(--ink); }
.gloss__pop b { display: block; font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--fill-text);
  margin-bottom: 6px; }
/* The real definitions run to a paragraph or more, so the panel scrolls
   rather than growing past the screen. The demo set was one line each and
   nothing here was ever tested against a long one. */
.gloss__def { display: block; max-height: 40vh; overflow-y: auto;
  -webkit-overflow-scrolling: touch; }
.gloss__more { display: inline-block; margin-top: var(--space-2);
  font-family: var(--font-sans); font-weight: var(--fw-label);
  font-size: var(--t-label-sm); letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--fill-text); text-decoration: none;
  border-bottom: var(--hair) solid currentColor; }

/* Foot of the section. */
.read__next { display: flex; gap: var(--space-3); padding: var(--space-6) var(--space-4);
  border-top: var(--hair) solid var(--rule); }
.read__next .btn { flex: 1; }

/* The end-of-module band: the one dark surface in the reading flow. */
.read__end { background: var(--ink); color: var(--paper); padding: var(--space-6) var(--space-4); }
.read__end p { max-width: var(--measure); line-height: var(--lh-body); margin: var(--space-2) 0 0; }

/* ── the section index, a companion from 1024 ─────────────────────────── */
.readidx { display: none; }

@media (min-width: 1024px) {
  .screen--read { max-width: calc(var(--spine) + 320px + var(--space-6)); }
  .screen--read .read-wrap {
    display: grid; grid-template-columns: 320px var(--spine);
    column-gap: var(--space-6); align-items: start;
  }
  .screen--read .readidx {
    display: block; grid-row: 1; grid-column: 1;
    align-self: start; position: sticky; top: var(--topbar-h);
    max-height: calc(100dvh - var(--topbar-h)); overflow-y: auto;
    border-right: var(--hair) solid var(--rule);
    /* Left padding as well as right: the column sits against the screen's own
       edge rule and the labels were starting on the line. */
    padding: var(--space-5) var(--space-5) var(--space-6) var(--space-5);
  }
  /* The back arrow is redundant where the whole module is listed alongside. */
  .screen--read .readbar [data-bi="read-back"] { display: none; }
  .screen--read .read-main { grid-row: 1; grid-column: 2; min-width: 0; }
  /* `relative`, not `static`. Both stop the bar sticking, which is the point
     of this rule at a width where the contents column is already alongside.
     But `static` also stops it being a positioning context, and the progress
     line inside it is absolutely positioned: with no positioned ancestor it
     escaped to the page and was drawn about a thousand pixels lower, a brown
     rule straight across the reading, growing longer with every section.
     `relative` lays out identically and gives the line something to hold on
     to. checks/04-render.cjs asserts the line sits inside the bar at 390,
     900 and 1440. */
  .screen--read .readbar { position: relative; border-bottom: none; }
}

.readidx__head { font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--ink-soft); }
.readidx__title { font-family: var(--font-serif); font-size: 22px; margin: var(--space-1) 0 var(--space-3); }
.readidx__count { font-size: 13px; color: var(--ink-mid); font-variant-numeric: tabular-nums;
  padding-bottom: var(--space-3); border-bottom: var(--hair) solid var(--rule); }
.readidx__row { display: flex; gap: var(--space-2); padding: 9px var(--space-3) 9px 0;
  color: var(--ink); text-decoration: none; font-size: 15px; line-height: 1.35;
  border-left: var(--bar-h) solid transparent; padding-left: var(--space-3); }
.readidx__row:hover { border-left-color: var(--rule); }
.readidx__row.is-here { border-left-color: var(--fill); font-weight: 500; }
.readidx__n { flex: none; color: var(--ink-soft); font-size: 13px;
  font-variant-numeric: tabular-nums; padding-top: 2px; }


/* ══ reading: the retractable contents column ═════════════════════════════
 * Desktop only. Retracted, the reading column centres on the page instead of
 * sitting off to the right. Note what this does and does not do: prose is
 * capped at --measure by decision, so the LINES do not get longer. What the
 * reader gains is the column in the middle of the screen and more room for
 * the things that may run full spine, tables and video. */
.readbar__idx { display: none; }
.readidx__bar { display: flex; align-items: center; justify-content: space-between;
                gap: var(--space-2); }

@media (min-width: 1024px) {
  .readbar__idx { display: grid; }

  .screen--read.is-idxoff { max-width: var(--spine); }
  .screen--read.is-idxoff .read-wrap { display: block; }
  .screen--read.is-idxoff .readidx { display: none; }
  /* The back arrow comes back when the contents are away: with no index
     beside it there is otherwise no route out of the section. */
  .screen--read.is-idxoff .readbar [data-bi="read-back"] { display: grid; }
}

/* Next and previous carry their destination. The titles are long, so the
   sub line truncates rather than wrapping the button out of shape. */
.read__next .btn--stack { min-width: 0; }
.read__next .btn--stack .btn__sub {
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}


/* ══ forms ════════════════════════════════════════════════════════════════
 * The family the system never had. It unlocks onboarding, the 24 tools,
 * search and account editing, and it fixes the capture sheet, which has been
 * rendering .tool-input and .tool-textarea with no styles at all.
 *
 * Rules of the house apply: paper ground, ink text, sharp rectangles, no
 * shadows. Pills stay reserved for actions, so nothing here is a pill. */
.field { margin-bottom: var(--space-4); }
.field__label { display: block; margin-bottom: 6px;
  font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--ink-mid); }
.field__help { margin-top: 6px; font-size: 14px; line-height: 1.45; color: var(--ink-soft); }

.tool-input, .tool-textarea, .field select {
  width: 100%; min-height: var(--touch);
  padding: 12px 14px; border: var(--line-w) solid var(--rule); border-radius: var(--radius-0);
  background: var(--paper); color: var(--ink);
  font-family: var(--font-sans); font-size: var(--t-body); line-height: 1.4;
}
.tool-textarea { min-height: 120px; resize: vertical; }
.tool-input::placeholder, .tool-textarea::placeholder { color: var(--ink-soft); }
.tool-input:focus-visible, .tool-textarea:focus-visible, .field select:focus-visible,
.check input:focus-visible + .check__box, .toggle:focus-visible {
  outline: 2px solid var(--fill); outline-offset: 2px;
}

/* Native select, with the chevron drawn rather than left to the browser. */
/* The signal glyphs at the head of each option come from a different font
   than the label text, so their sizes drift. Holding the line-height steady
   keeps the row height even whichever glyph is showing. */
/* Deliberately NOT scoped to .field. Every select on an app screen takes this,
   because the one that was left out (the two companion pickers on the
   dashboard, which sit in .dashyou__row rather than .field) rendered as a raw
   operating-system control in the middle of the design and read as a fault.
   Anything added later is covered without anyone remembering to opt in.
   Only the box treatment is shared; the .field rule above still owns the
   full-width form layout, so a select sitting inline in a row keeps its own
   natural width instead of stretching across the screen. */
select { appearance: none; -webkit-appearance: none; padding-right: 42px; line-height: 1.4;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' fill='none' stroke='%23211F1C' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 8 4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center; }

/* The box, for selects outside .field (which has its own width rule above). */
select:not(.field select) {
  min-height: var(--touch); padding-top: 10px; padding-bottom: 10px; padding-left: 14px;
  border: var(--line-w) solid var(--rule); border-radius: var(--radius-0);
  background-color: var(--paper); color: var(--ink);
  font-family: var(--font-sans); font-size: var(--t-body-sm);
  cursor: pointer;
}
select:focus-visible { outline: 2px solid var(--fill); outline-offset: 2px; }

/* Invalid: the hard-stop red on the rule and on the reason. Still never
 * colour alone, the reason is always written out. */
.field.is-invalid .tool-input,
.field.is-invalid .tool-textarea,
.field.is-invalid select { border-color: var(--signal-stop); }
.field__error { display: flex; align-items: center; gap: 6px; margin-top: 6px;
  font-size: 14px; font-weight: 500; color: var(--signal-stop); }

/* Choices. Checkbox is a square rounded at 8%, radio is a circle, both filled
 * with the module colour when chosen. The whole row is the target. */
.check { display: flex; align-items: flex-start; gap: var(--space-3);
  min-height: var(--touch); padding: 10px 0; cursor: pointer; }
.check input { position: absolute; opacity: 0; width: 0; height: 0; }
.check__box { width: 22px; height: 22px; flex: none; margin-top: 1px;
  border: var(--line-w) solid var(--ink); border-radius: var(--radius-square);
  display: grid; place-items: center; background: var(--paper); }
.check--radio .check__box { border-radius: 50%; }
.check input:checked + .check__box { background: var(--fill); border-color: var(--fill); }
.check input:checked + .check__box::after { content: ""; width: 10px; height: 10px;
  background: var(--on-fill); border-radius: inherit; }
.check__text { flex: 1; min-width: 0; font-size: var(--t-body); line-height: 1.4; }
.check__note { display: block; font-size: 14px; color: var(--ink-mid); margin-top: 2px; }

/* Toggle, promoted out of the You pane so there is one of them. */
.toggle { width: 44px; height: 26px; flex: none; border-radius: 999px;
  border: var(--line-w) solid var(--rule); background: var(--rule);
  position: relative; cursor: pointer; }
/* The switch is drawn 26px tall because that is what a switch looks like, but
   a finger needs 44. The target is extended above and below without changing
   what is drawn. */
.toggle::before { content: ""; position: absolute; left: 0; right: 0;
  top: -9px; bottom: -9px; }
.toggle::after { content: ""; position: absolute; top: 2px; left: 2px;
  width: 20px; height: 20px; border-radius: 50%; background: var(--paper);
  transition: transform var(--dur) var(--ease); }
.toggle[aria-pressed="true"] { background: var(--fill); border-color: var(--fill); }
.toggle[aria-pressed="true"]::after { transform: translateX(18px); }

/* Onboarding: where you are in a run of questions. Fifteen screens need it. */
.stepper { display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) 0; }
.stepper__count { flex: none; font-size: 13px; font-weight: 500; color: var(--ink-mid);
  font-variant-numeric: tabular-nums; }
.stepper__track { flex: 1; height: 2px; background: var(--rule); }
.stepper__fill { height: 100%; background: var(--fill); }

/* ══ tables ═══════════════════════════════════════════════════════════════
 * Reading copy and tool outputs both need these. Hairlines only, label-style
 * head, tabular numerals so columns of figures line up. */
.tbl { width: 100%; border-collapse: collapse; margin: var(--space-5) 0; font-size: 15px; }
.tbl th { text-align: left; padding: 10px 12px 10px 0;
  border-bottom: var(--line-w) solid var(--ink);
  font-weight: var(--fw-label); font-size: var(--t-label-sm);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--ink-mid); }
.tbl td { padding: 12px 12px 12px 0; border-bottom: var(--hair) solid var(--rule);
  vertical-align: top; line-height: 1.45; }
.tbl td:last-child, .tbl th:last-child { padding-right: 0; }
.tbl [data-num] { text-align: right; font-variant-numeric: tabular-nums; }
.tbl-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ══ states ═══════════════════════════════════════════════════════════════
 * Offline, loading and failure. .empty already covers "nothing here yet". */
.banner { display: flex; align-items: center; gap: var(--space-2);
  padding: 10px var(--space-4); background: var(--ink); color: var(--paper);
  font-size: 14px; font-weight: 500; }
.banner--quiet { background: none; color: var(--ink);
  border-bottom: var(--line-w) solid var(--ink); }

.skeleton { background: var(--rule); height: 14px; border-radius: 2px;
  margin-bottom: 10px; }
.skeleton--title { height: 26px; width: 60%; margin-bottom: var(--space-3); }
.skeleton--short { width: 40%; }
@media (prefers-reduced-motion: no-preference) {
  .skeleton { animation: skel 1.6s var(--ease) infinite; }
}
@keyframes skel { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }

.state { padding: var(--space-6) var(--space-4); text-align: center; }
.state__title { font-family: var(--font-serif); font-size: 22px; margin-bottom: var(--space-2); }
.state__note { color: var(--ink-mid); line-height: var(--lh-body);
  max-width: 42ch; margin: 0 auto var(--space-4); }


/* ══ safety-signal dots ═══════════════════════════════════════════════════
 * How full is the fact. The dot fills as certainty grows: solid green means
 * measured on your site, half amber means a typical range, an empty outline
 * means unknown, ask before deciding.
 *
 * Hard stops break the shape language on purpose: square and red, so they can
 * never be mistaken for a certainty state.
 *
 * Shape carries the meaning as much as colour does, because these have to
 * survive a greyscale photocopy and a reader who does not see colour. The
 * word is always written alongside. */
.dot { width: 14px; height: 14px; flex: none; display: inline-block;
       border-radius: 50%; border: 2px solid var(--ink); vertical-align: -2px; }
.dot--known     { background: var(--signal-known); border-color: var(--signal-known); }
.dot--estimated { border-color: var(--signal-estimated);
                  background: linear-gradient(to right,
                    var(--signal-estimated) 50%, transparent 50%); }
.dot--unknown   { background: transparent; border-color: var(--ink); }
/* Square, never round. */
.dot--stop      { border-radius: 0; background: var(--signal-stop);
                  border-color: var(--signal-stop); }

.signal { display: flex; align-items: baseline; gap: var(--space-2);
          padding: 6px 0; font-size: 15px; }
.signal b { font-weight: 600; }
.signal span { color: var(--ink-mid); }


/* The app-side tool screen and its timeline renderer were removed in July
 * 2026. Tools are not rebuilt in the app: they keep their own HTML and take
 * bi-tool-skin.css, bi-tool-normalise.js and bi-nav.js. Two routes to the
 * same tool is how a product ends up with two versions of the truth.
 *
 * What that code used and what stayed: the field, choice, table, state and
 * signal families above are system components and are still here, because
 * onboarding and the small screens need them. Only the tool-specific
 * arrangement went. */


/* The app-side onboarding screens were removed in July 2026. Onboarding is
 * its own flow, BI_onboarding_flow_final.html, with its own copy and its own
 * logic, and it takes the skin like the tools do. Two routes to the same
 * fifteen screens is how a product ends up with two versions of the truth. */


/* ── Print ──────────────────────────────────────────────────────────────
 * Anything a reader prints or saves as PDF is a document they take into a
 * meeting, so none of the app goes with it. bi-nav.js carries the matching
 * block for tool pages, whose chrome it injects itself and which therefore
 * cannot be reached from here.
 *
 * The padding reset is half the fix: .screen--hasbar reserves room for the
 * fixed bars, and without clearing it every printed page carries a band of
 * empty white where the bar used to be. */

/* ══ the library footer ═════════════════════════════════════════════════
 * Restored from the original module prototypes, which closed with this and
 * which the current pages do not. Rebuilt on the design system's own tokens
 * rather than ported: the original was written against JetBrains Mono, since
 * retired, and hard-coded its own rgba() paper colour in forty-eight places.
 * [data-surface="dark"] already existed and is documented as being for
 * exactly this, so the footer takes its ink and rules from there and sets
 * almost no colour of its own.
 *
 * Mobile first: one column, and the four-column shelf only above 900. */
.bifoot { margin-top: var(--space-7); }
.bifoot a { color: inherit; }

/* Underlines on the dark surface.
 *
 * The base rule is `a { text-decoration-color: var(--line) }`, and inside
 * [data-surface="dark"] --line resolves to --on-dark-fill #C9784B. That put a
 * saturated terracotta rule under roughly forty links at once: every module,
 * every tool, every system link. One accent per screen is the rule, and this
 * was forty, so the shelf read as a rash rather than as a list.
 *
 * On paper the underline carries the module colour because there is one link
 * in a paragraph. On ink the underline is structure, not accent, so it goes
 * paper and quiet, and hover does the work colour was doing: the rule
 * brightens to full paper and the word comes with it. Nothing moves, nothing
 * changes hue, and the strongest thing on the surface is still whatever the
 * reader is pointing at. */
[data-surface="dark"] a { text-decoration-color: var(--on-dark-soft); }
[data-surface="dark"] a:hover,
[data-surface="dark"] a:focus-visible { text-decoration-color: var(--on-dark); }

/* One cell per module, the whole library as a strip. Decorative and
   keyboard-skipped; every destination in it appears again as real text
   below, so nothing is reachable only by colour. */
.bifoot__ribbon { display: grid; grid-auto-flow: column; height: 4px; }
.bifoot__cell { display: block; height: 100%; transition: filter 160ms ease; }
.bifoot__cell:hover { filter: brightness(1.25); }

.bifoot__next { padding: var(--space-7) var(--space-4) var(--space-6);
  border-bottom: var(--hair) solid var(--rule);
  display: grid; gap: var(--space-4); align-items: end; }
.bifoot__eyebrow { font-family: var(--font-sans); font-size: var(--t-label);
  font-weight: 500; letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-mid); margin-bottom: var(--space-3); }
.bifoot__nexth { font-family: var(--font-serif); font-size: 34px; line-height: 1.12;
  color: var(--on-dark); max-width: 18em; text-wrap: balance; }
/* The accent word is the module you are going to, italic, the way every other
   headline in the system carries exactly one italic word. */
.bifoot__nexth em { font-style: italic; }
.bifoot__cta { justify-self: start; font-family: var(--font-sans); font-size: var(--t-body-sm);
  font-weight: 500; letter-spacing: .04em; text-transform: uppercase;
  padding: 11px 20px; border: var(--hair) solid var(--on-dark-soft); border-radius: 999px;
  /* An action, not a link: the pill and its border already say it is pressable,
     so the inherited underline was a second answer to a question nobody asked,
     and it was the one drawing a terracotta line through the word CONTINUE. */
  text-decoration: none;
  transition: border-color 160ms ease, background 160ms ease; }
.bifoot__cta:hover { border-color: var(--on-dark); background: rgba(252,251,249,.07); }

.bifoot__shelf { display: grid; gap: var(--space-6);
  padding: var(--space-6) var(--space-4); }
.bifoot__brandname { font-family: var(--font-serif); font-size: 26px; line-height: 1;
  color: var(--on-dark); margin-bottom: var(--space-2); }
.bifoot__tag { font-size: var(--t-body-sm); line-height: var(--lh-body);
  color: var(--ink-mid); max-width: 34em; margin-bottom: var(--space-3); }
.bifoot__tag em { font-style: normal; font-weight: 600; color: var(--on-dark); }
.bifoot__meta { font-size: var(--t-label-sm); letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-soft); }

.bifoot__colh { font-family: var(--font-sans); font-size: var(--t-label); font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--ink-mid);
  margin-bottom: var(--space-3); padding-bottom: var(--space-2);
  border-bottom: var(--hair) solid var(--rule); }

.bifoot__mods, .bifoot__links, .bifoot__toollist { list-style: none; margin: 0; padding: 0; }
.bifoot__mods { display: grid; gap: var(--space-2); }
.bifoot__mod { display: grid; grid-template-columns: 10px 2.4em 1fr;
  align-items: baseline; gap: var(--space-2); }
/* A colour-filled square, so it rounds like the logo tile. §6 puts the radius
   at 8% of the size and then names the exception: tiny swatches take a flat
   2px, because 8% of 10px is 0.8px and reads as an aliasing artefact rather
   than as a decision. Same 2px the mobile row swatch uses. */
/* Baseline, not centre. Five of the twelve module names wrap to two lines, and
   align-self:center put their swatch halfway down the pair, so a column that
   should read as one straight edge of colour beside one straight edge of type
   stepped in and out by half a line. Baseline sits every swatch on the first
   line of its own name, wrapped or not. */
.bifoot__swatch { width: 10px; height: 10px; align-self: baseline;
  border-radius: 2px; }
.bifoot__num { font-size: var(--t-label); color: var(--ink-soft); }
.bifoot__modname { font-family: var(--font-serif); font-size: 17px; color: var(--on-dark); }
/* No hover rule here any more. It said `text-decoration: underline`, and the
   shorthand quietly resets the two longhands the base `a` rule had set, so a
   module name's underline changed THICKNESS as the pointer crossed it while
   every other link in the shelf held still. Hover is one thing on this
   surface: the underline brightens. That lives with the other dark-surface
   link rules above. */

.bifoot__tools { display: grid; gap: var(--space-3); }
.bifoot__shelfmark { display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--t-label-sm); letter-spacing: .18em; color: var(--ink-soft);
  padding-bottom: 5px; border-bottom: var(--hair) solid var(--rule); }
.bifoot__shelfmark span { width: 18px; height: 4px; }
.bifoot__toollist { display: grid; gap: 5px; margin-top: var(--space-1);
  font-size: var(--t-body-sm); color: var(--ink-mid); }
.bifoot__toollist a:hover { color: var(--on-dark); }
.bifoot__links { display: grid; gap: var(--space-2);
  font-size: var(--t-body-sm); color: var(--ink-mid); }
.bifoot__links a:hover { color: var(--on-dark); }

.bifoot__base { display: flex; flex-wrap: wrap; gap: var(--space-3);
  justify-content: space-between;
  padding: var(--space-4); border-top: var(--hair) solid var(--rule);
  font-size: var(--t-label-sm); letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-soft); }

@media (min-width: 700px) {
  .bifoot__next { grid-template-columns: 1fr auto; }
  .bifoot__nexth { font-size: 44px; }
  .bifoot__tools { grid-template-columns: 1fr 1fr; gap: var(--space-4); }
}
@media (min-width: 900px) {
  .bifoot__shelf { grid-template-columns: 1.2fr 1.1fr 1.4fr .9fr; }
  .bifoot__next, .bifoot__shelf, .bifoot__base { padding-left: var(--space-6);
    padding-right: var(--space-6); }
}

/* The footer is navigation. On paper it is a page of dead links. */
@media print { .bifoot { display: none; } }

@media print {
  [data-bi="topbar"], [data-bi="pillbar"], [data-bi="drawer"],
  [data-bi="scrim"], [data-bi="sheet"], [data-bi="toast"],
  .appbar, .m-pillbar, .topbar, .drawer, .scrim, .toast,
  .screen__spacer {
    display: none !important;
  }
  .screen, .screen--hasbar { padding-top: 0 !important; padding-bottom: 0 !important; }
  body { background: #fff; }
  /* Links print as their words. A URL in brackets after every one of them is
     noise on a page someone reads on paper. */
  a { text-decoration: none; color: inherit; }
  /* Never split a row, a card or a signal across two sheets. */
  .row, .librow, .libitem, .actioncard, .signal, .gterm, .faq { break-inside: avoid; }
}
