/* ═════════════════════════════════════════════════════════════════════════════
   BISBE — the hi-fi lifecycle demo. The surface stylesheet.
   ═════════════════════════════════════════════════════════════════════════════

   WHAT THIS FILE IS. The stylesheet for a STATIC, READ-ONLY walkthrough of the
   Bisbe app: seven doors plus the screens that are not doors — each workspace
   landing on its work with one menu button to its own everything-page — and one
   client story running through them (the ratified C+A pattern, 2026-08-09).
   It is not a marketing page's stylesheet and it deliberately does not load
   /styles.css — a demo of an app has to look like the app, and the marketing
   sheet's page-level claims (hero grounds, section rhythm, its own h1) fight that.

   WHERE EVERY NUMBER CAME FROM — nothing here is re-derived by eye:

     · tokens (grounds / ink / gilt / lines / aurora)
         → AIU-STYLE-GUIDE v1.4.1 §1, transcribed via the shipped app stylesheet
           platform/src/app/(crm)/crm.css lines 52-136. Same hexes, same alphas.
     · the glass material
         → NOT re-implemented. The page links /assets/aiu-glass.css (the public
           port of the FOUNDER-accepted round-5 material, §10a) and points its
           --gl-accent at Bisbe's own token. `.gl` / `.gl--bar` / `.gl--sm` /
           `.gl--still` are that file's classes, used as the guide instructs:
           "a NEW glass surface adds the CLASS in the component".
     · type scale + .wrap + spacing
         → §3 + Appendix A.2/A.3 and crm.css .crm-h1/.crm-lede/.crm-eyebrow/
           .crm-mono/.crm-wrap (max-width 1120 · padding 0 28px, 0 18px phone).
     · masthead geometry
         → measured off the shipped app render
           docs/reviews/crm-app-shots/masthead-crm-only.light@1280.png:
           56px bar · content column starting at 108px on a 1280 viewport.
     · buttons / chips / inputs
         → crm.css .crm-cta-p/.crm-cta-g/.crm-chip + Appendix A.5.

   THE ACCENT is Bisbe forest --bisbe #123B2E with the gold node #D5A63A, read
   off the committed brand package (assets/bisbe/bisbe_v2_mark-color.svg). It is
   an IDENTITY colour and nothing else. FOUNDER is red/green colourblind, so:

     NOTHING IN THIS FILE ENCODES MEANING BY HUE. Every state cue in the demo is
     a GLYPH plus a LIGHTNESS step plus a WORD — ✓ done, ◔ waiting, ◇ not yet,
     ▼ behind, ● live. Read in greyscale the demo loses nothing. Forest green is
     never a "good" and there is no red "bad" anywhere; the one place a warning
     appears it is a ▼ glyph, a word, and a lightness step on neutral ground.

   ONE APP, NOT SIX. The six capabilities do NOT each get their own hue — that
   was the five-apps-in-a-trenchcoat reading §16 closed. They are told apart by
   monogram, name and position in the rail. One accent, one app.

   MOBILE-FIRST, LITERALLY. Every rule below is written for the 375px phone and
   only RELAXES upward. The phone gets a fixed bottom tab bar (the PWA idiom);
   the desktop gets the same six destinations as a left rail. Tap targets ≥44px,
   inputs ≥48px at 16px (below 16px iOS zooms the page on focus), safe-area
   insets honoured top and bottom.
   ═════════════════════════════════════════════════════════════════════════════ */

/* ═══════════════ TOKENS — Daylight Aurora. LIGHT IS PRIMARY. ═══════════════
   The warm-cream / tan family is banned site-wide (§1) and appears nowhere. */
:root {
  --g0: #fafbfd; --g1: #ffffff; --g2: #f0f3f8; --g3: #e6ebf2;
  --ink: #14171d; --mid: #444b59; --dim: #545b68;
  --gilt-text: #5c4108; --gilt-text-lg: #8a6210;
  --gilt-line: rgba(111, 78, 8, 0.4); --gilt-dim: rgba(138, 98, 16, 0.18);
  --line: rgba(20, 23, 29, 0.14); --line-soft: rgba(20, 23, 29, 0.08);
  --az-text: #1b4f8a; --glow: rgba(42, 102, 168, 0.22);

  /* BISBE'S OWN COLOUR. Identity, never state.
     --bisbe      the fill (a tile, a primary control) — 12.4:1 under white ink
     --bisbe-text the same hue as SMALL TEXT — 12.4:1 on the day ground
     --node       the gold signature dot from the mark. Decoration + the
                  story-follow marker. Never text, never a state. */
  --bisbe: #123b2e;
  --bisbe-text: #123b2e;
  --bisbe-ink: #ffffff;
  --node: #d5a63a;
  --accent: #123b2e;          /* ONE accent token → ONE focus ring app-wide */

  --aur-a: rgba(76, 134, 201, 0.28);
  --aur-b: rgba(132, 104, 214, 0.22);
  --aur-c: rgba(201, 165, 90, 0.2);
  --aur-fade: rgba(250, 251, 253, 0.65);
  --aur-floor: #fafbfd;

  /* the fence line (§10c) at the ≥3:1 value, so a withheld edge is genuinely
     visible rather than a hint */
  --fence-line: rgba(20, 23, 29, 0.34);

  --radius: 14px;
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);

  /* what /assets/aiu-glass.css reads for its hover glow */
  --gl-accent: var(--bisbe);

  --mast-h: 56px;
  color-scheme: light;
}

html[data-theme="dark"] {
  --g0: #0e1116; --g1: #141923; --g2: #1b212c; --g3: #242c38;
  --ink: #edf1f7; --mid: #b7c0cd; --dim: #97a1b0;
  --gilt-text: #d8b470; --gilt-text-lg: #d8b470;
  --gilt-line: rgba(216, 180, 112, 0.42); --gilt-dim: rgba(216, 180, 112, 0.18);
  --line: rgba(222, 232, 245, 0.13); --line-soft: rgba(222, 232, 245, 0.07);
  --az-text: #9fc4e8; --glow: rgba(84, 145, 212, 0.16);

  /* The night pair, by the lockup's own stated rule: a single value that clears
     WHITE fails on the night ground, so an accent is a PAIR.
       fill  #1B5744 — 8.42:1 under white ink, and a clear lightness step up off
                       the nocturne ground (a #123B2E fill on #0E1116 reads as a
                       hole rather than a control)
       text  #62C7A1 — 9.27:1 on --g0, and it holds up the raised rungs
                       (--g1 8.1:1 · --g2 7.0:1 · --g3 5.9:1), so a flag on the
                       heaviest card ground still clears the AA body floor. That
                       last rung is the one the CRM's own pass found missing. */
  --bisbe: #1b5744;
  --bisbe-text: #62c7a1;
  --accent: #62c7a1;
  /* MEASURED CVD NOTE, recorded where the token lives rather than in a report.
     Run with the gate's own colour science (scripts/cvd-sim.mjs lines 61-86,
     Vienot-Brettel-Mollon + dE76), this night fill sits at dE 8.2 from the CRM
     clay --c-cm #824032 under PROTANOPIA — below the dE>=11 floor that governs
     tokens which must be TOLD APART. It is not a defect on THIS surface: the
     demo carries ONE accent, the clay appears nowhere on it, and nothing here is
     distinguished by hue at all. It IS a real finding for the moment Bisbe's
     monogram lands in the app launcher or the homepage wall NEXT TO the CRM
     tile, which is a locked-accent ruling (Designer + CXO), not a demo change.
     Dispatched as such in the PR rather than parked as a note. */

  --aur-a: rgba(64, 118, 196, 0.2);
  --aur-b: rgba(118, 92, 206, 0.16);
  --aur-c: rgba(201, 165, 90, 0.12);
  --aur-fade: rgba(14, 17, 22, 0.6);
  --aur-floor: #0e1116;

  --fence-line: rgba(222, 232, 245, 0.38);
  color-scheme: dark;
}

/* ═══════════════ BASE ═══════════════ */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; min-width: 0; }
html { background: var(--g0); -webkit-text-size-adjust: 100%; }
body {
  background: var(--g0); color: var(--ink);
  font-family: 'Familjen Grotesk', system-ui, -apple-system, sans-serif;
  font-size: 16px; line-height: 1.55; -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
a { color: inherit; }
img, svg { max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
h1, h2, h3, h4 { letter-spacing: -0.022em; line-height: 1.1; font-weight: 700; }
table { border-collapse: collapse; width: 100%; }

/* A.1 · the global focus ring. Part of the §19 hard veto — every interactive
   element in the demo has it, including the rail, the tabs and the ribbon pips. */
:focus-visible { outline: 2px solid var(--gilt-text); outline-offset: 2px; border-radius: 6px; }

.bx-skip {
  position: absolute; left: -9999px; top: 0; z-index: 400;
  background: var(--g1); color: var(--ink); padding: 12px 18px;
  border: 1px solid var(--gilt-line); border-radius: 8px;
  font-family: 'JetBrains Mono', monospace; font-size: 12px;
}
.bx-skip:focus { left: 12px; top: 12px; }
.bx-vh {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ═══════════════ THE AURORA GROUND (Appendix A.0 · geometry is tuned) ═══════════════ */
.bx-aurora { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; contain: strict; }
.bx-aurora i { position: absolute; display: block; border-radius: 50%; }
.au-a { width: 1600px; height: 1200px; left: -18%; top: -38%; filter: blur(60px);
  background: radial-gradient(closest-side, var(--aur-a), transparent 75%); }
.au-b { width: 1300px; height: 1100px; right: -20%; top: -18%; filter: blur(70px);
  background: radial-gradient(closest-side, var(--aur-b), transparent 75%); }
.au-c { width: 1200px; height: 900px; left: 28%; top: 4%; filter: blur(80px);
  background: radial-gradient(closest-side, var(--aur-c), transparent 75%); }
.au-fade { inset: 0; border-radius: 0;
  background: linear-gradient(180deg, transparent 0%, var(--aur-fade) 38%, var(--aur-floor) 62%); }

/* ═══════════════ TYPE (§3 · Appendix A.3) ═══════════════ */
.bx-h1 { font-size: clamp(27px, 6.6vw, 38px); font-weight: 700; letter-spacing: -0.028em; line-height: 1.08; }
.bx-h2 { font-size: clamp(19px, 3.4vw, 23px); font-weight: 700; letter-spacing: -0.022em; }
.bx-lede { font-size: 15.5px; color: var(--mid); line-height: 1.62; max-width: 52ch; }
.bx-mono { font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: 0.06em; }
.bx-eyebrow {
  font-family: 'JetBrains Mono', monospace; font-size: 10px; font-weight: 600;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--bisbe-text);
}
/* the section lintel — Cinzel caps between two gilt hairlines (§10) */
.bx-lintel { display: flex; align-items: center; gap: 14px; margin: 0 0 14px; }
.bx-lintel > span {
  font-family: Cinzel, serif; font-weight: 700; font-size: 10px;
  letter-spacing: 0.26em; text-transform: uppercase; color: var(--gilt-text); white-space: nowrap;
}
.bx-lintel::after { content: ''; flex: 1; height: 1px; background: var(--gilt-line); }

/* ═══════════════ CONTROLS (crm.css .crm-cta-* · Appendix A.5) ═══════════════ */
.bx-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 0 16px; border-radius: 12px; border: 1px solid transparent;
  font-size: 14.5px; font-weight: 600; line-height: 1.2; text-align: center;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease);
}
.bx-btn--p { background: var(--bisbe); color: var(--bisbe-ink); }
.bx-btn--p:hover { background: color-mix(in oklab, var(--bisbe) 84%, black); }
.bx-btn--g { background: transparent; color: var(--ink); border-color: var(--line); }
.bx-btn--g:hover { background: var(--g2); }
/* --sm is a smaller TYPE size, never a smaller TARGET: the 44px floor holds for
   every standalone control in the demo (checks.mjs rule C). */
.bx-btn--sm { min-height: 44px; padding: 0 14px; font-size: 13px; border-radius: 11px; }
.bx-btn--wide { width: 100%; }
.bx-btn--tall { min-height: 52px; font-size: 16px; border-radius: 14px; }

/* the text affordance the app uses for "go deeper here" */
.bx-go {
  display: inline-flex; align-items: center; gap: 6px; min-height: 44px;
  color: var(--bisbe-text); font-size: 13.5px; font-weight: 600;
}
.bx-go:hover { text-decoration: underline; text-underline-offset: 3px; }

.bx-input {
  width: 100%; min-height: 48px; padding: 12px 14px; font: inherit; font-size: 16px;
  color: var(--ink); background: var(--g1); border: 1px solid var(--line); border-radius: 12px;
}
.bx-input::placeholder { color: var(--dim); }
.bx-input:focus { border-color: var(--accent); outline: none; }

/* ═══════════════ CHIPS + STATE MARKS ═══════════════════════════════════════
   FOUR CHANNELS, NONE OF THEM COLOUR: a glyph, a word, a border STYLE and a
   ground LIGHTNESS. `.is-*` sets only lightness + border style; the glyph and
   the word are in the markup, so a chip cannot exist without them. */
.bx-chip {
  display: inline-flex; align-items: center; gap: 6px; vertical-align: middle;
  font-family: 'JetBrains Mono', monospace; font-size: 9.5px; font-weight: 600;
  letter-spacing: 0.11em; text-transform: uppercase; line-height: 1.35;
  padding: 4px 9px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--g1); color: var(--mid); max-width: 100%;
}
.bx-chip .gy { font-size: 10px; line-height: 1; flex: none; }
.bx-chip.is-done { background: var(--g1); border-color: var(--line); color: var(--ink); }
.bx-chip.is-live { background: transparent; border-color: var(--glow); color: var(--az-text); }
.bx-chip.is-wait { background: var(--g2); border-color: var(--mid); color: var(--ink); }
.bx-chip.is-soon { background: transparent; border-style: dashed; border-color: var(--fence-line); color: var(--dim); }
.bx-chip.is-flag { background: var(--g3); border-color: var(--mid); color: var(--ink); font-weight: 700; }
/* THE STORY MARKER — the one thing that travels across every screen it is on: a glyph
   plus the claim number, on a gilt-edged chip.

   THE GLYPH IS GILT, NOT THE BRAND GOLD, and that was measured rather than
   chosen: Bisbe's #D5A63A renders at 2.25:1 on the day ground, which fails the
   4.5:1 small-text floor AND the 3:1 non-text floor — it would have shipped a
   sub-AA marker on every screen and nothing on the page would have looked
   wrong. --gilt-text is the accessible value of the same warm register (9.48:1
   light / 8.2:1 dark). The brand gold survives where it can carry a RING: the
   ribbon's dot and the arrived-at-step node below, both non-text and both given
   a hairline so their edge, not their fill, does the separating. */
.bx-chip.is-story { border-color: var(--gilt-line); background: var(--g1); color: var(--ink); }
.bx-chip.is-story .gy { color: var(--gilt-text); font-size: 11px; }
/* a receipt: which written page an answer came from, and when it was confirmed */
.bx-receipt {
  display: inline-flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: 0.04em;
  color: var(--gilt-text); border: 1px solid var(--gilt-line); border-radius: 999px;
  padding: 5px 11px; background: var(--g1);
}
.bx-kicker {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-family: 'JetBrains Mono', monospace; font-size: 9.5px; font-weight: 600;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--dim);
}

/* ═══════════════ CARDS + LISTS ═══════════════════════════════════════════════
   THE INTERIOR CARDS WEAR THE GLASS MATERIAL (§10a · glass ruling F2, 2026-08-09).
   This block used to argue the opposite, and BOTH of its reasons were wrong — it is
   worth writing down why, because the reasoning was plausible and it is exactly how a
   site-wide standard fails a second time.

     1 · IT REASONED FROM A STALE GUIDE. It cited the flat-fill-plus-hairline
         register from a working-tree copy of AIU-STYLE-GUIDE.md at v1.2.1 — a
         version in which the material does not exist. The CURRENT guide is v1.4.1:
         §10a makes the material the standard for EVERY raised object, site-wide,
         and §10d is the measured register of where it has landed. (That stale copy
         is synced in this same change — glass ruling F8 — so the next builder cannot
         repeat the mistake.)
     2 · IT INVERTED §20d. It said the demo must not move until the PLATFORM shell's
         §10d row moves, "in step with the app". §20d rules the other way round: the
         PROTOTYPE IS THE REQUIREMENTS DOCUMENT — it LEADS, and the app is built from
         it. A prototype that waits for the app to adopt a standard first is a
         prototype that can never introduce one, which is the opposite of its job.

   And the instruction it ended on — "do not fix it in isolation" — is the mechanism
   by which a surface stays off the standard indefinitely: it converts a measurable
   §10d row into a wait on someone else. §10d's own rule is that there are exactly two
   states, ON or a row in the backlog with an owner. There is no "waiting for the app".

   WHAT DOES *NOT* TAKE IT, and each has a rule behind it rather than a preference:
     · THE FENCE REGISTER (§10c) — `.bx-card--fence`, `.bx-tile.is-fence`,
       `.bx-tile.is-open`. Dashed means "you cannot have this yet"; the material is
       for objects that are REAL. A fenced tile wearing glass would read as shipped.
     · TEXT GROUNDS AND TABLES (F6) — `.bx-card--paper` (the orders table). Type stays
       on solid paper; the material goes on the container, never under the work.
     · THE DENSE COUNT LISTS (F6) — `.bx-roomlist`, `.bx-list` rows, `.bx-item` cards inside
       a glass column (never glass on glass, F3). A count row is type on paper, and a row
       inside a glass column is a second backdrop-filter compositing over the first, which
       reads as fog.
       ★ `.bx-rail` WAS IN THIS LIST AND CAME OUT (2026-08-09). It was filed here because it
       was excluded in the same pass, not because either reason applied to it: the rail is not
       inside anything (so F3 cannot bite) and it is not a text ground (its DOORS are, and
       they keep their solid grounds). It is a top-level CONTAINER, which is precisely what
       §10a's "every raised object" governs. FOUNDER found the same gap in the shipped app —
       *"why no visual treatment to the left tab selector? It doesnt fit with the rest of the
       page."* See the rail's own block below for the full reasoning.

   HOW IT IS APPLIED: the CLASS goes in the component (`class="bx-card gl"`), which is
   what §10a's own note requires — the stylesheet's surface list is a migration surface,
   not the API. Each surface DECLARES its own geometry (`--gl-radius`), because a surface
   that inherits the canonical radius while rendering a different one wears a corner
   bloom sized for a corner it does not have. And the flat register is scoped to
   `:not(.gl)` rather than deleted, because the fence and paper carve-outs above still
   need it — a surface may not carry BOTH, and this is how that is enforced in the
   cascade instead of by memory.

   None of this is claimed: `node scripts/global-style-audit.mjs` paints these surfaces
   in a real browser and reads the five checkable facts off the computed style, and the
   `bisbe-demo` rows in docs/design/style-audit/ are the evidence. */
.bx-card {
  padding: 15px 16px;
}
.bx-card:not(.gl) {
  background: var(--g1); border: 1px solid var(--line); border-radius: var(--radius);
}
/* Per-surface geometry, DECLARED (§10a): the demo's own corner, not the material's
   canonical one, so the rim mask and both corner terms derive from the real corner. */
.bx-card.gl, .bx-tile.gl, .bx-qcard.gl, .bx-course.gl, .bx-need.gl, .bx-col.gl {
  --gl-radius: var(--radius);
}
@media (min-width: 760px) { .bx-card { padding: 18px 20px; } }
.bx-card--flat:not(.gl) { background: var(--g2); }
.bx-card--fence { border-style: dashed; border-color: var(--fence-line); background: transparent; }
/* THE STORY MARKER IS AN INFORMATION CHANNEL, and on the material it moves to the TOP
   edge. §10a fact 4: the material draws no border, and "the ONLY legal border is an
   accent TOP-rule, which is an information channel … re-asserted over the material."
   The gilt rule is exactly that — it says "this card is claim #1042" — so it survives
   the material by moving from the left edge to the one legal edge, same colour, same
   3px, same meaning. Flat cards (the fence + paper carve-outs) keep the left rule. */
.bx-card--story:not(.gl) { border-left: 3px solid var(--gilt-line); }
.bx-card--story.gl { border-top: 3px solid var(--gilt-line); }
/* ARRIVED-AT-STEP. Set only when the viewer got here by clicking a ribbon pip,
   so a pip answers "where is step 4?" instead of merely swapping the screen. A
   ring plus a gold node — geometry and a marker, never a hue-only cue, and it
   sits OUTSIDE the box so it cannot be clipped by the card it marks. */
.is-lit { position: relative; box-shadow: 0 0 0 2px var(--gilt-line); }
.is-lit::before {
  content: ''; position: absolute; top: -6px; left: -6px; width: 12px; height: 12px;
  border-radius: 50%; background: var(--node); border: 2px solid var(--g0);
  box-shadow: 0 0 0 1px var(--fence-line);
}
.bx-stack { display: grid; gap: 10px; }
.bx-stack--lg { gap: 14px; }

/* the workspace row — one shape reused by every list in the app */
.bx-list { display: grid; }
.bx-row {
  display: grid; gap: 5px; padding: 13px 0; border-bottom: 1px solid var(--line-soft);
  text-align: left; width: 100%;
}
.bx-row:last-child { border-bottom: 0; }
@media (min-width: 640px) {
  .bx-row { grid-template-columns: minmax(0, 1fr) auto; gap: 8px 16px; align-items: center; }
}
.bx-row .t { font-size: 14.5px; line-height: 1.4; color: var(--ink); font-weight: 600; }
.bx-row .t small { display: block; margin-top: 4px; font-size: 13px; line-height: 1.5; color: var(--mid); font-weight: 400; }
.bx-row .s { justify-self: start; }
@media (min-width: 640px) { .bx-row .s { justify-self: end; } }
button.bx-row:hover { background: var(--g2); }

/* the stat strip — 2-up on a phone so the numbers stay big enough to read */
.bx-kpi {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px;
  background: var(--line-soft); border: 1px solid var(--line-soft);
  border-radius: 12px; overflow: hidden;
}
@media (min-width: 700px) { .bx-kpi { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.bx-kpi > div { background: var(--g1); padding: 11px 13px; min-width: 0; }
.bx-kpi b { display: block; font-size: 20px; line-height: 1.1; letter-spacing: -0.02em; }
.bx-kpi span {
  display: block; margin-top: 4px; font-family: 'JetBrains Mono', monospace;
  font-size: 8.5px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--dim); line-height: 1.45;
}

/* an inset note inside a card — the app's "here is the reason" box */
.bx-note {
  margin-top: 10px; padding: 11px 12px; border-radius: 10px;
  background: var(--g2); border: 1px solid var(--line-soft);
  font-size: 13px; line-height: 1.5; color: var(--ink);
}
.bx-note b { font-weight: 700; }
.bx-note .gy { color: var(--ink); }
.bx-aside {
  font-size: 12.5px; line-height: 1.6; color: var(--mid); max-width: 74ch;
}
.bx-aside b { color: var(--ink); font-weight: 600; }
/* THE ONE LINE THAT IS NOT AN ASIDE (Designer ruling 2, 2026-08-10). One sentence on
   the Training screen carries the product's whole argument — a question answered three
   times becomes a page written once — and it was wearing the footnote register. It now
   reads at the screen's own lede size, with the gilt rule the rest of this file uses
   for a sentence that is the point rather than a note about one. */
.bx-killer {
  border-left: 2px solid var(--gilt-line); padding: 3px 0 3px 13px;
  font-size: 15.5px; line-height: 1.55; color: var(--ink); max-width: 62ch;
}
.bx-killer b { font-weight: 600; }

/* ═══════════════ THE DEMO FRAME (scrolls away — the honest top strip) ═══════════════ */
.bx-frame {
  position: relative; z-index: 2;
  border-bottom: 1px solid var(--line); background: var(--g2);
  padding: 9px 0; padding-top: max(9px, env(safe-area-inset-top));
}
.bx-frame-in {
  display: flex; align-items: center; gap: 10px 14px; flex-wrap: wrap;
  max-width: 1220px; margin: 0 auto; padding: 0 18px;
}
@media (min-width: 900px) { .bx-frame-in { padding: 0 28px; } }
.bx-frame p {
  font-family: 'JetBrains Mono', monospace; font-size: 10px; line-height: 1.55;
  letter-spacing: 0.02em; color: var(--mid); flex: 1 1 240px; min-width: 0;
}
.bx-frame p b { color: var(--ink); font-weight: 600; }
.bx-frame a.bx-exit {
  display: inline-flex; align-items: center; gap: 7px; min-height: 44px; padding: 0 13px;
  border: 1px solid var(--line); border-radius: 9px; background: var(--g1);
  font-family: 'JetBrains Mono', monospace; font-size: 10px; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink); white-space: nowrap;
}
.bx-frame a.bx-exit:hover { background: var(--g2); }

/* the no-JS notice — a real statement of what you are seeing, in the page's own
   register, in the place the app chrome would have told you. Never a blank box
   and never a "loading" placeholder (§19.4). */
.bx-noscript {
  position: relative; z-index: 2; margin: 0 auto; max-width: 1220px;
  padding: 14px 18px; font-size: 13.5px; line-height: 1.55; color: var(--ink);
}
@media (min-width: 900px) { .bx-noscript { padding: 16px 28px; } }
.bx-noscript b { display: block; margin-bottom: 5px; font-weight: 700; }
.bx-noscript a { color: var(--az-text); text-decoration: underline; text-underline-offset: 2px; }

/* ═══════════════ THE APP MASTHEAD ══════════════════════════════════════════
   56px, measured off the shipped app render at 1280 (see the header note). It
   wears `.gl--bar` from /assets/aiu-glass.css, which is the variant with the
   viewport-bounded exceptions already in it (no side edges, no corners, no
   percent-sized pool) — the "white line / no gradient" class of defect.

   NO AIU WORDMARK. Bisbe stands alone on its own surfaces (the no-by-AIU
   binding); the identity zone is the Bisbe lockup and the product label. */
/* THE DRAWN BOTTOM HAIRLINE IS GONE (glass ruling · §10a fact 4). This masthead already
   wore `.gl .gl--bar`, and the audit measured it at PARTIAL 4/5 with exactly one failing
   fact: `border: bottom`. "The material never draws one" is not a stylistic preference —
   a drawn rule under a glass edge is the second line FOUNDER caught by name ("the border
   still has a white line"). The separation is the ladder's, as it is on the marketing
   masthead on the page next door, which measures `border: none` · MATERIAL 5/5. */
.bx-mast {
  position: sticky; top: 0; z-index: 60;
  height: var(--mast-h);
}
.bx-mast-in {
  height: 100%; display: flex; align-items: center; gap: 10px;
  max-width: 1220px; margin: 0 auto; padding: 0 14px;
}
@media (min-width: 900px) { .bx-mast-in { padding: 0 28px; } }
/* THE LOCKUP. ≥480px: the compact logo at 26px — FOUNDER's chosen mark, and the
   height the shipped /b-os nav already uses. <480px: the square B mark at 26px,
   because the wordmark's own lettering falls under 8px of cap height once the
   compact lockup is squeezed into a phone masthead beside a label and controls.
   Both files are theme-paired; only one of the four ever renders.

   TWO INDEPENDENT AXES, and they are kept on two different elements on purpose:
   the SIZE axis (compact lockup vs square mark) switches the wrapper span, the
   THEME axis (forest ink vs reversed) switches the img inside it. Driving both
   from `display` on one element is how one of the four silently wins. */
.bx-lock { display: inline-flex; align-items: center; line-height: 1; flex: none; }
.bx-lock img { height: 26px; width: auto; display: block; }
.bx-lock .lk-full { display: none; }
.bx-lock .lk-mark { display: block; line-height: 1; }
@media (min-width: 480px) { .bx-lock .lk-full { display: block; line-height: 1; } .bx-lock .lk-mark { display: none; } }
.bx-lock img.d { display: none; }
html[data-theme="dark"] .bx-lock img.l { display: none; }
html[data-theme="dark"] .bx-lock img.d { display: block; }
/* the product label. One entitled app in this demo, so it is a PLAIN LABEL with
   no caret and no switcher — the shell behaving correctly, not a dead control. */
.bx-prod {
  display: inline-flex; align-items: center; gap: 8px; padding-left: 11px; margin-left: 3px;
  border-left: 1px solid var(--line); line-height: 1; flex: 1 1 auto; min-width: 0;
}
.bx-prod > span {
  font-family: 'JetBrains Mono', monospace; font-size: 10.5px; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--bisbe-text); white-space: nowrap;
}
.bx-prod > em {
  font-style: normal; font-family: 'JetBrains Mono', monospace; font-size: 9.5px;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: none;
}
@media (min-width: 700px) { .bx-prod > em { display: block; } }
.bx-mast-utils { display: flex; align-items: center; gap: 8px; flex: none; }
.bx-theme {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 44px; min-width: 44px; padding: 0 13px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--g1);
  font-size: 12.5px; font-weight: 600; color: var(--ink); white-space: nowrap;
}
.bx-theme:hover { background: var(--g2); }
.bx-av {
  width: 36px; height: 36px; border-radius: 50%; flex: none;
  display: grid; place-items: center; background: var(--bisbe); color: var(--bisbe-ink);
  font-size: 12px; font-weight: 700; letter-spacing: 0.02em;
}

/* ═══════════════ THE STORY RIBBON ══════════════════════════════════════════
   The one thing that makes many screens one product: a persistent strip naming
   the claim you are following, eight numbered steps, which of them this screen
   shows, and where the story goes next. It is a real tablist-adjacent control
   set — every pip is a button, arrow-key reachable, and each one lands you on
   the screen where that step is visible.

   Sticky UNDER the masthead, so it survives every scroll. Phone height is the
   budget that mattered: masthead 56 + ribbon ~44 = 100px of a 667px screen,
   which is why the step LABEL renders as one line of text rather than eight
   labelled pips at phone width. */
/* ══ COLLAPSIBLE BELOW 900, AND ONLY BELOW 900 (Designer ruling 4, 2026-08-10) ══
   MEASURED, then ruled: masthead 56 + ribbon ~181 = ~237px of fixed chrome before a
   phone reader has seen one row of the app they came to look at. On a 480px visible
   area that is half the screen spent on a bar about the screen.

   WHAT COLLAPSING TAKES AWAY, and it is only ever these two: the say-line and the
   eight pips. What it never takes away is the four things a reader needs to know
   where they are and how to go on — the claim's identity, the step in WORDS, the
   next control, and the way to open the rest back up.

   WHY IN WORDS AND NOT PIPS. Eight 34px pips are a map; "Step 6 of 8" is an answer.
   Collapsed, the row answers; expanded, the map comes back.

   THE RESTING STATE IS EXPANDED. The markup ships open and JS closes it below 900 on
   load — the page's own motion law, which is that an enhancement may only ever ADD.
   A no-JS, print or crawler render gets the whole ribbon, never a bar with its
   contents display:none'd by a state attribute nothing set.

   DESKTOP IS UNTOUCHED. At ≥900 the toggle is display:none and the collapse rule is
   not in scope at all, so the one-row flex layout below is byte-for-byte what it was. */
.bx-ribbon {
  position: sticky; top: var(--mast-h); z-index: 55;
  background: var(--g1); border-bottom: 1px solid var(--line);
}
.bx-ribbon-in {
  display: grid; gap: 8px; max-width: 1220px; margin: 0 auto; padding: 9px 18px 10px;
}
/* ── THE COLLAPSED ROW — one 44px line, four things on it ───────────────────── */
@media (max-width: 899px) {
  .bx-ribbon-in { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px; }
  .bx-ribbon-head { flex: 1 1 auto; }
  .bx-pips { flex: 1 0 100%; }
  .bx-ribbon[data-open="false"] #bx-ribbon-say,
  .bx-ribbon[data-open="false"] .bx-pips { display: none; }
  .bx-ribbon[data-open="false"] #bx-next-lb { display: none; }
  .bx-ribbon[data-open="false"] .bx-next-short { display: inline; }
}
/* AT 320 THE ROW IS ABOUT 20px TOO WIDE and wraps to two, which is the one width where
   this had to be measured rather than reasoned about — the worst case is the DEFAULT
   screen, whose where-line is the longest string the ribbon ever shows ("Steps 1–2 of
   8"; every other screen says "Step n of 8"). What gives the width back is deliberately
   only ever slack: the gold node is decoration and aria-hidden, and the rest is gutter
   and padding. No word shrinks, nothing is hidden, and both controls stay 44px.
   Rendered at 320/360/375/390 in both themes — scripts/bisbe-story-375.mjs. */
@media (max-width: 400px) {
  .bx-ribbon-in { padding-left: 12px; padding-right: 12px; gap: 8px 7px; }
  .bx-ribbon[data-open="false"] .bx-ribbon-head { gap: 7px; }
  .bx-ribbon[data-open="false"] .bx-ribbon-head .nd { display: none; }
  .bx-ribbon[data-open="false"] .bx-ribbon-next { padding: 0 9px; gap: 6px; }
  .bx-ribbon[data-open="false"] .bx-ribbon-toggle { padding: 0; }
}
/* where-you-are, IN WORDS. Present in both states — it is the one thing the pips were
   really carrying, and it survives their absence. */
.bx-ribbon-where {
  font-size: 12.5px; font-weight: 600; color: var(--ink); line-height: 1.3; white-space: nowrap;
}
/* the short word the Next control wears when the tail is hidden. The button's own
   aria-label carries the full sentence either way, so nothing is lost to a reader
   who cannot see which of the two is rendered. */
.bx-next-short { display: none; font-weight: 600; }
.bx-ribbon-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; min-width: 44px; padding: 0 10px; flex: none;
  border: 1px solid var(--line); border-radius: 10px; background: var(--g1);
  color: var(--ink); font-size: 13px; line-height: 1;
}
.bx-ribbon-toggle:hover { background: var(--g2); }
.bx-ribbon-toggle .cv { display: inline-block; transition: transform .18s ease; }
.bx-ribbon[data-open="false"] .bx-ribbon-toggle .cv { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .bx-ribbon-toggle .cv { transition: none; } }
@media (min-width: 900px) { .bx-ribbon-toggle { display: none; } }
/* ONE ROW from 900 (found by rendering at 1440): as a 1fr/auto GRID the third
   child wrapped to a second row and stretched across the whole first column,
   which read as an empty full-width button. A flex row with the head taking the
   slack puts the pips and the next-control on the right where they belong. */
@media (min-width: 900px) {
  .bx-ribbon-in { display: flex; align-items: center; gap: 10px 20px; padding: 9px 28px 10px; }
  .bx-ribbon-head { flex: 1 1 auto; min-width: 0; }
  .bx-pips, .bx-ribbon-next { flex: 0 0 auto; }
}
.bx-ribbon-head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; min-width: 0; }
.bx-ribbon-head .nd {
  width: 10px; height: 10px; border-radius: 50%; background: var(--node); flex: none;
  box-shadow: 0 0 0 1px var(--fence-line);
}
.bx-ribbon-head b {
  font-family: 'JetBrains Mono', monospace; font-size: 10px; font-weight: 600;
  letter-spacing: 0.11em; text-transform: uppercase; color: var(--ink);
}
.bx-ribbon-head span { font-size: 12.5px; color: var(--mid); line-height: 1.4; }
.bx-pips { display: flex; align-items: center; gap: 4px; }
/* THE ONE DOCUMENTED TAP-TARGET EXCEPTION, and why it is honest.
   Eight pips at 44 WIDE cannot coexist on a 375 row (352px of pill inside 339px
   of usable width), so they are 44 TALL and 34 wide with real spacing between
   them — never overlapping hit areas, which would be worse than a small one.
   That is legitimate ONLY because the pip row is a REDUNDANT secondary path:
   every screen it reaches is also reachable from a ≥44×44 control (the phone tab
   bar, the desktop rail, and the next-in-the-story button). The §18 floor is
   REACHABILITY, and no destination in this demo depends on a pip.
   Asserted, not asserted-at: .scratch/checks.mjs rules A and B. */
.bx-pip {
  width: 34px; height: 44px; border-radius: 9px; flex: none;
  display: grid; place-items: center; border: 1px solid var(--line); background: var(--g1);
  font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 700; color: var(--dim);
}
@media (min-width: 700px) { .bx-pip { width: 40px; } }
.bx-pip:hover { background: var(--g2); color: var(--ink); }
/* DONE — solid ground, ink numeral, full border. */
.bx-pip[data-state="done"] { background: var(--g3); color: var(--ink); border-color: var(--mid); }
/* HAPPENING NOW — a solid ink ring on the plain ground. It is not "done" and it
   is not "not yet", and before this rule existed it fell through to the default
   and read as the weakest of the three. */
.bx-pip[data-state="here"] { background: var(--g1); border-color: var(--ink); border-width: 1.5px; color: var(--ink); }
/* AHEAD — dashed, the withholding register: this has not happened yet. */
.bx-pip[data-state="ahead"] { border-style: dashed; border-color: var(--fence-line); background: transparent; }
/* ON THIS SCREEN — the ink fill. Declared LAST because it ties every rule above
   on specificity and has to win all three. Fill + weight + aria-current, never hue. */
.bx-pip[aria-current="true"] { background: var(--ink); color: var(--g0); border-color: var(--ink); border-style: solid; }
.bx-ribbon-next {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 13px;
  border: 1px solid var(--gilt-line); border-radius: 10px; background: var(--g1);
  font-size: 13px; font-weight: 600; color: var(--ink); text-align: left; line-height: 1.3;
}
.bx-ribbon-next:hover { background: var(--g2); }
.bx-ribbon-next .gy { color: var(--gilt-text); font-size: 12px; flex: none; }
.bx-ribbon-next em { font-style: normal; color: var(--mid); font-weight: 400; }

/* ═══════════════ THE FRAME: rail (desktop) · bottom tabs (phone) ═══════════════ */
.bx-body { position: relative; z-index: 1; max-width: 1220px; margin: 0 auto; }
@media (min-width: 1000px) {
  .bx-body { display: grid; grid-template-columns: 216px minmax(0, 1fr); gap: 0; align-items: start; }
}
.bx-rail { display: none; }
@media (min-width: 1000px) {
  /* THE RAIL WEARS THE MATERIAL (§10a · glass ruling F6 CORRECTED, 2026-08-09).
     It carried `class="gl gl--still"` from this change forward; the flat register below is
     scoped `:not(.gl)` so the fence and paper carve-outs keep it, exactly as `.bx-card` does.

     WHY THE EARLIER CARVE-OUT WAS WRONG, since this file is where a reader will look: the
     F6 list read "THE RAIL AND ITS DENSE COUNT LISTS — `.bx-rail`, `.bx-roomlist`, `.bx-list`
     rows, `.bx-item` cards inside a glass column". The REASONS it gives are the glass-on-glass
     rule and the text-ground rule, and both are true of the two COUNT LISTS — a count row is
     type on paper, and a row inside a glass column is a second backdrop-filter compositing
     over the first, which reads as fog. Neither is true of the RAIL, which is not inside
     anything and is not a text ground: it is a top-level CONTAINER, the case §10a's "every
     raised object" and the CXO's "material on CONTAINERS, never a text ground" both govern.
     A container and two lists were filed under one heading because they were excluded in the
     same pass, and the heading outlived the reason.

     FOUNDER found it in the APP first — *"why no visual treatment to the left tab selector?
     It doesnt fit with the rest of the page."* — and the prototype had the identical gap, so
     "mirror the demo" would have shipped the defect. §20d: the prototype LEADS, so it moves
     here in the same change rather than trailing the app. The DOORS inside keep every signal
     they had; only the container's finish changes. */
  .bx-rail {
    display: block; position: sticky; top: calc(var(--mast-h) + 60px);
    padding: 22px 14px 22px 28px;
  }
  .bx-rail:not(.gl) { border-right: 1px solid var(--line-soft); }
  /* Per-surface geometry, DECLARED (§10a): the demo's own corner, so the rim mask and both
     corner terms derive from the corner this surface actually renders. */
  .bx-rail.gl { --gl-radius: var(--radius); }
  .bx-rail-h {
    font-family: 'JetBrains Mono', monospace; font-size: 9px; font-weight: 600;
    letter-spacing: 0.16em; text-transform: uppercase; color: var(--dim);
    padding: 0 10px 8px;
  }
  .bx-rail nav { display: grid; gap: 2px; }
}
.bx-railbtn {
  display: grid; grid-template-columns: 26px minmax(0, 1fr) auto; gap: 10px; align-items: center;
  width: 100%; min-height: 44px; padding: 6px 10px; border-radius: 10px;
  border: 1px solid transparent; text-align: left;
}
.bx-railbtn:hover { background: var(--g2); }
.bx-railbtn[aria-current="page"] { background: var(--ink); }
.bx-railbtn .mg {
  width: 26px; height: 26px; border-radius: 8px; display: grid; place-items: center;
  background: var(--g3); color: var(--ink); border: 1px solid var(--line);
  font-family: 'JetBrains Mono', monospace; font-size: 9px; font-weight: 700;
}
.bx-railbtn[aria-current="page"] .mg { background: var(--bisbe); color: var(--bisbe-ink); border-color: transparent; }
.bx-railbtn .lb { min-width: 0; }
.bx-railbtn .lb b { display: block; font-size: 14.5px; font-weight: 600; line-height: 1.25; color: var(--ink); }
.bx-railbtn[aria-current="page"] .lb b { color: var(--g0); font-weight: 700; }
.bx-railbtn .lb i {
  display: block; font-style: normal; margin-top: 2px;
  font-family: 'JetBrains Mono', monospace; font-size: 8.5px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--dim);
}
.bx-railbtn[aria-current="page"] .lb i { color: var(--g3); }
/* the count on a rail item — a real third column, not a negative-margin nudge.
   The number is decoration; the word ("4 items") lives in the aria-label. */
.bx-railbtn .ct {
  font-family: 'JetBrains Mono', monospace; font-size: 10.5px; font-weight: 700; color: var(--dim);
}
.bx-railbtn[aria-current="page"] .ct { color: var(--g3); }

/* ═══════════ THE PHONE SPINE — IMPORTED FROM THE SHIPPED APP ═══════════════════
   WHAT WAS HERE, and why it went. This bar carried SEVEN equal slots with a label
   step-down from 9.5px to 9px below 400px — a shape invented for this file alone,
   argued for in forty lines of comment, and shipped on no other surface. Meanwhile
   the read-only world, which is generated from the product's own components, ships
   the app's REAL spine: four doors drawn and the rest behind one native "More"
   sheet, 60px slots, 12px labels, 44px sheet rows.

   Two surfaces of one product disagreeing about its own navigation is the drift
   class, and the arithmetic is the proof of which one was right: seven across a
   375px screen is 53.6px a slot, so "Customers" had to be shrunk to 9px to fit a
   box smaller than the 44px touch floor is wide. Four across is 75px.

   SO THE VARIANT IS DELETED RATHER THAN PARAMETERISED (Designer ruling 3). This
   block is the world's own, transcribed from
   public/b-os/demo/world/screens.*.css `.bx-frame .bx-tabs` — same geometry, same
   class names, same sheet. A parameter would have kept both shapes alive and let
   them drift again; there is now one shape, and scripts/check-bos-vocab-and-doors.mjs
   CHECK 7 compares the two surfaces on every commit so the next drift fails loudly.

   THE THREE DELIBERATE DELTAS, and each is a consequence of the tour being one page
   rather than seven:
     1 · the controls are <button data-goto> and not <a href>. The tour swaps screens
         in place; there is nothing to navigate to.
     2 · every door carries its sub-label in the markup — <span class="lb"><b>Ask</b>
         <i>Home</i></span> — and the tab register hides the <i>. That is what lets a
         door MOVE between the bar and the sheet without its markup being rebuilt,
         which the world never needs because each of its pages is authored already
         in its final arrangement.
     3 · the bar keeps `.gl .gl--bar` (the glass material), which the world's frame
         does not use. Material, not geometry.

   THE SWAP RULE lives in demo.js, not here: the bar draws doors 1-4 of the rail, and
   when the door you are STANDING IN would fall into the sheet it takes slot 4 and
   displaces the door that was there. The door you are in is never hidden — which is
   the whole reason the app's own spine can afford to hide any of them. The authored
   markup is the no-swap case, so a scripting-off render is a correct bar rather than
   an empty one. */
.bx-tabs {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 70;
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  padding-bottom: env(safe-area-inset-bottom);
}
.bx-tabs:not(.gl) { border-top: 1px solid var(--line); background: var(--g1); }
@media (min-width: 1000px) { .bx-tabs { display: none; } }
.bx-tab,
.bx-more > summary {
  display: grid; gap: 3px; justify-items: center; align-content: center;
  min-height: 60px; min-width: 44px; padding: 6px 2px 8px;
  border-top: 2px solid transparent;
  text-decoration: none; cursor: pointer; list-style: none;
}
.bx-more > summary::-webkit-details-marker { display: none; }
.bx-tab .mg,
.bx-more > summary .mg {
  width: 22px; height: 22px; border-radius: 7px; display: grid; place-items: center;
  background: var(--g3); color: var(--ink);
  font-family: 'JetBrains Mono', monospace; font-size: 8px; font-weight: 700;
}
.bx-tab .lb,
.bx-more > summary .lb {
  font-size: 12px; font-weight: 600; color: var(--mid); line-height: 1.2; text-align: center;
}
/* delta 2 — one node, two positions. In the bar a door reads exactly as the world's
   `<span class="lb">Ask</span>`; in the sheet the row register below takes over. */
.bx-tab .lb b { display: inline; font-size: inherit; font-weight: inherit; color: inherit; }
.bx-tab .lb i { display: none; }
@media (max-width: 359px) {
  .bx-tab .lb,
  .bx-more > summary .lb { font-size: 11px; }
}
.bx-tab[aria-current="page"] { border-top-color: var(--ink); }
.bx-tab[aria-current="page"] .mg { background: var(--bisbe); color: var(--bisbe-ink); }
.bx-tab[aria-current="page"] .lb { color: var(--ink); font-weight: 700; }

.bx-more { position: relative; }
/* AN OPEN SHEET OUT-RANKS THE GUIDE, and this is a collision the world never had:
   its pages carry no guide bubble. Here the guide (z 80) and its pill (z 79) are both
   fixed just above the tab bar (z 70) — which is exactly where the sheet opens. The
   first run of the support harness found it by failing to click "Support" fifty-five
   times while the pill silently ate every one, and a visitor would have met the same
   thing without a harness to say so. A disclosure the reader has just opened owns the
   space until they close it; navigating closes it (demo.js), so it is never sticky.
   State attribute rather than `:has()` — demo.js sets it on the details' own toggle
   event, so the fix does not depend on selector support. */
.bx-tabs[data-sheet="open"] { z-index: 90; }
.bx-sheet {
  position: absolute; right: 0; bottom: calc(100% + 1px);
  min-width: min(76vw, 300px);
  display: grid; gap: 2px; padding: 8px;
  background: var(--g1); border: 1px solid var(--line);
  border-radius: 12px 12px 0 0;
  box-shadow: 0 -8px 24px rgba(20, 23, 29, 0.14);
}
html[data-theme="dark"] .bx-sheet { box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.5); }
.bx-sheet-row {
  display: grid; grid-template-columns: 26px minmax(0, 1fr) auto; gap: 10px; align-items: center;
  min-height: 44px; padding: 6px 8px; border-radius: 10px; text-decoration: none; text-align: left;
  border: 1px solid transparent; background: none; width: 100%;
}
.bx-sheet-row:hover { background: var(--g2); }
.bx-sheet-row[aria-current="page"] { background: var(--g3); }
.bx-sheet-row .mg {
  width: 26px; height: 26px; border-radius: 8px; display: grid; place-items: center;
  background: var(--g3); color: var(--ink); border: 1px solid var(--line);
  font-family: 'JetBrains Mono', monospace; font-size: 9px; font-weight: 700;
}
.bx-sheet-row .lb { min-width: 0; }
.bx-sheet-row .lb b { display: block; font-size: 14.5px; font-weight: 600; color: var(--ink); }
.bx-sheet-row .lb i {
  display: block; font-style: normal; margin-top: 2px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 8.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--mid);
}

/* ═══════════════ THE SCREEN ═══════════════ */
.bx-main { min-width: 0; padding: 20px 18px 96px; }
@media (min-width: 700px) { .bx-main { padding: 24px 28px 96px; } }
@media (min-width: 1000px) { .bx-main { padding: 26px 28px 60px; } }
.bx-scr { display: grid; gap: 18px; }
.bx-scr[hidden] { display: none; }
.bx-scr > section { display: grid; gap: 12px; min-width: 0; }

/* the screen head — eyebrow, title, lede, actions. Every screen carries it,
   which is what makes every screen read as one app. */
.bx-head { display: grid; gap: 9px; }
.bx-head-top { display: flex; align-items: flex-start; gap: 12px 16px; flex-wrap: wrap; }
/* CLASS-SCOPED, NOT `> div` (found by rendering). `.bx-head-top > div` also
   matched the actions div, which made it a full-width grid and stretched its
   one button across the whole column — every declaration computing exactly as
   written, and the wrong thing on screen. */
.bx-head-t { flex: 1 1 240px; min-width: 0; display: grid; gap: 7px; }
.bx-head-acts { flex: 0 0 auto; display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.bx-back {
  display: inline-flex; align-items: center; gap: 7px; min-height: 44px; padding: 0 10px;
  margin-left: -9px; border-radius: 8px; font-size: 13px; font-weight: 600; color: var(--mid);
}
.bx-back:hover { background: var(--g2); color: var(--ink); }

/* ═══════════════ ASK — the conversational spine ═══════════════ */
.bx-ask { display: grid; gap: 10px; }
/* STACKED ON THE PHONE (found by rendering at 375): side by side, the field's
   own placeholder was being clipped mid-word — the field is the whole point of
   this screen, so it gets the full width and the control sits under it. */
.bx-askbar { display: grid; gap: 9px; }
@media (min-width: 560px) { .bx-askbar { grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 10px; } }
.bx-askbar .bx-btn { min-height: 48px; }
@media (max-width: 559px) { .bx-askbar .bx-btn { width: 100%; } }
.bx-qa { display: grid; gap: 10px; }
.bx-q { font-size: 15.5px; font-weight: 700; line-height: 1.4; letter-spacing: -0.012em; }
.bx-a {
  font-size: 14.5px; line-height: 1.6; color: var(--ink);
  border-left: 3px solid var(--gilt-line); padding-left: 13px;
}
.bx-a b { font-weight: 700; }
.bx-who {
  display: flex; align-items: center; gap: 8px;
  font-family: 'JetBrains Mono', monospace; font-size: 9.5px; font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--dim);
}
.bx-who .av {
  width: 22px; height: 22px; border-radius: 6px; flex: none; display: grid; place-items: center;
  background: var(--g3); color: var(--ink); font-size: 8.5px; font-weight: 700;
}

/* ═══════════════ ROOMS — the thread ═══════════════ */
.bx-rooms { display: grid; gap: 12px; }
@media (min-width: 1100px) { .bx-rooms { grid-template-columns: 210px minmax(0, 1fr); gap: 20px; align-items: start; } }
.bx-roomlist { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 4px; scrollbar-width: none; }
.bx-roomlist::-webkit-scrollbar { display: none; }
@media (min-width: 1100px) { .bx-roomlist { display: grid; gap: 3px; overflow: visible; } }
.bx-roombtn {
  display: flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 12px; flex: none;
  border: 1px solid var(--line); border-radius: 10px; background: var(--g1);
  font-size: 13.5px; font-weight: 600; color: var(--mid); white-space: nowrap;
}
@media (min-width: 1100px) { .bx-roombtn { width: 100%; border-color: transparent; background: transparent; } }
.bx-roombtn:hover { background: var(--g2); color: var(--ink); }
.bx-roombtn[aria-current="true"] { background: var(--g3); color: var(--ink); border-color: var(--mid); }
/* the sub-rail's own footnote — only where the rail is a real column, because
   on a phone the room list is a horizontal strip and a paragraph in it would
   scroll off sideways. */
.bx-roomnote { display: none; }
@media (min-width: 1100px) { .bx-roomnote { display: block; padding: 12px 11px 0; border-top: 1px solid var(--line-soft); margin-top: 8px; font-size: 12px; } }
.bx-roombtn .n {
  margin-left: auto; font-family: 'JetBrains Mono', monospace; font-size: 9.5px; font-weight: 700; color: var(--dim);
}
.bx-thread { display: grid; gap: 2px; }
.bx-msg { display: grid; grid-template-columns: 30px minmax(0, 1fr); gap: 10px; padding: 10px 0; align-items: start; }
.bx-msg .av {
  width: 30px; height: 30px; border-radius: 9px; flex: none; display: grid; place-items: center;
  background: var(--g3); color: var(--ink); border: 1px solid var(--line);
  font-family: 'JetBrains Mono', monospace; font-size: 9.5px; font-weight: 700;
}
.bx-msg .bd { min-width: 0; display: grid; gap: 4px; }
.bx-msg .hd { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
.bx-msg .hd b { font-size: 13.5px; font-weight: 700; }
.bx-msg .hd i {
  font-style: normal; font-family: 'JetBrains Mono', monospace; font-size: 9.5px;
  letter-spacing: 0.05em; color: var(--dim);
}
.bx-msg .hd em {
  font-style: normal; font-family: 'JetBrains Mono', monospace; font-size: 8.5px; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--dim);
  border: 1px solid var(--line); border-radius: 999px; padding: 2px 7px;
}
.bx-msg p { font-size: 14.5px; line-height: 1.55; }
.bx-msg .acts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 3px; }
/* the app answering BESIDE the room — it has no seat in the room, so it is not
   a message bubble with an avatar. It is an attachment on the room, gilt-ruled. */
.bx-beside {
  margin: 4px 0 4px 40px; padding: 12px 13px; border-radius: 12px;
  background: var(--g2); border: 1px solid var(--line); border-left: 3px solid var(--gilt-line);
  display: grid; gap: 7px;
}
@media (max-width: 520px) { .bx-beside { margin-left: 0; } }
.bx-beside .lb {
  font-family: 'JetBrains Mono', monospace; font-size: 9px; font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--gilt-text);
}
.bx-beside p { font-size: 13.5px; line-height: 1.55; }
/* a work item posted into the thread */
.bx-sys {
  margin: 6px 0 6px 40px; padding: 12px 13px; border-radius: 12px;
  background: var(--g1); border: 1px solid var(--line); display: grid; gap: 8px;
}
@media (max-width: 520px) { .bx-sys { margin-left: 0; } }
.bx-sys .lb {
  font-family: 'JetBrains Mono', monospace; font-size: 9px; font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--dim);
}
.bx-sys b { font-size: 14px; font-weight: 700; line-height: 1.35; }
.bx-composer { margin-top: 10px; display: grid; gap: 8px; }
@media (min-width: 560px) { .bx-composer { grid-template-columns: minmax(0, 1fr) auto; align-items: end; } }

/* ═══════════════ CUSTOMERS — the account brief ═══════════════ */
.bx-acct { display: grid; gap: 10px; }
.bx-acct-top { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.bx-acct-top .mg {
  width: 46px; height: 46px; border-radius: 13px; flex: none; display: grid; place-items: center;
  background: var(--g3); color: var(--ink); border: 1px solid var(--line);
  font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 700;
}
.bx-table { font-size: 13.5px; }
.bx-table thead th {
  text-align: left; padding: 0 10px 8px 0; border-bottom: 1px solid var(--line);
  font-family: 'JetBrains Mono', monospace; font-size: 8.5px; font-weight: 600;
  letter-spacing: 0.11em; text-transform: uppercase; color: var(--dim); white-space: nowrap;
}
.bx-table tbody td { padding: 11px 10px 11px 0; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
.bx-table tbody tr:last-child td { border-bottom: 0; }
.bx-table td b { font-weight: 700; }
.bx-table td small { display: block; margin-top: 3px; font-size: 12px; color: var(--mid); line-height: 1.45; }
.bx-table .num { font-family: 'JetBrains Mono', monospace; font-size: 12px; white-space: nowrap; }
/* the phone shape for a table: each row becomes a labelled block */
@media (max-width: 700px) {
  .bx-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .bx-table tbody tr { display: grid; gap: 5px; padding: 12px 0; border-bottom: 1px solid var(--line-soft); }
  .bx-table tbody tr:last-child { border-bottom: 0; }
  .bx-table tbody td { display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: 10px; padding: 0; border: 0; }
  .bx-table tbody td::before {
    content: attr(data-l); font-family: 'JetBrains Mono', monospace; font-size: 8.5px; font-weight: 600;
    letter-spacing: 0.1em; text-transform: uppercase; color: var(--dim); padding-top: 3px;
  }
}

/* ═══════════════ WORK — the board (§12) ═══════════════════════════════════
   Phone (≤999): columns stack full-width at natural height and the page flows.
   Desktop: a fixed-height row, each column head pinned, each body scrolling on
   its own — so a long Done column never pushes Waiting off the screen. */
.bx-board { display: grid; gap: 12px; }
/* EQUAL HEIGHTS, SIZED TO THE TALLEST COLUMN — not a fixed 460px box. The first
   cut pinned the row height and left ~180px of visible dead space under a
   three-card column, which reads as a board that failed to load. Grid stretch
   equalises the three; the CAP on each body is what gives §12 its per-column
   scroll, so a long Done column still cannot push Waiting off the screen. */
@media (min-width: 1000px) {
  .bx-board { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; align-items: stretch; }
  .bx-col-b { max-height: 356px; overflow-y: auto; }
}
/* THE COLUMN IS THE CONTAINER AND TAKES THE MATERIAL; THE CARDS IN IT STAY FLAT
   (glass ruling F3). Never glass on glass — a stack of glass cards inside a glass column
   is two backdrop-filters compositing over each other, which reads as fog and costs a
   second filter pass; `.gl--lens` is capped at 1–2 per page for the same reason. The
   column is also `.gl--still`: §10b's "a surface that takes the material but is NOT a
   door" — a board column is not pressable, so it gets the material with the lift zeroed
   and the hover glow removed.

   THE CLIP IS MOVED TO THE CHILD, and this is the defect §10b names by name: `overflow:
   hidden` on a `.gl` host silently deletes the rim and the glow while every computed
   value still reads correct (the audit reports it as a `clip` column for exactly this
   reason). The clip was only ever here so the scrolling body's square corners could not
   poke out of the rounded column — so the BODY takes the radius and the clip, and the
   column stops clipping its own rim. */
.bx-col {
  display: grid; grid-template-rows: auto minmax(0, 1fr); min-width: 0;
}
.bx-col:not(.gl) {
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--g1); overflow: hidden;
}
/* THE COLUMN HEAD GOES TRANSPARENT ON THE MATERIAL. Its flat --g2 band is opaque, and
   an opaque band across the top 40px of a glass column covers precisely where the light
   ENTERS it — the lip glow and the entry pool are both anchored to the top-left corner
   (§10a properties 1–3) — so the column would read as glass only below its own header.
   The divider stays: it is a CONTAINMENT boundary between the head and the scrolling
   body, and the bar rule (v) says such a boundary is uniform, which a 1px solid line is.
   The head's type is mono uppercase --ink, the same weight of type every other glass card
   in the estate carries on the material. */
.bx-col.gl > .bx-col-h { background: transparent; }
/* The body takes the bottom corners so the scrolling list cannot square off the column's
   own radius. NO `overflow` is set here — the desktop rule above already gives this
   element `overflow-y: auto` for the per-column scroll (§12), and clipping it here would
   silently delete that scroll on the one screen it exists for. */
.bx-col.gl > .bx-col-b {
  border-bottom-left-radius: var(--radius); border-bottom-right-radius: var(--radius);
}
.bx-col-h {
  display: flex; align-items: center; gap: 9px; padding: 11px 13px;
  border-bottom: 1px solid var(--line); background: var(--g2);
}
.bx-col-h b {
  font-family: 'JetBrains Mono', monospace; font-size: 9.5px; font-weight: 700;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--ink);
}
.bx-col-h .n {
  margin-left: auto; font-family: 'JetBrains Mono', monospace; font-size: 10px; font-weight: 700; color: var(--dim);
}
.bx-col-b { display: grid; gap: 9px; padding: 11px; align-content: start; }
.bx-item {
  display: grid; gap: 7px; width: 100%; text-align: left; padding: 12px 13px;
  border: 1px solid var(--line); border-radius: 12px; background: var(--g1);
}
.bx-item:hover { background: var(--g2); }
.bx-item[aria-pressed="true"] { border-color: var(--mid); background: var(--g2); box-shadow: inset 3px 0 0 var(--gilt-line); }
.bx-item b { font-size: 13.5px; font-weight: 700; line-height: 1.4; }
.bx-item .mt {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-family: 'JetBrains Mono', monospace; font-size: 9px; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--dim);
}
/* the detail panel that rides beside the board */
.bx-detail { display: grid; gap: 12px; }
.bx-tl { display: grid; gap: 0; }
.bx-tl-i { position: relative; display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 11px; padding: 0 0 14px; }
.bx-tl-i:last-child { padding-bottom: 0; }
.bx-tl-i:not(:last-child)::after {
  content: ''; position: absolute; left: 12px; top: 26px; bottom: 2px; width: 1px; background: var(--gilt-dim);
}
.bx-tl-i .dt {
  width: 24px; height: 24px; border-radius: 7px; z-index: 1;
  display: grid; place-items: center; border: 1px solid var(--line); background: var(--g3);
  font-family: 'JetBrains Mono', monospace; font-size: 9.5px; font-weight: 700; color: var(--ink);
}
.bx-tl-i[data-state="here"] .dt { background: var(--ink); color: var(--g0); border-color: var(--ink); }
.bx-tl-i[data-state="ahead"] .dt { background: transparent; border-style: dashed; border-color: var(--fence-line); color: var(--dim); }
.bx-tl-i .bd { min-width: 0; display: grid; gap: 3px; padding-top: 2px; }
.bx-tl-i .bd b { font-size: 13.5px; font-weight: 700; line-height: 1.4; }
.bx-tl-i .bd span { font-size: 12.5px; color: var(--mid); line-height: 1.5; }
.bx-tl-i .bd time {
  font-family: 'JetBrains Mono', monospace; font-size: 9.5px; letter-spacing: 0.05em; color: var(--dim);
}

/* ═══════════════ TRAINING ═══════════════ */
.bx-course { display: grid; gap: 9px; padding: 14px 15px; }
.bx-course:not(.gl) { border: 1px solid var(--line); border-radius: var(--radius); background: var(--g1); }
.bx-course-h { display: flex; align-items: flex-start; gap: 11px; flex-wrap: wrap; }
.bx-course-h b { flex: 1 1 200px; min-width: 0; font-size: 15px; font-weight: 700; line-height: 1.35; }
.bx-bar { height: 7px; border-radius: 999px; background: var(--g3); overflow: hidden; border: 1px solid var(--line-soft); }
/* the fill reads as LENGTH, which is the information channel; the token is the
   text pair rather than the fill pair so the bar stays legible against its own
   track on the night ground (the fill value is tuned for white ink on top of it,
   not for sitting next to --g3). */
.bx-bar > i { display: block; height: 100%; background: var(--bisbe-text); }
.bx-legend { display: grid; gap: 9px; }
.bx-legend-i { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 11px; align-items: start; }
.bx-legend-i .bx-chip { align-self: start; }
.bx-legend-i p { font-size: 13px; line-height: 1.5; color: var(--mid); }
.bx-legend-i p b { color: var(--ink); font-weight: 600; }

/* ═══════════════ THE OWNER'S QUEUE ═══════════════ */
.bx-qcard { display: grid; gap: 10px; padding: 15px 16px; }
.bx-qcard:not(.gl) { border: 1px solid var(--line); border-radius: var(--radius); background: var(--g1); }
.bx-qcard.is-story:not(.gl) { border-left: 3px solid var(--gilt-line); }
/* The accent moves to the one legal edge on the material — §10a fact 4, same rule and
   same reasoning as `.bx-card--story.gl` above. */
.bx-qcard.is-story.gl { border-top: 3px solid var(--gilt-line); }
.bx-quote {
  padding: 12px 13px; border-radius: 10px; background: var(--g2);
  border: 1px solid var(--line-soft); font-size: 14px; line-height: 1.55;
}
.bx-quote em { font-style: italic; }
.bx-acts { display: flex; gap: 8px; flex-wrap: wrap; }
.bx-acts .bx-btn { flex: 1 1 auto; min-width: 104px; }
@media (min-width: 560px) { .bx-acts .bx-btn { flex: 0 0 auto; } }

/* ═══════════════ THE GUIDE — the agent with eyes ══════════════════════════
   The tutorial voice, one character, per §15's capability-level tour. It is a
   real dialog-ish panel: dismissible, focusable, keyboard-reachable, and it is
   also what answers every control in the demo — because the demo is read-only,
   and a control that silently does nothing is worse than one that tells you
   what it would have done. */
.bx-guide {
  position: fixed; z-index: 80; right: 14px; left: 14px; bottom: calc(66px + env(safe-area-inset-bottom));
  display: grid; gap: 8px; padding: 12px 13px;
  border-radius: 16px; background: var(--g1); border: 1px solid var(--line);
  box-shadow: 0 18px 44px -20px rgba(21, 30, 45, 0.34);
}
html[data-theme="dark"] .bx-guide { box-shadow: 0 20px 50px -22px rgba(0, 0, 0, 0.7); }
/* THE BOTTOM OFFSET IS KEYED TO THE TAB BAR, NOT TO "IS THIS A PHONE".
   The tab bar is fixed at the bottom until 1000px, so the guide has to clear it
   until 1000px. The first cut dropped the offset to 22px at 700 — which put the
   panel directly ON TOP of the tab bar for the whole 700-999 band, where the tab
   bar is the ONLY navigation (the rail does not exist below 1000). A pointer-event
   check found it: Playwright could not click a tab because the guide intercepted
   it, at 700 and 900, on every screen. That is a reachability failure, not a
   cosmetic overlap — the §18 floor is REACHABILITY.
   At 700 it becomes a right-hand panel (its width and side change); only at 1000,
   where the tab bar is gone, does it drop to the corner. */
@media (min-width: 700px) { .bx-guide { left: auto; width: 372px; right: 22px; gap: 10px; padding: 14px 15px; } }
@media (min-width: 1000px) { .bx-guide { bottom: 22px; } }
.bx-guide[hidden] { display: none; }
.bx-guide-h { display: grid; grid-template-columns: 36px minmax(0, 1fr) 44px; gap: 10px; align-items: center; }
@media (min-width: 700px) { .bx-guide-h { grid-template-columns: 42px minmax(0, 1fr) 44px; gap: 11px; } }
/* the mark sits on a WHITE disc in both themes — it was drawn for a light
   ground, and putting it on the night ground instead would lose the forest
   body and leave a floating pair of eyes. */
.bx-guide-av {
  width: 36px; height: 36px; border-radius: 50%; flex: none; overflow: hidden;
  display: grid; place-items: center; background: #ffffff; border: 1px solid var(--line);
}
.bx-guide-av img { width: 34px; height: 34px; display: block; }
@media (min-width: 700px) {
  .bx-guide-av { width: 42px; height: 42px; }
  .bx-guide-av img { width: 40px; height: 40px; }
}
.bx-guide-h b { font-size: 14px; font-weight: 700; line-height: 1.2; }
.bx-guide-h b span {
  display: block; margin-top: 3px; font-family: 'JetBrains Mono', monospace;
  font-size: 8.5px; font-weight: 600; letter-spacing: 0.11em; text-transform: uppercase; color: var(--dim);
}
.bx-guide-x {
  width: 44px; height: 44px; border-radius: 11px; display: grid; place-items: center;
  border: 1px solid var(--line); color: var(--mid); font-size: 15px; line-height: 1;
}
.bx-guide-x:hover { background: var(--g2); color: var(--ink); }
.bx-guide p { font-size: 13px; line-height: 1.52; color: var(--ink); }
@media (min-width: 700px) { .bx-guide p { font-size: 13.5px; line-height: 1.58; } }
.bx-guide p + p { margin-top: -2px; color: var(--mid); }
.bx-guide-f { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.bx-guide-f .bx-btn { flex: 1 1 auto; }
.bx-guide-step {
  font-family: 'JetBrains Mono', monospace; font-size: 9px; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--dim);
}
/* the re-open control, after a dismissal */
.bx-guide-fab {
  position: fixed; z-index: 79; right: 14px; bottom: calc(70px + env(safe-area-inset-bottom));
  display: grid; grid-template-columns: auto auto; gap: 8px; align-items: center;
  min-height: 46px; padding: 0 13px 0 7px; border-radius: 999px;
  background: var(--g1); border: 1px solid var(--line);
  box-shadow: 0 12px 30px -16px rgba(21, 30, 45, 0.4);
  font-size: 13px; font-weight: 600; color: var(--ink);
}
/* same rule, same reason: the pill clears the tab bar until the tab bar is gone */
@media (min-width: 700px) { .bx-guide-fab { right: 22px; } }
@media (min-width: 1000px) { .bx-guide-fab { bottom: 22px; } }
.bx-guide-fab[hidden] { display: none; }
.bx-guide-fab img { width: 32px; height: 32px; display: block; border-radius: 50%; background: #ffffff; }

/* ═══════════════ THE COLOPHON — the honest footer ═══════════════ */
.bx-colophon {
  margin-top: 26px; padding-top: 16px; border-top: 1px solid var(--line);
  display: grid; gap: 11px;
}
.bx-colophon p {
  font-family: 'JetBrains Mono', monospace; font-size: 9.5px; line-height: 1.85;
  letter-spacing: 0.03em; color: var(--dim); max-width: 96ch;
}
.bx-colophon p b { color: var(--mid); font-weight: 600; }
/* AN IN-PROSE LINK, deliberately NOT a 44px target: it sits inside a sentence and
   a 44px line box would break the paragraph. That is the one exception in the
   demo and it is safe because it is not the way out — the 44×44 "Back to Bisbe"
   control in the frame strip is, and it is present before the app even starts
   (checks.mjs rule C exempts links inside a <p>, and rule A proves the exit). */
.bx-colophon a { color: var(--az-text); text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--gilt-line); }
.bx-colophon a:hover { text-decoration-color: var(--az-text); }

/* ═══════════════ SOURCE-ORDER GUARD ══════════════════════════════════════════
   `.bx-card--fence` and `.bx-card--story` are declared up in the CARDS section,
   which means a later component that sets the `border` SHORTHAND silently wins
   the tie and takes the modifier's border-style and its ground back — the
   dashed withholding edge and the gilt story rule both vanish while every
   declaration still reads correct in the source. These compound selectors
   (0,2,0) restate them where they can no longer be outrun. Checked by
   .scratch/checks.mjs, not by eye. */
.bx-course.bx-card--fence,
.bx-qcard.bx-card--fence {
  border-style: dashed; border-color: var(--fence-line); background: transparent;
}
.bx-course.bx-card--story:not(.gl),
.bx-qcard.bx-card--story:not(.gl) { border-left: 3px solid var(--gilt-line); }
/* On the material the accent takes the one legal edge — §10a fact 4, and the same
   re-assertion the glass card rules make above. (0,3,0) here, so it still outruns any
   later component that sets the `border` shorthand, which is what this guard is for.) */
.bx-course.bx-card--story.gl,
.bx-qcard.bx-card--story.gl { border-top: 3px solid var(--gilt-line); }

/* ═══════════════ MOTION — additive only ══════════════════════════════════
   The resting state is VISIBLE. A screen swap gets a 220ms rise that runs with
   animation-fill-mode BACKWARDS, so a render that never fires it (a print, a
   screenshot, a crawler, a dead script) still ships the screen rather than a
   page of blank boxes. */
@media (prefers-reduced-motion: no-preference) {
  .bx-scr:not([hidden]) > section { animation: bxrise 0.24s var(--ease) backwards; }
  .bx-scr:not([hidden]) > section:nth-child(2) { animation-delay: 0.045s; }
  .bx-scr:not([hidden]) > section:nth-child(3) { animation-delay: 0.09s; }
  .bx-scr:not([hidden]) > section:nth-child(4) { animation-delay: 0.13s; }
  .bx-scr:not([hidden]) > section:nth-child(5) { animation-delay: 0.17s; }
  /* the everything-pages carry more sections than the original staircase covered */
  .bx-scr:not([hidden]) > section:nth-child(6) { animation-delay: 0.2s; }
  .bx-scr:not([hidden]) > section:nth-child(n+7) { animation-delay: 0.23s; }
}
@keyframes bxrise { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }

/* ═════════════════════════════════════════════════════════════════════════════
   THE RATIFIED C+A NAVIGATION PATTERN — the redesign's own block (2026-08-09)
   ═════════════════════════════════════════════════════════════════════════════

   SPEC · docs/design-hub/BISBE-CUSTOMERS-NAV-IA-2026-08-09/SUITE-NAV-PATTERN.md,
   ratified on FOUNDER's C+A ruling. Reference renders: candidate-c-work-first.html
   (the landing) and candidate-a-tiles.html (the everything-page). Every rule below
   is transcribed from one of those two files or from this stylesheet's own tokens
   above — no new hex, no new radius, no new type step. The two prototypes were
   written against the same crm.css transcription this file carries, which is why
   the transcription is a copy rather than a re-derivation.

   NOTHING HERE ENCODES MEANING BY HUE. The delta marks are glyph + number, the
   bars are lightness rungs on --g3, the fence is a dashed edge at the >=3:1
   --fence-line value, and every state carries its word. Read in greyscale it
   loses nothing. ═══════════════════════════════════════════════════════════ */

/* 2.4.11 FOCUS NOT OBSCURED — §1.3's floor, and it was genuinely missing: the
   tab bar is `position: fixed` at the bottom, so a keyboard user tabbing down a
   long everything-page landed underneath it. Scroll padding equal to the bar's
   height (plus the safe-area inset it pads itself with) is the fix. */
html { scroll-padding-bottom: calc(60px + env(safe-area-inset-bottom) + 10px); }
@media (min-width: 1000px) { html { scroll-padding-bottom: 24px; } }
/* THE STICKY PAIR OWES ITS OWN HEIGHT BACK (WCAG 2.4.11 · Designer ruling 4). The
   masthead and the ribbon are both sticky, so anything scrolled to by a fragment or by
   `scrollIntoView` lands UNDER them and the reader sees the wrong thing with no clue
   that they are looking at the wrong thing. `--ribbon-h` is written by demo.js from the
   ribbon's own measured height on load, on resize and on every toggle, so the reserve
   is the real number in both states rather than a guess that is wrong in one of them. */
html { scroll-padding-top: calc(var(--mast-h) + var(--ribbon-h, 64px) + 12px); }

/* ═══════════ THE MENU BUTTON (§1.3 · §1.5) ═══════════════════════════════════
   ONE control, in ONE place, in all seven doors: the top of the content column,
   directly under the workspace's own heading — inside the workspace, never in
   the spine. It SCROLLS WITH THE CONTENT, which is why it costs zero permanent
   chrome and why this pattern beats the rejected candidate B's 40px anchor strip.
   The label is always "Everything in <Workspace>" — never "Menu" (says nothing
   about where it goes), never "More" (implies leftovers), never a bare icon. */
.bx-everything {
  display: flex; align-items: center; gap: 10px; width: 100%;
  min-height: 48px; padding: 9px 14px; text-align: left;
  border: 1px solid var(--gilt-line); border-radius: 12px; background: var(--g1);
  transition: background 0.18s var(--ease);
}
@media (min-width: 560px) { .bx-everything { width: auto; } }
.bx-everything:hover { background: var(--g2); }
.bx-everything .lb { font-size: 14.5px; font-weight: 600; color: var(--ink); line-height: 1.25; }
.bx-everything .ct {
  font-family: 'JetBrains Mono', monospace; font-size: 9.5px; font-weight: 600;
  letter-spacing: 0.11em; text-transform: uppercase; color: var(--dim);
  margin-left: auto; text-align: right; line-height: 1.35;
}
.bx-everything .gy { color: var(--gilt-text); font-size: 12px; flex: none; }
/* A DOOR WHOSE EVERYTHING-PAGE THIS DEMO NAMES BUT DOES NOT DRAW. It is the same
   control in the same place — rule 3 is that the frame never changes shape per
   door — wearing the house's one withholding mark so it cannot be mistaken for a
   page that exists. It still ANSWERS, through the guide, like every other
   control here. */
.bx-everything.is-undrawn { border-style: dashed; border-color: var(--fence-line); background: transparent; }
.bx-everything.is-undrawn .lb { color: var(--mid); }

/* ═══════════ THE FIND BAR (candidate C) ══════════════════════════════════════
   A lookup job is performed by typing what you already know. Search is a
   DESTINATION, not a section (§2.1), which is why it is here and not a tile. */
.bx-find { display: grid; gap: 9px; }
.bx-findchips { display: flex; gap: 7px; flex-wrap: wrap; }
/* the chip is a real control here, so it takes the real target floor — 44px,
   not the 4px-padded label the same class is elsewhere */
.bx-findchips .bx-chip { min-height: 44px; padding: 0 13px; }
.bx-findchips .bx-chip:hover { background: var(--g3); color: var(--ink); }

/* ═══════════ THE GROUP HEAD ON THE LANDING ═══════════════════════════════════
   Three parts, and the third is load-bearing on the scoped Queue: the count, and
   the ONE-TAP ESCAPE out of the scope (countersign C4). */
.bx-grph { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.bx-grph h2 { font-size: 17px; font-weight: 700; letter-spacing: -0.015em; }
.bx-grph .n { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 700; color: var(--bisbe-text); }
.bx-grph .all {
  margin-left: auto; display: inline-flex; align-items: center; gap: 6px;
  min-height: 44px; padding: 0 10px; border-radius: 9px;
  font-size: 13px; font-weight: 600; color: var(--mid);
}
.bx-grph .all:hover { background: var(--g2); color: var(--ink); }
/* "showing X only · N of M" — countersign C5: a number is never scopeless. */
.bx-scopeline { font-size: 12.5px; line-height: 1.55; color: var(--mid); }
.bx-scopeline b { color: var(--ink); font-weight: 700; }

/* ═══════════ THE THING-THAT-NEEDS-YOU CARD (candidate C) ═════════════════════
   Named .bx-need rather than .bx-item because .bx-item is already the WORK
   BOARD's card in this file, and two different objects under one class name is
   how a stylesheet starts lying. Each card carries its own reason and its own
   control, so the common case costs zero navigation decisions. */
.bx-need { padding: 15px 16px; }
.bx-need:not(.gl) { background: var(--g1); border: 1px solid var(--line); border-radius: var(--radius); }
@media (min-width: 760px) { .bx-need { padding: 17px 18px; } }
/* one lightness rung up for the account you cannot let today end on — a rung,
   never a hue, and it still carries its glyph, its number and its word.
   ON THE MATERIAL THE RUNG IS DROPPED, and nothing is lost. Repainting the fill would
   delete the material outright (a surface may not carry both), and the alternative —
   re-pointing `--gl-body` one rung — would be inventing a token value by reasoning, on a
   material whose own floor is "contrast is MEASURED, not asserted" (§10a), and would owe
   a §10a clause under §10d's standing override check. The rung was never the only
   channel: this row carries ▼, −32, the word "Behind" and three reason sentences, which
   is what the CVD rule requires anyway (§5/§19 — meaning never rides on one channel).
   So the flat carve-outs keep the rung, and the glass row keeps the meaning. */
.bx-need--rung:not(.gl) { background: var(--g2); }
.need-top { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; margin-bottom: 3px; }
.need-g { font-style: normal; font-size: 12px; color: var(--dim); flex: none; }
.need-nm { font-weight: 700; font-size: 15.5px; letter-spacing: -0.01em; flex: 1 1 auto; min-width: 0; line-height: 1.35; }
.need-w {
  font-family: 'JetBrains Mono', monospace; font-size: 9.5px; font-weight: 600;
  letter-spacing: 0.11em; text-transform: uppercase; color: var(--dim); flex: none;
}
.need-sub { font-size: 13px; color: var(--mid); line-height: 1.5; }
/* THE REASONING, ON THE CARD. Printing it here is what makes approving in place
   honest — it never skips the reading, which is the app's whole trust promise. */
.bx-why {
  margin-top: 10px; padding: 10px 12px; border-radius: 12px;
  background: var(--g2); border: 1px solid var(--line-soft);
  font-size: 13px; line-height: 1.55; color: var(--ink);
}
.bx-need--rung .bx-why { background: var(--g1); }
.bx-why b { font-weight: 700; }
.bx-reasons { list-style: none; margin-top: 9px; display: grid; gap: 5px; }
.bx-reasons li { font-size: 13.5px; color: var(--mid); line-height: 1.5; display: flex; gap: 7px; align-items: flex-start; }
.bx-reasons li::before { content: '·'; flex: 0 0 auto; color: var(--dim); }
.bx-need .bx-acts { margin-top: 12px; }
/* the change since the last reading — glyph plus number plus a hidden word */
.bx-delta {
  display: inline-flex; align-items: baseline; gap: 4px; flex: none;
  font-family: 'JetBrains Mono', monospace; font-size: 12px; font-weight: 700; color: var(--ink);
}
.bx-delta i { font-style: normal; font-size: 11px; color: var(--mid); }

/* ═══════════ THE TO-DO ROW ═══════════════════════════════════════════════════ */
.bx-todo {
  display: flex; gap: 11px; align-items: flex-start; width: 100%; text-align: left;
  min-height: 44px; padding: 13px 0; border-bottom: 1px solid var(--line-soft);
}
.bx-todo:last-child { border-bottom: 0; }
.bx-todo:hover { background: var(--g2); }
.bx-todo .bxx {
  flex: none; width: 20px; height: 20px; border-radius: 6px; border: 1px solid var(--fence-line);
  display: grid; place-items: center; font-size: 11px; color: var(--dim); margin-top: 1px;
}
.bx-todo .tx { font-size: 14px; font-weight: 600; min-width: 0; line-height: 1.4; }
.bx-todo .tx small { display: block; font-weight: 400; font-size: 12.5px; color: var(--dim); margin-top: 3px; line-height: 1.5; }
.bx-todo .go { margin-left: auto; flex: none; font-size: 13px; font-weight: 600; color: var(--bisbe-text); }

/* ═══════════ THE DAY, QUIET, BELOW THE WORK ══════════════════════════════════ */
.bx-route { list-style: none; }
.bx-route li {
  display: flex; gap: 10px; align-items: baseline; padding: 9px 0;
  font-size: 14px; border-bottom: 1px solid var(--line-soft);
}
.bx-route li:last-child { border-bottom: 0; padding-bottom: 0; }
.bx-route .tm {
  flex: none; min-width: 44px; font-family: 'JetBrains Mono', monospace;
  font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--dim);
}
.bx-route .nm { min-width: 0; color: var(--mid); }
.bx-route .nm b { color: var(--ink); font-weight: 600; }

/* ═══════════ THE EVERYTHING-PAGE: THE TILE ROOM (candidate A) ════════════════
   The grid is crm.css's .crm-grid verbatim: one column at 375 by construction,
   and it reflows by auto-fill against a real minimum rather than shrinking
   columns. The tile MINIATURE is the rule that makes this a room rather than a
   menu (§1.2 rule 5): a real preview of the screen's own content, never a label
   and a chevron. */
.bx-room {
  list-style: none; display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 270px), 1fr));
}
@media (min-width: 700px) { .bx-room { gap: 16px; } }
.bx-tile {
  display: flex; flex-direction: column; gap: 6px; width: 100%; text-align: left;
  padding: 15px 16px 14px; min-height: 152px;
  color: var(--ink);
}
.bx-tile:not(.gl) {
  background: var(--g1); border: 1px solid var(--line); border-radius: var(--radius);
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease);
}
/* HOVER IS THE MATERIAL'S, NOT THE TILE'S (§10b). The old rule repainted the fill and
   darkened the border on hover — on a glass object that is a second register fighting
   the first, and §10b's ruling is one lift and a border that NEVER moves. A glass tile
   takes `.gl`'s own lift + glow; only the flat carve-outs keep the repaint. */
button.bx-tile:not(.gl):hover { background: var(--g2); border-color: var(--mid); }
/* WRAP, found by rendering: a fenced tile's title and its NOT-BUILT chip are
   both flex:none-ish in a 270px column, and without wrap the title broke
   mid-phrase to make room for the chip. Wrapping drops the chip to its own line
   instead, which is the readable order. */
.bx-tile .tile-t { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.bx-tile .tile-t b { font-size: 15.5px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.3; }
.bx-tile .tile-t .n {
  font-family: 'JetBrains Mono', monospace; font-size: 12px; font-weight: 700;
  color: var(--bisbe-text); margin-left: auto; flex: none;
}
.bx-tile .tile-s { font-size: 12.5px; color: var(--mid); line-height: 1.45; }
.bx-tile .mini {
  margin-top: auto; padding-top: 10px; border-top: 1px solid var(--line-soft);
  display: grid; gap: 4px; font-size: 11.5px; color: var(--dim); line-height: 1.4;
}
.bx-tile .mini .r { display: flex; gap: 7px; align-items: baseline; min-width: 0; }
.bx-tile .mini .r span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bx-tile .mini .k { font-family: 'JetBrains Mono', monospace; font-size: 10px; flex: none; color: var(--dim); }
.bx-tile .mini .g { font-style: normal; flex: none; font-size: 10px; }
/* the lightness bar — a rung, never a hue. --held is the SAME bar one step down
   in weight, so "available" and "held" are told apart by lightness and by the
   word beside them, not by colour. */
.bx-tile .bar { height: 6px; border-radius: 3px; background: var(--g3); overflow: hidden; flex: 1 1 auto; min-width: 40px; }
.bx-tile .bar > i { display: block; height: 100%; background: var(--mid); }
.bx-tile .bar--held > i { background: var(--dim); opacity: 0.45; }

/* A FENCED TILE (§2.6) — specified, not built. The dashed edge is the house's
   one withholding mark and it means "not available to you yet". It is a <div>,
   not a button: non-interactive is the spec's word, and a control that answers
   "nothing happens" is worse than one that was never a control. */
.bx-tile.is-fence { border-style: dashed; border-color: var(--fence-line); background: transparent; min-height: 128px; }
.bx-tile.is-fence .tile-t b { color: var(--mid); }
.chip-fence, .chip-open {
  margin-left: auto; flex: none;
  font-family: 'JetBrains Mono', monospace; font-size: 9px; font-weight: 600;
  letter-spacing: 0.11em; text-transform: uppercase; color: var(--dim);
  border: 1px dashed var(--fence-line); border-radius: 999px; padding: 3px 8px;
}
/* AN OPEN TILE — a real surface whose live context this demo cannot state
   without inventing a fact. Marked, not filled in. */
.bx-tile.is-open { border-style: dashed; border-color: var(--fence-line); background: transparent; }

/* ═══════════ THE QUEUE'S BY-ROOM STRIP (§2.7 · countersign C5) ═══════════════
   Each one opens the same Queue, scoped. A room with nothing waiting SHOWS ZERO
   AND STAYS VISIBLE, because an absence you can see is worth more than a shorter
   list. Zero is a lightness step down plus the numeral — never a colour. */
.bx-scope { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.bx-scope-h {
  font-family: 'JetBrains Mono', monospace; font-size: 9px; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--dim); flex: none;
}
.bx-scopebtn {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 13px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--g1);
  font-size: 13px; font-weight: 600; color: var(--ink);
}
.bx-scopebtn:hover { background: var(--g2); }
.bx-scopebtn .n { font-family: 'JetBrains Mono', monospace; font-size: 12px; font-weight: 700; color: var(--bisbe-text); }
.bx-scopebtn.is-zero { background: transparent; border-color: var(--line-soft); }
.bx-scopebtn.is-zero b { color: var(--dim); font-weight: 600; }
.bx-scopebtn.is-zero .n { color: var(--dim); }

/* the scope word on a queue card — countersign C5's "same construction" rule:
   the rail badge counts all rooms, a card says which room it is from, and both
   use the words "Queue · <Workspace>". */
.bx-chip.is-scope { background: transparent; border-color: var(--gilt-line); color: var(--ink); }
.bx-chip.is-scope .gy { color: var(--gilt-text); }
