/* ═══════════════════════════════════════════════════════════════════════════════════════════
   portal-d6.css — the Direction 6 layer (2026-09-16, owner's brief of the same day)
   ═══════════════════════════════════════════════════════════════════════════════════════════

   WHAT THIS IS. Direction 6 "Planner Desk" — `artifacts/portal-directions/
   EVORRAH_PORTAL_DIRECTIONS.html#06` — is the approved foundation for the Client Portal, and
   the live portal does not follow it closely enough. This file is the layer that makes it
   follow: one stylesheet, loaded LAST, scoped entirely to `body.d6`, owning the direction's
   tokens and its component vocabulary. Rooms are then re-templated onto those classes one at a
   time, each room's old CSS deleted in the same commit that replaces its markup.

   Its specification is `docs/execution/portal-d6/D6_FIDELITY_SPEC.md` — measured in a real
   browser at 1440/1024/390 on 2026-09-16, not read off the prototype's source. Every value
   below cites the section it comes from. Its strategy is `D6_RENDER_STRATEGY.md` §3; the
   rulings that bind it are the eleven decisions in `EXECUTION_LEDGER.md`.

   ── THE FOUR RULES OF THIS FILE (D6_RENDER_STRATEGY §3, ledger D10) ──────────────────────
   1. EVERY selector begins `body.d6 `. No exceptions — the whole layer can be switched off by
      removing one class, which is also how it is reverted. `app/styles/portal-nav.css` proved
      the approach: 147 `.d6` selectors, and it beats the rooms CSS today.
   2. NO RAW HEX in a rule. Chapter-08 token names, the `--d6-*` names declared at the top of
      this file, `transparent`, `currentColor`, `color-mix()`. The palette census counts a hex
      even when it is byte-identical to the token it mirrors.
   3. NO `!important`. The `body.d6` prefix IS the specificity budget. A rule that needs force
      is fighting an old rule that should be deleted instead.
   4. NO `transition: all`, and in fact no transition and no shadow: the direction declares
      none (`D6_FIDELITY_SPEC` §4 — of 271 elements on the prototype's Home, zero have a
      non-zero transition-duration, and every measured box-shadow is `none`).

   ── WHY IT CAN BE LOADED LAST AND STILL BE SAFE ──────────────────────────────────────────
   `portal-v15.css` carries the house 44px touch floor and relies on loading last to beat
   same-specificity rules in portal.css and portal-v10.css (test/portal-v15-touch-floor.test.mjs
   case 5). That case is re-stated for this file rather than bypassed: this sheet declares no
   `min-height` on any selector in the floor's roster, and its own controls carry their own
   floor (44px by default, 30px only on a fine pointer — ledger D6). See the dated note in
   that test.

   ── WHERE IT IS REGISTERED ───────────────────────────────────────────────────────────────
   app/client-portal.html (one <link>, last) · test/portal-colour-tokens (no bare literal) ·
   test/portal-retired-typeface (no retired face) · test/portal-design-tokens (radii and
   transitions) · test/portal-design-ratchet (its own measured ceilings) ·
   test/portal-zindex-scale (no bare band z-index) · the palette census discovers it by itself.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */

/* ── The page frame ────────────────────────────────────────────────────────────────────────
   D6_FIDELITY_SPEC §3: `.c06-page` is `padding: 14px 18px 44px`, and "nothing in Direction 6
   has 64px of anything". The live portal's `--portal-gutter` is 64px, 3.5× the prototype's
   18px, and it is the single biggest reason the rooms read as a different product at a glance.
   `body .tab-panel.active` (portal-v10.css:739) is (0,2,1) and so is `body.d6 .tab-panel`;
   this sheet loads later, so the tie goes to Direction 6 without `!important`, and it also
   beats portal-rooms.js's injected `#roomsCss` (0,1,0 selectors appended to <head> at boot). */
body.d6 .tab-panel { padding-inline: 18px; }
/* ⚠ THE SECOND SELECTOR IS NOT A DUPLICATE, IT IS THE RIGHT-HAND SIDE. Measured on the rig
   with only the rule above: `padding-left: 18px`, `padding-right: 64px`. portal-v10.css:1081
   writes `body:not(.portal-aside-visible) .tab-panel.active{padding-right:var(--v10-gutter)}`,
   which is (0,3,1) and outranks (0,2,1) whatever the order. `body.d6 .tab-panel.active` is
   also (0,3,1) and this sheet loads later, so the tie goes to Direction 6 — the honest way
   past a more specific rule is an equally specific one, never `!important` (rule 3). */
body.d6 .tab-panel.active { padding-inline: 18px; }

/* ── ONE LEFT EDGE, AND THE LAYER HAS TO SAY SO (2026-09-18, wave 5b, the phone/tablet step) ──
   THE DEFECT, measured on the rig on the Files room at three widths before this line:

       width   page gutter (this layer)   --v10-gutter   .items-section left   .d6-head left
        1440            18px                  64px            **194**              258
        1024            18px                  32px            **226**              258
         390            18px                  20px             **-2**               18

   `portal-v10.css` §10 is the owner's own "one left edge" law of 2026-08-25 — *"i dont love that
   the left formatting throughout the client portal changes everytime"* — and it works as a PAIR:
   a full-bleed surface cancels the page gutter with `margin-left:calc(-1 * var(--v10-gutter))`
   and supplies the same value back as its own interior. Both halves are stated in the token
   precisely so they cannot drift apart. What nobody could have stated in 2026-08-25 is that a
   later layer would change the page gutter itself: wave 1 set `padding-inline:18px` here, and
   from that moment the cancel was subtracting 64 from a page that only added 18. Every case-(b)
   surface — `.items-section`, `.docs-section`, `.creative-direction-banner`, `#briefSubmittedPanel`
   — has been hanging 46px past the page's own left edge at 1440, 14px at 1024, and **2px
   off-screen at 390**. The same token is read by `portal-nav.css`'s eight `#portalMain >` mounts,
   which were written to *"agree with the room below them"* and did not.

   ⚠ THE FIX IS THE TOKEN, NOT THE RULES, and that is the point. Re-stating eighteen selectors in
   the layer would leave the next one to be written wrong; binding the token under `body.d6` makes
   the law true by construction for every consumer, present and future — and it keeps the law's
   own guarantee, that the two halves are the same number, because they still read one name.
   `test/portal-left-edge` is untouched by it: that file loads tokens → portal.css → portal-v10.css
   with no `body.d6` and no layer, so it still reads 64 / 32 / 20 from `:root` and still measures
   the pair against itself at the three widths the token actually takes. */
body.d6 { --v10-gutter: 18px; }

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   1 · TOKENS — the direction's palette, type scale and shape, declared once
   ═══════════════════════════════════════════════════════════════════════════════════════════
   D6_FIDELITY_SPEC.md §1.2 and D6_RENDER_STRATEGY.md §4. Declared HERE rather than in
   tokens.css, which is ledger D10's ruling ("the layer owns the seven component classes and
   the token block") and departs from the strategy's §3 rule 5 on purpose, for three reasons:
   the layer stays removable in one line; tokens.css is shared with the public site and the
   CRM, and a 9.5px type token there sits inside test/type-floor's scope; and the palette
   census reads both files identically, so nothing is hidden by the choice.

   ⚠ THESE ARE NEW COLOURS, NOT ALIASES. #F0E7D8 is not --ch8-alabaster (#E7E3DD): the delta
   is 9/4/-5 and the alabaster reads grey where the prototype reads oat. #FBF6EE is not
   --ch8-linen. Substituting a chapter-08 value that is visibly different is a different
   design, and the owner's brief of 2026-09-16 is that the prototype wins (ledger D7). Every
   colour below that HAS an exact chapter-08 equivalent is written as that token instead —
   linen, onyx, bordeaux, crimson, the approve and revise signals are exact matches, and the
   four ink/rule steps are within a few points and are mapped rather than duplicated.

   Nineteen hexes are new to the portal's palette census (the seventeen §1.2 names the
   fidelity spec counts, plus --d6-paper #FFFDF9, which has no house equivalent at all, and
   the drawer's own rule). The census baseline was re-recorded with that reason stated. */
body.d6 {
  /* ── grounds · seven warm neutrals, 4–12 points of luminance apart (§1.3). Direction 6
     draws structure with GROUNDS AND HAIRLINES, never with shadow — this is the device. */
  /* (2026-09-27, design truth: the seven shell tokens that were declared here — bar, rail, rail
     hover / rule / mute / mute-mobile, sep — were read 0 times; the shell reads chapter 08.) */
  --d6-band: #F6EEE2;               /* status strip and filter bar */
  --d6-wash: #FBF6EE;               /* the "lifted row" — th, row hover, expanded row, inline */
  --d6-paper: #FFFDF9;              /* panel, card, tile — no house equivalent */
  --d6-flash: #F6E3E3;              /* the 1.1s "this number just changed" ground */
  --d6-hot-ring: #E7C9C9;           /* the ring on a hot count chip */
  --d6-box-line: #B9AC9B;           /* the select checkbox */
  /* ── the dark plate. Not used by wave 1a; declared with the rest so the palette is
     re-baselined once rather than five times as the waves land (§8.5, the bulk bar). */
  --d6-onyx-hover: #2C2724;
  --d6-plate: #221E1C;
  --d6-plate-line: #4E4640;
  --d6-plate-line-hover: #9A8E84;
  --d6-plate-warn: #F0B7B2;
  --d6-plate-warn-line: #5C4340;

  /* ── inks and rules · chapter 08 carries these, so they are MAPPED, not re-declared.
     §1.1: --ink/--onyx and the ground are exact; --ink-2 is Δ4/6/4 from onyx-78, --ink-3
     Δ15/8/4 from onyx-62, and the two rules are warmer in the prototype by ~20 points.
     Taking the house token is the honest trade: the deltas are invisible beside the
     structural change, and every one of them keeps the portal on the brand's own scale. */
  --d6-ground: var(--ch8-linen);
  --d6-ink: var(--ch8-onyx);
  --d6-ink-2: var(--ch8-onyx-78);
  --d6-ink-3: var(--ch8-onyx-62);
  --d6-line: var(--ch8-onyx-20);
  --d6-line-2: var(--ch8-onyx-12);

  /* ── signals (§1.1). Bordeaux, crimson, approve and revise are byte-exact matches of the
     prototype's own; amber is the house's quieter warn and is taken deliberately. */
  --d6-mark: var(--ch8-bordeaux);   /* the current-item rule, link buttons */
  --d6-hot: var(--ch8-crimson);     /* hot counts, the focus ring */
  --d6-good: var(--ch8-signal-approve);
  --d6-warn: var(--ch8-amber);
  --d6-bad: var(--ch8-signal-revise);

  /* ── type (§2, ledger D2). Two families and nothing else. The whole direction is drawn at
     13px with no rem: the portal's root is 112.5% = 18px, and at that scale every measurement
     in §2 is ~38% wrong and the density that IS the direction cannot exist. These are chrome
     sizes, not client prose, which is why they are px — the owner ruled the prototype's scale
     on 2026-09-16 (ledger D2), overriding the 2026-09-15 "portal scale wins". */
  --d6-sans: 'Inter', -apple-system, 'Helvetica Neue', Arial, sans-serif;
  --d6-serif: 'Crimson Pro', 'Times New Roman', serif;
  --d6-t-page: 25px;                /* h1, serif, sentence case */
  --d6-t-panel: 16.5px;             /* h2, serif, sentence case */
  --d6-t-row: 13.5px;               /* a row title, a rail item */
  --d6-t-base: 13px;                /* body, and a table cell */
  --d6-t-sec: 12.5px;               /* a sub-line, a button, a chip */
  --d6-t-strip: 12px;               /* the status strip, a table sub-cell */
  --d6-t-count: 11.5px;             /* counts and pills */
  --d6-t-chip: 11px;                /* the count chip in a panel head */
  --d6-t-th: 10px;                  /* table heads — uppercase, .09em */
  --d6-tr-th: .09em;
  --d6-lh: 1.45;
  --d6-lh-title: 1.1;

  /* ── shape (§4, ledger D3 — SUPERSEDED 2026-09-25). The owner's brief once named 4/6/7/8/999
     inside body.d6. Owner, 2026-09-25 (portal one-format): "fix the corners to the brand" — the
     house's three radii and nothing else (brand/brand.json tokens.radius: 0 default, 2px inputs,
     50% true circles only). The five names stay so every rule keeps reading one place. */
  --d6-r-box: 2px;                  /* the select checkbox, a swatch */
  --d6-r-ctl: 2px;                  /* every button, the search field */
  --d6-r-in: 2px;                   /* the inline answer block, the top-bar field */
  --d6-r-panel: 0;                  /* panel, table wrapper, filter bar, tile, card */
  --d6-r-pill: 2px;                 /* a pill is a LABEL, not a control */
  /* ── button letter-case, in ONE place (owner, 2026-09-25, portal one-format: the whole portal takes
     the format of Updates + Your desk, whose buttons are sentence case; docs/DECISIONS.md 2026-09-25).
     The 2026-09-24 ruling D1 said UPPERCASE; the question is back with the owner. His answer is these
     two lines: `uppercase` + `.08em` makes every .d6-btn in the portal uppercase at once. */
  --d6-btn-case: none;
  --d6-btn-track: 0;
}

/* ── The body scale ────────────────────────────────────────────────────────────────────────
   §2: the base is 13px/1.45 Inter set on the direction's root, and everything inherits from
   it. This is the single rule that makes the portal read at Direction 6's density rather than
   at the house's 18px/300 — measured on Home, 47 distinct leaf type combinations against the
   prototype's 22, most of them inherited rather than declared.
   ⚠ It is set on `body.d6` and not on `html`, so `rem` is untouched: long-form client copy
   that rides the house --text-* scale keeps its size, which is D2's own carve-out. */
body.d6 {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  line-height: var(--d6-lh);
  font-weight: 400;
  color: var(--d6-ink);
}
/* ⚠ BUT NOT THE BAR (merge of origin/main 7e556d7a28, 2026-09-25). The owner's "Portal bar:
   version 11" (live on main) is the header, and its reserves (198 / 220px signed in, 79 / 101
   signed out) were measured with the page's own type metrics inherited into it — Direction 6's
   13px / 1.45 body here shrank the bar's rows (a 56px top row against bar 11's measured 74 on an
   upright iPhone SE) and every reserve band went wrong. So the bar keeps the metrics it was
   measured with; everything below it reads Direction 6's scale. */
body.d6 .portal-hdr { font-size: 1rem; line-height: normal; }

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   2 · COMPONENTS — the whole vocabulary, seven objects
   ═══════════════════════════════════════════════════════════════════════════════════════════
   D6_FIDELITY_SPEC.md §8, measured in the browser. D6_RENDER_STRATEGY.md §3 lists what each
   one replaces: eleven card families, six hero bands, nine pill classes, four table classes.
   A room is re-templated onto these and its own classes are deleted in the same commit.

   ⚠ CONTROL HEIGHT IS 44px BY DEFAULT AND 30px ONLY ON A FINE POINTER (ledger D6). The
   direction draws 30px; the house law is "44px on a phone", and the two are reconciled by
   asking the device rather than the viewport — `(hover:hover) and (pointer:fine)` is a mouse,
   where 30px clears WCAG 2.5.8's 24px floor with room to spare. Writing it in this order
   matters: a device that answers neither query keeps the floor, so the fallback is always the
   safe one. Every phone, every touch screen and every unknown device gets 44.

   ⚠ THE CONTROL FACE IS `--d6-paper`, NOT PURE WHITE. The prototype writes `#fff` on buttons,
   chips and the search field. The brand book's first rule is that the only ground is ivory,
   and pure white is a `colour.forbidden` finding in scripts/brand-lint.mjs. #FFFDF9 against
   #FFFFFF is a delta of 0/2/6 — invisible beside a hairline, and it keeps the layer from
   introducing the one colour the house has ruled out. */

/* ── 8 · the head row ─────────────────────────────────────────────────────────────────────
   §8: h1 serif 25 sentence case, a 12.5px sub-line capped at 62ch, a spacer, and ONE primary
   action. Every room gets this and nothing else at the top — the six hero bands, the lens
   strips and the editorial letters the audit measured are what it replaces. */
body.d6 .d6-head { display: flex; align-items: flex-end; gap: 12px; margin: 2px 0 12px; }
body.d6 .d6-head h1 {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-page);
  line-height: var(--d6-lh-title);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  margin: 0;
}
body.d6 .d6-head p {
  margin: 3px 0 0;
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-3);
  max-width: 62ch;
}
body.d6 .d6-head .d6-sp { flex: 1; }
body.d6 .d6-head .d6-acts { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }

/* ── 8.1 · the grid and the panel — the unit the whole direction is built from ────────────
   12 columns, 12px gutter. `margin-top:auto` on the footer is what bottom-aligns two panels'
   actions in one row; the span is a CLASS rather than an inline style so the phone can undo
   it without `!important` (the prototype uses `grid-column:1/-1!important` and cannot). */
body.d6 .d6-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: 12px; }
body.d6 .d6-panel {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  display: flex;
  flex-direction: column;
  min-width: 0;
  box-shadow: none;
  grid-column: span 12;
}
/* ⚠ `display:flex` ABOVE OUTRANKS THE `hidden` ATTRIBUTE, AND THAT IS A REAL DEFECT, NOT A
   TIDY-UP (2026-09-18, wave 5a). The UA sheet's `[hidden]{display:none}` is one specificity
   point; a class that declares `display` beats it, so the moment a live host that is hidden by
   its own module gains `.d6-panel` it comes back on screen. Measured on the rig: the After
   section at the foot of What we handle is `hidden=true` because `afterScreen().present` is
   false on an event that has not happened, and it painted its heading anyway — §22A.3 at the
   page layer, which is the exact thing `fillAfterFoot()` exists to prevent. Restated for the
   component rather than as a blanket `body.d6 [hidden]`, so nothing else in the portal changes
   precedence. */
body.d6 .d6-panel[hidden] { display: none; }
/* §8.1 — the spans the prototype actually draws. 4 arrives with `pageDesigns()`'s three
   panels (2026-09-18, wave 4); 12 is the default above and is named here so a composition can
   say it rather than rely on it. */
body.d6 .d6-panel--4 { grid-column: span 4; }
body.d6 .d6-panel--5 { grid-column: span 5; }
body.d6 .d6-panel--6 { grid-column: span 6; }
body.d6 .d6-panel--7 { grid-column: span 7; }
body.d6 .d6-panel--12 { grid-column: span 12; }
body.d6 .d6-ph {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 12px;
  border-bottom: 1px solid var(--d6-line-2);
}
body.d6 .d6-ph h2 {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  line-height: var(--d6-lh-title);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  margin: 0;
}
body.d6 .d6-ph .d6-ct {
  font: 500 var(--d6-t-chip)/1 var(--d6-sans);
  font-variant-numeric: tabular-nums;
  color: var(--d6-ink-3);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-pill);
  padding: 3px 7px;
}
body.d6 .d6-ph .d6-ct.is-hot { color: var(--d6-hot); border-color: var(--d6-hot-ring); }
body.d6 .d6-ph .d6-sp { flex: 1; }
body.d6 .d6-pb { padding: 4px 12px 10px; }
body.d6 .d6-pf {
  margin-top: auto;
  padding: 8px 12px 10px;
  border-top: 1px solid var(--d6-line-2);
  display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
}

/* ── 8.2 · the row — the list item inside a panel ─────────────────────────────────────── */
body.d6 .d6-row {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 9px 0;
  border-bottom: 1px solid var(--d6-line-2);
}
body.d6 .d6-row:last-child { border-bottom: 0; }
body.d6 .d6-row .d6-gr { flex: 1; min-width: 0; }
/* ⚠ `display:block` ON BOTH, AND IT IS NOT DECORATION (2026-09-17, wave 2 — measured on the
   rig, `guests` fixture at 1440). §8.2 draws the title and its sub-line as two LINES; wave 1a
   wrote the two rules without a display, and a `<span>` is inline, so the first row of the
   re-templated Home read *"A suite is ready for your print sign-offSigning off sends it to the
   printer exactly as you last approved it."* — two sentences run together with no space, on
   every queue row. `margin-top` on an inline box is also ignored, so the 2px between them was
   never applied either. The prototype's own `.c06-row .t` / `.s` are `<div>`s; the portal draws
   spans because a row lives inside a `<span class="d6-gr">`, so the display has to be said. */
body.d6 .d6-row .d6-t {
  display: block;
  font-weight: 500; font-size: var(--d6-t-row); color: var(--d6-ink);
  line-height: var(--d6-lh);
}
body.d6 .d6-row .d6-s {
  display: block;
  color: var(--d6-ink-3); font-size: var(--d6-t-sec); margin-top: 2px;
  line-height: var(--d6-lh);
}
body.d6 .d6-acts { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }

/* ── 8.3 · buttons — three, and they are small ────────────────────────────────────────── */
body.d6 .d6-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  font-weight: 400;
  letter-spacing: var(--d6-btn-track);
  text-transform: var(--d6-btn-case);
  color: var(--d6-ink);
  white-space: nowrap;
  cursor: pointer;
  box-shadow: none;
}
body.d6 .d6-btn:hover { border-color: var(--d6-ink-3); }
body.d6 .d6-btn--pri {
  background: var(--ch8-onyx);
  border-color: var(--ch8-onyx);
  color: var(--ch8-linen);
}
body.d6 .d6-btn--pri:hover { background: var(--d6-onyx-hover); border-color: var(--d6-onyx-hover); }
body.d6 .d6-btn--lnk {
  border-color: transparent;
  background: transparent;
  padding: 0 4px;
  color: var(--d6-mark);
  text-decoration: underline;
  text-underline-offset: 3px;
}
body.d6 .d6-btn--lnk:hover { border-color: transparent; color: var(--d6-mark); }
body.d6 .d6-btn[disabled] { opacity: .45; cursor: not-allowed; }
/* PORTAL UX AUDIT 2026-09-27: a pressed button (a tool bar's open door) wears the house selected
   state — the sage rule `.d6-chip[aria-pressed="true"]` below carries (ledger D5) — not the onyx
   fill, which read as a second primary beside the sage chip in the same bar. A primary that
   toggles (Messages' "Message guests", portal-rooms.js messagesCompose) stays the primary. */
body.d6 .d6-btn:not(.d6-btn--pri)[aria-pressed="true"] {
  border-color: var(--ch8-signal-approve);
  color: var(--ch8-signal-approve);
  background: color-mix(in srgb, var(--ch8-signal-approve) 6%, var(--d6-paper));
}
body.d6 .d6-btn:focus-visible { outline: 2px solid var(--d6-hot); outline-offset: 2px; }

/* ── 8.5 · chips and the filter bar ───────────────────────────────────────────────────── */
body.d6 .d6-filters {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: 8px 10px;
  background: var(--d6-band);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  margin-bottom: 8px;
}
body.d6 .d6-filters .d6-sp { flex: 1; }
body.d6 .d6-chip {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 44px;
  padding: 0 15px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-pill);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  color: var(--d6-ink-2);
  white-space: nowrap;
  cursor: pointer;
}
body.d6 .d6-chip .d6-num { font-variant-numeric: tabular-nums; color: var(--d6-ink-3); }
body.d6 .d6-chip:focus-visible { outline: 2px solid var(--d6-hot); outline-offset: 2px; }
/* ⚠ THE PRESSED STATE IS THE HOUSE'S SAGE RULE, NOT THE PROTOTYPE'S ONYX FILL — ledger D5,
   the one deliberate departure from Direction 6. test/portal-onyx-fill-ban.test.mjs holds the
   owner's 2026-08-26 ruling on black chips, and a portal with two selected-state languages is
   worse than either one. Nothing structural is lost: the control still says so to a screen
   reader through aria-pressed, which is what that guard's own open case is about. */
body.d6 .d6-chip[aria-pressed="true"] {
  border-color: var(--ch8-signal-approve);
  color: var(--ch8-signal-approve);
  background: color-mix(in srgb, var(--ch8-signal-approve) 6%, var(--d6-paper));
}

/* ── 8.4 · the table, and the row that expands in place ──────────────────────────────── */
body.d6 .d6-tw {
  overflow-x: auto;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  background: var(--d6-paper);
}
body.d6 .d6-pb .d6-tw { border: 0; border-radius: 0; background: transparent; }
body.d6 .d6-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--d6-t-base);
  color: var(--d6-ink);
}
body.d6 .d6-table th {
  font: 600 var(--d6-t-th)/1 var(--d6-sans);
  letter-spacing: var(--d6-tr-th);
  text-transform: uppercase;
  color: var(--d6-ink-3);
  text-align: left;
  padding: 9px 10px;
  border-bottom: 1px solid var(--d6-line);
  background: var(--d6-wash);
  white-space: nowrap;
}
body.d6 .d6-table td {
  padding: 7px 10px;
  border-bottom: 1px solid var(--d6-line-2);
  vertical-align: middle;
}
body.d6 .d6-table tr:last-child td { border-bottom: 0; }
body.d6 .d6-table tbody tr.is-clickable:hover { background: var(--d6-wash); cursor: pointer; }
body.d6 .d6-table tr.is-open td { background: var(--d6-wash); }
body.d6 .d6-table .d6-num { font-variant-numeric: tabular-nums; color: var(--d6-ink-2); }
body.d6 .d6-table .d6-nm { font-weight: 500; }
body.d6 .d6-table .d6-sub { color: var(--d6-ink-3); font-size: var(--d6-t-strip); }
body.d6 .d6-table td.d6-r { text-align: right; white-space: nowrap; }
/* Rows expand IN PLACE — Direction 6 has no detail page for a piece, a message or a fact
   (§8.4). The drawer is a colspan row beneath the one that was pressed. */
body.d6 .d6-exp {
  padding: 2px 10px 12px;
  font-size: var(--d6-t-sec);
  color: var(--d6-ink-2);
  background: var(--d6-wash);
}

/* ── 8.6 · the pill — a LABEL, not a control ─────────────────────────────────────────── */
body.d6 .d6-pill {
  display: inline-flex; align-items: center; gap: 6px;
  height: 20px;
  padding: 0 9px;
  border-radius: var(--d6-r-pill);
  font: 500 var(--d6-t-chip)/1 var(--d6-sans);
  letter-spacing: 0;
  text-transform: none;
  white-space: nowrap;
  color: var(--d6-ink-2);   /* a11y 2026-09-27: ink-3 read 3.84:1 on this ground at 11px; ink-2 = 6.66:1 */
  background: var(--d6-line-2);
}
body.d6 .d6-pill--you { color: var(--d6-hot); background: var(--d6-flash); }
body.d6 .d6-pill--us { color: var(--d6-ink-2); background: var(--d6-band); }
body.d6 .d6-pill--done { color: var(--ch8-moss); background: color-mix(in srgb, var(--d6-good) 12%, var(--d6-paper)); }
body.d6 .d6-pill--wait { color: var(--d6-warn); background: color-mix(in srgb, var(--d6-warn) 12%, var(--d6-paper)); }

/* ── 8.7 · key/values, and the empty state ───────────────────────────────────────────── */
body.d6 .d6-kv {
  display: grid; grid-template-columns: auto 1fr; gap: 6px 14px;
  font-size: var(--d6-t-base);
  margin: 0;
}
body.d6 .d6-kv dt { color: var(--d6-ink-3); }
body.d6 .d6-kv dd { margin: 0; }
/* §8.9: an empty state is ONE sentence in muted ink. No illustration, no panel, no button —
   which is why `.d6-note` and `.d6-empty` are the same rule under two names: in this direction
   an empty state IS a note, and the prototype writes both with `.c06-note`. The two names are
   kept apart because they mean different things to a reader of the markup — a panel's standing
   sentence, and the sentence that stands in for a list that has nothing in it. */
body.d6 .d6-note,
body.d6 .d6-empty {
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-3);
  margin: 8px 0 0;
}

/* ── the fine pointer · 30px controls (ledger D6) ────────────────────────────────────────
   Last of the desktop block on purpose: it narrows the 44px floor above, and only for a
   device that has said it is a mouse. */
@media (hover: hover) and (pointer: fine) {
  body.d6 .d6-btn { min-height: 30px; padding: 0 11px; font-size: var(--d6-t-sec); }
  body.d6 .d6-btn--lnk { padding: 0 4px; }
  body.d6 .d6-chip { min-height: 30px; padding: 0 12px; font-size: var(--d6-t-sec); }
}

/* ── the phone · one breakpoint, 640px (§5) ───────────────────────────────────────────── */
@media (max-width: 640px) {
  body.d6 .d6-head { flex-direction: column; align-items: stretch; }
  body.d6 .d6-head h1 { font-size: 23px; }
  body.d6 .d6-grid { grid-template-columns: 1fr; }
  /* ⚠ EVERY SPAN IS NAMED HERE — wave 3's trap 7. A span is only undone by a selector at
     least as specific as the one that set it, and a span left standing in a one-column grid
     creates implicit columns rather than stacking. */
  body.d6 .d6-panel, body.d6 .d6-panel--4, body.d6 .d6-panel--5, body.d6 .d6-panel--6,
  body.d6 .d6-panel--7, body.d6 .d6-panel--12 { grid-column: auto; }
  body.d6 .d6-row { flex-direction: column; align-items: stretch; }
  body.d6 .d6-row .d6-acts { width: 100%; }
  body.d6 .d6-row .d6-acts .d6-btn { flex: 1; }
  /* ⚠ THE FLOOR COMES BACK ON A PHONE, BY WIDTH AS WELL AS BY POINTER, AND THE SECOND HALF IS
     NOT REDUNDANT. Ledger D6 is "30px on a fine pointer, 44px on a coarse pointer AND on every
     phone". A desktop browser at a 390px viewport — which is what every rendered guard in
     test/ and this session's own instrument uses — still answers `(pointer: fine)`, so the
     pointer arm alone left a 30px control on the measured phone: read off the rig at 390,
     minH 30. The house law is 44px on a phone whoever is holding it. */
  body.d6 .d6-btn { min-height: 44px; padding: 0 14px; font-size: var(--d6-t-row); }
  body.d6 .d6-btn--lnk { padding: 0 4px; }
  body.d6 .d6-chip { min-height: 44px; padding: 0 15px; font-size: var(--d6-t-row); }

  /* ── THE FLOOR REACHES THE CONTROLS THE LAYER DOES NOT OWN (2026-09-18, wave 5b, Step 10) ──
     Ledger D6 is "44px on every phone", and the rooms this programme re-templated obey it
     because their controls wear `.d6-btn`. Two rooms still draw their own: the Seating tiles
     (`portal-guests-seating.js` writes a bare `<button>` per plan suggestion, plus the capacity
     input, the zone select and the tile's own move/delete buttons) and the Design room's
     per-piece *Flag for designer*. Measured at 390 before this block, on the `guests` fixture:

         seating   22 × <button> "Add this"          h=26
                    2 × .table-cap-input             h=28
                    2 × .table-zone-input            h=30
                    8 × .table-card-del              h=30
         designs    4 × <button> "Flag for designer"  h=24

     Forty-one controls under the house floor on the two rooms where a phone does the most
     tapping. Stated by ROOM rather than by class because these are module-written elements with
     no class to name, and stated as `min-height` so nothing moves on a desktop.

     ⚠ THREE THINGS ARE DELIBERATELY NOT RAISED, and each has a written reason older than this
     wave. **Checkboxes and radios** stay at 24 — `portal.css`'s own block says why in as many
     words: an `appearance:auto` checkbox ignores padding in Chromium, so a 44px floor would draw
     a 44px BOX, and 24px is WCAG 2.5.8's criterion, which `test/portal-target-size-24px` pins.
     **`.evo-hint`** is a 14px glyph with a deliberate 44×44 `::after` pointer region that the
     same guard's case 3 exists to keep — the target is already 44. **An inline link inside a
     sentence** ("hello@evorrah.com", "ask us", "Your Designs") takes 2.5.8's own Inline
     exception; making it a block target would stack two targets on one line. The instrument
     reports all three under `exempt` rather than silently passing them. */
  body.d6 #tab-seating button,
  body.d6 #tab-seating select,
  body.d6 #tab-seating input:not([type="checkbox"]):not([type="radio"]),
  body.d6 #tab-designs button:not(.evo-hint) {
    min-height: 44px;
  }
  /* The four per-table icon buttons are SQUARE, so the floor has to be stated in both
     directions or a 44px-tall 30px-wide target is still under it. Declared here rather than in
     the seating block below because that block's own `min-width:30px` is later in the file and
     more specific; this is the same number said at the same specificity, which is the layer's
     rule 3 (narrow, never force). */
  body.d6 #tab-seating .table-card-del { min-width: 44px; min-height: 44px; }
}

/* ── *Flag for designer* — the treatment that came off an inline style (2026-09-18, wave 5b) ──
   `app/client-portal.html` wrote this control with `style="padding:4px 0;min-height:24px;
   display:inline-block;…"`, which pinned it at 24px at EVERY width — four of them on the
   Designs room, measured at 390 as 105×24. An inline style cannot be answered by a stylesheet
   when `!important` is forbidden (D10), so the three layout declarations were DELETED from the
   module and stated here. Nothing else about the control moved: it keeps its crimson, its
   underline, its weight and its inherited size, all still inline. */
body.d6 .ds-flag-stale {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 4px 0;
}
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 .ds-flag-stale { min-height: 30px; }
}

/* ── THE TWO PER-EVENT SCOPE CHECKBOXES, SQUARED AT EVERY WIDTH (2026-09-18, wave 5b) ─────────
   `portal-people.js` writes the access form's *"which events can they see"* checkboxes
   (`[data-ac-ev]` and `#ypAcEvery`), and the form is drawn in two rooms — People, and Plan &
   billing through `#s6PlanScreen`. Measured at 1440 and 1024: **13 × 13 px**, which is under
   WCAG 2.2 AA 2.5.8's 24px criterion, not merely under the house's 44px phone floor.
   `portal.css` already squares every checkbox to 24 at ≤640, with a written reason — an
   `appearance:auto` checkbox ignores padding in Chromium, so the box itself has to grow — and
   that block simply never applied above 640. `WAVE5A_CLOSE.md` §6 recorded these two as a
   "reading that is not 30/44" and pre-existing; measured against the criterion rather than the
   house floor they are a real finding, and this is the fix.
   ⚠ FROM THE LAYER ONLY. `portal-people.js` is a DO-NOT-EDIT peer, which is why the rule is here
   and names the module's own attributes rather than touching its render. 24 and not 44: 24 is
   the criterion, and the ≤640 block above already carries the phone. */
body.d6 input[type="checkbox"][data-ac-ev],
body.d6 input[type="checkbox"][data-ac-every],
body.d6 #ypAcEvery {
  width: 24px;
  height: 24px;
}

/* ── Shape, re-stated where the old skin is more specific ────────────────────────────────
   ⚠ NOT A DUPLICATE — A SPECIFICITY ANSWER, AND THE MEASUREMENT THAT MADE IT NECESSARY.
   On the rig the panel and the table wrapper drew their 8px and every BUTTON drew 0. The
   rule taking it away is `body[data-active-tab] .tab-panel button{border-radius:0}` in
   portal-v15.css — (0,2,2) against this layer's (0,2,1), so it wins on specificity whatever
   the load order. The house's answer to that is not `!important` (rule 3) and not a token
   change: it is to say the same thing at the specificity the argument is being had at.
   (0,3,1) beats (0,2,2) because the class column is compared first.
   Every one of these is the value declared above, read from the same token — a shape is
   written once and this block only decides where it applies. */
body.d6 .tab-panel .d6-btn { border-radius: var(--d6-r-ctl); }
body.d6 .tab-panel .d6-chip { border-radius: var(--d6-r-pill); }
body.d6 .tab-panel .d6-pill { border-radius: var(--d6-r-pill); }
body.d6 .tab-panel .d6-ct { border-radius: var(--d6-r-pill); }
body.d6 .tab-panel .d6-panel { border-radius: var(--d6-r-panel); }
body.d6 .tab-panel .d6-tw { border-radius: var(--d6-r-panel); }
body.d6 .tab-panel .d6-filters { border-radius: var(--d6-r-panel); }

/* ── The inbox's rows, inside the Direction 6 table ──────────────────────────────────────
   Updates is now a `.d6-table` whose `<tbody>` is the inbox's second host, so every row
   carries `.inbox-notif` — the class the read-state helpers find it by — and with it the
   drawer's CARD styling: a 3px coloured left rule, a tinted ground on the nudge, 14/20px
   padding. Those are right for a 320px drawer and wrong for a table; the row is not a card
   here. This block takes the card's decoration off inside the table only, and gives unread
   the direction's own signal instead: a heavier title and the count colour on the timestamp
   (§1.1 — crimson is the hot state). The drawer is untouched.
   ⚠ `.inbox-notif` IS NOT RENAMED. Every helper that clears an unread row — `_onNotifClick`,
   `markAllInboxRead`, `_recountInboxUnread` — finds rows by that class across both hosts. */
body.d6 .d6-table tr.inbox-notif { border-left: 0; background: transparent; padding: 0; }
body.d6 .d6-table tr.inbox-notif:hover { background: var(--d6-wash); }
body.d6 .d6-table tr.inbox-notif.unread .d6-nm { font-weight: 600; }
body.d6 .d6-table tr.inbox-notif.unread .d6-num { color: var(--d6-hot); }
body.d6 .d6-table tr.inbox-day-hdr td {
  font: 600 var(--d6-t-th)/1 var(--d6-sans);
  letter-spacing: var(--d6-tr-th);
  text-transform: uppercase;
  color: var(--d6-ink-3);
  background: var(--d6-wash);
  padding: 9px 10px;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   HOME · "YOUR DESK" — the six panels (2026-09-17, wave 2)
   ══════════════════════════════════════════════════════════════════════════════════════════
   Spec: D6_FIDELITY_SPEC.md §8.10. Everything the six panels need beyond the seven component
   classes already declared above — the page sub-line, the three serif figures, the dated
   Timeline row with its date gutter, the inline note inside a decision row, and the
   register-C "what this holds up" line. Nothing here is a new component: each is a variant of
   `.d6-row`, `.d6-note` or the head, which is why none of them introduces a class family.
   ⚠ The relocated cards keep their own modules' styling. `#conciergeWelcome`, `#milestonesMount`,
   `#questionsMount`, `#clientPlanCard` and the rest are moved INTO these panels by
   app/scripts/portal-rooms.js#homeRehome — this layer only gives them the panel's measure and
   takes off the standalone top rule each of them carried as a sibling of a tab panel. */

/* The page sub-line. `#rm-home-sub` holds either the welcome band (its module's two lines) or
   the static sentence homeSubline() writes when the band has nothing to say. */
body.d6 .d6-head #rm-home-sub { min-width: 0; }
body.d6 .d6-head .d6-sub {
  margin: 3px 0 0;
  font-size: var(--d6-t-strip);
  line-height: var(--d6-lh);
  color: var(--d6-ink-3);
  max-width: 62ch;
}

/* §8.7 — the three guest figures. Serif, lining tabular numerals, caption beneath. */
body.d6 .d6-figs { display: flex; gap: 20px; flex-wrap: wrap; margin: 4px 0 0; }
body.d6 .d6-fig {
  font-family: var(--d6-serif);
  font-size: 27px;
  font-weight: 400;
  line-height: 1;
  color: var(--d6-ink);
  font-variant-numeric: lining-nums tabular-nums;
}
body.d6 .d6-cap { margin-top: 3px; font-size: var(--d6-t-count); color: var(--d6-ink-3); }

/* §8.10 panel 3 — the Timeline row's date gutter. 74px in the prototype; `flex:0 0 74px` so a
   long month name wraps inside the gutter rather than pushing the title out of the panel. */
body.d6 .d6-row--dated .d6-when {
  flex: 0 0 74px;
  font-family: var(--d6-serif);
  font-size: var(--d6-t-row);
  line-height: var(--d6-lh);
  color: var(--d6-ink-2);
  font-variant-numeric: lining-nums tabular-nums;
}
body.d6 .d6-row--dated.is-done .d6-t { color: var(--d6-ink-2); }

/* Register C's "what this holds up", under the row it is about. The mark colour is the
   direction's own signal for a thing that is waiting — the same ink the count chip goes hot
   in — so the working view needs no second device to say "this one first". */
body.d6 .d6-row .d6-holds { color: var(--d6-mark); }

/* §8.8 — the note that answers inside the row that asked. Used by the decision row for what
   approving locks and what a change costs. */
body.d6 .d6-row .d6-inline {
  display: block;
  margin: 8px 0 2px;
  padding: 10px;
  background: var(--d6-wash);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-in);
}
body.d6 .d6-row .d6-inline .d6-lab {
  display: block;
  font-size: var(--d6-t-strip);
  line-height: var(--d6-lh);
  color: var(--d6-ink-2);
}
body.d6 .d6-row .d6-inline .d6-lab + .d6-lab { margin-top: 6px; }

/* The scope sentence under the derived minutes — its own line, not a second paragraph. */
body.d6 .d6-note .d6-sc { display: block; margin-top: 4px; }

/* ── The relocated cards, inside the panels that now hold them ───────────────────────────
   Each of these was authored as a sibling ABOVE every `.tab-panel` and carried the chrome of
   a standing card: its own rule above, its own outer margin, its own 1,072px measure. Inside
   a panel body it is a block of that panel. Nothing is hidden and no module is touched — this
   only takes off the spacing a card needs when it is alone on a page. */
body.d6 .d6-pb > #rm-home-needs:empty,
body.d6 .d6-pb > #rm-home-timeline:empty { display: none; }
/* Since the merge of both Homes (2026-09-25) these two hosts hold Version 3's slots, so they are
   never literally empty: a host whose slots hold nothing takes no room either. */
body.d6 .d6-pb > #rm-home-needs:not(:has(> .td-v3-slot > *)),
body.d6 .d6-pb > #rm-home-timeline:not(:has(> .td-v3-slot > *)) { display: none; }
body.d6 .d6-pb > #rm-home-timeline > .td-v3-slot + .td-v3-slot:has(> *) { margin-top: 10px; }
body.d6 .d6-pb > #rm-home-needs > *,
body.d6 .d6-pb > #rm-home-timeline > * { margin-left: 0; margin-right: 0; max-width: none; }
body.d6 .d6-pb #rm-home-sub > * { margin: 0; }

/* The welcome band, as the page's sub-line. It is two lines of its own — the event and how
   far away it is, then what is open — which is exactly §8.10's sub-line, so it loses the band
   chrome it wore when it stood alone above the panel and takes the sub-line's type. */
body.d6 .d6-head #conciergeWelcome .cw-band {
  border: 0;
  background: none;
  padding: 0;
  margin: 3px 0 0;
  max-width: 62ch;
}

/* ── The phone (§5) ─────────────────────────────────────────────────────────────────────── */
@media (max-width: 640px) {
  /* The date gutter is a line of its own above the title rather than a column, because 74px
     out of a 358px panel leaves the title 12 characters wide. `.d6-row` is already
     `flex-direction:column` on a phone, so this only has to release the fixed basis. */
  body.d6 .d6-row--dated .d6-when { flex: none; }
  body.d6 .d6-figs { gap: 16px; }
}

/* "What changed" inside the Timeline panel. `changedHtml()` still composes it from the design
   rows' own stamps — nothing about the reading changed — but its eyebrow was Montserrat 8.6px
   uppercase at .2em, which is the live portal's label voice and one of the four things
   D6_GAP_AUDIT §5 measures as making the portal not read as Direction 6. Direction 6 uses
   uppercase in exactly four roles and a panel's inner heading is not one of them (§2), so it
   takes the note voice instead. */
body.d6 .d6-pb .rm-changed { margin-top: 10px; }
body.d6 .d6-pb .rm-changed .k {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-2);
  margin-bottom: 2px;
}
body.d6 .d6-pb .rm-changed div.l {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-3);
}

/* The relocated question cards, inside "Needs you". `#questionsCard` and the RSVP-questions card
   are house cards — a Linen ground, a hairline, 26/28px padding — which is right when a card
   stands alone above a panel and wrong when it IS a block of one: a frame on a frame, at the
   panel's own measure. The modules keep every rule they own; this takes off the outer chrome and
   brings their eyebrow and title to the panel's voice (§2 — Direction 6 uses uppercase in exactly
   four roles and a block heading inside a panel is not one of them). */
body.d6 .d6-pb #questionsCard,
body.d6 .d6-pb #rsvpQuestionsMount > * {
  margin: 8px 0 0;
  padding: 0;
  border: 0;
  background: none;
}
body.d6 .d6-pb #questionsCard .pq-eyebrow {
  font-size: var(--d6-t-strip);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-2);
  margin: 0 0 2px;
}
body.d6 .d6-pb #questionsCard h2.pq-title {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  font-weight: 400;
  line-height: var(--d6-lh-title);
  margin: 0 0 4px;
}
body.d6 .d6-pb #questionsCard .pq-sub,
body.d6 .d6-pb #questionsCard .pq-empty {
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-3);
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 3 · GUESTS · SEATING · THE DAY (2026-09-18, ledger D11 wave 3)
   ═══════════════════════════════════════════════════════════════════════════════════════════
   The four components §8 measures that waves 1–2 had no screen for — the search field, the
   select box, the dark bulk plate and the tile grid — plus the two devices the Guests page
   needs to be one table: the phone card stack that REPLACES the table at ≤640 (§5: the
   direction does not scroll a table sideways on a phone, it renders a different component)
   and the footer line under it (`.k-row` in the prototype's own `pageGuests`).

   Every value is `D6_FIDELITY_SPEC.md` §8.5 / §8.7 / §5, measured in the browser 2026-09-16.
   The four rules of this file still hold: `body.d6` prefix, no raw hex, no `!important`, no
   `transition`.
   ── */

/* ── 8.5 · the search field in the filter bar ────────────────────────────────────────────
   A real `<input>`, unlike the top bar's `.c06-find` (a button styled as a field). The live
   `#agSearchInput` is ADOPTED into the bar and given this class — its `oninput` and its
   `aria-label` travel with the node, so `filterRsvpSearch()` is still what runs. */
body.d6 .d6-search {
  min-height: 44px;
  padding: 0 10px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  color: var(--d6-ink);
  width: 190px;
  max-width: 100%;
  margin: 0;
}
body.d6 .d6-search::placeholder { color: var(--d6-ink-3); }
body.d6 .d6-search:focus-visible { outline: 2px solid var(--d6-hot); outline-offset: 2px; }

/* The second row of the filter bar — the tools. `flex-basis:100%` puts them on their own line
   under the chips and the search, which is what the prototype draws at every width; on a phone
   §5 makes it a horizontal scroller rather than four rows of buttons. */
body.d6 .d6-tools {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  flex: 1 0 100%;
  margin-top: 2px;
}

/* ── THE CARD STACK'S OWN SELECT-ALL AND SORT (PG-003, 2026-09-20) ───────────────────────────
   At 720px and under, `app/styles/portal.css:2600` hides `.rsvp-table` and shows `.rsvp-cards`
   — the one responsive pattern of the live table that already matched the direction. Four
   controls lived in that table's `<thead>` and nowhere else: the select-all box and the three
   sortable column heads. This row is where they are on a phone, and it exists ONLY there: the
   `display:none` below is the same 720px the card stack switches on, so on a desktop the real
   `<thead>` is the only place either control is drawn and there is never a second copy of one
   state on screen. The markup is app/scripts/portal-rooms.js#guestCardToolsHtml() and both
   controls call the page's own `toggleSelectAllGuests()` / `sortRsvpBy()`. */
body.d6 .d6-cardtools {
  display: none;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  flex: 1 0 100%;
  margin-top: 2px;
}
@media (max-width: 720px) {
  body.d6 .d6-cardtools { display: flex; }
}
/* (the row has no `.d6-sp` — see guestCardToolsHtml()'s note.) */
body.d6 .d6-cardtools .d6-ct-all {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 44px;
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  color: var(--d6-ink-2);
  cursor: pointer;
}
/* 44 × 44, exactly like the `.guest-sel-chk` on each card beneath it — one tap target size for
   one job, and the house floor rather than 2.5.8's 24px minimum.
   ⚠ `#tab-rsvp` IS IN THE SELECTOR AND IS LOAD-BEARING — measured, not defensive. The page's
   own `<style>` block (app/client-portal.html:522) carries
   `#tab-rsvp input[type="checkbox"]{width:24px;height:24px;…}`, specificity (1,1,1), and it
   beat `body.d6 .d6-cardtools .d6-ct-all input` (0,3,2) whatever the load order: read back off
   the rig with CDP `CSS.getMatchedStylesForNode`, the control computed **24 × 24** with every
   other declaration of this block winning. That rule's own comment says the 44px tap target
   "returns under Evorrah's two-armed touch media query", and it does not — the rule carries no
   media query at all, which is why each card's checkbox has to wear an inline 44 × 44 to get
   one. The house answer to a more specific rule is a more specific rule, never `!important`
   (D10), so the id is repeated here. */
/* PORTAL UX AUDIT 2026-09-27: the card checkbox beneath is now a 24px box with 10px of margin
   (portal-guests-seating.js renders it inline), so select-all matches it — the same visible box,
   the same 44px footprint; the label around it stays 44px tall. */
body.d6 #tab-rsvp .d6-cardtools .d6-ct-all input {
  width: 24px;
  height: 24px;
  min-width: 24px;
  min-height: 24px;
  margin: 10px;
  flex-shrink: 0;
  cursor: pointer;
  accent-color: var(--d6-hot);
}
body.d6 .d6-cardtools .d6-ct-lab {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;  /* portal one format (2026-09-25): a label is sentence case, like the rail */
  color: var(--d6-ink-3);
}
body.d6 .d6-cardtools .d6-ct-sorts { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
body.d6 .d6-cardtools .d6-ct-sort { gap: 4px; }
/* The arrow is the table's own `#sortArrow*` glyph, mirrored — never a second derivation.
   It is `aria-hidden`, because the state it shows is on the button as `aria-pressed`. */
body.d6 .d6-cardtools .d6-ct-arrow { color: var(--d6-ink-3); }
body.d6 .d6-cardtools .d6-ct-sort[aria-pressed="true"] {
  border-color: var(--ch8-signal-approve);
  color: var(--ch8-signal-approve);
  background: color-mix(in srgb, var(--ch8-signal-approve) 6%, var(--d6-paper));
}
body.d6 .d6-cardtools .d6-ct-sort[aria-pressed="true"] .d6-ct-arrow { color: var(--ch8-signal-approve); }

/* ── 8.5 · the select box. 22×22 with the ✓ hidden until pressed; 44×44 on a phone. ─────── */
body.d6 .d6-box {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  padding: 0;
  border: 1px solid var(--d6-box-line);
  border-radius: var(--d6-r-panel);
  background: var(--d6-paper);
  color: transparent;
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  cursor: pointer;
}
body.d6 .d6-box[aria-pressed="true"] {
  border-color: var(--ch8-signal-approve);
  color: var(--ch8-signal-approve);
  background: color-mix(in srgb, var(--ch8-signal-approve) 6%, var(--d6-paper));
}
body.d6 .d6-box:focus-visible { outline: 2px solid var(--d6-hot); outline-offset: 2px; }

/* ── 8.5 · the bulk plate. The one dark surface in the direction. ───────────────────────── */
body.d6 .d6-bulk {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 8px 12px;
  margin: 0 0 8px;
  border-radius: var(--d6-r-panel);
  background: var(--d6-plate);
  color: var(--ch8-linen);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
}
body.d6 .d6-bulk .d6-cnt {
  font-family: var(--d6-serif);
  font-size: 17px;
  font-variant-numeric: lining-nums tabular-nums;
}
body.d6 .d6-bulk .d6-sp { flex: 1; }
body.d6 .d6-bulk .d6-btn {
  background: transparent;
  border-color: var(--d6-plate-line);
  color: var(--ch8-linen);
}
body.d6 .d6-bulk .d6-btn:hover { border-color: var(--d6-plate-line-hover); }
body.d6 .d6-bulk .d6-btn--warn { color: var(--d6-plate-warn); border-color: var(--d6-plate-warn-line); }
body.d6 .d6-bulk select {
  min-height: 30px;
  border: 1px solid var(--d6-plate-line);
  border-radius: var(--d6-r-ctl);
  background: transparent;
  color: var(--ch8-linen);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  padding: 0 8px;
  margin: 0;
}

/* ── 8.7 · the tile grid — one tile per table, on Seating ────────────────────────────────── */
body.d6 .d6-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: 8px;
}
body.d6 .d6-tile {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 10px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  color: var(--d6-ink);
  cursor: pointer;
}
body.d6 .d6-tile:hover { border-color: var(--d6-ink-3); }
body.d6 .d6-tile:focus-visible { outline: 2px solid var(--d6-hot); outline-offset: 2px; }
body.d6 .d6-tile .d6-th {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
}
body.d6 .d6-tile .d6-th b { font-weight: 500; }
body.d6 .d6-tile .d6-th span {
  font-size: var(--d6-t-count);
  color: var(--d6-ink-3);
  font-variant-numeric: tabular-nums;
}
body.d6 .d6-tile .d6-tw2 {
  display: block;
  margin-top: 4px;
  font-size: var(--d6-t-strip);
  color: var(--d6-ink-3);
  line-height: var(--d6-lh);
}

/* ── 8.7 · the phone card. §5: at ≤640 the table is REPLACED by this stack. ───────────────
   ⚠ THIS BLOCK DOES NOT DECIDE WHEN THE STACK IS SHOWN, AND THE FIRST DRAFT DID — which cost
   twenty-one controls on the measured phone. `app/styles/portal.css` already owns that switch
   (`.rsvp-cards{display:none}` at 2618, `display:block` inside its own phone block), so a
   `display:none` here at (0,2,0) beat it at every width and hid the card stack that REPLACES
   the table at 390: measured, every per-guest control — Edit, Remove, Select, the RSVP link,
   the five swap asks — gone from the census. A layer that dresses a live node must not also
   re-decide its visibility. Only the gap is set, and only where the stack is already laid out. */
body.d6 .d6-cards { gap: 8px; }
body.d6 .d6-card {
  display: flex; gap: 10px; align-items: flex-start;
  width: 100%;
  padding: 10px;
  text-align: left;
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  font-family: var(--d6-sans);
}
body.d6 .d6-card .d6-t { display: block; font-weight: 500; font-size: 14px; color: var(--d6-ink); }
body.d6 .d6-card .d6-s { display: block; margin-top: 2px; font-size: var(--d6-t-sec); color: var(--d6-ink-3); }

/* ── the footer line under a table (`pageGuests`'s own `.k-row`) ─────────────────────────── */
body.d6 .d6-foot {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 10px;
  font-size: var(--d6-t-sec);
  color: var(--d6-ink-3);
}
body.d6 .d6-foot .d6-sp { flex: 1; }

/* ── the detail a tool opens ──────────────────────────────────────────────────────────────
   §8.4: Direction 6 has no detail PAGE — a tool opens its material in place. On Guests the
   nine tools of `pageGuests` open here, between the filter bar and the table, one at a time,
   so the list is never more than a scroll away and nothing had to be given a route of its own. */
body.d6 .d6-detail {
  margin: 0 0 8px;
  padding: 10px 12px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-in);
  background: var(--d6-wash);
}
body.d6 .d6-detail[hidden] { display: none; }
body.d6 .d6-detail > [hidden] { display: none; }

/* ── the fine pointer · 30px (ledger D6), for the four controls added above ─────────────── */
/* ⚠ `(min-width: 641px)` IS NOT DECORATION — ledger D6, and it was measured wrong first.
   A desktop browser at a 390px viewport still answers `(pointer: fine)`, which is what every
   rendered guard and this session's instrument use, so a bare fine-pointer arm placed AFTER
   the 640px block wins at 390 and leaves a 30px control on the measured phone: read off the rig
   at 390, minH 30. The house law is 44px on a phone whoever is holding it, so every fine-pointer
   narrowing in this file that is written after the phone block carries the width too. */
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 .d6-search { min-height: 30px; font-size: var(--d6-t-sec); }
  body.d6 .d6-box { width: 22px; height: 22px; border-radius: var(--d6-r-box); }
}

/* ── the phone · one breakpoint, 640px (§5) ──────────────────────────────────────────────── */
@media (max-width: 640px) {
  body.d6 .d6-search { width: 100%; }
  /* §5 made `.c06-tools` a horizontal scroller. PORTAL ONE FORMAT (owner, 2026-09-25): no control
     hides off the edge behind a sideways scroll — the owner's Vision note ("I need to scroll
     horizontally to get to the 10th part") is the rule for every row of controls. They wrap. */
  body.d6 .d6-tools { flex-wrap: wrap; overflow-x: visible; }
  body.d6 .d6-tools .d6-btn { flex: 0 0 auto; }
  body.d6 .d6-tiles { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
}

/* ── THE DAY · the host is the grid (wave 3) ───────────────────────────────────────────────
   `pageDay()` is `head(…)` over ONE twelve-column grid of span-6 panels. `#rm-host-day` is the
   node portal-day-lens.js holds and relocate() fills, so it IS that grid: the head spans 12,
   each card is a `.d6-panel--6`, and the two `<div>`s the module splits its cards across
   (`#dayLensBriefA` / `#dayLensBriefB`, a reading order, not a layout) are `display:contents`
   so their panels are grid items rather than two tall columns.
   ⚠ THE FIVE RELOCATED SECTIONS ARE DRESSED, NOT MOVED. `relocate()` wraps each in an
   `.rm-sec` slot with a heading and a blurb — the same slot Experience, Events and People use —
   so the slot is given the panel's chrome HERE, scoped to this host, and the section inside it
   keeps every listener, every control and its own module. Nothing is rebuilt. Re-templating
   what is inside each one is waves 4–5. */
body.d6 #rm-host-day {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 12px;
  align-items: start;
  max-width: none;
  padding: 0 0 8px;
}
body.d6 #rm-host-day > #dayLensBriefA,
body.d6 #rm-host-day > #dayLensBriefB { display: contents; }
/* ⚠ DESCENDANT, NOT CHILD, AND `display:contents` IS WHY. The two brief nodes are grid items
   in the DOM and NOT in the layout, so the panels inside them are the grid's children for
   placement and its GRANDCHILDREN for a `>` selector. A child selector here silently matched
   nothing — measured at 390 as a 27px panel. Everything that places a day panel is written
   as a descendant for that reason. */
body.d6 #rm-host-day .d6-head { grid-column: 1 / -1; margin-bottom: 0; }
body.d6 #rm-host-day .d6-panel { grid-column: span 12; }
body.d6 #rm-host-day .d6-panel--6 { grid-column: span 6; }
/* A `.d6-grid` nested INSIDE a day panel (none today; waves 4–5 may add one) must not inherit
   the host's placement, so the two rules above are the host's own and stop at its panels. */
body.d6 #rm-host-day .d6-panel .d6-panel { grid-column: auto; }

/* The relocated sections, wearing the panel. `.rm-sec-h` is the slot's one word a host scans
   for at 4pm (portal-rooms.js MOVES) — it becomes the panel heading; `.rm-sec-b` is its
   sentence, which becomes the note directly under it. */
body.d6 #rm-host-day > .rm-sec {
  grid-column: span 12;
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 9px 12px 10px;
  min-width: 0;
}
body.d6 #rm-host-day > .rm-sec > .rm-sec-h {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  font-weight: 400;
  line-height: var(--d6-lh-title);
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  margin: 0;
}
body.d6 #rm-host-day > .rm-sec > .rm-sec-b {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-3);
  margin: 3px 0 8px;
  max-width: 62ch;
}
/* The photo space arrives as its own card from its own module; inside the grid it is a panel
   like the rest, and its standalone margins are the ones a card needs when it is alone. */
body.d6 #rm-host-day > #photoSpaceCard {
  grid-column: span 6;
  margin: 0;
  min-width: 0;
}

/* The three voices a briefing needs and a note alone cannot carry: "we could not read this"
   (§44D.1 — never the same as empty), a warning, and the house's own line under the vendors.
   Each is the note's type with ONE device added, because §9's list of what the direction does
   not have includes a second ink for prose. */
body.d6 .d6-unread {
  border-left: 2px solid var(--d6-line);
  padding-left: 12px;
  margin-left: 0;
}
body.d6 .d6-warn { color: var(--d6-warn); }
body.d6 .d6-s { color: var(--d6-ink-3); }
body.d6 .d6-house {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--d6-line-2);
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-2);
}
body.d6 .d6-chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
body.d6 .d6-plain { list-style: none; margin: 0; padding: 0; }
body.d6 .d6-plain li {
  padding: 8px 0;
  border-top: 1px solid var(--d6-line-2);
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-2);
}
body.d6 .d6-plain li:first-child { border-top: 0; }
/* A medical row is marked with the direction's own signal for a thing that is waiting, on the
   left edge of the row — never with a second ground, which the table has none of. */
body.d6 .d6-table tr.d6-alert td:first-child { border-left: 2px solid var(--d6-bad); }

@media (max-width: 640px) {
  /* ⚠ THE HOST IS ITS OWN GRID, SO §5's ONE-COLUMN RULE HAS TO NAME IT. The block above
     collapses `.d6-grid`, and `#rm-host-day` is not that class — measured on the rig at 390
     before this rule existed: a twelve-column track 354px wide gave each span-6 panel **7px**.
     One column, and every child spans it, whatever span it carries at desktop. */
  body.d6 #rm-host-day { grid-template-columns: 1fr; }
  /* ⚠ EVERY SPAN IS NAMED, AND `> *` IS NOT ENOUGH — the trap that cost two measurements.
     `body.d6 #rm-host-day > *` is (1,1,1); `> .rm-sec` above is (1,2,1) and KEEPS its
     `span 12`, which in a one-column explicit grid creates ELEVEN IMPLICIT COLUMNS. Read off
     the rig at 390: `grid-template-columns` computed `222px 0px ×11` and every panel was
     222px wide inside a 354px host — 354 minus eleven 12px gaps. A span is undone by a
     selector at least as specific as the one that set it. */
  body.d6 #rm-host-day > *,
  body.d6 #rm-host-day > .rm-sec,
  body.d6 #rm-host-day .d6-panel,
  body.d6 #rm-host-day .d6-panel--6,
  body.d6 #rm-host-day > #photoSpaceCard { grid-column: 1 / -1; }
}

/* ── SEATING · the relocated section, wearing the direction (wave 3) ───────────────────────
   `#seatGuestsPhase` is static markup in client-portal.html that portal-guests-seating.js
   addresses by id and writes into twenty times over; wave 1b relocated the LIVE node into this
   room and nothing here rebuilds it. So the page is composed the way the prototype composes
   it — `portal-rooms.js#fillSeating()` puts the head above the section and the two panels
   below it — and this block gives what is INSIDE the section the direction's chrome.

   ⚠ THE TABLE LIST IS THE PROTOTYPE'S TILE GRID. `pageSeating()` draws `.c06-tiles` — a
   `repeat(auto-fill,minmax(132px,1fr))` grid of paper tiles, each a table's name, its
   `seated/seats` and its first names (§8.7). `renderTablesListEl()` already draws exactly those
   facts, as a vertical list of `.table-card` rows, and each row carries six controls the
   prototype's tile does not have: a capacity input, a zone select, up, down, rename and remove.
   Every one of them is kept. The list becomes the grid and the row becomes the tile; the track
   is 240px rather than 132px because a tile here holds those six controls as well as the facts.
   ⚠ NOTHING IS HIDDEN BY THIS BLOCK. It changes ground, rule, radius and type — never display. */
/* ⚠ THE SLOT HEADING GOES ON THIS ONE PAGE, AND ONLY ON THIS ONE. `relocate()` wraps every
   moved section in an `.rm-sec` slot carrying a heading and a blurb, and that is right for a
   section being given a home in a room it did not come from — it is how a host knows what the
   block is. Here the ROOM IS THE SECTION: the page's own `<h1>` says Seating and the head's
   sub-line says how many are seated at how many tables, so "SEATING · Your tables, who is at
   each one, and how much room is left" is the page's third name for itself in eighty pixels.
   §9: the direction has one name per page. The slot, the node and the blurb's words all stay —
   this hides two labels on one page, and every other `.rm-sec` in the portal is untouched. */
body.d6 #tab-seating > #rm-host-seating > #rm-slot-seatGuestsPhase > .rm-sec-h,
body.d6 #tab-seating > #rm-host-seating > #rm-slot-seatGuestsPhase > .rm-sec-b { display: none; }

body.d6 #tab-seating .guests-phase {
  background: transparent;
  border-top: 0;
}
body.d6 #tab-seating .guests-phase-hd { padding: 0; gap: 8px; }
body.d6 #tab-seating .guests-phase-body { padding: 0; }
/* The numbered eyebrow ("03 ◆ Seat Guests") is the phase ladder's marker, and §9 lists both
   the ladder and the glyph among the things Direction 6 does not have — the page's own `<h1>`
   says Seating. It is the one thing this block takes off the screen, and it is a LABEL with no
   control and no number in it; `portal-rooms.js#fillSeating()` writes the heading that replaces
   it. The CLASS stays in portal.css because seven generated platform screens lift it. */
body.d6 #tab-seating .guests-phase-eyebrow { display: none; }

body.d6 #tab-seating .tables-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 8px;
  margin-bottom: 12px;
}
body.d6 #tab-seating .table-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 9px 10px;
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
}
body.d6 #tab-seating .table-card-name {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  font-weight: 500;
  color: var(--d6-ink);
  flex: 1 1 auto;
  min-width: 0;
}
body.d6 #tab-seating .table-card-count {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-count);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-3);
  font-variant-numeric: tabular-nums;
}
/* ⚠ THE FOUR PER-TABLE BUTTONS KEEP THEIR 24px BOX IN BOTH DIRECTIONS (WCAG 2.2 AA 2.5.8, and
   test/portal-target-size-24px.test.mjs measures it). Direction 6's own control is 30px on a
   mouse and 44px on a phone, which this gives them — the floor is raised, never lowered. */
body.d6 #tab-seating .table-card-del {
  min-width: 30px;
  min-height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 6px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  color: var(--d6-ink-2);
  opacity: 1;
}
body.d6 #tab-seating .table-card-del:hover { border-color: var(--d6-ink-3); color: var(--d6-ink); }

/* The room's own buttons — the name generator, the auto-seat row, the add-table row — take the
   direction's button rather than the live portal's 10.4px uppercase Montserrat plate. */
body.d6 #tab-seating .tng-generate-btn,
body.d6 #tab-seating .tng-apply-btn,
body.d6 #tab-seating .tng-mode-tab,
body.d6 #tab-seating .seating-toggle-btn {
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
}
body.d6 #tab-seating .tng-generate-btn:hover,
body.d6 #tab-seating .tng-apply-btn:hover,
body.d6 #tab-seating .tng-mode-tab:hover,
body.d6 #tab-seating .seating-toggle-btn:hover { border-color: var(--d6-ink-3); background: var(--d6-paper); }
/* D5 — the pressed state is the house's sage rule, never Direction 6's onyx fill. */
/* ⚠ THE RING IS INSET ON THE VIEW TOGGLE, AND IT IS A REAL DEFECT THIS WAVE FOUND. Its wrapper
   `.seating-view-toggle` is `overflow:hidden` (app/styles/portal.css), so the global
   `:focus-visible{outline:2px solid; outline-offset:2px}` painted OUTSIDE the clip and changed
   **0 pixels** — measured by scripts/portal-a11y-audit.mjs at BOTH widths, which is how it is
   told apart from the instrument's known one-off reading. The same defect portal-rooms.js
   recorded for the room rail in §35 (2026-09-01) and answered the same way: drawn inside the
   box it is whole at every width. */
body.d6 #tab-seating .seating-toggle-btn:focus-visible,
body.d6 #tab-seating .tng-mode-tab:focus-visible {
  outline: 2px solid var(--d6-hot);
  outline-offset: -3px;
}
body.d6 #tab-seating .tng-mode-tab.active,
body.d6 #tab-seating .seating-toggle-btn.active {
  border-color: var(--ch8-signal-approve);
  color: var(--ch8-signal-approve);
  background: color-mix(in srgb, var(--ch8-signal-approve) 6%, var(--d6-paper));
}
body.d6 #tab-seating .tng-select,
body.d6 #tab-seating .tng-write-textarea {
  min-height: 44px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  color: var(--d6-ink);
}
body.d6 #tab-seating .tng-write-hint,
body.d6 #tab-seating .tables-empty-hint,
body.d6 #tab-seating .guests-phase-locked-msg,
body.d6 #tab-seating .seating-touch-tip {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  font-style: normal;
  color: var(--d6-ink-3);
}

/* The map view's own tables, and the unseated pool the goal names — the same tile, and a list
   whose chips are the direction's pills rather than dashed boxes. */
body.d6 #tab-seating .seating-map-grid {
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 8px;
}
body.d6 #tab-seating .seating-table-card {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 9px 10px;
}
body.d6 #tab-seating .seating-table-name {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  font-weight: 500;
  color: var(--d6-ink);
  border-bottom: 1px solid var(--d6-line-2);
}
body.d6 #tab-seating .seating-guest-item {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-strip);
  color: var(--d6-ink-2);
  border-bottom-color: var(--d6-line-2);
}
body.d6 #tab-seating #seatingGuestPool {
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  background: var(--d6-wash);
  padding: 9px 12px;
}
body.d6 #tab-seating .seating-pool-title {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
}
body.d6 #tab-seating .seating-pool-chips .seating-pool-chip {
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-pill);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-count);
  color: var(--d6-ink-2);
  padding: 4px 10px;
}
body.d6 #tab-seating .seat-plan-card,
body.d6 #tab-seating #seatingHistoryCard,
body.d6 #tab-seating #seatingSuggestionsCard,
body.d6 #tab-seating #seatingReleaseCard {
  border-radius: var(--d6-r-panel);
}

/* ⚠ `(min-width: 641px)` IS NOT DECORATION — ledger D6, and it was measured wrong first.
   A desktop browser at a 390px viewport still answers `(pointer: fine)`, which is what every
   rendered guard and this session's instrument use, so a bare fine-pointer arm placed AFTER
   the 640px block wins at 390 and leaves a 30px control on the measured phone: read off the rig
   at 390, minH 30. The house law is 44px on a phone whoever is holding it, so every fine-pointer
   narrowing in this file that is written after the phone block carries the width too. */
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #tab-seating .tng-generate-btn,
  body.d6 #tab-seating .tng-apply-btn,
  body.d6 #tab-seating .tng-mode-tab,
  body.d6 #tab-seating .seating-toggle-btn,
  body.d6 #tab-seating .tng-select { min-height: 30px; font-size: var(--d6-t-sec); }
  body.d6 #tab-seating .table-card-del { min-width: 24px; min-height: 24px; }
}

/* ── GUESTS · one table, a filter bar over it, one line under it (wave 3) ───────────────────
   `portal-rooms.js#fillGuestsRoom()` composes the page — head, filter bar, the detail a tool
   opens, the live list, the footer line — and this block gives what it composes the direction's
   chrome. Everything below is scoped to `#tab-rsvp` so it cannot reach a room this wave has not
   measured, and nothing in it sets `display` on a control.
   ── */

/* The phase chassis. `#guestListPhase` is a white band with its own top rule and a 64px inset,
   inside a panel that already supplies the page's 18px gutter — three frames around one list. */
body.d6 #tab-rsvp .guests-phase {
  background: transparent;
  border-top: 0;
}
body.d6 #tab-rsvp .guests-phase-hd { padding: 0; }
body.d6 #tab-rsvp .guests-phase-body { padding: 0; }
/* The numbered eyebrow ("01 ◆ Guest List", "02 ◆ RSVP Tracker") — §9: Direction 6 has no
   numbered section ladder and no glyph before a heading, and the page's own `<h1>` says Guests.
   A label with no control and no number in it; the CLASS stays in portal.css because seven
   generated platform screens lift it. */
body.d6 #tab-rsvp .guests-phase-eyebrow { display: none; }
/* The hero is the three relocated devices' old wrapper. Emptied of them it is 0px of nothing,
   but it keeps its own vertical rhythm, which would leave a gap above the filter bar. */
body.d6 #tab-rsvp .rsvp-hero { margin: 0; padding: 0; border: 0; }

/* The chips. The four live `.rsvp-filter-btn`s are adopted into the bar and given `.d6-chip`;
   these rules take off the uppercase Montserrat plate they wore and let the component through. */
body.d6 #tab-rsvp .d6-chiprow { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
body.d6 #tab-rsvp .rsvp-filter-btn.d6-chip {
  font-family: var(--d6-sans);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-2);
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-pill);
}
/* D5 — the pressed chip is the house's sage rule, never Direction 6's onyx fill. `.active` is
   what `filterRsvp()` sets; `aria-pressed` is mirrored onto the same node so the state is
   announced as well as painted, and both are matched so neither can drift. */
body.d6 #tab-rsvp .rsvp-filter-btn.d6-chip.active,
body.d6 #tab-rsvp .rsvp-filter-btn.d6-chip[aria-pressed="true"] {
  border-color: var(--ch8-signal-approve);
  color: var(--ch8-signal-approve);
  background: color-mix(in srgb, var(--ch8-signal-approve) 6%, var(--d6-paper));
}

/* The table. `renderRsvpTable()` writes `.rsvp-table`'s rows and three badge classes; the
   wrapper and the table itself carry `.d6-tw` / `.d6-table` now, so the component's own `th`
   and `td` rules apply and these three lines are only what the component does not already say.
   ⚠ THE BADGES BECOME PILLS RATHER THAN BEING RENAMED. A pill is a LABEL (§8.6) and the three
   states keep the house's own signal inks — approve, revise, and the muted rule for awaiting. */
body.d6 #tab-rsvp .rsvp-table.d6-table th {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  letter-spacing: var(--d6-tr-th);
  color: var(--d6-ink-3);
  background: var(--d6-wash);
  padding: 9px 10px;
}
body.d6 #tab-rsvp .rsvp-table.d6-table td {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  font-weight: 400;
  color: var(--d6-ink);
  padding: 7px 10px;
  border-bottom: 1px solid var(--d6-line-2);
}
body.d6 #tab-rsvp .rsvp-badge-yes,
body.d6 #tab-rsvp .rsvp-badge-no,
body.d6 #tab-rsvp .rsvp-badge-pending {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 9px;
  border-radius: var(--d6-r-pill);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-chip);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
}
body.d6 #tab-rsvp .rsvp-badge-yes {
  color: var(--d6-good);
  background: color-mix(in srgb, var(--d6-good) 12%, var(--d6-paper));
}
body.d6 #tab-rsvp .rsvp-badge-no {
  color: var(--d6-bad);
  background: color-mix(in srgb, var(--d6-bad) 10%, var(--d6-paper));
}
body.d6 #tab-rsvp .rsvp-badge-pending {
  color: var(--d6-ink-3);
  background: var(--d6-line-2);
}
/* §8.9 — an empty state is ONE sentence in muted ink. No italic, no centring, no 40px of air. */
body.d6 #tab-rsvp .rsvp-empty {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-style: normal;
  text-align: left;
  color: var(--d6-ink-3);
  padding: 10px 12px;
}

/* The bulk plate — the one dark surface in the direction (§8.5). `#bulkBar` carries `.d6-bulk`;
   these bring the controls the live bar draws onto it. */
body.d6 #tab-rsvp .bulk-bar.d6-bulk .bulk-bar-count {
  font-family: var(--d6-serif);
  font-size: 17px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ch8-linen);
  font-variant-numeric: lining-nums tabular-nums;
}
body.d6 #tab-rsvp .bulk-bar.d6-bulk .rsvp-add-btn,
body.d6 #tab-rsvp .bulk-bar.d6-bulk .rsvp-del-btn {
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--d6-plate-line);
  border-radius: var(--d6-r-ctl);
  background: transparent;
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ch8-linen);
}
body.d6 #tab-rsvp .bulk-bar.d6-bulk .rsvp-del-btn {
  color: var(--d6-plate-warn);
  border-color: var(--d6-plate-warn-line);
  background: transparent;
}
/* ⚠ MEASURED, NOT ASSUMED. `#F0B7B2` on `--d6-plate` `#221E1C` is 9.6:1 and `--ch8-linen` on
   it is 14.9:1; the two inline inks these replaced computed **1.91:1** on the same ground
   (axe, serious, WCAG 1.4.3 — the five blockers the wave-3 walk found). §8.5's own recipe. */
body.d6 #tab-rsvp .bulk-bar.d6-bulk .bulk-clear {
  min-height: 44px;
  padding: 0 11px;
  border: 1px solid transparent;
  border-radius: var(--d6-r-ctl);
  background: transparent;
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ch8-linen);
}
body.d6 #tab-rsvp .bulk-bar.d6-bulk .bulk-clear:hover { border-color: var(--d6-plate-line-hover); }
body.d6 #tab-rsvp .bulk-bar.d6-bulk .fld-select {
  min-height: 44px;
  border: 1px solid var(--d6-plate-line);
  border-radius: var(--d6-r-ctl);
  background: transparent;
  color: var(--ch8-linen);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  margin: 0;
}

/* The phone card stack. `app/styles/portal.css` decides WHEN it is shown (its own 720px block,
   which wave 5's one-breakpoint step owns); this only gives the card the direction's chrome —
   §8.7's paper ground, hairline, 8px radius, and a 14px/500 title over a 12.5px sub-line. */
body.d6 #tab-rsvp .rsvp-card {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 10px;
  margin-bottom: 8px;
}
body.d6 #tab-rsvp .rsvp-card-name {
  font-family: var(--d6-sans);
  font-size: 14px;
  font-weight: 500;
  line-height: var(--d6-lh);
  color: var(--d6-ink);
}
body.d6 #tab-rsvp .rsvp-card-kv > span {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-strip);
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-3);
}
body.d6 #tab-rsvp .rsvp-card-kv > strong {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 500;
  color: var(--d6-ink);
}
body.d6 #tab-rsvp .rsvp-card-empty {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-style: normal;
  text-align: left;
  color: var(--d6-ink-3);
  padding: 10px 12px;
}

/* The footer line. `#agFooter` and `#rsvpPager` are adopted into it, so they lose the block
   layout each had when it stood alone under the table. */
body.d6 #tab-rsvp .d6-foot #agFooter,
body.d6 #tab-rsvp .d6-foot #rsvpPager {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  border: 0;
  padding: 0;
  background: transparent;
}
body.d6 #tab-rsvp .d6-foot .ag-footer-count,
body.d6 #tab-rsvp .d6-foot #rsvpPagerLabel {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-3);
}
body.d6 #tab-rsvp .d6-foot .ag-footer-link,
body.d6 #tab-rsvp .d6-foot .gen-btn-ghost {
  min-height: 44px;
  padding: 0 11px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
}

/* The header row the search field and its button came out of, and the privacy line that stands
   with the list. Every word is kept; the only thing taken off the screen is the "All Guests"
   title, which the page's own `<h1>` already says (§9: the direction has one name per page).

   ⚠ THE ROW ITSELF IS NOT HIDDEN, AND THE FIRST DRAFT HID IT — which BLINDED A GUARD RATHER
   THAN BREAKING ONE. `test/portal-phone-layout-reachable.test.mjs` cases 9 and 10 measure
   `.ag-search-row` against the column it sits in, at four phone widths, and they open
   `client-portal.html` WITHOUT a token — so `fillGuestsRoom()` never runs there and the two
   controls are still inside the row on the page those cases see. `display:none` on the wrapper
   made case 10's mutation reproduce nothing and case 9 pass over a row that was not on screen:
   a guard that has stopped measuring, which is worse than a red one because it reads as fine.
   So the wrapper only loses its 28px of standalone padding. On the real page the row is empty
   by the time a client sees it — both of its controls are in the filter bar — and an empty
   flex row with no padding is zero pixels tall. Measured on the rig: `#tab-rsvp` is 2,013px at
   1440 with the row in it, against 3,528 before this wave. */
/* ⚠ THE SIDE PADDING STAYS, AND ONLY THE VERTICAL GOES. Zeroing all four WIDENED the column
   `.ag-search-row` sits in, which is the column case 10's mutation measures against — so the
   mutation reproduced nothing and a load-bearing fix read as redundant. A layer that dresses a
   live node must not move the box a guard measures. */
body.d6 #tab-rsvp .ag-hdr-wrap { padding-top: 0; padding-bottom: 0; }
body.d6 #tab-rsvp .ag-title,
body.d6 #tab-rsvp .ag-title-eyebrow { display: none; }
body.d6 #tab-rsvp #rm-guests-search { display: inline-flex; align-items: center; gap: 6px; }
body.d6 #tab-rsvp .ag-search-btn.d6-btn {
  min-height: 44px;
  padding: 0 11px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
}
body.d6 #tab-rsvp .rsvp-list-hdr-outer {
  padding: 0;
  background: transparent;
  border: 0;
  box-shadow: none;
  margin: 0 0 8px;
}
body.d6 #tab-rsvp .rsvp-guest-count {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-3);
}
body.d6 #tab-rsvp .guests-privacy-line {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-3);
}

/* The four details a tool opens. Each holds a live section that was authored to stand alone on
   a page, so inside the detail it loses the standalone card chrome and keeps everything else —
   the same undressing wave 2 did for `#questionsCard` inside a panel (WAVE2_CLOSE trap 4). */
body.d6 #tab-rsvp .d6-detail .guests-phase { background: transparent; border-top: 0; }
body.d6 #tab-rsvp .d6-detail .guests-phase-hd,
body.d6 #tab-rsvp .d6-detail .guests-phase-body { padding: 0; }
body.d6 #tab-rsvp .d6-detail #rsvpRagRow,
body.d6 #tab-rsvp .d6-detail #rsvpMetaBar { margin: 0 0 10px; }
body.d6 #tab-rsvp .d6-detail #addGuestDrawer,
body.d6 #tab-rsvp .d6-detail #guestSettingsAccordion,
body.d6 #tab-rsvp .d6-detail #recentlyRemovedPanel { margin: 0; }
body.d6 #tab-rsvp .d6-detail summary {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
}
body.d6 #tab-rsvp .d6-detail .rsvp-add-btn,
body.d6 #tab-rsvp .d6-detail .addr-import-btn,
body.d6 #tab-rsvp .d6-detail .rsvp-addr-toggle {
  min-height: 44px;
  padding: 0 14px;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
}
body.d6 #tab-rsvp .d6-detail .rsvp-add-btn { background: var(--ch8-onyx); border-color: var(--ch8-onyx); color: var(--ch8-linen); }

/* ⚠ `(min-width: 641px)` IS NOT DECORATION — ledger D6, and it was measured wrong first.
   A desktop browser at a 390px viewport still answers `(pointer: fine)`, which is what every
   rendered guard and this session's instrument use, so a bare fine-pointer arm placed AFTER
   the 640px block wins at 390 and leaves a 30px control on the measured phone: read off the rig
   at 390, minH 30. The house law is 44px on a phone whoever is holding it, so every fine-pointer
   narrowing in this file that is written after the phone block carries the width too. */
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #tab-rsvp .d6-foot .ag-footer-link,
  body.d6 #tab-rsvp .d6-foot .gen-btn-ghost,
  body.d6 #tab-rsvp .bulk-bar.d6-bulk .rsvp-add-btn,
  body.d6 #tab-rsvp .bulk-bar.d6-bulk .rsvp-del-btn,
  body.d6 #tab-rsvp .bulk-bar.d6-bulk .bulk-clear,
  body.d6 #tab-rsvp .bulk-bar.d6-bulk .fld-select,
  body.d6 #tab-rsvp .d6-detail .rsvp-add-btn,
  body.d6 #tab-rsvp .d6-detail .addr-import-btn,
  body.d6 #tab-rsvp .d6-detail .rsvp-addr-toggle,
  body.d6 #tab-rsvp .ag-search-btn.d6-btn { min-height: 30px; font-size: var(--d6-t-sec); }
}

/* ── D4 · NOTHING IN THIS DIRECTION MOVES, AND THE INSTRUMENT COUNTED THE ONES THAT DID ──────
   `D6_FIDELITY_SPEC.md` §4, measured: of the 271 elements the prototype's Home renders, ZERO
   have a non-zero transition-duration, and `transition` is declared nowhere in concept-06 — a
   hover changes colour instantly. The live controls these three rooms adopt were written with
   `transition:all .2s var(--ease)`, and on the empty-list fixtures the instrument read
   `transition-property:all` rising **2 → 6** the moment the four filter chips became visible in
   the bar (they had been inside a header that is hidden until the list has rows).
   ⚠ `none`, NOT A SHORTER DURATION. Rule 4 of this file is "name the property or declare none —
   the direction declares none", and a 60ms fade is still motion. Named per control rather than
   swept with a `*` selector, because a rule that reaches every descendant of a room would also
   reach a module's own progress bar, which is not chrome and is not ours. */
body.d6 #tab-rsvp .rsvp-filter-btn,
body.d6 #tab-rsvp .rsvp-del-btn,
body.d6 #tab-rsvp .rsvp-add-btn,
body.d6 #tab-rsvp .addr-import-btn,
body.d6 #tab-rsvp .rsvp-addr-toggle,
body.d6 #tab-rsvp .ag-search-btn,
body.d6 #tab-seating .tng-generate-btn,
body.d6 #tab-seating .tng-apply-btn,
body.d6 #tab-seating .tng-mode-tab,
body.d6 #tab-seating .seating-toggle-btn,
body.d6 #tab-seating .table-card,
body.d6 #tab-seating .table-card-del { transition: none; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   MESSAGES · the page, and the flow folded behind its head (2026-09-18, ledger D11 wave 4)
   ══════════════════════════════════════════════════════════════════════════════════════════
   `pageMessages()` is head + filter bar + ONE table. The composer, the run-up panel, the send
   plan and the comms-intelligence pane stay inside `#messagesPhase` and are folded behind the
   head's one primary action — `app/scripts/portal-rooms.js#messagesCompose()` writes the
   `hidden` attribute on the slot, so nothing here decides visibility (wave 3's trap 1: a layer
   that dresses a live node must not also re-decide its display). These rules dress; they do
   not hide.
   ── */

/* The table sits directly under the filter bar, outside every box. */
body.d6 #rm-msg-table { margin: 0 0 12px; }
/* §44D.1 — the delivery-truth line is filled by a round trip that may answer nothing; an empty
   box must not draw a gap above the rows it is about. */
body.d6 .d6-msg-list > #bcDeliveryTruth:empty { display: none; }
body.d6 .d6-msg-list > #bcDeliveryTruth { margin-bottom: 8px; }

/* The drawer row. `[hidden]` on a `<tr>` is the UA's own rule and is left to it; this only
   takes the cell's own border off so the drawer reads as part of the row above it. */
body.d6 .d6-table tr.d6-exp-row > td { border-bottom: 1px solid var(--d6-line-2); }

/* ── RCL-D138 · ON A PHONE EACH MESSAGE IS A STACKED ROW, NOT A SIDEWAYS TABLE ──────────────
   RETEST-J (production 23e70ee, iPhone 13 WebKit, 2026-09-25): the Messages table was 522 px
   wide inside a 352 px `.d6-tw` scroller, so Edit / Cancel sat at x=403-531 in a 390 px viewport
   with nothing on screen saying they were there. `.d6-tw` may scroll sideways (§5), but a row's
   own controls must not hide behind that scroll. So at the house's one phone breakpoint the
   message rows stack the way `.d6-row` already does at 640 px: the message on its own line, To ·
   How · When · Status as one wrapping line under it, and the row's buttons on their own line,
   full width, each at the 44 px floor. The column heads are kept for screen readers only.
   ⚠ `tr[hidden]` IS RESTATED: a `display` on the row would otherwise beat the UA's [hidden] rule
   and open every drawer row. Scoped to `.d6-msg-list`; every other `.d6-table` is untouched. */
@media (max-width: 640px) {
  body.d6 .d6-msg-list .d6-tw { overflow-x: visible; }
  body.d6 .d6-msg-list .d6-table,
  body.d6 .d6-msg-list .d6-table tbody { display: block; width: 100%; }
  body.d6 .d6-msg-list .d6-table thead {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  body.d6 .d6-msg-list .d6-table tr.is-clickable {
    display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px;
    padding: 10px 10px; border-bottom: 1px solid var(--d6-line-2);
  }
  body.d6 .d6-msg-list .d6-table tr.is-clickable > td { display: block; padding: 0; border-bottom: 0; min-width: 0; }
  body.d6 .d6-msg-list .d6-table tr.is-clickable > td:first-child { flex: 1 1 100%; overflow-wrap: anywhere; }
  body.d6 .d6-msg-list .d6-table tr.is-clickable > td.d6-r {
    flex: 1 1 100%; display: flex; flex-wrap: wrap; gap: 8px; text-align: left; white-space: normal;
  }
  body.d6 .d6-msg-list .d6-table tr.is-clickable > td.d6-r:empty { display: none; }
  body.d6 .d6-msg-list .d6-table tr.is-clickable > td.d6-r .d6-btn { flex: 1 1 0; }
  body.d6 .d6-msg-list .d6-table tr.d6-exp-row:not([hidden]),
  body.d6 .d6-msg-list .d6-table tr.d6-exp-row:not([hidden]) > td { display: block; }
  body.d6 .d6-msg-list .d6-table tr[hidden] { display: none; }
}

/* The slot heading over the folded flow. The room's `<h1>` already says Messages, so the
   relocated section keeps its SENTENCE — which is the promise about sending — and loses the
   word it repeats. Scoped to this one slot; every other `.rm-sec` in the portal is untouched,
   exactly as wave 3 scoped Seating's. */
body.d6 #tab-experience #rm-slot-messagesPhase > .rm-sec-h { display: none; }

/* ⚠ THE ONE PLACE THIS FILE DECIDES A DISPLAY, AND IT IS NOT A DEFAULT (wave 3's trap 1).
   `.d6-folded` is written by app/scripts/portal-rooms.js#messagesCompose() and by nothing
   else: the class exists so the fold survives `relocate()`, which rewrites `slot.hidden` on
   every room change, and so `applySlot()`'s inline `display:none` still wins on the slot the
   sub-nav is not showing. No live node is given a visibility it did not already have — the
   flow is open whenever the client has opened it. */
body.d6 #tab-experience .rm-sec.d6-folded { display: none; }

/* D4 · nothing in this direction moves — the controls this room adopts, named one by one for
   the reason the block above gives. */
body.d6 #tab-experience .pcs-btn,
body.d6 #tab-experience .bc-act,
body.d6 #tab-experience .gt-card { transition: none; }

/* The live stop control, sitting in the tools row: it is a button in a row of buttons now, so
   it wears the row's height and the row's ink rather than its own chip chrome. Its module
   still writes its words and its state — this only dresses what that module drew. */
body.d6 #rm-msg-tools #portalCommsStop { display: inline-flex; align-items: center; gap: 6px; margin: 0; }
body.d6 #rm-msg-tools .pcs-btn {
  min-height: 44px;
  padding: 0 11px;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
}
body.d6 #rm-msg-tools .pcs-btn:hover { border-color: var(--d6-ink-3); }

/* ⚠ `(min-width: 641px)` — ledger D6 and wave 3's trap 8, for the same reason as every other
   fine-pointer arm in this file. */
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #rm-msg-tools .pcs-btn { min-height: 30px; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   SET-UP · the six sections as Direction 6's expand-in-place table (2026-09-18, wave 4)
   ══════════════════════════════════════════════════════════════════════════════════════════
   `pageSetup()` draws the six sections as ONE table — `<td>` number, section, "N / M",
   a state pill, and an Open/Close button — whose row opens a `colspan` drawer beneath it
   (§8.4). The live card is already that table in BEHAVIOUR: six `section.es-mod`s, "Section N
   of 6" in `.es-hd`, `← Back` / `Next →` in every section's foot, and "Saved." beside the step
   line. Nothing in app/scripts/portal-event-setup.js is edited by this wave — not one line of
   its render, not the pager, not the Vision slot inside section C. These rules give the rows
   the direction's chrome and take the CARD off the card, which is D9's half of the job.

   ⚠ NOTHING HERE DECIDES A DISPLAY (wave 3's trap 1). Every rule below dresses; the six
   sections open and close by their own module's `is-open` class, exactly as before.
   ── */

/* The card is not a card any more — the page has a head of its own and the sections are the
   table. Ground, border and the 24px outdent go; the padding becomes the table's. */
body.d6 #tab-setup #eventSetupCard .es-card {
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  font-family: var(--d6-sans);
}
/* §8.4 · the table wrapper. One hairline round the six rows, paper inside. */
body.d6 #tab-setup .es-body {
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  background: var(--d6-paper);
  overflow: hidden;
}
/* §8.4 · the `th` row. "Section 2 of 6 · The design" plus the save state IS the table's head
   here: it names what the rows are and what state they are in, which is what a `<thead>` does. */
body.d6 #tab-setup .es-hd:not(.es-hd-done) {
  margin: 0;
  padding: 9px 12px;
  background: var(--d6-paper);
  border-bottom: 1px solid var(--d6-line);
}
body.d6 #tab-setup .es-step {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  font-weight: 600;
  letter-spacing: var(--d6-tr-th);
  text-transform: uppercase;
  color: var(--d6-ink-3);
}
body.d6 #tab-setup .es-step-t { letter-spacing: 0; text-transform: none; color: var(--d6-ink-2); }
body.d6 #tab-setup .es-live { margin: 0; padding: 0 12px; font-size: var(--d6-t-sec); }
body.d6 #tab-setup .es-live:empty { padding: 0; }

/* §8.4 · a row. `td` padding, a hairline under, the wash on hover and while open. */
body.d6 #tab-setup .es-mod { border-top: 0; border-bottom: 1px solid var(--d6-line-2); }
body.d6 #tab-setup .es-mod:last-of-type { border-bottom: 0; }
body.d6 #tab-setup .es-mod-hd {
  padding: 7px 12px;
  gap: 10px;
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  color: var(--d6-ink);
  min-height: 44px;
}
body.d6 #tab-setup .es-mod-hd:hover { background: var(--d6-wash); }
body.d6 #tab-setup .es-mod.is-open > .es-mod-hd { background: var(--d6-wash); }
body.d6 #tab-setup .es-mod-hd:focus-visible { outline: 2px solid var(--d6-hot); outline-offset: -3px; }
/* the row's first cell — a number, so it reads as one (§8.4 `.num`) */
body.d6 #tab-setup .es-mod-num {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-count);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  opacity: 1;
  color: var(--d6-ink-3);
}
/* §2 · the section's name is the row's `.nm`: sans, 13.5, weight 500 — NOT the 1.15rem serif
   a card title wants. The serif in this direction belongs to the page title and the panel
   headings, and a table row is neither. */
body.d6 #tab-setup .es-mod-title {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  font-weight: 500;
  color: var(--d6-ink);
}
body.d6 #tab-setup .es-mod-title em {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  letter-spacing: var(--d6-tr-th);
  opacity: 1;
  color: var(--d6-ink-3);
}
/* §8.6 · the state, as the direction's pill. "3 needed" waits; "6 of 6" is done. The colour
   stays the module's own (`.is-done` is bound to the house success token), so what the pill
   MEANS is still decided by the code that knows. */
body.d6 #tab-setup .es-mod-status {
  display: inline-flex; align-items: center; gap: 6px;
  height: 20px;
  padding: 0 9px;
  border-radius: var(--d6-r-pill);
  background: var(--d6-band);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-count);
  font-weight: 500;
  letter-spacing: 0;
  color: var(--d6-ink-2);
}
body.d6 #tab-setup .es-mod.is-done .es-mod-status {
  background: color-mix(in srgb, var(--ch8-signal-approve) 12%, var(--d6-paper));
  color: var(--ch8-signal-approve);
}
/* ⚠ A PILL HUGS ITS WORDS. `.es-mod-hd` is a `2.4em 1fr auto` grid; on a phone the title takes
   the whole measure and the status cell drops to a row of its own, where `auto` becomes the
   track's full width and the pill painted 320px of ground behind "2 needed" (measured at 390).
   `justify-self:start` is the grid's own answer and changes nothing at desktop. */
body.d6 #tab-setup .es-mod-status { justify-self: start; }
/* §8.4 · the drawer. Same ground as the expanded row above it, so the two read as one block. */
body.d6 #tab-setup .es-mod-body {
  padding: 2px 12px 14px;
  background: var(--d6-wash);
  font-size: var(--d6-t-sec);
}
body.d6 #tab-setup .es-intro { font-size: var(--d6-t-sec); color: var(--d6-ink-2); opacity: 1; }

/* §8.3 · the buttons inside this room. Sentence case, no tracking, no uppercase, no fill but
   onyx — the direction's three buttons, applied to the controls the card already draws. */
body.d6 #tab-setup .es-btn {
  min-height: 44px;
  padding: 0 14px;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--ch8-onyx);
  border-radius: var(--d6-r-ctl);
  background: var(--ch8-onyx);
  color: var(--ch8-linen);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  transition: none;
}
body.d6 #tab-setup .es-btn:hover { background: var(--d6-onyx-hover); border-color: var(--d6-onyx-hover); }
body.d6 #tab-setup .es-btn.es-defaults-btn,
body.d6 #tab-setup .es-btn.es-go {
  background: var(--d6-paper);
  border-color: var(--d6-line);
  color: var(--d6-ink);
}
body.d6 #tab-setup .es-link {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  color: var(--d6-mark);
}
/* The head's action row: the live `.es-foot-main` is moved here by
   portal-rooms.js#setupHead(), so it stops being a foot and becomes the head's own actions. */
body.d6 #tab-setup #rm-setup-acts .es-mod-foot.es-foot-main {
  margin: 0;
  padding: 0;
  border-top: 0;
  gap: 8px;
  justify-content: flex-end;
}
/* "How to use this" keeps its own foot, below the table. */
body.d6 #tab-setup .es-howto-foot { border-top: 0; margin-top: 10px; padding-top: 0; }
body.d6 #tab-setup .es-howto {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  letter-spacing: 0;
  text-transform: none;
  opacity: 1;
  color: var(--d6-ink-3);
}

/* D4 · nothing in this direction moves. */
body.d6 #tab-setup .es-btn,
body.d6 #tab-setup .es-pill,
body.d6 #tab-setup .es-mod-hd { transition: none; }

/* ⚠ `(min-width: 641px)` — ledger D6 and wave 3's trap 8. */
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #tab-setup .es-btn { min-height: 30px; padding: 0 11px; font-size: var(--d6-t-sec); }
  body.d6 #tab-setup .es-mod-hd { min-height: 30px; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   DESIGNS · the pieces first, the panels after, the hero at the foot (2026-09-18, wave 4)
   ══════════════════════════════════════════════════════════════════════════════════════════
   `pageDesigns()` is head + "Your pieces" (span 12) + three span-4 panels. The pieces are
   `#designSuiteGrid`, painted by the portal's own renderer with every version, verb, comment
   box and history behind it — NOT redrawn here, for the reason wave 3 gave on Seating. These
   rules give the grid and the relocated hero the direction's chrome and nothing else.
   ── */
body.d6 #tab-designs #rm-des-panels { margin: 12px 0; }
/* The hero, relocated to the foot: it is the room's own summary of where the pieces stand, so
   it reads as a quiet strip rather than a band. No band ground, no serif headline shout.
   Portal one format (2026-09-25): on the panel's paper like every other block in the room. */
body.d6 #tab-designs #dsHero.ds-hero {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 10px 12px;
  margin: 0;
}
body.d6 #tab-designs #dsHero .ds-hero-eyebrow {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  font-weight: 600;
  letter-spacing: var(--d6-tr-th);
  text-transform: uppercase;
  color: var(--d6-ink-3);
}
body.d6 #tab-designs #dsHero .ds-hero-h1 {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  font-weight: 400;
  margin: 4px 0 6px;
  color: var(--d6-ink);
}
body.d6 #tab-designs #dsHero .ds-spill {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-count);
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-2);
}
body.d6 #tab-designs #dsHero .ds-share-btn,
body.d6 #tab-designs .crq-btn {
  min-height: 44px;
  padding: 0 11px;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  transition: none;
}
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #tab-designs #dsHero .ds-share-btn,
  body.d6 #tab-designs .crq-btn { min-height: 30px; }
}

/* D4 · nothing in this direction moves, and the instrument counted the ones that did. On
   `#tab-designs` at 1440 it read `transition-property:all` on TWELVE elements across three
   classes — the piece grid's version tabs and both verbs on every piece card. `none`, not a
   shorter duration: rule 4 of this file is "name the property or declare none — the direction
   declares none". Named per control rather than swept with a `*` selector, for the reason the
   wave-3 block above gives: a rule reaching every descendant of a room would also reach a
   module's own progress bar, which is not chrome and is not ours. */
body.d6 #tab-designs .bb-vtab,
body.d6 #tab-designs .bb-action-approve,
body.d6 #tab-designs .bb-action-revise,
/* …and the three the `new` fixture adds, where the production-details panel is open and the
   menu/dish rows are drawn: 38 elements across `.menu-dc`, `.pd-remove-btn` and `.pd-add-btn`.
   Measured per fixture rather than assumed — the same page carries a different set of live
   controls at different stages of an event. */
body.d6 #tab-designs .menu-dc,
body.d6 #tab-designs .pd-remove-btn,
body.d6 #tab-designs .pd-add-btn { transition: none; }

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 5a · WHAT WE HANDLE — the twelve parts as the direction's table
   ═══════════════════════════════════════════════════════════════════════════════════════════
   `pageHandle()` draws this page as ONE table with four columns, and `portal-s3-handle.js`
   now emits exactly that (`<div class="d6-tw s3__tw"><table class="d6-table">`). The four
   rules that stacked the parts as bordered `<li>`s — `.s3__parts`, `.s3__part`, `.s3__row`
   and its 720px stack — are deleted from that module's own sheet in the same commit (D9), so
   what is written here is the part of the table its own module does not know about: how wide
   each column is, and how the cells read at the direction's density.

   ⚠ NOTHING HERE RESTATES A COLOUR. The five registers keep the inks `portal-s3-handle.js`
   already gives them (`.s3__part--report .s3__line` moss, `--offer` full ink, `--absent`
   quiet, and the unread italic), because those are claims about what a row SAYS and this
   block is about where a row sits. */
body.d6 #tab-handle .s3__tw { margin: 0 0 12px; }
body.d6 #tab-handle .s3__n { width: 34px; }
body.d6 #tab-handle .s3__name-cell .s3__part-name {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-row);
  font-weight: 400;
  margin: 0;
  color: var(--d6-ink);
}
body.d6 #tab-handle .s3__who { width: 34%; }
body.d6 #tab-handle .s3__who .s3__line {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  margin: 0;
}
/* The fourth column is the contract's affordance slots. They keep their order and their
   emptiness rule (`.s3__aff:empty{display:none}` is that module's); this lays them out as a
   right-aligned wrap rather than a flex row that fought the cell. */
body.d6 #tab-handle .s3__aff-cell .s3__affs {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 4px 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}
body.d6 #tab-handle .s3__aff {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-count);
  color: var(--d6-ink-3);
  margin: 0;
}
/* A fact label is a LABEL in the one format (sentence case, untracked, the sub-line step — the
   recipe the Guests detail labels already wear), not an eyebrow in caps. It is date-driven: «This
   week» only draws when an item falls due this week, so the census read 0 at 07:39Z on 2026-09-29
   and ten `caps` findings on the sections fixture at 13:37Z. This rule beats the base module's
   injected eyebrow and portal-v15.css A.1 by the id; test/portal-s3-fact-label-one-format.test.mjs
   pins it. */
body.d6 #tab-handle .s3__fact-label {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  letter-spacing: 0;
  text-transform: none;
}
/* The screen's own count sentence is the page head's sub-line now, so the copy inside the
   table's host is hidden rather than said twice. `.s3__count` is NOT removed — the module
   writes it, five guards read it, and `handleSub()` reads it back — it is taken out of the
   visual flow the same way `portal.css` already treats `.s3__title`. */
body.d6 #tab-handle #rm-s3 > .s3__count {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
/* The two adopted cards sit inside a panel body, so their own outer frame is a frame on a
   frame — the departure wave 2 recorded on `#questionsCard` and fixed the same way. */
body.d6 #tab-handle .d6-pb .po-collapsed,
body.d6 #tab-handle .d6-pb .hr-card {
  border: 0;
  background: transparent;
  margin: 0;
  padding: 0;
}
body.d6 #tab-handle .d6-pb .po-choice,
body.d6 #tab-handle .d6-pb .hr-how {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  letter-spacing: 0;
  text-transform: none;
}
/* The After section is the prototype's span-12 panel at the foot (PLACE['after']='handle').
   It is a live host, so it carries the panel frame and its own screen keeps its insides. */
body.d6 #tab-handle #rm-after-foot { margin-top: 12px; }
body.d6 #tab-handle #rm-after-foot > #rm-host-after,
body.d6 #tab-handle #rm-after-foot > #rm-s5 { padding: 4px 12px 10px; }
/* D4 · nothing in this direction moves. Measured on the rig: `#tab-handle` read
   `transition-property:all` on the readiness gauge's fill and on the ownership card's toggle.
   Named per control rather than swept, for the reason the wave-3 block gives. */
body.d6 #tab-handle .hr-fill,
body.d6 #tab-handle .po-choice,
body.d6 #tab-handle .s3__change,
body.d6 #tab-handle .s3__door { transition: none; }

/* ── The readiness card, dressed to §2's roles inside its panel (wave 5a) ─────────────────
   `#hostReadinessMount` is `portal-readiness.js`'s live card, relocated into this room by wave
   2. It is NOT rebuilt and NOT hidden — every priority, the gauge and *"How to use this card"*
   are the module's own — but inside a `.d6-panel` it was drawing its own card ground and a
   23/47/21px type ladder that belongs to no role in §2. What is written here is where each of
   its lines sits on the direction's scale, and nothing about what it says.
   ⚠ THE GAUGE KEEPS ITS COLOUR. `.hr-fill` is the one place this screen uses a hue to carry a
   reading, and the card states the same fact in words beside it — taking the colour off would
   be a change to the claim, not to the chrome. */
body.d6 #tab-handle #rm-handle-ready #hostReadinessMount,
body.d6 #tab-handle #rm-handle-ready #hostReadinessBlock,
body.d6 #tab-handle #rm-handle-ready .hr-card { background: transparent; }
body.d6 #tab-handle #rm-handle-ready .hr-state-w {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  line-height: var(--d6-lh-title);
}
/* TCA CD-16 (2026-09-29): the readiness "why" line now also surfaces on Home › Now (a door link that no
   longer fits a moved event is flagged), so it takes the house size wherever it appears, not only in
   What we handle (portal census: it read 15.84px from portal-readiness.js's .88rem fallback). */
body.d6 .hr-state-y { font-size: var(--d6-t-sec); }
body.d6 #tab-handle #rm-handle-ready .hr-num { font-size: 27px; }
body.d6 #tab-handle #rm-handle-ready .hr-unit { font-size: var(--d6-t-sec); }
body.d6 #tab-handle #rm-handle-ready .hr-h {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-row);
  font-weight: 400;
}
body.d6 #tab-handle #rm-handle-ready .hr-sub {
  font-size: var(--d6-t-th);
  letter-spacing: var(--d6-tr-th);
}
body.d6 #tab-handle #rm-handle-ready .hr-foot,
body.d6 #tab-handle #rm-handle-ready .hr-howto { font-size: var(--d6-t-sec); }

/* The ownership card's own control, on the layer's ghost register. `portal-ownership.js` draws
   `.po-choice` and binds it; it is not given `.d6-btn` in that module because this session does
   not own its render, so the four declarations that matter are stated here instead. */
body.d6 #tab-handle #rm-handle-own .po-choice {
  min-height: 44px;
  padding: 0 11px;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
}
body.d6 #tab-handle #rm-handle-own .po-collapsed > div:first-child {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  letter-spacing: var(--d6-tr-th);
  color: var(--d6-ink-3);
}
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #tab-handle #rm-handle-own .po-choice { min-height: 30px; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 5a · the four shared objects the remaining rooms needed and the layer did not have
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Every one is a role §2 or §8 already names; none is a new idea. They are here rather than in
   a room's block because Event page, Files, Plan & billing, Events and People all draw them.

   §2 · the EYEBROW. It replaces the 0.62rem/.14em/700 Montserrat label eight live renderers wrote
   inline. ⚠ SENTENCE CASE SINCE 2026-09-25 (portal one format): the owner's two model screens set
   capitals in exactly two roles — the rail's group headings and a table's `th` — and flagged the
   spaced-caps eyebrows of Designs → Your brief ("YOUR VISION", "THE WORLD") as the old style. It
   was 10px/.09em uppercase; it is now the direction's label: 12.5px, 500, quiet ink. */
body.d6 .d6-eyebrow {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-3);
  margin: 0 0 6px;
}
body.d6 .d6-eyebrow--good { color: var(--d6-good); }

/* ── THE ARRIVAL SELL PANEL ON GUESTS · `#dayOfPlanNote` (2026-09-18, wave 5b) ──────────────
   `portal-guests-seating.js#renderDoorPlanNote()` wrote this box as four inline `style`
   attributes — a dashed hairline with `border-radius:0`, an `opacity:.6` eyebrow at `.68rem`
   and two dimmed `--text-sm` lines. Trap 3 of `WAVE5A_CLOSE.md`: an inline `style` cannot be
   answered by a stylesheet when `!important` is forbidden (D10), so they were DELETED from the
   module and the treatment states itself here. D3's radius, §2's eyebrow role, `.d6-note` for
   the two sentences. The dash is kept: this is a panel about something the plan does NOT
   include, and the direction's solid hairline would read as a section the client owns. */
/* GUESTS-31 (main's UX format pass 2026-09-24, carried into D6 at the merge): the house hairline,
   not a dashed rule. */
body.d6 #dayOfPlanNote {
  margin: 12px 0;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 12px 14px;
}
body.d6 #dayOfPlanNote .d6-note:first-of-type { margin-top: 0; }
/* Owner 2026-09-28 ("fix these."): the Seating page's sell line on a plan without service 07
   (portal-guests-seating.js#renderSeatingPlanNote) — one d6 note in the panel, no frame of its
   own (the census counts a frame inside a panel as box-in-box). Spacing only. */
body.d6 #seatingPlanNote { margin: 12px 0; }
body.d6 #seatingPlanNote .d6-note { margin: 0; }

/* §8.1 · the serif line INSIDE a panel body — the claim a panel's own `<h2>` heads but does
   not make. Sentence case, the panel's own size, so a panel never carries two title scales. */
body.d6 .d6-lede {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  font-weight: 400;
  line-height: var(--d6-lh-title);
  color: var(--d6-ink);
  margin: 0 0 6px;
}

/* §8.5's field, generalised: the direction draws ONE input treatment and uses it for search,
   for a note and for a row of a list. 30px on a fine pointer and 44px otherwise, like every
   other control (ledger D6) — set as a min-height so the type can shrink without the target. */
body.d6 .d6-field {
  width: 100%;
  min-height: 44px;
  padding: 8px 10px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  line-height: var(--d6-lh);
  color: var(--d6-ink);
  transition: none;
}
body.d6 textarea.d6-field { resize: vertical; }
body.d6 .d6-field:focus-visible { outline: 2px solid var(--d6-hot); outline-offset: 2px; }
body.d6 .d6-lab {
  display: block;
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  color: var(--d6-ink-2);
  margin: 0 0 4px;
}
body.d6 .d6-count {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-count);
  color: var(--d6-ink-3);
  margin-top: 4px;
}
/* A tick and the thing it is about. The box keeps the browser's own control — the direction
   draws a 4px square and the platform already gives one at the right size (§4). */
body.d6 .d6-check {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin-bottom: 8px;
  cursor: pointer;
  font-size: var(--d6-t-base);
}
body.d6 .d6-check input { margin-top: 3px; }
body.d6 .d6-check .d6-s { display: block; font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 .d6-field { min-height: 30px; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 5a · EVENT PAGE — the panel's own chrome, on the direction's scale
   ═══════════════════════════════════════════════════════════════════════════════════════════
   `portal-event-website.js` wrote its whole panel as inline `style` attributes, which no
   stylesheet can answer; those attributes are GONE from that module in this commit (D9) and
   what they said is here, in the roles §2 and §8 already name. Five classes, and no colour that
   is not a `--d6-*` token. */
body.d6 .ew-panel { margin: 0; padding: 0; background: transparent; border: 0; }
body.d6 .ew-lede-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
body.d6 .ew-price {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-row);
  color: var(--d6-ink);
  font-variant-numeric: lining-nums tabular-nums;
}
body.d6 .ew-linkrow { margin-top: 10px; display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
body.d6 .ew-url,
body.d6 .d6-url {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  color: var(--d6-ink);
  word-break: break-all;
}
body.d6 .ew-offline { margin-top: 10px; }
body.d6 .ew-attr { margin-top: 14px; }
body.d6 .ew-attr-t { font-size: var(--d6-t-base); color: var(--d6-ink); }
body.d6 .ew-editor { margin-top: 14px; border-top: 1px solid var(--d6-line-2); padding-top: 12px; }
body.d6 .ew-editor > summary { cursor: pointer; list-style: none; }
body.d6 .ew-editor > summary::-webkit-details-marker { display: none; }
body.d6 .ew-editor-b { margin-top: 10px; }
body.d6 .ew-group { margin: 14px 0 4px; }
body.d6 .ew-reglinks { margin-top: 6px; }
body.d6 .ew-welcome { font-family: var(--d6-serif); font-size: var(--d6-t-row); }
/* The repeatable list rows keep their `flex:N` inline — it is a layout RATIO the renderer
   computes per field, not chrome — and take everything else from `.d6-field`. */
body.d6 .ew-list .ew-row { display: flex; gap: 6px; margin-bottom: 6px; }
body.d6 .ew-list .ew-row .d6-field { min-width: 0; }
/* D4 · the attribution switch is the owner's own control (2026-08-28) and keeps its geometry
   and its fill; what it does not keep is motion, which this direction has none of. */
/* The panel's state eyebrow IS the page head's sub-line (`pageSub()` reads it back), so it is
   taken out of the visual flow rather than said twice — the same treatment `portal.css` already
   gives `.s3__title` and this file gives `#rm-s3 > .s3__count`. It is NOT removed: the module
   writes it, the head reads it, and a screen reader still meets it once, in the head. */
body.d6 #rm-page-see #eventWebsitePanel > .d6-eyebrow {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 5a · EVENTS — the occasions list, dressed as the direction's rows
   ═══════════════════════════════════════════════════════════════════════════════════════════
   `pageEvents()` draws a four-column table; `renderMyEvents()` in `app/client-portal.html` is
   the ONE writer of this list and what it writes is richer than four columns — live / past /
   archive groups, celebration families under their own headings, the canonical lifecycle label,
   the *Balance due* pill on the Pay-now button's own two conditions, and *↓ Files* only where
   files can exist. Re-drawing that here would be a second reading of a client's own archives,
   so the rows are DRESSED rather than rebuilt: the card's ground and 20px gap become the table's
   hairline row, its section titles become the direction's `th` role, and its buttons the ghost
   register. Nothing about what a row says is touched. */
body.d6 #tab-my-events .billing-container { max-width: none; }
body.d6 #tab-my-events .billing-section { margin-bottom: 12px; }
body.d6 #tab-my-events .billing-section-title {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  font-weight: 600;
  letter-spacing: var(--d6-tr-th);
  text-transform: uppercase;
  color: var(--d6-ink-3);
  border-bottom: 1px solid var(--d6-line);
  padding-bottom: 6px;
}
/* ── "Hosting again?" — the one panel that centred itself (PG-007, 2026-09-20) ──────────────
   The heading and the sentence under it were centred by INLINE `style` attributes in
   app/client-portal.html, which this layer cannot answer without `!important` (D10 forbids it).
   The declarations move here whole — the section's own bottom margin and the title's absent
   rule — and the alignment then falls to the direction's own left measure, so the panel reads
   like the fourteen others. Nothing about the offer, its anchor or its words changed. */
body.d6 #tab-my-events .billing-section--again { margin-bottom: 0; }
body.d6 #tab-my-events .billing-section--again .billing-section-title {
  border-bottom: none;
  padding-bottom: 0;
}
body.d6 #tab-my-events #myEventsList { gap: 0; }
body.d6 #tab-my-events .billing-plan-card {
  background: var(--d6-paper);
  border: 0;
  border-bottom: 1px solid var(--d6-line-2);
  border-radius: 0;
  padding: 10px 12px;
  margin: 0;
}
body.d6 #tab-my-events .billing-plan-card:last-child { border-bottom: 0; }
body.d6 #tab-my-events .billing-plan-card .plan-value {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-row);
  font-weight: 400;
}
body.d6 #tab-my-events .billing-plan-card .plan-label {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-3);
}
body.d6 #tab-my-events .billing-action-btn {
  min-height: 44px;
  padding: 0 11px;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  transition: none;
}
/* The head's own primary keeps the fill the layer gives `.d6-btn--pri`; the class arrives on
   the live anchor in `eventsActs()`, so this only stops the module's own face outranking it. */
body.d6 #tab-my-events .d6-head .billing-action-btn.d6-btn--pri {
  background: var(--d6-ink);
  border-color: var(--d6-ink);
  color: var(--d6-paper);
}
/* The event switcher, ledger D8's one relocation onto this page. It is the live chip and its
   dropdown; only its type and its frame are said here. */
body.d6 #tab-my-events .hdr-event-switcher { margin: 0 0 12px; }
body.d6 #tab-my-events .hdr-event-chip {
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
}
body.d6 #tab-my-events .hdr-event-label {
  font-size: var(--d6-t-th);
  letter-spacing: var(--d6-tr-th);
  color: var(--d6-ink-3);
}
body.d6 #tab-my-events .hdr-event-name { font-size: var(--d6-t-row); }
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #tab-my-events .billing-action-btn { min-height: 30px; }
}
/* The switcher's dropdown is an empty node until it is opened, and it was reserving its own
   height under the chip — measured 60px of nothing between the switcher and the list. */
body.d6 #tab-my-events .hdr-event-dropdown:empty { display: none; }
/* "Hosting again?" is the prototype's span-12 panel at the foot of this page. It is a live
   `.billing-section` centred by an inline `text-align`, which a stylesheet cannot answer — so
   what is said here is the frame and the reading measure, and `eventsActs()` takes the control
   out of it into the head, which is where `cap('events','Begin another occasion','pri')` puts
   it. The centred paragraph is left as the module wrote it. */
body.d6 #tab-my-events .billing-section:last-child {
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  background: var(--d6-paper);
  padding: 10px 12px;
}
body.d6 #tab-my-events .billing-section:last-child > p {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  color: var(--d6-ink-3);
  max-width: 62ch;
  margin: 0 auto;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 5a · PEOPLE — dressed from outside, because `portal-people.js` is a peer
   ═══════════════════════════════════════════════════════════════════════════════════════════
   That file owns the whole access model and is NOT edited by this wave, so this room is dressed
   the way wave 4 dressed Set-up: in the layer alone. The module already draws each of its four
   blocks as a `.yp-hh` heading over a `.yp-sub` sentence — which is §8.1's panel head and note
   exactly — so what is written here is the frame around each block and where its own type sits
   on §2's scale. Not one word, control or state is touched.
   ⚠ `.yp-eyebrow` AND `.yp-title` ARE THE EMPTY STATE'S, and they stay a heading: the module
   draws them only when there is nobody on the account yet ("Your people will appear here."),
   which is §8.9's empty state and the one place this room says something in the serif voice. */
body.d6 #tab-roster #maison-your-people { display: block; }
body.d6 #tab-roster .yp-head { margin-bottom: 12px; }
body.d6 #tab-roster .yp-eyebrow {
  font-size: var(--d6-t-th);
  letter-spacing: var(--d6-tr-th);
  color: var(--d6-ink-3);
  margin-bottom: 4px;
}
body.d6 #tab-roster .yp-title {
  font-family: var(--d6-serif);
  font-style: normal;
  font-weight: 400;
  font-size: var(--d6-t-panel);
  margin: 0 0 4px;
}
body.d6 #tab-roster .yp-sub {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-3);
  margin: 0 0 8px;
  max-width: 62ch;
}
/* The three blocks the prototype draws as panels: the roster ("Which events each person sees"
   and "Who can invite" are both inside `.yp-access`, which is where the module puts the scope
   fieldset and the standing rule), the promotion of guests you already have, and the vendor
   book. Each gets the panel frame; each keeps its own insides. */
body.d6 #tab-roster .yp-access,
body.d6 #tab-roster .yp-promote,
body.d6 #tab-roster .yp-vendors {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 10px 12px;
  margin: 0 0 12px;
}
body.d6 #tab-roster .yp-hh {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  margin: 0 0 4px;
}
/* The roster's own rows are already a four-column grid with a name, a role, a state and its
   actions — the direction's table without the `<table>`. Only its type scale is said here. */
body.d6 #tab-roster .yp-ac-row { padding: 8px 0; border-bottom: 1px solid var(--d6-line-2); }
body.d6 #tab-roster .yp-ac-who { font-size: var(--d6-t-base); }
body.d6 #tab-roster .yp-ac-who em,
body.d6 #tab-roster .yp-ac-who em.yp-ac-by { font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
body.d6 #tab-roster .yp-ac-role,
body.d6 #tab-roster .yp-ac-state {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  letter-spacing: var(--d6-tr-th);
}
body.d6 #tab-roster .yp-ac-role em,
body.d6 #tab-roster .yp-ac-state em { font-size: var(--d6-t-sec); letter-spacing: 0; }
body.d6 #tab-roster .yp-ac-note { font-family: var(--d6-sans); font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
/* Every control in the room on the ghost register, and the role pills on the house sage rule
   the layer already applies to `[aria-pressed="true"]` (ledger D5). */
body.d6 #tab-roster .yp-btn,
body.d6 #tab-roster .yp-btn-sm,
body.d6 #tab-roster .yp-btn-save,
body.d6 #tab-roster .yp-btn-quiet,
body.d6 #tab-roster .yp-btn-primary,
body.d6 #tab-roster .yp-btn-danger,
body.d6 #tab-roster .yp-pick-back,
body.d6 #tab-roster .yp-q-more,
/* ⚠ `.yp-ar-ref` WAS MISSED BY THE FIRST LIST, AND THE WALK IS WHAT FOUND IT (2026-09-18). It is
   *"Add to your people"* on the guests-you-already-have row, and the accessibility audit read it
   at **141×24 on a phone** — under the house's 44px floor, which is the one warning this room
   gained while the two the committed record carries (`.yp-btn-quiet` 123×29, `.yp-btn-sm` 115×27)
   were fixed by the same block. Derived from `portal-people.js`'s own sheet rather than guessed:
   its control classes are the `.yp-btn*` family plus these four. */
body.d6 #tab-roster .yp-ar-ref,
body.d6 #tab-roster .yp-ac-resend,
body.d6 #tab-roster .yp-ac-x,
body.d6 #tab-roster .yp-ac-pill,
body.d6 #tab-roster .po-choice {
  min-height: 44px;
  padding: 0 11px;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  transition: none;
}
body.d6 #tab-roster .yp-v-in,
body.d6 #tab-roster .yp-ac-add input {
  min-height: 44px;
  padding: 8px 10px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  color: var(--d6-ink);
}
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #tab-roster .yp-btn,
  body.d6 #tab-roster .yp-btn-sm,
  body.d6 #tab-roster .yp-btn-save,
  body.d6 #tab-roster .yp-btn-quiet,
  body.d6 #tab-roster .yp-btn-primary,
  body.d6 #tab-roster .yp-btn-danger,
  body.d6 #tab-roster .yp-pick-back,
  body.d6 #tab-roster .yp-q-more,
  body.d6 #tab-roster .yp-ar-ref,
  body.d6 #tab-roster .yp-ac-resend,
  body.d6 #tab-roster .yp-ac-x,
  body.d6 #tab-roster .yp-ac-pill,
  body.d6 #tab-roster .po-choice,
  body.d6 #tab-roster .yp-v-in,
  body.d6 #tab-roster .yp-ac-add input { min-height: 30px; }
}
/* The set-up card's loading line (portal-event-setup.js poll) is a panel of its own while the
   first answer is on its way — a line on the bare page is the census's "loose" text. */
body.d6 #eventSetupCard > .es-loading {
  margin: 0 0 12px;
  padding: 10px 12px;
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  font-size: var(--d6-t-base);
  color: var(--d6-ink-2);
}
/* THE VENDOR BOOK'S ROWS (2026-09-27, census + a11y walk). No fixture had a supplier until the
   supplier-workspace walks seeded two on the delivered client, and the first reading found the
   module's own sheet underneath: the role at 11.16px/.16em capitals, the name at .86rem (15.48px,
   off the scale), the × at 18px, and "Payments" / "Send them a link" / × at 17–19px tall. The
   role reads in sentence case at 12px (the census refuses capitals outside a table head); the name and its sub-line
   the roster's sizes; the two text links stay links, only tall enough to hit (24px here, 44px
   on a touch screen below), and the × gets a square of its own. */
body.d6 #tab-roster .yp-v-role {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-strip);
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-2);
}
/* The read-only row's role pill sits on Onyx-12 (#DDD6CE); its sub-line in Onyx-62 read 3.84:1
   there (axe, 21:41Z, once a supplier's access row showed on the delivered fixture). Onyx-78. */
body.d6 #tab-roster .yp-ac-role.d6-pill em { color: var(--d6-ink-2); }
body.d6 #tab-roster .yp-v-name { font-size: var(--d6-t-base); color: var(--d6-ink); }
body.d6 #tab-roster .yp-v-name em { font-size: var(--d6-t-sec); color: var(--d6-ink-2); }
body.d6 #tab-roster .yp-v-row { align-items: center; }
body.d6 #tab-roster .yp-v-send {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  color: var(--d6-ink-2);
}
body.d6 #tab-roster .yp-v-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  min-height: 24px;
  padding: 0;
  font-size: var(--d6-t-panel);
  color: var(--d6-ink-2);
}
@media (hover: none), (pointer: coarse), (max-width: 640px) {
  body.d6 #tab-roster .yp-v-send { min-height: 44px; }
  body.d6 #tab-roster .yp-v-x { min-height: 44px; }   /* width stays 24: the module grids give × a 26px column */
}
/* `.yp-head .yp-sub` is what `peopleSub()` reads back into the page head, so it is taken out
   of the visual flow rather than printed twice — the same treatment `#rm-s3 > .s3__count` and
   the event page's state eyebrow get. The heading above it, "Your people will appear here.",
   STAYS: that is §8.9's empty state and the sentence is not a duplicate of anything. */
body.d6 #tab-roster .yp-head > .yp-sub {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
/* The primary fill must outrank the module's own face when `peopleHeadNode()`'s button is the
   layer's own — it already is, and this only stops a module control adopted into the head from
   painting the ghost register over it. */
body.d6 #tab-roster .d6-head .d6-btn--pri {
  background: var(--d6-ink);
  border-color: var(--d6-ink);
  color: var(--d6-paper);
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 5a · PLAN & BILLING — the plan first, then what it holds, then what can be added
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Every surface on this page writes money or reads a receipt — the balance checkout, Stripe's
   own portal, the invoice and receipt routes, `portal-tier-upgrade.js`'s cards and
   `portal-s6-plan.js`'s entitlement screen — so NONE of them is rebuilt and none is given a
   second caller. What is written here is where their type sits on §2's scale and what frame
   each block wears, which is the same treatment wave 4 gave Set-up's six section rows.
   ⚠ THE ORDER IS THE OWNER'S OWN (2026-09-14): current plan first — name, pieces, paid on,
   status — then what it includes, then what you can add, then moving up. Nothing here reorders
   anything; the sections are in that order already. */
body.d6 #tab-billing .billing-container { max-width: none; }
body.d6 #tab-billing .billing-section { margin: 0 0 12px; }
body.d6 #tab-billing .billing-section-title,
body.d6 #tab-billing summary.billing-section-title {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  border-bottom: 1px solid var(--d6-line);
  padding-bottom: 6px;
  margin-bottom: 8px;
}
body.d6 #tab-billing .billing-plan-card {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 4px 12px 8px;
  margin: 0 0 8px;
}
/* §8.7's key-value row: the label is the quiet half and the value is the fact. The live markup
   is already a two-span row per fact; only the 25px value and the tracked label move. */
body.d6 #tab-billing .plan-detail {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--d6-line-2);
}
body.d6 #tab-billing .plan-detail:last-child { border-bottom: 0; }
body.d6 #tab-billing .plan-label {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-3);
}
/* ⚠ NOT RIGHT-ALIGNED. `#billingEntitlement` is a WHOLE SENTENCE in a `.plan-value` — "Marked
   paid — we are reconciling this with our records…" — and right-aligning it set four ragged
   lines against the left edge of everything else on the page. Measured on the rig. */
body.d6 #tab-billing .plan-value {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-row);
  font-weight: 400;
  color: var(--d6-ink);
  font-variant-numeric: lining-nums tabular-nums;
}
/* `portal-s6-plan.js`'s five blocks are already one subject each with a heading and a list —
   §8.1's panel without the frame. They get the frame. */
body.d6 #tab-billing .s6 { margin: 0; }
body.d6 #tab-billing .s6__title {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
body.d6 #tab-billing .s6__block {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 10px 12px;
  margin: 0 0 12px;
}
body.d6 #tab-billing .s6__block-title {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  margin: 0 0 6px;
  color: var(--d6-ink);
}
/* An empty list reserves no height — `#billingInvoicesList` is empty until a charge exists and
   was holding 40px of nothing inside its card. */
body.d6 #tab-billing #billingInvoicesList:empty,
body.d6 #tab-billing #billingCelebrationList:empty,
body.d6 #tab-billing #billingActionMsg:empty { display: none; }
body.d6 #tab-billing .s6__plan-label { font-family: var(--d6-serif); font-size: var(--d6-t-panel); }
body.d6 #tab-billing .s6__plan-price { font-family: var(--d6-serif); font-size: var(--d6-t-row); }
body.d6 #tab-billing .s6__addon,
body.d6 #tab-billing .s6__rung,
body.d6 #tab-billing .s6__gift,
body.d6 #tab-billing .s6__plan-tagline,
body.d6 #tab-billing .s6__plan-designs,
body.d6 #tab-billing .s6__parts,
body.d6 #tab-billing .s6__credit,
body.d6 #tab-billing .s6__people-count { font-size: var(--d6-t-base); }
/* The refunds and cancellation wording is a DISCLOSURE and its text is binding, so it keeps the
   body role D2 carves out for legal blocks — no smaller — and only the disclosure's own chrome
   is said here. Not one word of it is touched. */
/* Portal one format (2026-09-25): it sits INSIDE the Invoices panel, so it is that panel's last
   block — a hairline above it, never a second frame. */
body.d6 #tab-billing #billingRefundSection,
body.d6 #tab-billing #billingDataSection {   /* CRM-SYNC-IN-17's "Your data" follows refunds into the Invoices panel */
  background: transparent;
  border: 0;
  border-top: 1px solid var(--d6-line-2);
  border-radius: 0;
  padding: 10px 0 0;
}
body.d6 #tab-billing #billingRefundDetails > summary,
body.d6 #tab-billing #billingDataDetails > summary { cursor: pointer; list-style: none; }
body.d6 #tab-billing #billingRefundDetails > summary::-webkit-details-marker,
body.d6 #tab-billing #billingDataDetails > summary::-webkit-details-marker { display: none; }
body.d6 #tab-billing #billingRefundDetails p,
body.d6 #tab-billing #billingDataDetails p { max-width: 72ch; }
/* Every control on the page on the ghost register. `#payBalanceBtn` keeps the fill it already
   has — it is the one control on this screen that is the next thing a client would press. */
body.d6 #tab-billing .billing-action-btn {
  min-height: 44px;
  padding: 0 11px;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  transition: none;
}
body.d6 #tab-billing #payBalanceBtn {
  background: var(--d6-ink);
  border-color: var(--d6-ink);
  color: var(--d6-paper);
}
body.d6 #tab-billing .upgrade-option-card {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
}
body.d6 #tab-billing .upgrade-info-txt { font-size: var(--d6-t-base); color: var(--d6-ink-2); }
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #tab-billing .billing-action-btn { min-height: 30px; }
}
/* D4 · nothing in this direction moves. The three the instrument counts on `#tab-billing` are
   the upgrade cards, the role pills and the progress strip's steps — named per control rather
   than swept with a `*`, for the reason the wave-3 block gives.
   ⚠ `.pp-step` / `.pp-dot` LEFT THIS LIST 2026-09-18 (wave 5b, the delete step): the strip they
   stilled is `#billingProgressBar`, and it is out of `app/client-portal.html` altogether, with
   its family out of `portal.css`. A `transition:none` on a class nothing wears is the kind of
   residue D9 exists to stop. */
body.d6 #tab-billing .upgrade-option-card,
body.d6 #tab-billing .po-choice,
body.d6 #tab-billing .yp-ac-pill { transition: none; }

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 5a · FILES — the piece cards are the direction's table, dressed in place
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Every `.ndl-piece` is a file name, a state badge and two format rows, each with its own real
   download or its own locked reason — the prototype's File · State · action row, twice per
   piece. They are NOT redrawn (a second grid would be two drawings of a client's own files with
   the downloads behind them); what is written here is where each line sits on §2's scale and
   what frame the card wears. */
body.d6 .d6-off { display: none; }
body.d6 #tab-suite .ndl-piece {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 10px 12px;
  margin: 0 0 8px;
}
body.d6 #tab-suite .ndl-piece-name {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  font-weight: 400;
}
body.d6 #tab-suite .ndl-approved-badge {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  font-weight: 600;
  letter-spacing: var(--d6-tr-th);
  color: var(--d6-good);
}
body.d6 #tab-suite .ndl-fmt { padding: 7px 0; border-top: 1px solid var(--d6-line-2); }
body.d6 #tab-suite .ndl-fmt-type {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  font-weight: 600;
  letter-spacing: var(--d6-tr-th);
  text-transform: uppercase;
  color: var(--d6-ink-3);
}
body.d6 #tab-suite .ndl-fmt-spec { font-family: var(--d6-sans); font-size: var(--d6-t-base); color: var(--d6-ink); }
body.d6 #tab-suite .ndl-fmt-sub { font-family: var(--d6-sans); font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
body.d6 #tab-suite .ndl-fmt-btn,
body.d6 #tab-suite .ndl-dl-all-btn,
body.d6 #tab-suite .ndl-ap-btn {
  min-height: 44px;
  padding: 0 11px;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  transition: none;
}
/* The head's own primary keeps the fill; `filesActs()` puts `.d6-btn--pri` on the live node. */
body.d6 #tab-suite .d6-head .ndl-dl-all-btn.d6-btn--pri,
body.d6 #tab-suite .ndl-ap-btn {
  background: var(--d6-ink);
  border-color: var(--d6-ink);
  color: var(--d6-paper);
}
/* The sign-off panel is the one clear primary task while it is open, so it keeps its prominence
   and loses only its own card scale. Its six checks, its typed name and its gate are untouched. */
body.d6 #tab-suite .ndl-approval {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 10px 12px;
}
body.d6 #tab-suite .ndl-ap-head strong { font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; }
body.d6 #tab-suite .ndl-ap-sub,
body.d6 #tab-suite .ndl-ap-pieces,
body.d6 #tab-suite .ndl-ap-row,
body.d6 #tab-suite .ndl-ap-err { font-size: var(--d6-t-base); }
body.d6 #tab-suite .ndl-ap-sign input {
  min-height: 44px;
  padding: 8px 10px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
}
/* The status strip stays — it is `renderFileStates()`'s own three words and the rail badge is
   pushed from the same pass — and reads as the direction's figures rather than a KPI band. */
/* ⚠ THE STRIP'S GROUND GOES WITH THE CELLS' (2026-09-18, wave 5a — the accessibility walk found
   this, not a screenshot). Taking the ground off `.fst` and leaving it on `.fstates` put the
   direction's quiet ink (`--ch8-onyx-62`, #6B6864) on the strip's own #E7E3DD band, where axe
   measured **4.33:1 at 11.5px** — three serious findings, one per cell, at both widths. Chapter 08
   keeps onyx-62 for AA on LINEN, which is the page's ground and what the cells now sit on. */
body.d6 #tab-suite .fstates { gap: 20px; margin: 0 0 10px; background: transparent; border: 0; }
body.d6 #tab-suite .fst { background: transparent; border: 0; padding: 0; }
body.d6 #tab-suite .fst b { font-family: var(--d6-serif); font-size: 27px; font-weight: 400; }
body.d6 #tab-suite .fst span { font-family: var(--d6-sans); font-size: var(--d6-t-count); color: var(--d6-ink-3); }
/* The two relocated hero bands, undressed to a quiet strip below the files — the treatment
   `#dsHero` got on Designs (wave 4) and `#rsvpHero` on Guests (wave 3). */
body.d6 #tab-suite .review-hero-block,
body.d6 #tab-suite .delivered-hero-block {
  background: transparent;
  border: 0;
  border-top: 1px solid var(--d6-line);
  padding: 12px 0 0;
  margin: 12px 0 0;
}
body.d6 #tab-suite .rhb-heading,
body.d6 #tab-suite .dhb-heading {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  font-weight: 400;
  line-height: var(--d6-lh-title);
}
body.d6 #tab-suite .rhb-eyebrow,
body.d6 #tab-suite .dhb-eyebrow,
body.d6 #tab-suite .dhb-cap {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  font-weight: 600;
  letter-spacing: var(--d6-tr-th);
  color: var(--d6-ink-3);
}
body.d6 #tab-suite .rhb-sub,
body.d6 #tab-suite .dhb-sub,
body.d6 #tab-suite .rhb-keep-note,
body.d6 #tab-suite .dhb-note { font-family: var(--d6-sans); font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
/* The documents ledger, "what's in your suite" and the keepsake banner take the panel frame and
   the panel heading; their rows and their states are their own modules'. */
body.d6 #tab-suite .doc-shelf,
body.d6 #tab-suite .item-card,
body.d6 #tab-suite .keepsake-banner {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
}
body.d6 #tab-suite .doc-title { font-family: var(--d6-serif); font-size: var(--d6-t-row); font-weight: 400; }
body.d6 #tab-suite .doc-sub { font-family: var(--d6-sans); font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
body.d6 #tab-suite .keepsake-title { font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; }
body.d6 #tab-suite .keepsake-title em { font-family: var(--d6-serif); font-style: normal; }
body.d6 #tab-suite .keepsake-sub { font-family: var(--d6-sans); font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #tab-suite .ndl-fmt-btn,
  body.d6 #tab-suite .ndl-dl-all-btn,
  body.d6 #tab-suite .ndl-ap-btn,
  body.d6 #tab-suite .ndl-ap-sign input { min-height: 30px; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 5a · SET-UP — the two panels wave 4 could not build, and the 7/5 split
   ═══════════════════════════════════════════════════════════════════════════════════════════
   `pageSetup()` is a span-12 hot panel, then "The six sections" at span 7 beside "Your vision
   brief" at span 5. The six sections ARE `#eventSetupCard` — `portal-event-setup.js`'s own node,
   whose `.es-body` is rewritten on every answer — so the card cannot go inside a CSS grid this
   file owns without taking that module's render with it. The split is made the other way: the
   card and the vision panel are floated beside each other above 900px and stack below it, which
   is the same 7/5 the prototype draws and costs that module no edit.
   ⚠ `portal.css`'s 900px ceiling on `#tab-setup` was DELETED in this commit (D10 lets it lose
   rules): it capped the page at a reading measure, which was the right argument while the page
   WAS the card, and the two panels are exactly what the other five columns are for. */
@media (min-width: 900px) {
  body.d6 #tab-setup > #eventSetupCard {
    float: left;
    width: calc(58.333% - 6px);
    margin-right: 12px;
  }
  body.d6 #tab-setup > #rm-setup-vision {
    float: right;
    width: calc(41.667% - 6px);
    margin-top: 8px;
  }
  body.d6 #tab-setup::after { content: ''; display: block; clear: both; }
}
body.d6 #tab-setup > #rm-setup-vision { margin-bottom: 12px; }
body.d6 #tab-setup #rm-setup-panels { margin: 0 0 12px; }
/* The hot count on "Questions from your designer" is the direction's one crimson reading, and
   it is hidden rather than zeroed where the list has not been read (§44D.1). */
body.d6 #tab-setup .d6-ct[hidden] { display: none; }
/* ⚠ THE CARD'S OWN POINTER IS NOW THE PANEL ABOVE IT (D9, wave 5a). `.es-callout-ask` is the
   two-line pointer `portal-event-setup.js` draws inside the card — *"Evorrah needs one more
   thing … Answer it in Questions for you →"* — and it exists because the card is *"where a
   question is about to land, so saying nothing at all would be worse"*. `pageSetup()` puts that
   statement at the TOP of the page as a span-12 hot panel, which is where the client meets it
   first, so the callout is the same claim said twice on one screen. It is taken out of the
   visual flow rather than removed: the node, its `data-es-questions` control and its words are
   the module's, this session does not edit that module, and a screen reader still meets the
   claim once — in the panel, which carries the question's own words and the count. */
/* ⚠ CORRECTED 2026-09-18 (wave 5b): THIS WAS THE sr-only IDIOM AND IT LEFT A FOCUSABLE GHOST.
   The block above says the client "meets the claim once — in the panel", and the rule under it
   did the opposite: `position:absolute; width:1px; height:1px; clip:rect(0 0 0 0)` is the
   VISUALLY-HIDDEN recipe, which keeps a node in the accessibility tree AND in the tab order on
   purpose. So the claim was made TWICE to a screen reader, and the callout's own
   `data-es-questions` button stayed Tab-reachable while painting nothing.

   MEASURED on the rig, `review` fixture (the only one carrying an open studio question, so the
   only one where this callout renders at all) at 1440: the button *"Answer it in Questions for
   you →"* has a box of **193 × 24 at (274, 433)** inside a clipping parent whose box is
   **30 × 26 at (259, 361)** — the control is entirely outside its own clip, 72px below it. It is
   focusable, it is in the tab order, and **focusing it changes zero pixels**, which
   `scripts/portal-a11y-audit.mjs` reports as a BLOCKER: *"no visible focus on button.es-link
   (0px changed)"*, on phone and desktop both. `.es-link:focus-visible` declares a 2px outline at
   `outline-offset:2px` in `portal-event-setup.css` and it was being clipped away — wave 3's
   defect exactly ("a focus ring painted outside an `overflow:hidden` clip changed zero pixels").

   ⚠ `display:none`, AND NOTHING IS LOST BY IT, which is the only reason it is allowed.
   `pageSetup()`'s span-12 *Questions from your designer* panel renders under the SAME condition
   as this callout — both appear only when `MaisonQuestions` reports an open question — and it
   carries the question's own words, the hot count, and its own *Answer it* control, which wave
   5a's walk pressed and recorded landing on `#questionsMount`. The claim is made once and the
   door is open once, which is what wave 5a's own comment above says the design intends.
   The node, its control and its words remain the module's; `portal-event-setup.js` is not
   edited. */
body.d6 #tab-setup #eventSetupCard .es-callout-ask { display: none; }
/* N12's tool and the picker that scopes it, inside the Messages filter bar's tools row. The
   picker is `portal-guest-segments.js`'s own node; only its place in the row is said here. */
body.d6 #rm-msg-invites { display: inline-flex; align-items: center; gap: 6px; }
body.d6 #rm-msg-invites[hidden] { display: none; }
/* ⚠ A `.d6-btn` CARRYING `hidden` IS STILL DRAWN WITHOUT THIS (wave 6a). `body.d6 .d6-btn` sets
   `display:inline-flex`, which is one specificity step above the UA sheet's `[hidden]{display:none}`
   — the same trap §5a's tidy-up wrote up at the top of this file, answered the same way: per
   component, never a blanket `body.d6 [hidden]`. `msgChangesTool()` shuts the "Changes to tell
   guests" door when main's `#hostUpdatesMount` drew nothing, and this is what makes that take. */
body.d6 #rm-msg-tools .d6-btn[hidden] { display: none; }
body.d6 #rm-msg-invites #guestSegmentPicker { display: inline-flex; align-items: center; }
body.d6 #rm-msg-tools #emailAllInvitesBtn {
  min-height: 44px;
  padding: 0 11px;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  transition: none;
}
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #rm-msg-tools #emailAllInvitesBtn { min-height: 30px; }
}
/* ⚠ LSF-D207 (launch stress test, W1 X17, 2026-09-29): AT 375 px THIS ROW WAS 635 px WIDE.
   `.d6-tools` wraps (§5 below), but a wrapping flex row is as wide as its widest ITEM, and this
   group was one item that never wrapped: `#rm-msg-invites` and the picker it holds are one-line
   `inline-flex`es, so the row's own box became label + <select> (inline min-width 230px, as wide
   as its longest option) + the reach note + the send button (gen-btn's nowrap). `body.d6` clips
   sideways, so "WhatsApp channel", "Stop messages for all events" and "Counts are a guide …"
   were cut at the edge and could not be scrolled to — PORTAL ONE FORMAT (owner, 2026-09-25):
   no control hides off the edge. On a phone the group takes its own line and everything in it
   wraps, capped at the row. Rendered at 375 px: test/lsf-d207-messages-tools-fit-a-phone.test.mjs. */
@media (max-width: 640px) {
  body.d6 #rm-msg-invites { flex-wrap: wrap; flex: 1 1 100%; min-width: 0; }
  body.d6 #rm-msg-invites #guestSegmentPicker { flex-wrap: wrap; min-width: 0; max-width: 100%; }
  body.d6 #rm-msg-invites #guestSegmentSelect { max-width: 100%; }
  body.d6 #rm-msg-tools #emailAllInvitesBtn { white-space: normal; max-width: 100%; text-align: left; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 5a · THE MESSAGES COMPOSER — the inside of the flow wave 4 folded
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Wave 4 put the composer behind the page head's one primary action and recorded that what is
   INSIDE the flow was still the module's own chrome. This is that chrome, on §2 and §8's roles:
   `portal-broadcast.js` wrote every field, label, button and box as an inline `style` attribute,
   and those are gone from that module in this commit (D9). Nothing about what it SENDS changed —
   the twelve payload functions and both literal tables are byte-identical. */
body.d6 #broadcastComposer .bc-box {
  margin: 12px 0 8px;
  padding: 10px 12px;
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
}
body.d6 #broadcastComposer .bc-row { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
body.d6 #broadcastComposer .bc-ctl { flex: 1 1 180px; min-width: 0; }
body.d6 #broadcastComposer .bc-fld { margin-bottom: 10px; }
/* MESSAGES-14 (main's UX format pass 2026-09-24, carried at the merge): subject and message share
   ONE measure, 720px, so their right edges line up instead of 520px beside a full-width box. */
body.d6 #broadcastComposer .bc-subject { max-width: 720px; }
body.d6 #broadcastComposer .bc-lab-note { color: var(--d6-ink-3); }
body.d6 #broadcastComposer .bc-acts { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
body.d6 #broadcastComposer .bc-preview { margin-top: 12px; }
body.d6 #broadcastComposer textarea.d6-field { line-height: var(--d6-lh); max-width: 720px; }
/* The run-up panel: `.d6-row`s inside a `.d6-panel`, which is the shape it already had. */
body.d6 #broadcastComposer .bc-seq { margin: 0 0 12px; }
body.d6 #broadcastComposer .bc-seq-n { color: var(--d6-ink-3); }
/* ⚠ AMBER STAYS ON THE ONE LINE THAT IS A WARNING. "N of these are already scheduled" is the
   only sentence in this panel that stops a host doing something twice, and the direction's own
   warn token is the same reading the module already used. */
body.d6 #broadcastComposer .bc-seq-warn { color: var(--d6-warn); }

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   PG-008 · THE REST OF THE COMPOSER — the preview, the banner and the send row (2026-09-20)
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Wave 5a took the inline `style` attributes off the composer's FIELDS and said the residue was
   "three remaining inline-styled nodes". Measured at `20b831f690` it was **47 inline `style=`
   attributes in `app/scripts/portal-broadcast.js`, 21 of them naming `'Montserrat'`** — the
   family D2 retires from the portal — across the stale-facts banner, the two preview-pending
   panels, and every line of the rendered preview and its send row. After this commit that file
   carries **0**, and every one of them is a class here.

   ⚠ WHAT THIS IS NOT. Not one payload function is touched: `_bcDraft`, `_bcAuth`, `_bcPost`,
   `sendBroadcastNow`, `scheduleBroadcast`, `saveBroadcastDraft`, `cancelBroadcast`,
   `editBroadcast`, `_bcCommit`, `_bcTwoStepConfirm`, `previewBroadcast` and `sendBroadcastProof`
   are byte-identical to `origin/main`, as are both literal tables. No id, no `onclick`, no
   sentence and no order changed — `#bcWhen`, `#bcZoneNote`, `#bcPreviewPanel`, `.btn-spinner`
   and the three `.bc-act` buttons the touch-floor guard counts are all exactly where they were.
   ⚠ AND THE STATE THAT WAS IN A STYLE IS NOW IN A CLASS, NOT IN A SECOND DERIVATION. The SMS
   cost block used to compute its own border and ink from `pricey`; it now writes
   `bc-pv-note--quiet` from the same boolean and the recipe lives here once, shared with the
   WhatsApp estimate that always drew the quiet version.
   ⚠ SIZES ARE THE DIRECTION'S TOKENS, not the .68/.72/.74/.76/.78/.84rem ladder that was inline
   (six sizes on one panel, against §2's nine for the whole product). Nothing else moves: the
   grounds, the rules, the 2px left borders and the ceremony gold on the two cost notes are the
   declarations that were there, relocated. */
body.d6 .bc-truth {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-strip);
  color: var(--d6-ink-2);
  margin-bottom: 8px;
}
body.d6 #broadcastComposer .bc-stale {
  margin: 0 0 14px;
  padding: 11px 14px;
  border: 1px solid color-mix(in srgb, var(--ch8-amber) 40%, transparent);
  background: color-mix(in srgb, var(--ch8-amber) 9%, transparent);
  border-radius: var(--d6-r-panel);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  line-height: var(--d6-lh);
  color: var(--d6-warn);
}
body.d6 #broadcastComposer .bc-stale-cta {
  margin-left: 4px;
  padding: 0;
  background: none;
  border: none;
  color: var(--d6-hot);
  font-family: var(--d6-sans);
  font-size: inherit;
  font-weight: 600;
  text-decoration: underline;
  cursor: pointer;
}
/* The two states `#bcPreviewPanel` shows before a preview exists: checking, and could not check.
   One recipe, because they are the same kind of sentence. */
body.d6 #broadcastComposer .bc-pv-wait {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  color: var(--d6-ink-2);
  padding: 8px 0;
}
body.d6 #broadcastComposer .bc-pv {
  padding: 14px 16px;
  background: color-mix(in srgb, var(--ch8-onyx) 4%, transparent);
  border: 1px solid var(--d6-line-2);
  border-radius: var(--d6-r-panel);
}
body.d6 #broadcastComposer .bc-pv-head {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  color: var(--d6-ink);
  margin-bottom: 6px;
}
body.d6 #broadcastComposer .bc-pv-list {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  color: var(--d6-ink-2);
  line-height: 1.6;
  margin-bottom: 8px;
}
body.d6 #broadcastComposer .bc-pv-lh { margin-bottom: 3px; }
/* Every bordered note in the preview — the WhatsApp availability line, the WhatsApp estimate,
   the SMS cost, the merge-token warning. `--quiet` is the un-alarmed register. */
body.d6 #broadcastComposer .bc-pv-note {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  line-height: 1.6;
  margin-bottom: 8px;
  padding: 10px 12px;
  border-left: 2px solid var(--d6-warn);
  color: var(--d6-ink);
}
body.d6 #broadcastComposer .bc-pv-note--quiet {
  padding: 8px 10px;
  border-left-color: color-mix(in srgb, var(--ch8-onyx) 15%, transparent);
  color: var(--d6-ink-2);
}
/* The refusal. Crimson is the direction's own `--d6-hot`, which is the value that was inline. */
body.d6 #broadcastComposer .bc-pv-stop {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  line-height: 1.6;
  margin-bottom: 8px;
  padding: 8px 10px;
  border-left: 2px solid var(--d6-hot);
  color: var(--d6-ink-2);
}
body.d6 #broadcastComposer .bc-pv-samples {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  line-height: 1.55;
  margin-bottom: 8px;
  color: var(--d6-ink-2);
}
body.d6 #broadcastComposer .bc-pv-samples-h { margin-bottom: 4px; font-weight: 600; }
body.d6 #broadcastComposer .bc-pv-sample {
  padding: 6px 8px;
  margin-bottom: 4px;
  background: var(--ch8-linen);
  border: 1px solid var(--d6-line-2);
}
body.d6 #broadcastComposer .bc-pv-text { margin-top: 2px; white-space: pre-wrap; }
body.d6 #broadcastComposer .bc-kv { margin-top: 2px; }
body.d6 #broadcastComposer .bc-gap { margin-top: 4px; }
body.d6 #broadcastComposer .bc-b { font-weight: 600; }
body.d6 #broadcastComposer .bc-b--stop { color: var(--d6-hot); }
/* ⚠ THE CEREMONY GOLD IS KEPT ON EXACTLY THE TWO NOTES THAT ALREADY CARRIED IT and is not
   spread: `sc.note` (why a text costs more than one segment) and the "not a real placeholder"
   head. Both were `var(--gold,#BC9862)` inline; the fallback hex goes because this layer admits
   a hex only in a `--d6-*` declaration (portal-colour-tokens), and the token is the same value.
   This is a RELOCATION, not a ruling on the gold: `portal-contrast-and-naming` case 5's ceiling
   is over `portal.css` and is untouched, and nothing here is a new use of the colour. */
body.d6 #broadcastComposer .bc-note-gold { margin-top: 4px; color: var(--gold); }
body.d6 #broadcastComposer .bc-b--gold { color: var(--gold); }
body.d6 #broadcastComposer .bc-dim { color: var(--d6-ink-2); }
body.d6 #broadcastComposer .bc-quiet { color: var(--d6-ink-3); }
body.d6 #broadcastComposer .bc-ink { color: var(--d6-ink); }
body.d6 #broadcastComposer .bc-pv-subject {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  color: var(--d6-ink-2);
  margin-bottom: 6px;
}
body.d6 #broadcastComposer .bc-pv-sms {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  color: var(--d6-ink);
  background: var(--ivory);
  border: 1px solid var(--d6-line-2);
  padding: 10px;
  margin-bottom: 8px;
  white-space: pre-wrap;
}
body.d6 #broadcastComposer .bc-pv-frame {
  width: 100%;
  height: 320px;
  border: 1px solid var(--d6-line-2);
  background: var(--ivory);
}
body.d6 #broadcastComposer .bc-pv-acts {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  margin-top: 12px;
}
/* ⚠ SEND KEEPS BLOODSTONE AND DOES NOT BECOME `.d6-btn--pri`. tokens.css calls that colour
   "ACTION ONLY — never 'broken', never decoration", and this is the one irreversible action in
   the composer; the direction's onyx primary is used by every head in the portal, so wearing it
   here would make the send button look like "Add a piece". What it TAKES from `.d6-btn` is the
   geometry — the 44px floor with the 30px fine-pointer arm, the radius and the type — which is
   more than the 33px box it measured as an inline-styled bare button. */
body.d6 #broadcastComposer .bc-send {
  background: var(--bloodstone);
  border-color: var(--bloodstone);
  color: var(--ivory);
  letter-spacing: .04em;
}
body.d6 #broadcastComposer .bc-send:hover {
  background: var(--bloodstone-dk);
  border-color: var(--bloodstone-dk);
}
/* The in-flight state, rendered from `_bcBusyAction` — see the note at its call site. */
body.d6 #broadcastComposer .bc-send[disabled] { opacity: .62; cursor: progress; }
body.d6 #broadcastComposer .bc-none {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  color: var(--d6-ink-2);
}
/* `#bcWhen` takes `.d6-field`'s recipe but not its `width:100%` — it sits in a flex row beside
   the send button and is sized by its own value. */
body.d6 #broadcastComposer .bc-when {
  min-height: 44px;
  padding: 7px 9px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  color: var(--d6-ink);
}
body.d6 #broadcastComposer .bc-when:focus-visible { outline: 2px solid var(--d6-hot); outline-offset: 2px; }
body.d6 #broadcastComposer #bcZoneNote {
  flex-basis: 100%;
  font-family: var(--d6-sans);
  font-size: var(--d6-t-strip);
  line-height: 1.5;
  margin-top: 2px;
}
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #broadcastComposer .bc-when { min-height: 30px; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 6a · MAIN'S TWO GUEST-COMMUNICATION PANELS, DRESSED (2026-09-18)
   ═══════════════════════════════════════════════════════════════════════════════════════════
   `origin/main` added these on 2026-09-15 (guest communications, WP-F) and Direction 6 had not
   placed them: `#hostUpdatesMount` (portal-host-updates.js) and `#commsProgressMount`
   (portal-comms-progress.js), both mounted inside `#messagesPhase` and therefore carried into
   the Experience room's Messages slot by `relocate()`. Their door is the tools row —
   "Changes to tell guests" and "Delivery & bounces" (portal-rooms.js#onMsgTool).

   ⚠ BOTH MODULES ARE DO-NOT-EDIT PEERS AND NOT ONE BYTE OF THEM IS TOUCHED. They ship exactly
   as main wrote them, which is the rule for anything main changed on a write path: every
   sentence, control, confirm and refusal here is theirs. Each injects its own `<style>` into
   the head at runtime (`#phu-styles`, `#pcm-styles`), so those rules land AFTER this file in
   document order — this block wins on specificity instead (`body.d6 #tab-experience` prefixes
   a selector that is already `#mount .class`), with no `!important` anywhere, per D10.

   ⚠ WHAT IS ACTUALLY WRONG WITHOUT THIS, and it is D2 rather than taste: the injected sheets
   ask for **Montserrat** on the mount, every button and every field, and **Libre Baskerville**
   on the card's "why" line. D2 retires Montserrat from the portal and makes Crimson Pro the
   serif. They also draw seven type sizes that are not in §2's scale (.58 / .6 / .62 / .65 /
   .72 / .78 / 1rem) and set uppercase tracking on roles the direction does not track. The
   guards do not see any of it — `portal-retired-typeface` reads stylesheets on disk, and these
   rules are a JavaScript string — so this is the only thing standing between the direction and
   two panels drawing the pre-redesign house inside a Direction 6 room. Measured 8/8 green on
   that guard both before and after, for the same reason.

   ⚠ WHAT IS DELIBERATELY NOT FIXED: the buttons stay square. `portal-v10.css`'s square law is
   `body .tab-panel button:not([class*="d6-"]){border-radius:0!important}`, so a radius given
   from here is a dead declaration (wave 5b, trap 4 — measured `0px` with the token resolving
   to 6px), and the only way to earn one is a `d6-` class in main's module. `.pcs-btn` and
   `.bc-act` are square in this room for exactly the same reason, so these two are consistent
   with every other module-drawn control in the portal rather than an exception to it.
   ⚠ AND NO VISIBILITY IS DECIDED HERE (wave 3's trap 1). `available:false` / `ledger:'pending'`
   are normal states each module answers with its own quiet line; this only dresses what they
   drew. Heights follow D6: 44px, and 30px on a fine pointer above 640. */

body.d6 #tab-experience #hostUpdatesMount,
body.d6 #tab-experience #commsProgressMount {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  line-height: var(--d6-lh);
  color: var(--d6-ink);
}

/* The section heads. Each module prints one eyebrow and one sentence; the room's `<h1>` already
   says Messages, so these read as the panel role rather than as a second page title. */
body.d6 #tab-experience .phu-h,
body.d6 #tab-experience .pcm-h {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  font-weight: 600;
  letter-spacing: var(--d6-tr-th);
  color: var(--d6-ink-3);
}
body.d6 #tab-experience .phu-sub,
body.d6 #tab-experience .pcm-sub {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-2);
}

/* Cards and boxes take the panel ground and the panel rule. A radius IS allowed on these —
   the square law names `button`, `div`, `section`, `article`, `a` and `span`, and these are
   `div`s, so `--d6-r-panel` would be killed too; it is therefore not asked for, and the
   direction's own answer for a bordered box inside a panel is the rule, not the corner. */
body.d6 #tab-experience .phu-card,
body.d6 #tab-experience .pcm-card,
body.d6 #tab-experience .pcm-item {
  border-color: var(--d6-line);
  background: var(--d6-paper);
}
body.d6 #tab-experience .phu-quiet,
body.d6 #tab-experience .pcm-quiet {
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  border-left-color: var(--d6-line);
  background: var(--d6-wash);
  color: var(--d6-ink-2);
}

/* The serif line on a suggestion card — Crimson Pro at the panel role, not Libre Baskerville
   at 1rem. This is the one sentence on the card that says what changed. */
body.d6 #tab-experience .phu-why {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  line-height: var(--d6-lh-title);
  color: var(--d6-ink);
}

/* Meta, counts and the row figures. `.phu-nr` / `.pcm-nr` are the "not readable" word and stay
   italic — that distinction is the modules' own and is a fact, not a decoration. */
body.d6 #tab-experience .phu-meta,
body.d6 #tab-experience .phu-change,
body.d6 #tab-experience .pcm-meta,
body.d6 #tab-experience .pcm-advice,
body.d6 #tab-experience .pcm-row {
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-2);
}
body.d6 #tab-experience .phu-nr,
body.d6 #tab-experience .pcm-nr { color: var(--d6-ink-3); }
body.d6 #tab-experience .pcm-ct {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  font-weight: 600;
  letter-spacing: var(--d6-tr-th);
  text-transform: uppercase;
  color: var(--d6-ink-3);
}
body.d6 #tab-experience .pcm-row b,
body.d6 #tab-experience .phu-meta b { font-family: var(--d6-serif); color: var(--d6-ink); }

/* The uppercase tracked labels the direction DOES keep — a pill and a state word — at §2's
   own count/chip sizes rather than at .58rem. */
body.d6 #tab-experience .phu-tag,
body.d6 #tab-experience .phu-state,
body.d6 #tab-experience .pcm-who,
body.d6 #tab-experience .phu-sample .phu-who {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  letter-spacing: var(--d6-tr-th);
}
body.d6 #tab-experience .phu-tag { border-color: var(--d6-mark); color: var(--d6-mark); }

/* The quoted words, the previews and the notes. */
body.d6 #tab-experience .phu-words,
body.d6 #tab-experience .phu-sample,
body.d6 #tab-experience .phu-note {
  font-size: var(--d6-t-base);
  line-height: var(--d6-lh);
  background: var(--d6-wash);
}
body.d6 #tab-experience .phu-words { border-left-color: var(--d6-line); }
body.d6 #tab-experience .phu-sample { border-left-color: var(--d6-mark); }
body.d6 #tab-experience .phu-note { border-left-color: var(--d6-bad); }
body.d6 #tab-experience .phu-note.is-ok { border-left-color: var(--d6-good); }
body.d6 #tab-experience .phu-warn,
body.d6 #tab-experience .pcm-warn { font-size: var(--d6-t-sec); color: var(--d6-bad); }
body.d6 #tab-experience details.phu-prev > summary {
  font-size: var(--d6-t-sec);
  letter-spacing: 0;
  color: var(--d6-mark);
}

/* The controls. Words, states and disabled reasons are the modules'; this is height, ink and
   ground only — the same treatment `.pcs-btn` gets four hundred lines above. */
body.d6 #tab-experience .phu-btn,
body.d6 #tab-experience .pcm-btn {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  min-height: 44px;
  padding: 0 11px;
  border-color: var(--d6-line);
  background: var(--d6-paper);
  color: var(--d6-ink);
}
body.d6 #tab-experience .phu-btn:hover,
body.d6 #tab-experience .pcm-btn:hover { border-color: var(--d6-ink-3); }
/* ⚠ THE PRIMARY KEEPS ITS ONYX PLATE, and that is not the fill D5 rules against: `portal-onyx-fill-ban`
   is about a TOGGLED control's selected state wearing a black fill instead of the house sage rule.
   `.phu-go` / `.pcm-go` are not toggles — they are the "Send now" / "Yes, ask Evorrah" primary in a
   confirm row, which is the same role `.d6-btn--pri` draws in onyx everywhere else in this layer.
   ⚠ AND IT IS `--ch8-onyx`, NOT A `--d6-` NAME. There is no `--d6-onyx` token — the dark plate
   declares `--d6-onyx-hover` and `--d6-plate`, and `.d6-btn--pri` (line ~360) paints from the
   house tokens directly. The first cut of this block asked for `var(--d6-onyx)`, which does not
   resolve; the declaration fell back to the initial value, and because this rule still WON the
   cascade it wiped the module's own onyx fill and left "Send now" transparent with linen text
   — invisible. Caught by reading the computed background off the rig, not by reading the CSS. */
body.d6 #tab-experience .phu-btn.phu-go,
body.d6 #tab-experience .pcm-btn.pcm-go {
  background: var(--ch8-onyx);
  border-color: var(--ch8-onyx);
  color: var(--ch8-linen);
}
body.d6 #tab-experience .phu-link,
body.d6 #tab-experience .pcm-link {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  color: var(--d6-mark);
}
body.d6 #tab-experience .phu-form label {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  letter-spacing: var(--d6-tr-th);
  color: var(--d6-ink-3);
}
/* ⚠ 16px ON THE FIELD IS NOT A TYPE CHOICE AND IS LEFT ALONE — below it iOS zooms the page on
   focus, which is why the module set it and why `portal-a11y-audit.mjs` has IOS_ZOOM_FONT. */
body.d6 #tab-experience .phu-field {
  font-family: var(--d6-sans);
  border-color: var(--d6-line);
  background: var(--d6-paper);
  color: var(--d6-ink);
}
body.d6 #tab-experience .phu-btn,
body.d6 #tab-experience .pcm-btn,
body.d6 #tab-experience .phu-field,
body.d6 #tab-experience .phu-link,
body.d6 #tab-experience details.phu-prev > summary { transition: none; }

/* ⚠ `(min-width: 641px)` — ledger D6 and wave 3's trap 8, as every other fine-pointer arm in
   this file. The 44px floor above stays on a phone and on any coarse pointer. */
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #tab-experience .phu-btn,
  body.d6 #tab-experience .pcm-btn { min-height: 30px; }
  body.d6 #tab-experience details.phu-prev > summary { min-height: 30px; }
  body.d6 #tab-experience .phu-link { min-height: 30px; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 5a · SIGN IN — the screens a client meets before the portal exists
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Three screens share this markup: sign in, *"send me a new access code"*, and the activation
   screen after checkout. `app/scripts/portal-auth.js` is a DO-NOT-EDIT peer — it writes every
   live failure sentence, the 401, the rate limit and the network drop — so this is dressed from
   outside in the layer alone, exactly as People is. Not one word, control or state is touched.
   ⚠ THIS BLOCK ONLY WORKS BECAUSE `d6` IS ON THE `<body>` TAG NOW (`app/client-portal.html`).
   It was added by `portal-nav.js#build()`, which runs only after sign-in, so these were the one
   part of the redesign the layer could not reach: they drew the house's scale while every room
   behind them drew Direction 6's.
   ⚠ FORM FIRST. The direction's own answer to "what is this screen for" is the two fields and
   the button; the title falls from 33px to §2's 25px page role and the sub-line to 12.5px, so
   the first thing on the screen is the task rather than a headline about it.
   ⚠ THE ERRORS ALREADY NAME THE FIX and are left alone. `#loginErr`'s shipped default ends
   *"…check your inbox and your spam folder, or have a new code sent below"* and the recovery is
   the next control; `#forgotMsg` is written by that module. What this adds is that a failure is
   READ at the body role rather than at 11px. */
body.d6 .login-screen { background: var(--d6-ground); }
/* ── THE SIGN-IN WORDMARK STAYS THE BRAND'S, AND THE RULE THAT SAID OTHERWISE IS GONE ────────
   (PG-004, owner item B1 in docs/execution/portal-d6/OWNER_DECISIONS_AND_BLOCKERS.md, 2026-09-20)

   A `body.d6 .login-logo` block stood here setting `--d6-sans` / `--d6-t-th` / 600 /
   `--d6-tr-grp` / uppercase / `--d6-ink`, and NOT ONE of those six declarations ever took:
     · family + weight  lose to `body[data-active-tab] .login-screen .login-logo`
                        (app/styles/portal-v15.css:442, specificity 0,3,0 against this 0,2,0)
     · size + tracking + colour  lose to `.login-logo{…!important}` (app/styles/portal.css:4523),
                        and D10 forbids this layer answering an `!important` with another
     · text-transform   was already `uppercase` on the base rule (app/styles/portal.css:1575)
   Measured in Chromium on the rig at 1440 before removal: `Montserrat / 14.04px / 500 /
   4.7736px / uppercase / rgb(20,20,20)` — every value the OTHER rules give, none of them this
   block's. The whole rule was decoration in a file, and the brand checker was reading it and
   reporting a change that does not happen (two entries under `app/styles/portal-d6.css` in
   `brand/brand-lint-baseline.json`, `wordmark.family` and `wordmark.tracking`).

   ⚠ NOTHING IS REMOVED FROM THE PRODUCT. Not a pixel moves: the wordmark computed the same
   before and after, because a dead rule is a claim, not a capability. The owner's ruling B1
   is *"leave it as Montserrat and delete the dead rule"* — the canonical wordmark is
   `brand.json` `tokens.wordmark` (Montserrat 500 / 0.34em, canonical-logo migration 2026-08-20)
   and the sign-in screen is the most brand-facing page in the product. D2's real subject is the
   fifteen rooms behind this screen, and Montserrat is gone from them.
   ⚠ IF THE OWNER EVER SAYS THE OPPOSITE, the fix is NOT to write this rule again — it would be
   dead a second time. It is to unpick the two rules named above, which is a brand change.
   The two baseline entries may be re-recorded with `npm run brand:gate:update`; a count going
   DOWN never blocks the drift gate, so nothing is required for this to land. */
body.d6 .login-box {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
}
body.d6 .login-title {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-page);
  font-weight: 400;
  line-height: var(--d6-lh-title);
  color: var(--d6-ink);
}
body.d6 .login-title em { color: var(--d6-mark); font-style: normal; }
body.d6 .login-sub {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  color: var(--d6-ink-3);
  max-width: 62ch;
}
/* §2 sets uppercase tracked type in FOUR roles and a form label is not one of them. */
body.d6 .login-fld label {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-2);
}
body.d6 .login-fld input,
body.d6 .login-pass-wrap input {
  min-height: 44px;
  padding: 8px 10px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  font-weight: 400;
  color: var(--d6-ink);
  transition: none;
}
body.d6 .login-fld input:focus-visible { outline: 2px solid var(--d6-hot); outline-offset: 2px; }
body.d6 .login-fld-hint,
body.d6 .login-hint,
body.d6 .login-recover-lead {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  color: var(--d6-ink-3);
}
/* The one primary action of the screen, on the direction's own primary: onyx, sentence case,
   no tracking. 44px here at EVERY width — a sign-in button is the one control a client has, and
   the fine-pointer exemption is not worth the pixel it saves. */
body.d6 .login-btn,
body.d6 .login-recover-btn {
  min-height: 44px;
  padding: 0 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--d6-r-ctl);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  transition: none;
}
body.d6 .login-btn {
  background: var(--d6-ink);
  border: 1px solid var(--d6-ink);
  color: var(--d6-paper);
}
body.d6 .login-recover-btn {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  color: var(--d6-ink);
  text-decoration: none;
}
body.d6 .login-text-btn {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-mark);
}
/* An error is a sentence a client has to act on, so it is read at the body role — not at 11px,
   which is where it sat. `--d6-hot` is the direction's own crimson and the same one the rail
   uses for a hot count. */
body.d6 .login-err {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  line-height: var(--d6-lh);
  color: var(--d6-hot);
  max-width: 62ch;
}
body.d6 .login-recover {
  border-top: 1px solid var(--d6-line-2);
  background: transparent;
}
body.d6 .login-eye { color: var(--d6-ink-3); background: transparent; border: 0; }

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 5a · THE DESIGN ROOM'S OTHER FOUR VIEWS
   ═══════════════════════════════════════════════════════════════════════════════════════════
   `#conceptHero` gets exactly the treatment `#dsHero` got in wave 4 — relocated below the
   moodboard it describes and undressed to a quiet strip — which is the other half of
   `D6_RENDER_STRATEGY` Step 7. ⚠ THE `.ds-hero` CSS FAMILY IS STILL IN FOUR SHEETS and is wave
   5b's: `test/portal-left-edge` uses it as its own alignment fixture, `renderPinned()` looks the
   hero up by that class, `build-platform-screens.mjs` lifts `portal.css` whole, and v10/v15 name
   `.ds-hero-h1` inside selector lists shared with six other classes. After this, nothing WEARS
   the band; what is left is the rules, and removing them is four coupled edits. */
/* Portal one format (2026-09-25): a panel like every block in the room, not a strip. */
body.d6 #tab-concept #conceptHero.ds-hero {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 9px 12px 10px;
  margin: 12px 0;
}
body.d6 #tab-concept #conceptHero .ds-hero-body { padding: 0; }
body.d6 #tab-concept #conceptHero .ds-hero-eyebrow {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  font-weight: 600;
  letter-spacing: var(--d6-tr-th);
  text-transform: uppercase;
  color: var(--d6-ink-3);
  margin-bottom: 4px;
}
body.d6 #tab-concept #conceptHero .ds-hero-h1 {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  font-weight: 400;
  line-height: var(--d6-lh-title);
  margin: 0 0 6px;
  color: var(--d6-ink);
}
/* ⚠ THE `<em>` IS ITS OWN LINE AND THAT IS NOT DECORATION — it is where the sentence breaks.
   `#conceptHeroMood` follows *"Your moodboards,"* with no space in the markup, so the two run
   together the moment the `<em>` is inline. `portal.css`'s `.ds-hero-h1 em{display:block}`
   carried that until wave 5b retired the band around it, so the layer states it (2026-09-18).
   The bloodstone the old rule also set does NOT come with it: D6 has one ink for a heading. */
body.d6 #tab-concept #conceptHero .ds-hero-h1 em,
body.d6 #tab-designs #dsHero .ds-hero-h1 em {
  display: block;
  font-style: italic;
  color: inherit;
}
body.d6 #tab-concept #conceptHero .ds-hero-sub {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  color: var(--d6-ink-3);
  max-width: 62ch;
  margin: 0;
}
body.d6 #tab-concept #conceptHero .ds-hero-stats { border-top: 0; padding-top: 8px; gap: 20px; }
body.d6 #tab-concept #conceptHero .ds-hero-stat-lbl {
  font-size: var(--d6-t-th);
  letter-spacing: var(--d6-tr-th);
  color: var(--d6-ink-3);
}
body.d6 #tab-concept #conceptHero .ds-hero-stat-val {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-row);
  font-weight: 400;
  color: var(--d6-ink);
  font-variant-numeric: tabular-nums;
}
/* The one call to action on this view keeps being a call to action — it is the door to the
   choose flow — and takes the direction's primary rather than an uppercase tracked band. */
body.d6 #tab-concept #conceptHero .ds-hero-cta {
  min-height: 44px;
  padding: 0 11px;
  margin-top: 10px;
  border-radius: var(--d6-r-ctl);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  background: var(--d6-ink);
  border-color: var(--d6-ink);
  color: var(--d6-paper);
  transition: none;
}
/* The letter, the frame and the three sibling views take the panel frame and §2's scale. Their
   renderers — `fillS2()`, `renderDna()` and the adopted `#tab-briefing` — are untouched. */
body.d6 #tab-concept .cdl-card,
body.d6 #tab-concept .suite-iframe-wrap,
body.d6 #tab-review .s2__block,
body.d6 #tab-dna .dna-card {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
}
body.d6 #tab-concept .cdl-salutation,
body.d6 #tab-concept .cdl-body { font-family: var(--d6-sans); font-size: var(--d6-t-base); }
body.d6 #tab-concept .iframe-expand-btn {
  min-height: 44px;
  padding: 0 11px;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  transition: none;
}
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #tab-concept #conceptHero .ds-hero-cta,
  body.d6 #tab-concept .iframe-expand-btn { min-height: 30px; }
}
/* ⚠ THE PLATE FRAME IS NOT TURNED OFF HERE ANY MORE — IT IS GONE AT SOURCE (2026-09-18, wave
   5b, the delete step). Two `content:none` rules stood here to un-draw `portal-v10.css` §6's
   `.ds-hero::after` hairline on the two relocated strips. `.ds-hero` has left that signature's
   own selector list, so there is no pseudo-element to cancel and this layer carries one fewer
   rule that exists only to answer an older sheet. */

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   WAVE 5a · EVENT DETAILS AND UPDATES — the §8 polish wave 1 could not finish
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Both pages were re-templated in wave 1a, before the later rooms settled what the components
   look like on a LIVE node. What is here is the drift: the brief card wave 2 relocated into
   Event details still wore its own card and an uppercase tracked *Request a correction*, and
   the Updates table's own row chrome had not been given the table's cell scale. */
body.d6 #tab-details #eventBriefCard {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  margin-top: 12px;
}
body.d6 #tab-details .peb-eyebrow {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-th);
  font-weight: 600;
  letter-spacing: var(--d6-tr-th);
  text-transform: uppercase;
  color: var(--d6-ink-3);
}
body.d6 #tab-details .peb-title {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  font-weight: 400;
}
body.d6 #tab-details .peb-sub { font-family: var(--d6-sans); font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
body.d6 #tab-details .peb-fix {
  min-height: 44px;
  padding: 0 11px;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  transition: none;
}
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #tab-details .peb-fix { min-height: 30px; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   THE EVENT SWITCH VEIL (LH5-C4-D06, 2026-09-20)
   ══════════════════════════════════════════════════════════════════════════════════════════
   Direction 6's treatment of the one thing a client sees while switchPortalEvent() re-reads
   the rooms for another event. The CORRECTNESS half — hiding the event we are leaving — is a
   rule in app/client-portal.html's own <style>, next to the function, because a page served
   without this stylesheet must still not answer a switch by leaving event A on screen. What
   is here is only how that moment LOOKS, so it belongs with the rest of the direction.

   Two skeleton bars and one sentence: the shape of a page arriving, in the layer's own
   paper/line/ink. `position:sticky` because the hidden rooms keep their height (see that
   note) — a client who was reading 3,000px down would otherwise be told nothing at all.

   ⚠ STILL, NOT PULSING. The first draft breathed the bars on a 1.4s loop. The layer declares
   no motion at all (D6_FIDELITY_SPEC §4: of the 271 elements the prototype draws on Home,
   ZERO have a non-zero transition-duration) and test/portal-design-ratchet case 7 says in so
   many words that gaining motion "is an owner decision, not a rule added in passing". A
   skeleton reads as loading from its shape; it does not need to move to say so.
   ⚠ AND NO `!important` — rule 3 of this layer, case 6 of the same ratchet. It does not need
   any: nothing else in the house styles #portalSwitchVeil. */
body.d6 #portalSwitchVeil {
  position: sticky;
  top: 12px;
  z-index: 2;
  display: block;
  padding: 18px 20px 20px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  background: var(--d6-paper);
}
body.d6 #portalSwitchVeil .d6-switch-veil-bar {
  height: 11px;
  max-width: 340px;
  margin-bottom: 9px;
  border-radius: var(--d6-r-box);
  background: var(--d6-wash);
}
body.d6 #portalSwitchVeil .d6-switch-veil-bar.is-short { max-width: 190px; }
body.d6 #portalSwitchVeil .d6-switch-veil-say {
  margin: 14px 0 0;
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  color: var(--d6-ink-3);
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   THE ORGANIZATION BAND BELONGS TO ONE PAGE (LH5-MATRIX-D03, 2026-09-20)
   ══════════════════════════════════════════════════════════════════════════════════════════
   Nothing is declared here, and that is the fix rather than an omission — read it before
   adding a rule back.

   app/scripts/portal-org-home.js mounted the band at the top of <body>, outside every
   .tab-panel. Measured signed in as an organization at 1440x900: 44px of identity bar + 103px
   of events portfolio + 892px of Organization Intelligence standing above the room host on
   ALL FIFTEEN sidebar items, so the visible <h1> of thirteen of them began at 1117px where an
   individual account's begins at ~98px.

   The obvious repair was a [data-active-tab] belt here, the PX-005 idiom portal.css puts
   under #eventSetupCard. It cannot be written in this layer: #portal-org-bar carries an
   inline `display:flex`, so the rule needs `!important`, and rule 3 of this file is that it
   has none (test/portal-design-ratchet case 6 — measured, it went red).

   So the module mounts the band INSIDE #tab-my-events under `body.d6` instead, and the
   panel's own display:none is what takes it off the other fourteen items — the cascade doing
   the work rather than force, which is what rule 3 is asking for. Verified after the move:
   the visible <h1> begins at 95–172px on all fifteen. */

/* ── THE UPDATES ROOM'S CONTACT LINE IS CRIMSON (owner 2026-09-24, carried at the merge of main
   b8962de91d) ─────────────────────────────────────────────────────────────────────────────────
   The owner, on main's pre-D6 Updates foot: its mailto "red and not blue", and "make Message
   Evorrah red too" (main 1769929d4b, portal-v15.css `.up-foot`). Direction 6 moved both into
   the "Questions?" panel (portal-rooms.js fillUpdates), where `.up-foot` no longer exists, so
   the ruling is said here, on the D6 nodes. Crimson #9D0617 on the panel's white is ~9:1 — AA
   for body text — and both are 12.5px or more, over chapter 8.5's 12px floor for coloured type. */
body.d6 .up-wrap .d6-note a[href^="mailto:"] {
  color: var(--ch8-crimson);
  border-bottom-color: var(--ch8-crimson);
}
body.d6 .up-wrap .d6-pf [data-up-msg] { color: var(--ch8-crimson); }

/* Direction 6's panel chrome for the cards in the V3 slots of the combined Home (paper ground,
   the direction's hairline and radius) — so readiness, the brief, who-handles-what and the head
   count read as panels of the same set as Needs you · Timeline · Guests. */
body.d6[data-active-tab] .tab-panel .td-grid--v3 .td-v3-slot #hostReadinessBlock .hr-card {
  background: var(--d6-paper);
  border-color: var(--d6-line);
  border-radius: var(--d6-r-panel);
}

/* GCA G6-07's invitation line under the Guests panel's people figures (merge of both Homes). */
body.d6 .d6-home--guests .d6-inv b { font-weight: 600; color: var(--d6-ink); }
body.d6 .d6-home--guests .d6-inv span { margin-left: .3em; }

/* The studio's plan inside Home's Timeline panel (merge of both Homes): a 7-wide column cannot give
   the date, the sentence AND the three actions a row each — measured at 1280, the sentence got 50px.
   In a panel the actions go under the sentence; the phone's one-column rule is untouched. */
@media (min-width: 641px) {
  body.d6 .d6-home .plan-row { grid-template-columns: 132px minmax(0, 1fr); }
  body.d6 .d6-home .plan-row-act { grid-column: 2; justify-content: flex-start; }
}

/* ════════════════════════════════════════════════════════════════════════════════════════
   PORTAL ONE FORMAT (owner, 2026-09-25) — the parts of each room that were still drawn in
   their own module's style, brought onto the direction. docs/execution/portal-one-format/.
   ════════════════════════════════════════════════════════════════════════════════════════ */

/* ── Your event book · The day, and After's documents band ────────────────────────────────
   The module now writes the direction's own row / note / buttons (portal-event-book.js); its
   `.eb-*` sheet stays as the fallback its guard pins (portal-event-book-band case 10). Inside a
   panel the card stops being a second tinted box — the panel is the frame. */
body.d6 .eb-card {
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
}
body.d6 .eb-card .d6-acts { margin-top: 10px; }
body.d6 .eb-card .eb-share {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--d6-line-2);
}
body.d6 .eb-card .eb-label {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-3);
  margin-bottom: 4px;
}
body.d6 .eb-card .eb-input {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  color: var(--d6-ink);
}
body.d6 .eb-card .eb-msg {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-2);
  background: var(--d6-wash);
  border: 1px solid var(--d6-line-2);
  border-radius: var(--d6-r-in);
  padding: 10px 12px;
}
body.d6 .eb-card .eb-link { font-family: var(--d6-sans); font-size: var(--d6-t-strip); }

/* ── One format · the families portal-rooms.js#oneFormatTag() adopts ─────────────────────────
   Each adopted element carries `data-d6-adopt` beside the direction's class, and these rules are
   scoped to that attribute: they restate the direction at (1,3,1) so they outrank the UX pass's
   component canon in portal-v15.css (`body.d6[data-active-tab] :where(…) :is(…)`, (0,3,1)) and
   every module's injected sheet — and they touch NOTHING that was already a d6 control, so no
   room's own tuning of `.d6-btn` moves. Crimson is left to small signals (owner, 2026-09-25:
   "i like the use of the red in small/little details"): no adopted control is filled with it. */
body.d6 #portalMain [data-d6-adopt].d6-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 0 14px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  font-weight: 400;
  font-style: normal;
  line-height: 1.2;
  letter-spacing: var(--d6-btn-track);
  text-transform: var(--d6-btn-case);
  text-decoration: none;
  color: var(--d6-ink);
  box-shadow: none;
  cursor: pointer;
}
body.d6 #portalMain [data-d6-adopt].d6-btn:hover { border-color: var(--d6-ink-3); }
body.d6 #portalMain [data-d6-adopt].d6-btn--pri {
  background: var(--ch8-onyx);
  border-color: var(--ch8-onyx);
  color: var(--ch8-linen);
}
body.d6 #portalMain [data-d6-adopt].d6-btn--pri:hover { background: var(--d6-onyx-hover); border-color: var(--d6-onyx-hover); }
body.d6 #portalMain [data-d6-adopt].d6-btn--lnk {
  border-color: transparent;
  background: transparent;
  padding: 0 4px;
  color: var(--d6-mark);
  text-decoration: underline;
  text-underline-offset: 3px;
}
body.d6 #portalMain [data-d6-adopt].d6-btn[disabled] { opacity: .45; cursor: not-allowed; }
body.d6 #portalMain [data-d6-adopt].d6-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 0 12px;
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-pill);
  background: var(--d6-paper);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-2);
  box-shadow: none;
}
/* The modules mark a chosen chip with a class, not aria-pressed — both read as the sage state. */
body.d6 #portalMain [data-d6-adopt].d6-chip[aria-pressed="true"],
body.d6 #portalMain [data-d6-adopt].d6-chip.is-on,
body.d6 #portalMain [data-d6-adopt].d6-chip.on,
body.d6 #portalMain [data-d6-adopt].d6-chip.active,
body.d6 #portalMain [data-d6-adopt].d6-chip.selected,
body.d6 #portalMain [data-d6-adopt].d6-chip.bb-vtab-active {
  border-color: var(--ch8-signal-approve);
  color: var(--ch8-signal-approve);
  background: color-mix(in srgb, var(--ch8-signal-approve) 6%, var(--d6-paper));
}
body.d6 #portalMain [data-d6-adopt].d6-pill {
  display: inline-flex; align-items: center; gap: 6px;
  height: 20px;
  padding: 0 9px;
  border: 0;
  border-radius: var(--d6-r-pill);
  font-family: var(--d6-sans);
  font-size: var(--d6-t-chip);
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0;
  text-transform: none;
  white-space: nowrap;
  color: var(--d6-ink-2);   /* a11y 2026-09-27: ink-3 read 3.84:1 on this ground at 11px; ink-2 = 6.66:1 */
  background: var(--d6-line-2);
}
body.d6 #portalMain [data-d6-adopt].d6-pill--done { color: var(--ch8-moss); background: color-mix(in srgb, var(--d6-good) 12%, var(--d6-paper)); }
body.d6 #portalMain [data-d6-adopt].d6-pill--wait { color: var(--d6-warn); background: color-mix(in srgb, var(--d6-warn) 12%, var(--d6-paper)); }
body.d6 #portalMain [data-d6-adopt].d6-pill--you { color: var(--d6-hot); background: var(--d6-flash); }
body.d6 #portalMain [data-d6-adopt].d6-ttl {
  font-family: var(--d6-serif);
  font-size: var(--d6-t-panel);
  font-weight: 400;
  font-style: normal;
  line-height: var(--d6-lh-title);
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
}
body.d6 #portalMain [data-d6-adopt].d6-lbl {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 500;
  font-style: normal;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-3);
}
/* A second frame inside a panel is flattened; where two sit one after the other a hairline
   separates them, the way rows are separated — never a box in a box. */
body.d6 #portalMain [data-d6-adopt].d6-flat {
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
  margin-top: 0;
}
body.d6 #portalMain [data-d6-adopt].d6-flat + [data-d6-adopt].d6-flat {
  border-top: 1px solid var(--d6-line-2);
  padding-top: 10px;
  margin-top: 10px;
}
body.d6 #portalMain [data-d6-adopt].d6-btn[hidden],
body.d6 #portalMain [data-d6-adopt].d6-chip[hidden],
body.d6 #portalMain [data-d6-adopt].d6-pill[hidden] { display: none; }
/* Size: the direction's own two steps (ledger D6) — 44px by default and on a phone, 30px only
   for a mouse on a screen wider than a phone, exactly as `.d6-btn` / `.d6-chip` themselves. */
body.d6 #portalMain [data-d6-adopt].d6-btn,
body.d6 #portalMain [data-d6-adopt].d6-chip { min-height: 44px; }
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #portalMain [data-d6-adopt].d6-btn { min-height: 30px; padding: 0 11px; font-size: var(--d6-t-sec); }
  body.d6 #portalMain [data-d6-adopt].d6-btn--lnk { padding: 0 4px; }
  body.d6 #portalMain [data-d6-adopt].d6-chip { min-height: 30px; }
}
/* A choosable card: one control the size of a tile. The direction's paper and hairline, the
   control radius, sentence case; chosen reads as the chip's sage. */
body.d6 #portalMain [data-d6-adopt].d6-tile {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-ctl);
  box-shadow: none;
  text-transform: none;
  letter-spacing: 0;
  font-family: var(--d6-sans);
}
body.d6 #portalMain [data-d6-adopt].d6-tile:hover { border-color: var(--d6-ink-3); }
body.d6 #portalMain [data-d6-adopt].d6-tile[aria-pressed="true"],
body.d6 #portalMain [data-d6-adopt].d6-tile[aria-checked="true"],
body.d6 #portalMain [data-d6-adopt].d6-tile.is-on,
body.d6 #portalMain [data-d6-adopt].d6-tile.active,
body.d6 #portalMain [data-d6-adopt].d6-tile.selected {
  border-color: var(--ch8-signal-approve);
  background: color-mix(in srgb, var(--ch8-signal-approve) 6%, var(--d6-paper));
}
body.d6 #portalMain [data-d6-adopt].d6-tile b,
body.d6 #portalMain [data-d6-adopt].d6-tile strong,
body.d6 #portalMain [data-d6-adopt].d6-tile [class$="-name"],
body.d6 #portalMain [data-d6-adopt].d6-tile [class$="-title"] { font-weight: 500; text-transform: none; letter-spacing: 0; }
/* Fields everywhere in the portal take the direction's field: paper, hairline, the sans. (Their
   corner stays portal-v10's 2px — that rule is !important and covers every input.) */
body.d6 #portalMain input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]),
body.d6 #portalMain select,
body.d6 #portalMain textarea {
  background-color: var(--d6-paper);
  border-color: var(--d6-line);
  font-family: var(--d6-sans);
  color: var(--d6-ink);
}
body.d6 #portalMain [data-d6-adopt].d6-name {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-row);
  font-weight: 500;
  font-style: normal;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
}
/* A block that sat in no panel wears the panel's frame; a framed block inside another flattens. */
body.d6 #portalMain [data-d6-adopt].d6-framed {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  box-shadow: none;
  padding: 9px 12px 10px;
  margin: 0 0 12px;
}
body.d6 #portalMain [data-d6-adopt].d6-framed [data-d6-adopt].d6-framed {
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 0;
  background: transparent;
}
/* A table's own hairline box inside a framed block would be a frame in a frame. */
body.d6 #portalMain [data-d6-adopt].d6-framed .d6-tw { border: 0; border-radius: 0; }
body.d6 #portalMain [data-d6-adopt].d6-txt {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
  font-weight: 400;
  font-style: normal;
  line-height: var(--d6-lh);
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-2);
}
body.d6 #portalMain [data-d6-adopt].d6-gone { display: none; }
body.d6 #portalMain [data-d6-adopt].d6-blurb {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-sec);
  font-weight: 400;
  line-height: var(--d6-lh);
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink-3);
  max-width: 62ch;
  margin: 3px 0 8px;
}
/* A framed block that already sits inside a real panel (or a slot wearing one) is not a second
   frame — it flattens, so the same module reads right on Home, in its own room and in The day. */
body.d6 #portalMain .d6-panel [data-d6-adopt].d6-framed,
body.d6 #portalMain .rm-sec [data-d6-adopt].d6-framed {
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 0;
  background: transparent;
}
/* After the event, Home's "still open" list reads as the panel's rows. */
body.d6 #portalMain .td-v3-row--after .cw-open { list-style: none; margin: 4px 0 0; padding: 0; }
body.d6 #portalMain .td-v3-row--after [data-d6-adopt].cw-open-i {
  display: flex; flex-direction: column; gap: 2px;
  padding: 9px 0;
  border-bottom: 1px solid var(--d6-line-2);
}
body.d6 #portalMain .td-v3-row--after .cw-after [data-d6-adopt].cw-go { margin-top: 10px; }
body.d6 #portalMain .td-v3-row--after .cw-band { padding: 0; margin: 0; border: 0; background: transparent; }
body.d6 #portalMain .td-v3-row--after .cw-open { border-top: 1px solid var(--d6-line-2); }
body.d6 #portalMain .td-v3-row--after [data-d6-adopt].cw-open-i { border-top: 0; }
body.d6 #portalMain .td-v3-row--after [data-d6-adopt].cw-link { align-self: flex-start; }
/* The decision row: title and version on the left, the four actions on the right, and the two
   consequences as the row's notes across its full width underneath. */
body.d6 .d6-row.d6-row--decide { flex-wrap: wrap; }
body.d6 .d6-row--decide > .d6-row-notes {
  order: 3;
  flex-basis: 100%;
  display: flex; flex-direction: column; gap: 2px;
}
body.d6 .d6-row--decide > .d6-row-notes > .d6-note { display: block; margin: 0; max-width: 78ch; }
/* Layout the old sheet held with `!important` inside rules now scoped to body:not(.d6) — restated
   here WITHOUT it, so the one-format look lands and nothing moves. The hover lift on .item-card is
   deliberately not restated: the direction has no motion (portal-d6.css rule 4). */
body.d6 #portalMain .bb-piece-name { flex: 1; min-width: 0; }
body.d6 #portalMain .bb-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  justify-content: stretch;
  margin-top: 12px;
  padding: 10px 12px;
  border-top: 1px solid var(--d6-line-2);
  background: var(--d6-paper);   /* the approve bar is sticky (portal.css) — it must stay opaque */
  box-shadow: none;
}
body.d6 #portalMain .bb-actions.bb-actions-next { display: block; padding: 0; margin-top: 0; border-top: 0; }
body.d6 #portalMain .item-card { flex-wrap: wrap; gap: 8px 14px; }
body.d6 #portalMain .docs-section-hdr { margin-bottom: 0; }
/* A framed slot that is a <span> in the markup (Messages' "More for your guests") needs a box. */
body.d6 #portalMain span[data-d6-adopt].d6-framed { display: block; }
/* An adopted button keeps its own height beside a taller field in a flex row. */
body.d6 #portalMain [data-d6-adopt].d6-btn { align-self: center; }
/* ── Your vision, inside the set-up card ────────────────────────────────────────────────
   Owner, 2026-09-25, on a screenshot of this form: "it was not adapted at all to the client
   portal....i need to scrol horizontally to get to the 10th part." The ten sections WRAP — every
   one is on screen, none behind a sideways scroll — and each is a chip, the current one sage. */
body.d6 #portalMain #tab-briefing .bsn-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  overflow: visible;
  padding: 0;
  margin: 0;
  list-style: none;
}
body.d6 #portalMain #tab-briefing .brief-section-nav { margin: 0 0 14px; padding: 0 0 12px; }
body.d6 #portalMain #tab-briefing [data-d6-adopt].bsn-item .bsn-num { color: var(--d6-ink-3); font-variant-numeric: tabular-nums; }
/* The form's own sections sat 52px apart for a page of their own; inside a card they are rows. */
body.d6 #portalMain #esVisionBody .sec { padding: 14px 0 12px; }
body.d6 #portalMain [data-d6-adopt].d6-q {
  font-family: var(--d6-serif);
  font-size: 21px;
  font-weight: 400;
  font-style: normal;
  line-height: 1.2;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
  margin: 4px 0 6px;
}
body.d6 #portalMain [data-d6-adopt].d6-q em { font-style: normal; color: inherit; }
/* The Vision's page container is a tab panel wearing the page ground; inside the set-up card it
   is not a second ground. And the section's display numeral (a 3.4rem "01" drawn before every
   question) is said once already, in the section chips above — it goes; the small step label
   ("01 · The People") stays. */
body.d6 #portalMain #esVisionBody #tab-briefing { background: transparent; }
body.d6 #portalMain #esVisionBody .sec-hdr::before { content: none; display: none; }
body.d6 #portalMain #esVisionBody .sec-hdr { display: block; }

/* ── PORTAL ONE FORMAT · Set-up sections (2026-09-25) ─────────────────────────────────────
   The open section reads like every panel on the desk: the "Included" line is a sentence, not
   a 17px bold shout; the hint under a question is the grey secondary line, not italic; a long
   choice wraps inside the panel instead of running off its edge. */
body.d6 #portalMain #eventSetupCard .es-included { font-size: var(--d6-t-row); line-height: 1.45; color: var(--d6-ink-2); }
body.d6 #portalMain #eventSetupCard .es-included strong { font-size: inherit; font-weight: 500; color: var(--d6-ink); }
body.d6 #portalMain #eventSetupCard .fld-hint { font-style: normal; font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
/* a long choice wraps inside the panel; the chip keeps the pill token (the brand's 2px) */
body.d6 #portalMain [data-d6-adopt].d6-chip { white-space: normal; max-width: 100%; text-align: left; overflow-wrap: anywhere; border-radius: var(--d6-r-pill); }
body.d6 #portalMain #esVisionBody .sec-hdr { margin: 0 0 12px; padding: 0; }
body.d6 #portalMain #esVisionBody .sec { padding-bottom: 12px; }
/* the question is one line of thought: the markup's <br> goes, a space takes its place */
body.d6 #portalMain #esVisionBody .sec-title br { display: none; }
body.d6 #portalMain #esVisionBody .sec-title br + em::before { content: " "; }
body.d6 #portalMain #esVisionBody .fld { margin: 0 0 16px; }
/* a recommendation: the desk's note — a hairline box on paper, no left bar, no tint */
body.d6 #portalMain #eventSetupCard .es-rec {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 8px 12px;
  font-size: var(--d6-t-row);
  color: var(--d6-ink-2);
}
/* ⚠ A CALLOUT IS THE RULEBOOK'S 'panel (framed)', EXACTLY (px-audit 2026-10-03). The review's
   "Still needed:" / "Please fix:", "Decisions across your events", "Your set-up is open again"
   and "Your changes are saved, but not sent" were a framed paper box no approved component
   describes (the one-format census counted 18 the moment Review & send became reachable). The
   markup now says `.d6-framed` and this draws it as the panel: paper, hairline, square, the
   panel's own 9 / 12 / 10 inset, the panel's 14px ink. The rulebook has no red box, so the
   `.es-callout-warn` bloodstone edge (portal-event-setup.css) is not carried: the bold heading
   words — "Still needed", "Please fix" — are what say it is something to act on. */
body.d6 #portalMain #eventSetupCard .es-callout.d6-framed {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  box-shadow: none;
  padding: 9px 12px 10px;
  font-family: var(--d6-sans);
  font-size: 14px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
}
body.d6 #portalMain #eventSetupCard .es-callout.d6-framed > strong { font-weight: 500; color: var(--d6-ink); }
/* the Vision parked at the card's foot (no card C on screen — the review) is the same panel, so
   its payoff line sits inside a frame like it does inside card C (portal-event-setup.js
   mountVision() sets `.d6-framed` only there) */
body.d6 #portalMain #eventSetupCard .es-card > .es-vision.d6-framed {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  box-shadow: none;
  padding: 9px 12px 10px;
  margin: 12px 0 0;
  font-family: var(--d6-sans);
  font-size: 14px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d6-ink);
}
body.d6 #portalMain #eventSetupCard .es-rec strong { font-weight: 500; color: var(--d6-ink); }
/* start from a description: the question's own size, a plain textarea */
body.d6 #portalMain #preBriefPanel { padding: 12px 0; }
body.d6 #portalMain #preBriefPanel .fld-textarea { font-size: var(--d6-t-row); background: var(--d6-paper); border-color: var(--d6-line); border-radius: var(--d6-r-in); }
/* the Vision sits on the set-up's own column: no second gutter inside section C */
body.d6 #portalMain #esVisionBody #tab-briefing { padding: 0; }
body.d6 #portalMain #esVisionBody .sec { margin-bottom: 0; }
body.d6 #portalMain #esVisionBody { padding-left: 0; padding-right: 0; }
/* a piece still with Evorrah is a ROW of the suite panel, not a tinted box inside it */
body.d6 #portalMain #designSuiteGrid .bb-pending-row { background: transparent; border: 0; border-top: 1px solid var(--d6-line-2); margin: 0; padding: 9px 12px; }
body.d6 #portalMain #designSuiteGrid .bb-pending-row:first-child { border-top: 0; }
body.d6 #portalMain #designSuiteGrid .bb-pending-row:hover { background: transparent; }
/* the wall over the artwork keeps its plate (the words must read over any artwork); its label
   speaks in sentence case like every other label on the desk */
body.d6 #portalMain .rm-wall-k { font-size: var(--d6-t-sec); letter-spacing: 0; text-transform: none; }
/* the SENT Vision reads like the event brief: each part a row under a hairline, each answer a
   label beside its value — no left bar, no tinted part, no answer drawn as a field */
body.d6 #portalMain #esVisionMount .bsp-inner { padding-top: 8px; padding-bottom: 12px; }
body.d6 #portalMain #esVisionMount .bsp-sec { background: transparent; border: 0; border-top: 1px solid var(--d6-line-2); border-radius: 0; margin: 0; }
body.d6 #portalMain #esVisionMount .bsp-sec-hdr { padding: 9px 0; border: 0; background: transparent; }
body.d6 #portalMain #esVisionMount .bsp-sec-title { font-family: var(--d6-sans); font-size: var(--d6-t-row); font-weight: 500; color: var(--d6-ink); letter-spacing: 0; }
body.d6 #portalMain #esVisionMount .bsp-sec-grid { display: block; }
body.d6 #portalMain #esVisionMount .bsp-field { display: grid; grid-template-columns: 180px minmax(0, 1fr); gap: 12px; padding: 5px 0; border: 0; }
body.d6 #portalMain #esVisionMount .bsp-fld-val { background: transparent; border: 0; padding: 0; font-family: var(--d6-sans); font-size: var(--d6-t-row); font-weight: 400; color: var(--d6-ink); }
body.d6 #portalMain #esVisionMount .bsp-note,
body.d6 #portalMain #esVisionMount .pieces-empty-hint { font-style: normal; font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
@media (max-width: 640px) {
  body.d6 #portalMain #esVisionMount .bsp-field { grid-template-columns: minmax(0, 1fr); gap: 2px; }
}
body.d6 #portalMain #homeTimelineCard .pd-piece-hdr div { letter-spacing: 0; }
/* a state pill never stretches to the row's width when a phone stacks the row */
@media (max-width: 640px) {
  body.d6 #portalMain .d6-row > .d6-pill { align-self: flex-start; width: auto; }
}
/* the header's two small labels and its one button speak the desk's language too */
body.d6 .portal-hdr #hdrEventChip::before,
body.d6 .portal-hdr .hdr-client-chip::before { font-size: 11.5px; letter-spacing: 0; text-transform: none; }
body.d6 #portalCommsStop .pcs-btn { border: 1px solid var(--d6-line); border-radius: var(--d6-r-ctl); background: var(--d6-paper); color: var(--d6-ink); }
/* every hint under a question is the grey secondary line, never italic */
body.d6 #portalMain .pd-field-hint,
body.d6 #portalMain .fld-hint { font-style: normal; font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
/* one gap between blocks, 12px, set by the block itself — the two old ones in Guests go */
body.d6 #portalMain #attendeesPanel.d6-framed { margin-top: 0; }
body.d6 #portalMain #rm-host-guests { padding-top: 0; }
/* A GUEST ROW'S ACTIONS, ONE ORDERLY TREATMENT (owner, 2026-09-25: "look at the mess of the 7
   buttons in the right"). Each action is a same-size, labelled button in sentence case
   (portal-guests-seating.js#_guestRowActionsHtml), set in one aligned grid: three columns of equal
   width on a wide screen, the same grid across the phone card. The brand's 2px control corner. */
body.d6 #portalMain .rsvp-table th.rsvp-acts,
body.d6 #portalMain .rsvp-table td.rsvp-acts { width: 1%; min-width: 0; text-align: left; padding-left: 8px; padding-right: 8px; }
body.d6 #portalMain .rsvp-table td.rsvp-acts { white-space: normal; line-height: normal; vertical-align: middle; }
body.d6 #portalMain .rsvp-acts .ra-grid { display: grid; grid-template-columns: repeat(3, 76px); gap: 4px; }
body.d6 #portalMain .rsvp-acts .ra-grid > .rsvp-edit-btn,
body.d6 #portalMain .rsvp-acts .ra-grid > .rsvp-del-btn {
  width: 100%; min-width: 0; min-height: 26px; margin: 0; padding: 0 6px;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--d6-sans); font-size: var(--d6-t-sec); font-weight: 400; line-height: 1.2;
  letter-spacing: 0; text-transform: none; white-space: nowrap;
  color: var(--d6-ink-2); background: var(--d6-paper);
  border: 1px solid var(--d6-line); border-radius: var(--d6-r-ctl);
}
body.d6 #portalMain .rsvp-acts .ra-grid > .rsvp-edit-btn:hover,
body.d6 #portalMain .rsvp-acts .ra-grid > .rsvp-del-btn:hover { color: var(--d6-ink); border-color: var(--d6-ink-3); }
body.d6 #portalMain .rsvp-acts .ra-grid > .rsvp-edit-btn[disabled] { opacity: .45; cursor: not-allowed; }
/* the phone card: the same grid, full width, under the reply */
body.d6 #portalMain .rsvp-acts--card { margin: 8px 0 0; }
body.d6 #portalMain .rsvp-acts--card .ra-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 640px), (pointer: coarse) {
  body.d6 #portalMain .rsvp-acts .ra-grid > .rsvp-edit-btn,
  body.d6 #portalMain .rsvp-acts .ra-grid > .rsvp-del-btn { min-height: 44px; }
}
/* in a room whose host already spaces its blocks with a gap, a framed block adds no margin of its own */
body.d6 #portalMain #rm-host-day > [data-d6-adopt].d6-framed,
body.d6 #portalMain .d6-grid > [data-d6-adopt].d6-framed { margin-bottom: 0; }
/* the three ways to name tables are chips side by side — no square frame round the set */
body.d6 #portalMain .tng-mode-tabs { border: 0; gap: 6px; }
/* a state pill is as wide as its words — never stretched to its column */
body.d6 #portalMain [data-d6-adopt].d6-pill { width: fit-content; max-width: 100%; }
/* Files: the three counts are cells of one panel, the print/share halves are columns of the piece — no boxes inside */
body.d6 #portalMain #fileStates .fst { border: 0; border-radius: 0; background: transparent; }
body.d6 #portalMain #fileStates .fst + .fst { border-left: 1px solid var(--d6-line-2); }
body.d6 #portalMain .ndl-piece .ndl-fmt { border: 0; background: transparent; }
body.d6 #portalMain .ndl-piece .ndl-fmt + .ndl-fmt { border-left: 1px solid var(--d6-line-2); }
/* Plan & billing: the plan's name on the serif scale; its tagline the grey secondary line */
/* PORTAL UX AUDIT 2026-09-27: 21px sat above its own 16.5px panel title — a value inside a panel is
   never bigger than the panel's head; the tagline is a sub-line, so the sans, not a small serif */
body.d6 #portalMain #billingPlanName { font-size: var(--d6-t-panel); line-height: 1.25; }
body.d6 #portalMain #tab-billing #billingPackageIncludes #billingPackageTagline { font-style: normal; font-family: var(--d6-sans); font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
/* Guests before the first guest: one panel, the desk's buttons, a one-line question */
body.d6 #portalMain #guestsEmptyState .guests-empty-actions .d6-btn { letter-spacing: var(--d6-btn-track); text-transform: var(--d6-btn-case); min-height: 44px; }
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #portalMain #guestsEmptyState .guests-empty-actions .d6-btn { min-height: 30px; }
}
body.d6 #portalMain #guestsEmptyState .guests-empty-title br { display: none; }
body.d6 #portalMain #guestsEmptyState .guests-empty-title br + em::before { content: " "; }
body.d6 #portalMain #guestsEmptyState .guests-empty-title em { font-style: normal; color: inherit; }
/* Designs › Your brief: the identity's big lines on the serif scale; the label's source note in the label's own voice */
/* (PORTAL UX AUDIT 2026-09-27: 21px → the panel step; a line inside "Your event's identity" is not
   bigger than that panel's own 16.5px title) */
body.d6 #portalMain #evoIdentity .evi-big { font-size: var(--d6-t-panel); line-height: 1.25; }
body.d6 #portalMain #evoIdentity .evi-lbl i { letter-spacing: 0; text-transform: none; }
/* the record follows the room's panel: a short room no longer pushes it a screen down */
body.d6 #portalMain .tab-panel.active:has(+ #approvalHistoryMount #approvalHistoryCard) { min-height: 0; }
body.d6 #portalMain #rmBriefVision { max-width: none; }
body.d6 #portalMain #guestsEmptyState .guests-empty-actions .d6-btn { padding: 0 14px; font-size: var(--d6-t-sec); }
/* a block drawn flat because it usually sits inside a panel wears the panel itself when a room
   places it straight on the page (after the event, Guests hosts who came, the run of show,
   the event book and the day's paper) */
body.d6 #portalMain .tab-panel > [data-d6-adopt].d6-flat {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 9px 12px 10px;
  margin: 0 0 12px;
}
/* AFTER THE EVENT · the recap reads like the desk: each band a panel, its body on the panel's paper,
   the figures as cells of one row, the arrival chart in the quiet rule tone, empty lines grey */
body.d6 #portalMain #rm-s5 .s5__body,
body.d6 #portalMain #rm-s5 .er-card,
body.d6 #portalMain #rm-s5 .ty-box { background: transparent; border: 0; padding: 0; margin: 0; box-shadow: none; }
body.d6 #portalMain #rm-s5 .s5__tri { font-style: normal; font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
body.d6 #portalMain #rm-s5 .er-figs { border: 0; background: transparent; }
body.d6 #portalMain #rm-s5 .er-fig { border: 0; outline: 0; background: transparent; }
body.d6 #portalMain #rm-s5 .er-fig + .er-fig { border-left: 1px solid var(--d6-line-2); }
body.d6 #portalMain #rm-s5 .er-bar { background: var(--ch8-onyx-20); }
/* the recap sits in the after-event panel, so its bands are that panel's sections: a hairline
   between them, the band's title over its body */
body.d6 #portalMain #rm-s5 .s5__band { padding: 10px 0; margin: 0; }
body.d6 #portalMain #rm-s5 .s5__band + .s5__band { border-top: 1px solid var(--d6-line-2); }
body.d6 #portalMain #rm-s5 .s5__band-title { margin: 0 0 6px; }
body.d6 #portalMain #rm-after-foot #rm-host-after { padding: 0 12px 10px; }
/* THE AFTER-EVENT RECAP IN THE ONE FORMAT (owner ask 2026-09-27 ~23:39Z, "fix it", on his 15:50Z
   screenshot of What we handle › After: "how come this part is still not coherent with the whole
   client portal?"). Every band now opens with ONE title, the panel-section title the roster already
   gives .s5__band-title (d6-ttl, serif 16.5):
   · the recap band printed three labels over itself — "The recap", the eyebrow "Your event" /
     "Just finished", then its 21px headline. The headline names the band, so it takes the band
     title's step (16.5, not 21 — never bigger than the panel's own head) and the other two stand
     down (the band title stays for a screen reader);
   · the recap's inner sections ("Your guests", "Who came") are sub-labels, 22+18px apart → the
     band rhythm (10+10 under a hairline);
   · the figures are the desk's figure row (.d6-figs): left, 20px apart, no cell rules;
   · the open checks lost their crimson rule to the row style above but kept its 12px indent, so
     they hung 12px right of everything else — they are rows on the text column now;
   · the recap's remaining lines ("Messages sent to guests — 0") drew as boxed chips side by side —
     they are rows of a list. */
body.d6 #portalMain #rm-s5 .s5__band--recap > .s5__band-title {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; margin: 0;
}
body.d6 #portalMain #rm-s5 .er-eyebrow { display: none; }
body.d6 #portalMain #rm-s5 .er-h { font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; line-height: 1.25; margin: 0; }
body.d6 #portalMain #rm-s5 .er-sub { margin: 3px 0 0; color: var(--d6-ink-2); }
body.d6 #portalMain #rm-s5 .er-sec { margin: 10px 0 0; padding: 10px 0 0; border-top: 1px solid var(--d6-line-2); }
body.d6 #portalMain #rm-s5 .er-st {
  font-family: var(--d6-sans); font-size: var(--d6-t-strip); font-weight: 500; letter-spacing: 0;
  text-transform: none; color: var(--d6-ink-3); margin: 0 0 6px;
}
body.d6 #portalMain #rm-s5 .er-figs { display: flex; flex-wrap: wrap; gap: 20px; }
body.d6 #portalMain #rm-s5 .er-fig,
body.d6 #portalMain #rm-s5 .er-fig + .er-fig { padding: 0; text-align: left; border: 0; }
body.d6 #portalMain #rm-s5 .er-l {
  margin-top: 3px; font-family: var(--d6-sans); font-size: var(--d6-t-count); letter-spacing: 0;
  text-transform: none; color: var(--d6-ink-3);
}
body.d6 #portalMain #rm-s5 .s5__check { padding: 10px 0; margin: 0; }
body.d6 #portalMain #rm-s5 ul.s5__sections { display: block; margin: 8px 0 0; padding: 0; list-style: none; }
body.d6 #portalMain #rm-s5 ul.s5__sections > li.s5__section {
  display: block; width: auto; margin: 0; padding: 7px 0; font-size: var(--d6-t-base); color: var(--d6-ink);
}
@media (max-width: 640px) {
  body.d6 #portalMain #rm-s5 .er-figs { gap: 16px; }
}
/* the one toast surface speaks the desk's language: sentence case, the control radius */
body.d6 .portal-toast { text-transform: none; letter-spacing: 0; border-radius: var(--d6-r-ctl); font-size: var(--d6-t-sec); }
/* "From your booking" is a note like "Our recommendation": a hairline box on paper */
body.d6 #portalMain #eventSetupCard .es-booking { background: var(--d6-paper); border: 1px solid var(--d6-line); border-radius: var(--d6-r-panel); }
/* DESIGNS › DIRECTION · the moodboard letter has its gutter; "Tell us in one move" is a section of
   the card under a hairline, not a tinted box inside it; the empty-state line is the grey line */
body.d6 #portalMain .cdl-card { padding: 14px 16px 16px; }
body.d6 #portalMain .mri { background: transparent; border: 0; border-top: 1px solid var(--d6-line-2); border-radius: 0; padding: 10px 0; }
body.d6 #portalMain .mri-out:empty::before { font-style: normal; color: var(--d6-ink-3); }
/* a phone draws each guest as a card: in the one format they are the panel's rows, not boxes in it */
@media (max-width: 640px) {
  body.d6 #portalMain #rsvpCards .rsvp-card { background: transparent; border: 0; border-bottom: 1px solid var(--d6-line-2); border-radius: 0; box-shadow: none; margin: 0; }
  body.d6 #portalMain #rsvpCards .rsvp-card:last-child { border-bottom: 0; }
}
/* the list/map switch is two chips side by side — no frame round the pair */
body.d6 #portalMain #seatingViewToggle { border: 0; background: transparent; gap: 6px; }
/* the three file counts stay one row of three cells on a phone too */
body.d6 #portalMain #fileStates { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* HOME › THE PLAN · the road's steps read at row size, like the Timeline panel on the desk */
body.d6 #portalMain .rm-plan-road .rd-t { font-size: var(--d6-t-row); }
body.d6 #portalMain .s1__subjects { gap: 6px; }

/* ══ PORTAL ONE FORMAT · THE CENSUS FIXES (owner, 2026-09-25: "the portal-one-format branch whole
   thing needs to be the same... I cannot have issues like these again and repeating") ══════════
   scripts/portal-one-format-census.mjs walks every room on seven fixtures at 1440 and 390 and
   counts what is outside the format. What it named, family by family, takes the direction's own
   values here: the type scale (--d6-t-*, the 27px figure), the panel frame, sentence case, one
   height per line of controls, the brand's corners. Re-dress only — no control is added or lost.
   (Selectors are written out in full, never grouped in :is() — test/portal-d6-two-pages.test.mjs
   reads every selector of this layer and holds each one to body.d6.) */

/* every room's own title is the page head: serif 25, 23 on a phone */
body.d6 #portalMain .rm-screen-h { font-family: var(--d6-serif); font-size: var(--d6-t-page); font-weight: 400; line-height: var(--d6-lh-title); letter-spacing: 0; text-transform: none; }
@media (max-width: 640px) { body.d6 #portalMain .rm-screen-h { font-size: 23px; } }

/* a panel's own head (a section title inside a room): serif 16.5 */
body.d6 #portalMain h3.es-part-hd,
body.d6 #portalMain h3.s2__piece-name,
body.d6 #portalMain #crqCard h3,
body.d6 #portalMain #rosCard h3,
body.d6 #portalMain .s4__register,
body.d6 #portalMain .s6__rung-label,
body.d6 #portalMain .s6__referral-code,
body.d6 #portalMain .bsp-sec-title,
body.d6 #portalMain .plan-value {
  font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; line-height: 1.25; letter-spacing: 0; text-transform: none;
}
body.d6 #portalMain .s6__rung-delta-amt { font-size: var(--d6-t-panel); }
body.d6 #portalMain .doc-row-num,
body.d6 #portalMain #cdlSignature { font-size: var(--d6-t-panel); }

/* a big number is the layer's figure: serif 27 (the countdown, the readiness score, the counts) */
body.d6 #portalMain .hr-num,
body.d6 #portalMain .er-n,
body.d6 #portalMain .ech-num,
body.d6 #portalMain #evoIdentity b,
body.d6 #portalMain .ds-spill-count { font-family: var(--d6-serif); font-size: 27px; font-weight: 400; line-height: 1.1; letter-spacing: 0; }
body.d6 #portalMain .ds-spill-count { font-size: 17px; }
body.d6 #portalMain .rm-wall-h { font-family: var(--d6-serif); font-size: var(--d6-t-page); font-weight: 400; }

/* a row's title and a value: 13.5; body and notes: 13 */
body.d6 #portalMain .ps-piece,
body.d6 #portalMain .s4__name,
body.d6 #portalMain .peb-v,
body.d6 #portalMain .yp-pr-name,
body.d6 #portalMain li.er-row,
body.d6 #portalMain .bsp-fld-val,
body.d6 #portalMain #ypSharePanel b { font-size: var(--d6-t-row); }
body.d6 #portalMain #printSpecs td,
body.d6 #portalMain #printSpecs th,
body.d6 #portalMain .yp-sub,
body.d6 #portalMain .yp-v-empty,
body.d6 #portalMain .er-note,
body.d6 #portalMain .er-sub,
body.d6 #portalMain .testi-sub,
body.d6 #portalMain .ndl-ap-note,
body.d6 #portalMain #billingRefundDetails,
body.d6 #portalMain #billingRefundDetails p,
body.d6 #portalMain #billingRefundDetails a,
body.d6 #portalMain #billingRefundDetails strong,
body.d6 #portalMain #billingDataDetails,
body.d6 #portalMain #billingDataDetails p { font-size: var(--d6-t-base); }
body.d6 #portalMain #printSpecs th { font-size: var(--d6-t-th); letter-spacing: var(--d6-tr-th); }

/* the "i" beside a question is a small mark on the line, not a 17px letter */
body.d6 #portalMain .evo-hint { font-size: var(--d6-t-sec); }

/* one height per line of controls: cards and choices side by side stretch to the tallest */
body.d6 #portalMain #prodFormatGrid,
body.d6 #portalMain .dop-grid,
body.d6 #portalMain .es-pills { align-items: stretch; }
/* an actions group that folds onto a second line folds into an aligned grid */
body.d6 #portalMain .evi-actions,
body.d6 #portalMain .dhb-actions { display: grid; grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); gap: 8px; }

/* (census, second pass) a plan's name on My events is a row title at every width */
body.d6[data-active-tab] #portalMain .tab-panel .billing-plan-card .plan-value { font-family: var(--d6-serif); font-size: var(--d6-t-row); line-height: 1.3; }
/* the set-up's review and effects heads are panel heads */
body.d6 #portalMain #eventSetupCard .es-review h3,
body.d6 #portalMain #eventSetupCard .es-effects h3 { font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; }
/* Home's opening line ("Your event", while it reads) sits in the panel it is about to become */
body.d6 #portalMain .td-wrap.td-opening { background: var(--d6-paper); border: 1px solid var(--d6-line); border-radius: var(--d6-r-panel); padding: 12px 16px; }
/* an open check after the event is a row, not a box inside the panel */
body.d6 #portalMain #rm-s5 li.s5__section { border: 0; border-top: 1px solid var(--d6-line-2); border-radius: 0; background: transparent; }

/* THE VISION WHEN A ROOM SHOWS IT ON ITS OWN (Designs › Your Vision, Files › Your Vision — for the
   moment before the set-up card adopts it, and the read-out once it is sent): the page head's
   serif, no spaced capitals, and every block in a panel. Scoped away from the set-up card, which
   adopts the whole #tab-briefing and frames it itself. */
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .brief-hero .eyebrow,
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .bsp-hero-eyebrow { display: none; }
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .hero-title,
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .bsp-hero-title { font-family: var(--d6-serif); font-size: var(--d6-t-page); font-weight: 400; line-height: var(--d6-lh-title); letter-spacing: 0; text-transform: none; margin: 0 0 4px; }
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .hero-title em,
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .bsp-hero-title em { font-size: inherit; font-style: normal; }
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .hero-title br,
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .bsp-hero-title br { display: none; }
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .hero-title br + em::before,
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .bsp-hero-title br + em::before { content: " "; }
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .hero-sub,
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .bsp-hero-sub { font-size: var(--d6-t-sec); color: var(--d6-ink-2); max-width: 62ch; margin: 0; }
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .brief-hero,
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .bsp-hero { background: var(--d6-paper); border: 1px solid var(--d6-line); border-radius: var(--d6-r-panel); padding: 12px 16px; margin: 0 0 12px; text-align: left; }
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) #pkgStrip,
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .brief-prog,
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) #briefProgPayoff { background: var(--d6-paper); border: 1px solid var(--d6-line); border-radius: var(--d6-r-panel); padding: 10px 16px; margin: 0 0 12px; box-shadow: none; }
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .pkg-lbl,
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .brief-prog-lbl,
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) #briefProgSection,
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .bp-count { text-transform: none; letter-spacing: 0; font-size: var(--d6-t-sec); color: var(--d6-ink-3); }
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .pkg-val { font-size: var(--d6-t-row); }
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) #briefSubmittedPanel.on { background: var(--d6-paper); border: 1px solid var(--d6-line); border-radius: var(--d6-r-panel); padding: 12px 16px; }
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .bsp-sec-title { font-family: var(--d6-serif); font-size: var(--d6-t-panel); text-transform: none; letter-spacing: 0; }
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) .bsp-fld-val { font-size: var(--d6-t-row); }
body.d6 #portalMain #tab-briefing:not(#esVisionBody *) > form#briefForm { background: var(--d6-paper); border: 1px solid var(--d6-line); border-radius: var(--d6-r-panel); padding: 12px 16px; }
/* the plan card's List / Calendar switch: the chip's own face, and no `transition: all` — it was
   caught mid-animation by the census (0.8px corners, 0.79px tracking) on its way to the chip */
body.d6 #portalMain #clientPlanCard .plan-view-btn { transition: none; letter-spacing: 0; text-transform: none; border-radius: var(--d6-r-pill); }
/* Home's designer questions card: its own rem sizes (.92 / .86 / 1.05rem) fell between the steps */
body.d6 #portalMain #questionsCard h2.pq-title { font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; }
body.d6 #portalMain #questionsCard .pq-q { font-size: var(--d6-t-row); }
body.d6 #portalMain #questionsCard .pq-answer { font-size: var(--d6-t-base); }
/* the waiting list: its rows are the desk's rows, the list carries no bullets of its own */
body.d6 #portalMain #waitlistPanel .wl-list { list-style: none; margin: 8px 0 0; padding: 0; }
body.d6 #portalMain #waitlistPanel .wl-note:empty { display: none; }
/* (sections fixture) the duplicate / counted-twice note is a line of the guest-list panel */
body.d6 #portalMain #rsvpDupReview.rsvp-dup-note { margin: 0 0 10px; padding: 8px 0 0; border: 0; border-top: 1px solid var(--d6-line-2); background: transparent; font-family: var(--d6-sans); font-size: var(--d6-t-sec); line-height: 1.5; color: var(--d6-ink-2); }
/* the written guestbook's signatures are row names, not an italic 18px */
body.d6 #portalMain #guestbookQrCard .gb-name { font-family: var(--d6-sans); font-style: normal; font-size: var(--d6-t-row); font-weight: 500; }
/* the waiting list is a panel of Guests like the others: paper, hairline, the panel head */
body.d6 #portalMain #waitlistPanel { background: var(--d6-paper); border: 1px solid var(--d6-line); border-radius: var(--d6-r-panel); padding: 12px 16px; margin: 0 0 12px; }
body.d6 #portalMain #waitlistPanel h3.d6-ttl { font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; margin: 0; }
/* the album's share link reads as a line, not a tinted box inside the panel */
body.d6 #portalMain #photoSpaceCard #psShareLink { background: transparent; border: 0; border-bottom: 1px solid var(--d6-line-2); border-radius: 0; padding: 2px 0; }
/* What we handle › "Also decided" is a panel head */
body.d6 #portalMain #rm-s3 h3.s3__also-title { font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; }

/* ── GUESTS' DETAIL DRAWERS (census, sections fixture: the tool doors opened) ──────────────
   Add guest, Guest settings, Concierge, Replies and Recently removed open inside one detail box
   under the filter bar. The box is a panel; the Replies tracker's labels, figures and steps take
   the desk's case, figure and row sizes. */
body.d6 #portalMain #rm-guests-detail { background: var(--d6-paper); border: 1px solid var(--d6-line); border-radius: var(--d6-r-panel); padding: 12px 16px; margin: 0 0 12px; }
body.d6 #portalMain #rm-guests-detail[hidden] { display: none; }
body.d6 #portalMain #rm-guests-detail .rsvp-rag-lbl,
body.d6 #portalMain #rm-guests-detail .rsvp-meta-lbl,
body.d6 #portalMain #rm-guests-detail .rsvp-chip-lbl,
body.d6 #portalMain #rm-guests-detail .guests-step-num,
body.d6 #portalMain #rm-guests-detail .guests-step-lbl,
body.d6 #portalMain #rm-guests-detail .guests-step-lbl span,
body.d6 #portalMain #rm-guests-detail .guests-step span,
body.d6 #portalMain #rm-guests-detail .rsvpq__h,
body.d6 #portalMain #rm-guests-detail .eah,
body.d6 #portalMain #rm-guests-detail .gih { text-transform: none; letter-spacing: 0; font-size: var(--d6-t-sec); font-family: var(--d6-sans); }
body.d6 #portalMain #rm-guests-detail .rsvpq__h,
body.d6 #portalMain #rm-guests-detail .eah,
body.d6 #portalMain #rm-guests-detail .gih { font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; color: var(--d6-ink); }
body.d6 #portalMain #rm-guests-detail .rsvp-rag-num,
body.d6 #portalMain #rm-guests-detail .rsvp-chip-num { font-family: var(--d6-serif); font-size: 27px; font-weight: 400; line-height: 1.1; }
body.d6 #portalMain #rm-guests-detail .rsvp-meta-val { font-size: var(--d6-t-row); }

/* The day › run of show: the incident log is a section of the card, not a framed box in it */
body.d6 #portalMain #rosCard details.ros-form { border: 0; border-top: 1px solid var(--d6-line-2); border-radius: 0; background: transparent; padding: 10px 0 0; }
body.d6 #portalMain #rosCard details.ros-form > summary { font-size: var(--d6-t-row); }
/* Experience › Personal (reached from Guests › Personalised files): the story-images line and the
   door & access set-up are sections of the panel — a hairline above, the row sizes, no box in a box. */
body.d6 #portalMain #invitationsPhase .story-images-row { border: 0; border-top: 1px solid var(--d6-line-2); padding: 10px 0; }
body.d6 #portalMain #invitationsPhase .story-images-say { font-size: var(--d6-t-base); }
body.d6 #portalMain #doorSetupPanel.door-setup { border: 0; border-top: 1px solid var(--d6-line-2); padding: 10px 0; margin-top: 12px; }
body.d6 #portalMain #doorSetupPanel .door-setup-sum { font-family: var(--d6-serif); font-size: var(--d6-t-panel); letter-spacing: 0; text-transform: none; opacity: 1; }
body.d6 #portalMain #doorSetupPanel .door-setup-body { font-size: var(--d6-t-base); }
body.d6 #portalMain #doorSetupPanel code { font-size: var(--d6-t-sec); }

/* ── MESSAGES › THE COMPOSE FLOW (census: the compose door and its tools opened) ────────────
   Inside the Messages fold every card is a SECTION of the one panel — a hairline above, no second
   frame or tinted ground; heads are the panel head, labels are sentence case, the sample a guest
   reads is a quoted line, not a grey box. */
body.d6 #portalMain #messagesPhase .gj-card,
body.d6 #portalMain #messagesPhase .prb,
body.d6 #portalMain #messagesPhase .evo-card.bc-card,
body.d6 #portalMain #messagesPhase .paa,
body.d6 #portalMain #messagesPhase .bc-seq { background: transparent; border: 0; border-top: 1px solid var(--d6-line-2); border-radius: 0; box-shadow: none; padding: 12px 0 0; margin: 12px 0 0; }
body.d6 #portalMain #messagesPhase .pcm-sample { background: transparent; border: 0; border-left: 2px solid var(--d6-line); border-radius: 0; padding: 2px 0 2px 10px; }
body.d6 #portalMain #messagesPhase .bc-lbl,
body.d6 #portalMain #messagesPhase #commsProgressMount h5,
body.d6 #portalMain #messagesPhase .pcm-who,
body.d6 #portalMain #messagesPhase .pcm-h,
body.d6 #portalMain #messagesPhase .gj-eyebrow,
body.d6 #portalMain #messagesPhase .prb-h,
body.d6 #portalMain #messagesPhase .paa-h { text-transform: none; letter-spacing: 0; font-family: var(--d6-sans); font-size: var(--d6-t-sec); }
body.d6 #portalMain #messagesPhase .pcm-h,
body.d6 #portalMain #messagesPhase .prb-h,
body.d6 #portalMain #messagesPhase .paa-h,
body.d6 #portalMain #messagesPhase .gj-eyebrow { font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; color: var(--d6-ink); }
body.d6 #portalMain #messagesPhase .gj-title { font-family: var(--d6-sans); font-size: var(--d6-t-row); }
/* the composer's actions fold into an aligned grid, not a ragged stack */
body.d6 #portalMain #messagesPhase .bc-acts { display: grid; grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); gap: 8px; }
/* Messages › Changes to tell guests (#hostUpdatesMount): each proposed change is a section of the
   fold, its wording and a guest's sample are quoted lines — no card in a card, no spaced capitals */
body.d6 #portalMain #hostUpdatesMount .phu-card { background: transparent; border: 0; border-top: 1px solid var(--d6-line-2); border-radius: 0; box-shadow: none; padding: 12px 0 0; }
body.d6 #portalMain #hostUpdatesMount .phu-words,
body.d6 #portalMain #hostUpdatesMount .phu-sample { background: transparent; border: 0; border-left: 2px solid var(--d6-line); border-radius: 0; padding: 2px 0 2px 10px; }
body.d6 #portalMain #hostUpdatesMount .phu-h,
body.d6 #portalMain #hostUpdatesMount .phu-who,
body.d6 #portalMain #hostUpdatesMount .phu-tag { text-transform: none; letter-spacing: 0; font-family: var(--d6-sans); font-size: var(--d6-t-sec); }
body.d6 #portalMain #hostUpdatesMount .phu-h { font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; color: var(--d6-ink); }
/* The day › the RSVP QR preview (main, 2026-09-26: the Day room reaches door links and QR codes —
   #qrPanel joins the door slot). Caught by the census re-walk over the merge: it is a section of the
   door panel, the code sits on the paper, the label is sentence case. */
body.d6 #portalMain #qrPanel { background: transparent; border: 0; border-top: 1px solid var(--d6-line-2); border-radius: 0; box-shadow: none; padding: 12px 0 0; }
body.d6 #portalMain #qrPanel #qrCanvasWrap { background: transparent; border: 0; border-radius: 0; box-shadow: none; }
body.d6 #portalMain #qrPanel .qr-info-label { text-transform: none; letter-spacing: 0; font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; color: var(--d6-ink); }
/* Designs › a piece's rounds counter ("One round of changes left") speaks the desk's sentence case in
   every state, not only the zero state — caught by the census re-walk over main dcfc8b86f, when a
   fixture's piece first showed a used round (.partial) */
body.d6 #portalMain .bb-rev-counter { text-transform: none; letter-spacing: 0; font-weight: 500; font-size: var(--d6-t-sec); }

/* ══ PORTAL UX AUDIT (2026-09-27) ═════════════════════════════════════════════════════════════════
   Owner's brief, 2026-09-26: "make every page and state feel like one polished, coherent product".
   Measured by scripts/portal-ux-walk.mjs over the eight census fixtures at 1440 and 390; each rule
   below names what the walk read before it. */

/* ONE FIELD. On a desktop pointer the portal drew seven field treatments on one event — 30, 37, 41,
   44, 46, 48 and 49px tall, text at 12.5, 13, 14.04 and 16.2px (portal-v15's `.tab-panel input`
   recipe is .9rem = 16.2px on the desktop's 18px root, with 12px 14px of padding) — beside 30px
   buttons. §8.5's field is 30px on a fine pointer with the body size, so every field takes it. The
   phone is left alone: it already reads 16px (below that iOS zooms the page on focus) at 44px+. */
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6[data-active-tab] #portalMain input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="hidden"]):not([type="submit"]):not([type="button"]),
  body.d6[data-active-tab] #portalMain select {
    min-height: 30px;
    height: auto;
    padding: 5px 10px;
    font-size: var(--d6-t-base);
    line-height: 1.3;
  }
  body.d6[data-active-tab] #portalMain textarea {
    padding: 8px 10px;
    font-size: var(--d6-t-base);
    line-height: var(--d6-lh);
  }
}

/* ── PORTAL UX AUDIT (2026-09-27) · package V — layout and hierarchy fixes read off the walk ──
   Each rule names the finding (V1…V19 in the audit's list) and what the rig measured before it. */

/* V1 · The day › RSVP QR preview on a phone: the code and its words sat side by side and the
   nowrap download button ran to x=470 in a 390px viewport. Stacked, and the button's label wraps. */
@media (max-width: 640px) {
  body.d6 #portalMain #qrPanel { flex-direction: column; align-items: flex-start; }
  body.d6 #portalMain #qrPanel .qr-info { flex: none; width: 100%; min-width: 0; }
  body.d6 #portalMain #qrPanel .qr-download-btn { white-space: normal; max-width: 100%; text-align: left; }
}

/* V2 · What we handle on a phone: the parts table was 475px inside a 352px scroller, so every
   "Change" button and every upgrade price sat off-screen while the intro says "choose Change".
   Each part stacks — number, part, who runs it, its action — the way RCL-D138 stacks the
   Messages rows above. The column heads stay for screen readers; `tr[hidden]` is restated. */
@media (max-width: 640px) {
  body.d6 #tab-handle .s3__tw { overflow-x: visible; }
  body.d6 #tab-handle .s3__tw .d6-table,
  body.d6 #tab-handle .s3__tw .d6-table tbody { display: block; width: 100%; }
  body.d6 #tab-handle .s3__tw .d6-table thead {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  body.d6 #tab-handle .s3__tw .d6-table tr.s3__part {
    display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 4px 8px;
    padding: 10px 10px; border-bottom: 1px solid var(--d6-line-2);
  }
  body.d6 #tab-handle .s3__tw .d6-table tr.s3__part > td {
    display: block; width: auto; min-width: 0; padding: 0; border-bottom: 0;
    text-align: left; white-space: normal;
  }
  body.d6 #tab-handle .s3__tw .d6-table tr.s3__part > td.s3__n { grid-row: 1 / span 3; }
  body.d6 #tab-handle .s3__tw .d6-table tr.s3__part > td:not(.s3__n) { grid-column: 2; }
  body.d6 #tab-handle .s3__tw .d6-table tr.s3__part .s3__affs { justify-content: flex-start; }
  body.d6 #tab-handle .s3__tw .d6-table tr[hidden] { display: none; }
}

/* V4 · Designs › Finishing details › Order of Service: "Remove" kept the 28px width portal.css
   gave the old × glyph, so its label spilled out of the box. It is as wide as its word. On a
   phone the row wraps — the type on its own line, the label field and Remove under it — where    the
   field had shrunk to 66px beside a 178px select. */
body.d6 #portalMain .pd-remove-btn { width: auto; }
@media (max-width: 640px) {
  body.d6 #portalMain .pd-row:has(> .pd-select) { flex-wrap: wrap; }
  body.d6 #portalMain .pd-row:has(> .pd-select) > .pd-select { flex: 1 1 100%; }
  body.d6 #portalMain .pd-row:has(> .pd-select) > .pd-input { min-width: 0; }
}

/* V6 · A single-event account's event name in the phone header was a bordered 44px box — the
   same box as the account button beside it — and it opens nothing. portal-event-header.js
   removes role="button" when there is one event (a menu of one), so that is what this keys on;
   a family account's switcher keeps its box. Above 700px portal-nav.css already draws no box. */
@media (max-width: 700px) {
  :root.portal-signed-in body.d6 .hdr-event-chip:not([role="button"]) {
    border-color: transparent;
    background: transparent;
    padding-left: 0;
    cursor: default;
  }
}

/* V7 · Home › The plan: the readiness card is a direct child of #portalMain, not of a room, so it
   had no gutter — flush at x=0 on a phone and against the sidebar at 1440. The room's own 18px. */
body.d6 #portalMain > #hostReadinessMount { margin-inline: 18px; }

/* V8 · "57 %": portal-readiness.js gives the unit a 2px left margin; the figure reads "57%". */
body.d6 #portalMain .hr-unit { margin-left: 0; }

/* V9 · Plan & billing at 1440: the Invoices panel stretched to the height of the plan panel beside
   it (2,121px, most of it empty). Each panel is as tall as what it holds. */
body.d6 #tab-billing #rm-plan-panels { align-items: start; }

/* V10 · The footer on a phone: the reCAPTCHA line wraps under the links and left the separator
   after "Contact" dangling at the end of the first line. On a phone that separator is the line
   break — a zero-height block, so the second line starts clean. */
@media (max-width: 640px) {
  body.d6 #portalFooter > span:nth-of-type(3) { display: block; height: 0; overflow: hidden; visibility: hidden; }
}

/* V11 · Empty frames. Set-up's Questions panel drew an empty 19px footer strip (`.d6-pf`, no
   children), and Guests drew a 21px framed #rsvpHero whose only child — #rsvpMetaBar — is hidden
   until there are guests. Hidden only while truly empty; the moment either has content it shows. */
body.d6 #portalMain .d6-pf:empty { display: none; }
body.d6 #tab-rsvp #rsvpHero:not(:has(> :not([style*="display:none"]):not([style*="display: none"]):not([hidden]))) { display: none; }

/* V12 · Hierarchy: sub-heads nested inside a 16.5px panel title were bigger than it — the
   finishing-details sections ("Menu Card", "Order of Service", "Delivery Format", "Your
   stationery timeline", .pd-piece-name at 23.4px) and What we handle's screen-reader heading    and
   count (28.8 / 23.4px, visually hidden, said at the same step so the census reads true).    The
   plan card's values ("9 designs", the date, the price) were a 13.5px serif beside 12.5px    sans
   labels; they are the body sans now. The page's own h1 stays 25. */
body.d6 #portalMain .pd-piece-name { font-size: var(--d6-t-panel); }
body.d6 #portalMain .s3__title,
body.d6 #portalMain .s3__count { font-size: var(--d6-t-panel); }
body.d6 #portalMain #billingPlanSection .billing-plan-card:not(#billingPackageIncludes) .plan-value:not(#billingPlanName) {
  font-family: var(--d6-sans);
  font-size: var(--d6-t-base);
}

/* V13 · Sibling panels that touched: the designs status and the Direction letter above "Your
   record" (0px), "Message Evorrah" above Print specifications (4px), and on Files the suite    list,
   "Your documents" and the countdown (0px — portal.css forces `.items-section` and    `.docs-
   section` to `margin:0!important`; their bottom margin reads --d6-stack-gap now). "Message
   Evorrah" carries an inline `margin:34px 0 4px`, so the gap is the panel below it. The house gap
   is 12px. Guests › Waiting list was padded 12px 16px against its siblings' 9px 12px 10px; it takes
   theirs. (Its 28px gap above is an inline `marginTop` from portal-waitlist.js — not reachable
   from this layer.) */
body.d6 #portalMain #approvalHistoryMount > #approvalHistoryCard { margin-top: 12px; }
body.d6 #portalMain #tab-designs .designs-ask + #printSpecs.d6-framed { margin-top: 12px; }
body.d6 #portalMain #tab-suite > .items-section.d6-framed,
body.d6 #portalMain #tab-suite > .docs-section.d6-framed { --d6-stack-gap: 12px; }
body.d6 #portalMain #waitlistPanel.d6-framed { padding: 9px 12px 10px; }

/* V14 · Designs › Delivery format: three tiles in portal-production-details.js's inline two-column
   grid left the third alone on a row, and the descriptions were italic. One column on a phone;
   three across on a desktop — the inline template cannot be outranked here (no !important in    this
   layer), so the tiles are placed on one row and the third takes an implicit 1fr column. */
body.d6 #portalMain #prodFormatGrid .print-opt-desc { font-style: normal; }
@media (max-width: 640px) {
  body.d6 #portalMain #prodFormatGrid > .print-opt-card { grid-column: 1 / -1; }
}
@media (min-width: 641px) {
  body.d6 #portalMain #prodFormatGrid:has(> .print-opt-card:nth-child(3):last-child) { grid-auto-columns: 1fr; }
  body.d6 #portalMain #prodFormatGrid:has(> .print-opt-card:nth-child(3):last-child) > .print-opt-card:nth-child(1) { grid-row: 1; grid-column: 1; }
  body.d6 #portalMain #prodFormatGrid:has(> .print-opt-card:nth-child(3):last-child) > .print-opt-card:nth-child(2) { grid-row: 1; grid-column: 2; }
  body.d6 #portalMain #prodFormatGrid:has(> .print-opt-card:nth-child(3):last-child) > .print-opt-card:nth-child(3) { grid-row: 1; grid-column: 3; }
}

/* V16 · Guests' filter bar on a phone: the search sat right of an empty 70px spacer — it takes the
   full row — and "Select all" ran straight into "Sort", whose buttons had already wrapped to the
   next line. Select all takes its own line, so "Sort" opens the line its buttons are on. (The
   select-all box is matched to the card checkboxes in its own rule above.) */
@media (max-width: 640px) {
  body.d6 #tab-rsvp #rm-guests-search { flex: 1 1 100%; }
  body.d6 #tab-rsvp .d6-cardtools .d6-ct-all { flex: 1 1 100%; }
}

/* V17 · Events at 1440: "View Event" stretched 1,029px across its card. As wide as its words. */
body.d6 #portalMain #myEventsList .billing-plan-card > .billing-action-btn { width: auto; justify-self: start; }

/* V18 · People and Plan & billing: "Account holder Every event" and "Opened their link September 18,
   2026" set the pill at 11px and its <em> at 12.96px — two sizes in one pill. One. */
body.d6 #portalMain .d6-pill.yp-ac-role,
body.d6 #portalMain .d6-pill.yp-ac-state,
body.d6 #portalMain .d6-pill.yp-ac-role em,
body.d6 #portalMain .d6-pill.yp-ac-state em { font-size: var(--d6-t-chip); letter-spacing: 0; }

/* V19 · Sentence-case sub-lines were letter-spaced (.06em "How should your finished files reach
   you…", "When to send…"; Files' "4 pieces" in the rule portal.css now keeps to the old look). */
body.d6 #portalMain .pd-piece-spec,
body.d6 #portalMain .items-sec-sub { letter-spacing: 0; }

/* ONE TYPE SCALE, AT EVERY WIDTH. The site's root is 18px on a desktop (tokens.css `112.5%`) and
   16px on a phone, so every legacy rem size the room modules still carry grows 12.5% on a desktop
   only: .78rem is 12.5px on the phone and 14.04px at 1440, beside the direction's 13px body. The
   census reads those as "a step in all but the third decimal" (±0.6px); side by side they are not.
   scripts/portal-ux-walk.mjs listed every setter still off the --d6-t-* steps on the eight fixtures;
   these give each its step by role — body 13 · row/name 13.5 · sub-line and control 12.5 · count
   11.5. */
body.d6 #portalMain .ep-p,
body.d6 #portalMain .es-part-intro,
body.d6 #portalMain .evi-p,
body.d6 #portalMain .evi-sub,
body.d6 #portalMain .mts-unknown,
body.d6 #portalMain .pdp-intro,
body.d6 #portalMain .peb-sub,
body.d6 #portalMain .rm-plan-sub,
body.d6 #portalMain .rm-wall-p,
body.d6 #portalMain .ros-run,
body.d6 #portalMain .ros-note,
body.d6 #portalMain .ros-sub,
body.d6 #portalMain .rsvpq__done,
body.d6 #portalMain .rsvpq__p,
body.d6 #portalMain .rsvpq__q,
body.d6 #portalMain .s5__check-detail,
body.d6 #portalMain .s5__offer-note,
body.d6 #portalMain .s5__offer-label,
body.d6 #portalMain .s5__offer-price,
body.d6 #portalMain .s5__doc-d,
body.d6 #portalMain .yp-ac-note,
body.d6 #portalMain .gb-text,
body.d6 #portalMain .gb-state,
body.d6 #portalMain .print-opt-desc,
body.d6 #portalMain .rev-rem-body,
body.d6 #portalMain .ros-openh,
body.d6 #portalMain .dhb-path,
body.d6 #portalMain .doc-sub,
body.d6 #portalMain .dop-s,
body.d6 #portalMain .hhrow,
body.d6 #portalMain .hr-sub,
body.d6 #portalMain .pcm-item,
body.d6 #portalMain .pcm-sample,
body.d6 #portalMain .guests-phase-locked-msg,
body.d6 #portalMain .ps-set,
body.d6 #portalMain .yp-pr-meta,
body.d6 #portalMain .s2__verdict,
body.d6 #portalMain .s1__cost,
body.d6 #portalMain .pd-adv-date,
body.d6 #portalMain .es-save,
body.d6 #portalMain .s4__word,
body.d6 #portalMain .s4__verb-label,
body.d6 #portalMain .att-who,
body.d6 #portalMain .mrc__ask,
body.d6 #portalMain .s6__rung-add,
body.d6 #portalMain .s6__gifts-label,
body.d6 #portalMain .yp-ac-scope-o,
body.d6 #portalMain .rsvp-pending-email,
body.d6 #portalMain #bcProofNote,
body.d6 #portalMain #tab-my-events .evo-note,
body.d6 #portalMain .doc-shelf-note { font-size: var(--d6-t-base); }
body.d6 #portalMain .s6__person-name,
body.d6 #portalMain .es-booking-who,
body.d6 #portalMain .mile-what,
body.d6 #portalMain .rsvp-pending-name,
body.d6 #portalMain .gq-dup-who,
body.d6 #portalMain .pd-adv-row-lbl,
body.d6 #portalMain .s2__awaiting,
body.d6 #portalMain .rsvp-card-name { font-size: var(--d6-t-row); }
body.d6 #portalMain .es-change,
body.d6 #portalMain .es-defer,
body.d6 #portalMain .hr-howto,
body.d6 #portalMain .menu-del-btn,
body.d6 #portalMain .es-btn,
body.d6 #portalMain .peb-prov,
body.d6 #portalMain .s6__person-role { font-size: var(--d6-t-sec); }
body.d6 #portalMain .doc-shelf-count,
body.d6 #portalMain .gb-time,
body.d6 #portalMain .item-num,
body.d6 #portalMain .bb-pr-num { font-size: var(--d6-t-count); }
body.d6 #portalMain .er-axis { font-size: var(--d6-t-th); }
/* the snap above set Set-up's "Change" and "I don't know yet" in the control size; on a phone they
   keep the house's 44px target (portal-ux-walk read "Change" at 24px tall at 390) */
@media (max-width: 640px) {
  body.d6 #portalMain .es-change,
  body.d6 #portalMain .es-defer { min-height: 44px; }
}
/* What we handle › the parts panel (#rm-s3): its intro, "Moving up…" and "Also decided" sat flush on
   the panel's left border (the frame carries no padding; only the table's cells do), and a 1080px
   cap left it ~85px short of the page head and the panels beneath. Full width, and the words take the
   same 12px inset as the table's first column. */
body.d6 #tab-handle #rm-s3 > .s3__note,
body.d6 #tab-handle #rm-s3 > .s3__also > :not(.d6-tw) { padding-left: 12px; padding-right: 12px; }
/* RCL-D184 layout remainder (stress test RETEST-V29, C3 After at 1440): the readiness items that
   "See what is open" unfolds hung their bullets outside the card's left edge — the list had no
   inset of its own. The bullets sit inside the text column, in the sub-line voice of the rows. */
body.d6 #portalMain .s5__check-items {
  margin: 8px 0 4px;
  padding-left: 18px;
  list-style: disc outside;
}
body.d6 #portalMain .s5__check-item {
  margin: 4px 0;
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-2);
}

/* Messages › the host's own sends (send-plan.js, portal only): a section of the panel, not a tinted
   box inside it — the census read a second frame and a 14.76px title here (2026-09-27). */
body.d6 #portalMain .sp-direct { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--d6-line-2); }
body.d6 #portalMain .sp-direct-h { font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; color: var(--d6-ink); margin: 0 0 6px; }

/* ── SUPPLIERS (owner ruling 2026-09-27) · app/scripts/portal-suppliers.js ─────────────────
   Everything the page draws is a d6 object; these are the few arrangements it adds. */
body.d6 .sw-wrap h3 { font-size: var(--d6-t-base); font-weight: 600; margin: 16px 0 8px; }
body.d6 .sw-cap { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* The hidden captions sit INSIDE the sideways scrollers (the Actions column of "What you have
   shared"). With no positioned ancestor in the scroller an absolute caption escaped it and widened
   the whole page to 480 px on a phone (a11y walk 2026-09-29: #roomBtn-details unclickable after
   Suppliers). The scroller holds them. */
body.d6 .sw-scroll { position: relative; }
body.d6 .sw-cats { list-style: none; margin: 0; padding: 0; }
body.d6 .sw-cat { padding: 12px 0; border-bottom: 1px solid var(--d6-line); display: grid; gap: 6px; }
body.d6 .sw-cat:last-child { border-bottom: 0; }
body.d6 .sw-cat-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 12px; }
body.d6 .sw-wrap .sw-cat-name { margin: 0; }
body.d6 .sw-cat-state { display: inline-flex; align-items: center; gap: 8px; }
body.d6 .sw-cat-why { margin: 0; font-size: var(--d6-t-base); }
body.d6 .sw-soft { color: var(--d6-ink-3); }
body.d6 .sw-cat-acts { flex-wrap: wrap; }
/* "How we help with this one" (owner, 2026-10-03): a ruled block inside the category, house type only. */
body.d6 .sw-help { display: grid; gap: 12px; padding: 12px 0 4px; border-top: 1px solid var(--d6-line); }
body.d6 .sw-help-grid { display: grid; gap: 16px 24px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
body.d6 .sw-help h4.d6-eyebrow { margin: 0 0 6px; }
body.d6 .sw-help-list { margin: 0; padding-left: 1.2em; display: grid; gap: 4px; font-size: var(--d6-t-base); }
body.d6 ul.sw-help-list { list-style: disc; }
body.d6 ol.sw-help-list { list-style: decimal; }
body.d6 .sw-help-why, body.d6 .sw-help-tip { margin: 0 0 6px; font-size: var(--d6-t-base); }
body.d6 .sw-help-tips { display: grid; gap: 4px; }
body.d6 .sw-vendors { display: grid; gap: 10px; padding: 4px 0 8px; }
/* One saved file per line, its name and date first and its actions after, wrapping as a whole
   (2026-09-27: inline, the date and note were squeezed into a column one word wide). */
body.d6 .sw-files { display: grid; gap: 0; margin-top: 6px; }
body.d6 .sw-file { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; padding: 6px 0; border-top: 1px solid var(--d6-line); }
body.d6 .sw-file > .d6-s { flex: 1 1 220px; min-width: 0; }
body.d6 .sw-vendor > .d6-gr { flex: 1 1 320px; min-width: 0; }
body.d6 .sw-chips { margin: 0 0 4px; }
body.d6 .sw-review { padding: 4px 0 8px; }
body.d6 .sw-up { position: relative; cursor: pointer; }
body.d6 .sw-up .sw-file-in { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
body.d6 .sw-up:focus-within { outline: 2px solid var(--d6-hot); outline-offset: 2px; }
body.d6 .sw-form, body.d6 .sw-compare { border-top: 1px solid var(--d6-line); padding-top: 8px; }
body.d6 .sw-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px 14px; }
body.d6 .sw-grid--wide { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 10px; }
body.d6 .sw-f { display: grid; gap: 4px; align-content: start; min-width: 0; }
body.d6 .sw-scroll { overflow-x: auto; }
body.d6 .sw-scroll:focus-visible { outline: 2px solid var(--d6-hot); outline-offset: 2px; }
body.d6 .sw-compare th[scope="row"] { text-align: left; white-space: nowrap; }
body.d6 .sw-missing { color: var(--d6-ink-3); }
body.d6 .sw-swatches { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 8px 0; font-size: var(--d6-t-sec); }
body.d6 .sw-swatch { display: inline-flex; align-items: center; gap: 6px; }
body.d6 .sw-dot { width: 14px; height: 14px; border: 1px solid var(--d6-line); border-radius: var(--d6-r-box); }
body.d6 .sw-kit-item { display: grid; gap: 4px; padding: 8px 0; border-bottom: 1px solid var(--d6-line); }
body.d6 .sw-notices { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
body.d6 .sw-notice { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; }
body.d6 .sw-notice-t { flex: 1 1 260px; font-size: var(--d6-t-base); }
body.d6 .sw-figs { margin-bottom: 12px; }
body.d6 .sw-bench { padding: 8px 0; border-top: 1px solid var(--d6-line); }
body.d6 .sw-bench p { margin: 0 0 6px; }
body.d6 .sw-scroll th .d6-s { display: block; }
body.d6 .sw-method { border: 0; margin: 0; padding: 0; }
body.d6 .sw-kit-list { display: grid; gap: 2px; padding-left: 24px; }
body.d6 .sw-status .d6-field { width: auto; }
body.d6 .sw-cat-plan { grid-template-columns: repeat(2, minmax(0, 220px)); }
body.d6 .sw-vendor { flex-wrap: wrap; }
body.d6 .sw-preview { margin: 8px 0 12px; padding: 12px 14px; border: 1px solid var(--d6-line); background: var(--d6-wash); }
body.d6 .sw-compare td .d6-s, body.d6 .sw-scroll td .d6-s { display: block; }
body.d6 .sw-grid .d6-note { margin: 0 0 6px; }
@media (max-width: 640px) {
  body.d6 .sw-grid, body.d6 .sw-grid--wide { grid-template-columns: minmax(0, 1fr); }
  body.d6 .sw-compare th[scope="row"] { white-space: normal; }
}
/* ── PORTAL A11Y, 2026-09-27 (the supplier chat's portal a11y walk, fixture `delivered`, 14:53Z: 36 axe
   color-contrast blockers + one target under 24px, in rooms today's releases re-dressed). At the source:
   the quiet pill's ink is ink-2 and the done pill's is the deeper moss (both rules above, 6.66:1 and
   5.32:1 on their grounds); the rest are the few that carry their own colour. */
/* the Events list and the seating state reuse the house evo-pill — same two inks */
body.d6 #portalMain .evo-pill.is-quiet { color: var(--d6-ink-2); }
body.d6 #portalMain .evo-pill.is-done { color: var(--ch8-moss); }
/* Set-up's "optional" tag: its 62% opacity read #A29E98 on #E8E2DA, 2.07:1 — full ink-2, 7.45:1 */
body.d6 #portalMain .es-opt,
body.d6 #portalMain .es-sens { opacity: 1; color: var(--d6-ink-2); }
/* Designs › a piece with no preview yet: its labels sit on the dark plate — linen, 16.4:1 (the
   label class the layer adopted them into drew them in ink-3, 3.32:1) */
body.d6 #portalMain .bb-preview-empty-lbl .bb-preview-empty-name,
body.d6 #portalMain .bb-preview-empty-lbl .bb-preview-empty-sub { color: var(--ch8-linen); }
/* Guests › Written guestbook: the <summary> that opens it was 21px tall — a 24px target */
body.d6 #portalMain summary:has(> .gb-open) { min-height: 24px; padding-block: 2px; box-sizing: border-box; }
/* Event website › "The opening line and gifts — quick edit": the <summary> that opens it was 18px tall —
   a 24px target (seen once the plan counts as including the website, lsf-portal-fixes 2026-09-29) */
body.d6 #portalMain summary.d6-eyebrow { min-height: 24px; padding-block: 3px; box-sizing: border-box; }

/* ── PORTAL ONE FORMAT · the Vision questionnaire (census, 2026-09-27) ───────────────────────
   The census walked Set-up › Your vision step by step (s0 … s8, s_story) on the `vision` fixture
   and read 74 findings at 1440 and 45 at 390 — the questionnaire was still drawn in the house's
   18px body, spaced capitals and square crimson controls inside the direction's set-up card. Its
   choice controls (palette families, curated / wheel, chapters) and its two recommendation
   buttons are adopted by portal-rooms.js ONE_FORMAT (chip, onyx primary, link, tile); the rules
   here put the rest on the --d6-t-* scale in sentence case. Re-dress only: no wording, control or
   handler changes. */
/* the line under a question is the section's note: body size, not the house's 18px */
body.d6 #portalMain #esVisionBody .sec-desc { font-size: var(--d6-t-base); line-height: 1.55; }
/* a drop zone's title, the recommendation's reasoning, a tone's sample line */
body.d6 #portalMain #esVisionBody .upload-ttl { font-size: var(--d6-t-row); line-height: 1.35; }
body.d6 #portalMain #esVisionBody .pd-rec-why { font-size: var(--d6-t-base); line-height: 1.55; }
body.d6 #portalMain #esVisionBody .tone-card-sample { font-size: var(--d6-t-row); }
/* a feeling's and a palette's name on its tile: the row title (they were rem sizes — 17 / 20.7px on
   the desktop's 18px root, 15.2 / 18.4px on a phone's 16px) */
body.d6 #portalMain #esVisionBody .feeling-name,
body.d6 #portalMain #esVisionBody .pal-card-name-v3 { font-size: var(--d6-t-row); font-weight: 500; }
/* the pieces count is the layer's figure at every width (1.5rem read 27px on a desktop, 24px on a phone) */
body.d6 #portalMain #esVisionBody .dp-bar-txt strong { font-size: 27px; }
/* the suggested wording reads as the card it will become: the serif, the panel size */
body.d6 #portalMain #esVisionBody .pd-adv-head { font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; line-height: 1.35; }
/* labels in spaced capitals speak in sentence case */
body.d6 #portalMain #esVisionBody .pd-rec-badge,
body.d6 #portalMain #esVisionBody .type-lbl,
body.d6 #portalMain #esVisionBody .sc-ctr,
body.d6 #portalMain #esVisionBody .sc-chapter-num { letter-spacing: 0; text-transform: none; }
body.d6 #portalMain #esVisionBody .pd-rec-badge { font-size: var(--d6-t-sec); font-weight: 500; }
body.d6 #portalMain #esVisionBody .sc-ctr { font-size: var(--d6-t-count); }
body.d6 #portalMain #esVisionBody .sc-chapter-num { font-size: var(--d6-t-chip); }
/* a chapter's and a default choice's title: the panel title's serif */
body.d6 #portalMain #esVisionBody .sc-chapter-title,
body.d6 #portalMain #esVisionBody .type-default-title { font-family: var(--d6-serif); font-size: var(--d6-t-panel); font-weight: 400; font-style: normal; line-height: 1.25; }
/* "Leave it to us": its lines inherited the wrapper's inline .82rem */
body.d6 #portalMain #esVisionBody #dpFillWrap label > span > strong,
body.d6 #portalMain #esVisionBody #dpFillWrap label > span > span:not(#dpFillLabel) { font-size: var(--d6-t-base); }
/* AM / PM: a pair of choices beside the time — the control step (44px by touch, 30px with a
   mouse), centred on the row rather than stretched to the selects beside it */
body.d6 #portalMain #esVisionBody #f_time_am,
body.d6 #portalMain #esVisionBody #f_time_pm { min-height: 44px; }
@media (min-width: 641px) and (hover: hover) and (pointer: fine) {
  body.d6 #portalMain #esVisionBody div:has(> #f_time_am) { align-self: center; }
  body.d6 #portalMain #esVisionBody #f_time_am,
  body.d6 #portalMain #esVisionBody #f_time_pm { min-height: 30px; height: 30px; }
}
/* the palette families and the curated / wheel pair are chips (ONE_FORMAT): a chip row, not the
   underline strip they were drawn as */
body.d6 #portalMain #esVisionBody .pal-tabs,
body.d6 #portalMain #esVisionBody .pal-mode-toggle { gap: 6px; border-bottom: 0; }
body.d6 #portalMain #esVisionBody .pal-tab { margin: 0; }
body.d6 #portalMain #esVisionBody .pal-mode-btn { flex: 0 0 auto; white-space: nowrap; }
/* the story's chapters: numbered chips (the face says 1 · 2 · 3; the name stays "Chapter N");
   the one open reads as the chosen chip's sage, not a crimson bar */
body.d6 #portalMain #esVisionBody .sc-dot {
  width: auto;
  min-width: 30px;
  height: auto;
  margin: 0;
  padding: 0 10px;
  box-sizing: border-box;
  justify-content: center;
  background-clip: border-box;
  transform: none;
}
body.d6 #portalMain #esVisionBody .sc-dots { display: flex; gap: 6px; }
body.d6 #portalMain #esVisionBody .sc-dot.sc-cur {
  border-color: var(--ch8-signal-approve);
  color: var(--ch8-signal-approve);
  background: color-mix(in srgb, var(--ch8-signal-approve) 6%, var(--d6-paper));
}

/* CENSUS 2026-09-28 05:45Z (states earlier walks had not caught on screen; the J28 pass-2 census
   read the same families on main):
   · the Vision pager's phone strip ("SECTION 1 OF 12 · START FROM A DESCRIPTION") spoke in
     tracked capitals — sentence case at the strip step, like the status strip;
   · Files' review / delivered hero sits straight on the room after the pieces (orderFilesRoom),
     so it is a panel of its own, and its "Files" eyebrow reads in sentence case;
   · the "could not check / read … just now" notes in Messages were Alabaster boxes inside their
     panel — they keep their crimson rule and lose the fill. */
body.d6 .brief-mobile-progress { text-transform: none; letter-spacing: 0; font-size: var(--d6-t-strip); }
body.d6 #tab-suite > .review-hero-block,
body.d6 #tab-suite > .delivered-hero-block {
  background: var(--d6-paper);
  border: 1px solid var(--d6-line);
  border-radius: var(--d6-r-panel);
  padding: 10px 12px;
  margin: 0 0 12px;
}
body.d6 #tab-suite .rhb-eyebrow,
body.d6 #tab-suite .dhb-eyebrow { text-transform: none; letter-spacing: 0; font-size: var(--d6-t-strip); }
body.d6 #portalMain .phu-alert,
body.d6 #portalMain .pcm-alert { background: transparent; }
/* Designs › The Suite: "This piece may be out of date." (client-portal.html, a design approved
   before the event's venue, date or name changed). It drew its own inline amber box, radius and
   .8rem — the census read it as a box on the delivered fixture once that event's date moved
   (2026-09-28). The inline style is gone; here it is a note on the text column with the amber
   "running late" rule, like the Messages notes above. */
body.d6 .bb-stale-note {
  margin: 12px 12px 0;
  padding: 2px 0 2px 10px;
  border: 0;
  border-left: 2px solid var(--ch8-amber);
  border-radius: 0;
  background: transparent;
  font-size: var(--d6-t-sec);
  line-height: var(--d6-lh);
  color: var(--d6-ink-2);
}
