/* ═════════════════════════════════════════════════════════════════════════════
   home.css — what only the couples page needs. Loaded non-blocking after
   /v22/v22.css, which carries everything the three pages share.

   ⚠ WHY THIS FILE EXISTS RATHER THAN MORE OF v22.css: gate 12 forbids unused CSS
   above 10KB. Home's seating diagram, dream-board wall and circle list are used
   on Home and nowhere else; shipping them to the supplier and Together pages
   would be dead weight measured against that ceiling. The split follows the
   existing convention exactly — /home/home.css, /for-suppliers/for-suppliers.css,
   /together/together.css already do this.

   Tokens are declared in home.html's critical <style>. See v22.css §1.
   ALL USER-FACING COPY IS DRAFT (Rule 5).
   ═════════════════════════════════════════════════════════════════════════════ */

/* ── the numbered journey stages ─────────────────────────────────────────────── */
.step{display:flex;flex-direction:column;gap:11px}
.step__rule{display:flex;align-items:center;gap:12px}
.step__n{width:40px;height:40px;border-radius:50%;border:1px solid var(--rule);display:flex;align-items:center;justify-content:center;font-family:var(--play);font-size:16px;color:var(--gold-2);flex:none}
.step__rule i{flex:1;height:1px;background:rgba(231,207,146,.28)}
.step__t{font-family:var(--play);font-size:clamp(17px,2vw,20px);color:var(--paper);line-height:1.25}
.step__d{font-size:13.5px;line-height:1.55;color:var(--on-green-ivory)}

/* ── the first-session checklist ─────────────────────────────────────────────── */
.check{display:flex;align-items:center;gap:10px}
.check i{width:17px;height:17px;border-radius:50%;background:var(--brand);color:var(--band);font-size:10px;display:flex;align-items:center;justify-content:center;flex:none;font-style:normal}
.check span{font-size:14px;color:var(--ink-panel)}

/* ── the three "knows your wedding" figures ──────────────────────────────────── */
.facts{background:var(--panel);border:1px solid var(--hair-gold-2);padding:clamp(14px,2vw,20px);display:flex;flex-direction:column}
.fact{display:flex;align-items:center;gap:12px;padding:13px 4px;border-bottom:1px solid rgba(231,207,146,.18)}
.fact:last-child{border-bottom:0}
.fact__k{flex:1;font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--on-green-ivory)}
.fact__v{font-family:var(--play);font-size:clamp(18px,2.2vw,22px);color:var(--paper)}

/* ── the six Drive tiles ─────────────────────────────────────────────────────── */
.file{background:var(--panel);border:1px solid rgba(231,207,146,.2);padding:14px 12px;display:flex;flex-direction:column;gap:7px}
.file__n{font-size:13px;color:var(--paper)}
.file__c{font-size:11.5px;color:var(--on-green-ivory)}

/* ── the admin tick list ─────────────────────────────────────────────────────── */
.ticks{display:flex;flex-direction:column;gap:9px}
.ticks li{list-style:none;font-size:14.5px;color:rgba(243,239,228,.86);display:flex;gap:.6em}
.ticks li::before{content:'✓';color:var(--gold-2);flex:none}

/* ── the "everything comes together" icon list ───────────────────────────────── */
.feats{display:flex;flex-direction:column;gap:15px}
.feat{display:flex;gap:12px;align-items:flex-start}
.feat svg{flex:none;margin-top:2px}
.feat__t{font-size:14.5px;color:var(--paper)}
.feat__d{font-size:13px;color:var(--on-green-ivory)}

/* ── the reply progress bar ──────────────────────────────────────────────────── */
.bars{display:flex;gap:2px}
.bars i{flex:1;height:24px;background:#E3DAC6}
.bars i[data-in]{background:var(--brand)}
.bars i[data-now]{background:var(--rule)}

/* ── the budget split bar ────────────────────────────────────────────────────── */
.split-bar{display:flex;height:9px;border:1px solid rgba(31,58,48,.14)}
.split-bar i:nth-child(1){background:var(--brand)}
.split-bar i:nth-child(2){background:var(--rule)}
.split-bar i:nth-child(3){background:var(--chrome)}

/* ── TABLE 4. The seating diagram. Eight seats on a circle, positioned by
      percentage so the whole thing scales with its box rather than needing a
      second set of numbers at each breakpoint. ─────────────────────────────── */
.table4{position:relative;width:100%;max-width:330px;margin:0 auto;aspect-ratio:1}
.table4 svg{position:absolute;inset:0;width:100%;height:100%}
.table4__mid{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);display:flex;flex-direction:column;align-items:center;gap:2px}
.table4__mid b{font-family:var(--play);font-size:19px;font-weight:400;color:var(--ink-head)}
.table4__mid span{font-size:9.5px;letter-spacing:.18em;text-transform:uppercase;color:var(--ink-quiet)}
.seat{position:absolute;transform:translate(-50%,-50%);display:flex;flex-direction:column;align-items:center;gap:3px;width:74px;text-align:center}
.seat i{width:30px;height:30px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-style:normal;background:var(--paper);border:1px solid rgba(31,58,48,.22);color:var(--ink-panel)}
.seat[data-new] i{background:var(--brand);border-color:var(--rule);color:var(--gold)}
.seat b{font-size:10.5px;line-height:1.2;color:var(--ink);font-weight:300}
.seat[data-new] b{color:var(--ink-head);font-weight:500}
.seat u{font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:var(--gold-text);text-decoration:none}
/* ⚠ at 390px the 74px seat labels around a ~300px circle collide. The names step
   down and the table shrinks rather than the labels overlapping — §5's "drawn app
   panels must stay legible", and nothing goes below the 12px floor because these
   are already sub-12px LABELS inside a diagram, not body copy. */
@media (max-width:560px){
  .table4{max-width:270px}
  .seat{width:62px}
  .seat b{font-size:10px}
}
.legend{display:flex;flex-wrap:wrap;gap:14px;justify-content:center;padding-top:2px}
.legend span{display:flex;align-items:center;gap:6px;font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-quiet)}
.legend i{flex:none}

/* ── the inner circle list ───────────────────────────────────────────────────── */
.circle{background:var(--paper);border:1px solid var(--hair-ink);padding:clamp(16px,2.2vw,22px);display:flex;flex-direction:column;gap:1px}
.circle__head{display:flex;justify-content:space-between;align-items:baseline;gap:10px;padding-bottom:12px}
.circle__head span:first-child{font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--gold-text)}
.circle__head span:last-child{font-size:11.5px;color:var(--ink-quiet)}
.who{display:flex;align-items:center;gap:11px;padding:11px 0;border-top:1px solid rgba(31,58,48,.1)}
.who i{width:28px;height:28px;border-radius:50%;background:rgba(169,136,78,.18);border:1px solid rgba(169,136,78,.5);color:#5C4A22;font-size:11px;font-style:normal;display:flex;align-items:center;justify-content:center;flex:none}
.who[data-them] i{background:var(--brand);color:var(--gold)}
.who b{flex:1;font-size:14px;font-weight:300;color:var(--ink-head)}
.who span{font-size:11.5px;color:var(--ink-quiet)}

/* ── the ask/answer pair ─────────────────────────────────────────────────────── */
.qa{display:flex;flex-direction:column;gap:12px}
.qa__q{background:var(--band);padding:14px 16px}
.qa__a{background:var(--brand);border-left:3px solid var(--rule);padding:14px 16px}
.qa__lbl{font-size:10px;letter-spacing:.2em;text-transform:uppercase;display:block;margin-bottom:5px}
.qa__q .qa__lbl{color:var(--ink-mute)}
/* ⚠⚠ SOLID GOLD, NOT THE .9 FLOOR, AND §5'S TABLE IS INCOMPLETE ON THIS.
   §5 gives "minimum alpha for small text on green: gold .9". Measured, that floor
   holds on PANEL green (#123F30 → 7.37:1) and FAILS on BRAND green (#1C6B4F →
   4.16:1) for this 10px label. §5's table only ever specified ivory-on-brand-
   green, never gold-on-brand-green, so there was no floor to follow here.
   Solid #F0DCA6 measures 4.74:1. Token added rather than a literal, so the next
   gold label on brand green inherits the right answer. */
.qa__a .qa__lbl{color:var(--on-brand-gold)}
.qa__q p{font-size:14px;line-height:1.5;color:var(--ink-panel)}
.qa__a p{font-size:14px;line-height:1.5;color:var(--paper)}

/* ── the drafted-reply card ──────────────────────────────────────────────────── */
.draft{background:var(--band);padding:clamp(16px,2.2vw,22px);display:flex;flex-direction:column;gap:10px;border-left:3px solid var(--rule)}
.draft__h{font-family:var(--play);font-size:15px;color:var(--ink-head)}
.draft__b{font-size:13.5px;line-height:1.65;color:var(--ink-panel)}

/* ── the start block ─────────────────────────────────────────────────────────── */
.start{max-width:900px;margin:0 auto;display:flex;flex-direction:column;align-items:center;gap:clamp(18px,3vh,30px);text-align:center}
.start__seal{width:clamp(46px,5.4vw,58px);height:auto;display:block;animation:floaty 6s ease-in-out infinite}

/* ═════════════════════════════════════════════════════════════════════════════
   THE DESK FRAME — the `ps` player only. `MOCKUP_the_suppliers_section_v2.html`,
   his approved redesign of "Information for your suppliers".

   ⚠⚠ SCOPED TO ONE PLAYER, DELIBERATELY. Every selector below is under
   `.player--desk`, a class carried by `[data-player="ps"]` and by nothing else on
   any of the three pages. The other four players on Home keep their `.win`
   row-lists byte-untouched, and `for-suppliers.html` / `together.html` never load
   this sheet at all — home.css is Home-only (see this file's header). That is why
   the desk lives here rather than in the shared v22.css: gate 12's unused-CSS
   ceiling, and his "this section only".

   ⚠ THE `dk` PREFIX IS NOT DECORATION. The mockup's own class names — .desk .rail
   .pane .tile .num .grid .send .stage — are generic enough that four of them would
   have collided with the site's namespace the day anything else claimed them, and
   `.tiles` ALREADY exists in v22.css:246. One prefix, one owner.

   ⚠ NO NEW TOKENS. Every value below is an existing `/v22` custom property from the
   three pages' inline :root, so `v22TokenParity.test.mjs` is unaffected. The two
   places the mockup and the site disagreed are recorded at the rule.

   ALL USER-FACING COPY IS DRAFT (Rule 5).
   ═════════════════════════════════════════════════════════════════════════════ */

/* The desk is a wider subject than a row-list window, so the caption yields to it.
   Follows v22.css:118-120's `.player--72/74/70` idiom exactly; .62/1.38 is the
   mockup's own ratio. */
.player--desk .frame{grid-template-columns:minmax(0,.62fr) minmax(0,1.38fr)}
/* ⚠⚠ THIS OVERRIDE IS LOAD-BEARING AND ITS ABSENCE IS A LIVE BUG ELSEWHERE.
   v22.css's ≤900px collapse is `.frame{grid-template-columns:minmax(0,1fr)}` —
   specificity 0,1,0. Any `.player--X .frame` rule is 0,2,0 and BEATS it, so a
   variant player does not collapse on a phone unless it says so itself. Measured
   on `main` at 390: `ps` (a bare `.player`) collapses to one 342px column; `fs`
   (`.player--72`) does NOT — it holds two columns of 114.688px + 203.922px, a
   114px caption column on a phone. That is a pre-existing defect on the money and
   appointments players and it is OUT OF SCOPE here (his "this section only"); it
   is written up in the close. This rule is why `ps` does not join them. */
@media (max-width:900px){.player--desk .frame{grid-template-columns:minmax(0,1fr)}}

/* ── the window ──────────────────────────────────────────────────────────────── */
/* ⚠ 12px RADIUS, AND IT IS A DEVIATION FROM THE SITE'S OWN IDIOM, TAKEN ON PURPOSE.
   All ten `.win` product shots on this site are SHARP (v22.css:173 declares no
   radius). The mockup draws the desk at 12px and he approved that picture, so the
   picture wins over the inference — but the desk will sit ~200px from a sharp
   `.win` on the same page and that is his eye at the walk. One line to reverse. */
.player--desk .dk{background:var(--paper);border:1px solid var(--hair-ink-2);border-radius:12px;overflow:hidden;box-shadow:0 40px 80px -36px rgba(0,0,0,.62);color:var(--ink);display:flex;flex-direction:column}
.player--desk .dk__bar{display:flex;align-items:center;gap:7px;padding:11px 14px;background:var(--chrome);border-bottom:1px solid rgba(31,58,48,.14)}
.player--desk .dk__bar i{width:9px;height:9px;border-radius:50%;background:rgba(31,58,48,.16);flex:none}
.player--desk .dk__bar i:first-child{background:var(--rule)}
/* 10px, and it is the same role as v22.css's `.win__cap` — the chrome caption
   exception named in that file's §5, not a content string. */
.player--desk .dk__cap{margin-left:6px;font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-mute)}
.player--desk .dk__live{margin-left:auto;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--pos);flex:none}

.player--desk .dk__body{display:grid;grid-template-columns:132px minmax(0,1fr)}
@media (min-width:901px){.player--desk .dk__body{min-height:340px}}

/* ── the emerald rail, with the live room lit ────────────────────────────────── */
.player--desk .dk__rail{background:var(--header);padding:16px 12px;display:flex;flex-direction:column;gap:2px;min-width:0}
.player--desk .dk__brand{font-family:var(--play);font-size:15px;color:var(--gold);letter-spacing:.02em;margin-bottom:12px;padding-left:8px}
.player--desk .dk__room{font-size:12.5px;color:var(--on-green-ivory);padding:8px 10px;border-radius:7px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.player--desk .dk__room--on{color:var(--paper);background:rgba(240,220,166,.12);box-shadow:inset 2px 0 0 var(--gold-2)}

/* ── the pane ────────────────────────────────────────────────────────────────── */
.player--desk .dk__pane{padding:clamp(16px,2vw,24px);display:flex;flex-direction:column;gap:14px;min-width:0}
.player--desk .dk__head{display:flex;justify-content:space-between;align-items:baseline;gap:12px;border-bottom:1px solid var(--hair-ink);padding-bottom:12px}
.player--desk .dk__title{font-family:var(--play);font-size:19px;color:var(--ink-head);line-height:1.2}
.player--desk .dk__meta{font-size:12px;color:var(--ink-quiet);flex:none}
.player--desk .dk__meta--pos{color:var(--pos)}

/* the figures. ⚠ `flex-wrap` not `auto-fit` — v22.css §6 forbids auto-fit on a
   fixed small row, and three figures at 390 must be allowed to wrap rather than
   crush to 60px each. */
.player--desk .dk__nums{display:flex;gap:18px;flex-wrap:wrap}
.player--desk .dk__num{display:flex;flex-direction:column;gap:3px}
.player--desk .dk__num b{font-family:var(--play);font-size:26px;font-weight:500;color:var(--ink-head);line-height:1}
.player--desk .dk__num span{font-size:11.5px;color:var(--ink-quiet);letter-spacing:.04em}
.player--desk .dk__num u{text-decoration:none;color:var(--pos)}

/* the "who gets what" tiles. ⚠ auto-fit IS correct here and only here: the row is
   2-or-3 tiles depending on the step, not a fixed card count, which is exactly the
   case v22.css §6's ban carves out. minmax floor 150px, so it is two-up at 768 and
   one-up at 390 rather than shrinking text. */
.player--desk .dk__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px}
/* ⚠ the tile ground is `--paper`, not the mockup's literal #fff. There is no pure
   white in this site's token table and the tile already separates on its hairline;
   minting an untokened white for a 3-unit difference would put a value outside the
   parity guard's reach. */
.player--desk .dk__tile{border:1px solid var(--hair-ink);border-radius:9px;padding:12px 13px;display:flex;flex-direction:column;gap:6px;background:var(--paper);min-width:0}
/* 10px uppercase micro-label — the same class of string as v22.css:181's
   `.win__flag`, which is already 10px inside a window frame. Content in this pane
   stays at 12px and above. */
.player--desk .dk__k{font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--gold-text)}
.player--desk .dk__n{font-family:var(--play);font-size:15px;color:var(--ink-head);line-height:1.25}
.player--desk .dk__d{font-size:12.5px;color:var(--ink-quiet);line-height:1.45}
.player--desk .dk__tile--done{border-color:rgba(28,107,79,.32)}
.player--desk .dk__tile--done .dk__k{color:var(--pos)}

/* ⚠ HER LEFT EDGE IS THE SAME 3px #C9A96A AS EVERY OTHER IRIS LINE ON THE SITE
   (v22.css §5's first non-negotiable) and she stays Cormorant italic. This is not
   a second Iris treatment — it is `.iris`/`.iris__say` re-expressed for a pane
   that owns its own padding rhythm. */
.player--desk .dk__iris{border-left:3px solid var(--rule);background:var(--band);padding:11px 13px;font-family:var(--corm);font-style:italic;font-size:15px;line-height:1.5;color:var(--pos)}

/* ⚠ A SPAN, NEVER A <button>. The mockup draws `<button class="send">`; the live
   section it replaces uses `<span class="pill pill--go">`. A real button inside a
   decorative product shot is a focus stop that does nothing when its frame is on
   — so the live section's own choice is kept and only the LOOK is taken. */
.player--desk .dk__send{align-self:flex-start;background:var(--brand);color:var(--gold);border-radius:8px;padding:11px 18px;font-size:12px;letter-spacing:.16em;text-transform:uppercase}

/* ── ≤900px: the rail lies down and shows only the live room ─────────────────── */
/* The mockup's own phone rule. The rail is a picture of an app's navigation, so
   five dead room names stacked above the content is five lines of noise on a
   phone; the lit one is the only part carrying meaning at that width. */
@media (max-width:900px){
  .player--desk .dk__body{grid-template-columns:minmax(0,1fr)}
  .player--desk .dk__rail{flex-direction:row;align-items:center;flex-wrap:wrap;gap:4px;padding:10px 12px}
  .player--desk .dk__brand{margin-bottom:0;padding-left:0}
  .player--desk .dk__room{font-size:12px;padding:6px 9px}
  .player--desk .dk__room:not(.dk__room--on){display:none}
  .player--desk .dk__room--on{margin-left:auto;box-shadow:none}
  .player--desk .dk__title{font-size:17px}
  .player--desk .dk__num b{font-size:23px}
}

/* ── the ps player's prev/next row ───────────────────────────────────────────── */
/* ⚠ THE ROW EXISTS SO THE ARROWS ARE NOT INSIDE THE TABLIST. `#ps-dots` carries
   `role="tablist"` and its children should be tabs; the arrows sit beside it.
   `.dots` keeps its own `margin-top` for the other nine players and loses it here,
   where `.pctrl` owns the spacing instead. */
.pctrl{display:flex;align-items:center;gap:10px;margin-top:clamp(10px,1.6vh,16px)}
.pctrl .dots{flex:1;min-width:0;margin-top:0}

/* 44px, per gate 8, and the whole button is the hit area — unlike `.dot`, whose
   44px box is transparent around a 3px rule, an arrow is a real drawn control and
   its ring IS the target. Colours are the night-green surface's: the ps section
   sits inside the dark section and nowhere else, so there is deliberately no
   `.sec--band`/`.sec--paper` variant here to go stale unused. */
.parrow{flex:none;width:44px;height:44px;padding:0;border:1px solid var(--hair-gold-2);border-radius:50%;background:transparent;color:var(--gold-2);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation;transition:border-color .2s var(--ease),background .2s var(--ease)}
.parrow:hover{border-color:var(--gold-2);background:rgba(240,220,166,.08)}
.parrow:focus-visible{outline:2px solid var(--gold-2);outline-offset:2px}

/* ⚠ THE ARROWS GO WHEREVER THE DOT RAIL GOES, AND FOR THE SAME REASON. With no JS
   there is no driver to press; under reduced motion every frame is already on
   screen in document order, so an arrow would drive the reader back into a
   single-frame view they were deliberately spared. v22.js's `move()` refuses in
   the second case too — this is the visible half of that guard, not a duplicate
   of it. */
html.no-js .parrow{display:none}
@media (prefers-reduced-motion:reduce){.parrow{display:none}}

/* ═════════════════════════════════════════════════════════════════════════════
   THE FEATURE CAROUSEL — one player, five features, and each frame still holds
   that feature's own four-step player. Home only; the sheet is Home-only.

   ⚠ NO NEW TOKENS. Everything below is an existing /v22 custom property, so
   v22TokenParity is untouched. The gold is `--gold-2`/`--gold-text`, the real
   champagne — never `--gold` from the app's palette, which is slate.
   ═════════════════════════════════════════════════════════════════════════════ */

/* ⚠⚠ ONE COLUMN AT EVERY WIDTH, AND THAT IS NOT THE DEFAULT. A `.frame` is a
   two-column caption|window grid; an OUTER frame holds a whole feature section —
   its heading, its own player and its own dot rail — so it is a single column.
   Specificity 0,2,0, which is deliberate: it must also beat v22.css:157's ≤900px
   rule, and it wants the same answer at both ends rather than a different one. */
/* ⚠⚠ `>` AND NOT A DESCENDANT SELECTOR, AND THIS COST A RENDER TO FIND. Written
   as `.player--feat .frame` it also matched the TWENTY INNER frames — every
   feature's caption|window pair collapsed to one column at 1280, cap stacked over
   window with a screen of dead green under it. The outer frames are direct
   children; the inner ones are grandchildren and must keep v22.css's own
   two-column layout. A nested carousel makes every unqualified `.frame` rule in
   this file a trap. */
/* ⚠ SUPERSEDED IN THIS FILE by the two-column rule in THE FEATURE DESKTOP block
   below — struck here rather than left to lose a cascade fight silently.
   ~~.player--feat > .frame{grid-template-columns:minmax(0,1fr);gap:0;align-items:stretch}~~ */

/* ⚠⚠ THE INNER DOT RAIL IS PINNED TO THE BOTTOM OF THE ROW, AND THIS IS A
   MEASUREMENT, NOT A PREFERENCE. All five features stack in one grid cell, so the
   row is as tall as the tallest — RSVPs, whose seat-diagram step is 564px at 1280
   against ~313px for the rest. Measured tails on the other four: 186-306px at
   every width. Top-aligned, that tail hangs under each short feature as a hole.
   Pinned, three things improve at once: the space falls between the window and the
   rail where it reads as breathing room; the dot rail lands in the SAME PLACE on
   all five, so the controls do not walk up and down the screen every 10 seconds;
   and nothing jumps on advance, which is the property v22.css:111 chose the shared
   grid cell for in the first place. */
/* ⚠ RETIRED, NOT LEFT POINTING AT NOTHING. This pinned each feature's INNER dot
   rail to the bottom of the row. The inner players are gone this arc — the frames
   hold text and one desktop — so the selector can no longer match, and a rule that
   cannot match is the "guard anchored on a gravestone" class in stylesheet form.
   The `.feat`/`height:100%` pair above it goes for the same reason: the outer frame
   is `align-items:center` again now that its content is a fixed-ratio screen.
   ~~.player--feat > .frame > .feat > .dots{margin-top:auto}~~ */
.feat{display:flex;flex-direction:column;gap:clamp(16px,2.6vh,26px);min-width:0}
.feat__head{gap:9px;max-width:56ch}

/* ⚠ THE OUTER CONTROLS SIT ABOVE THE FRAMES. Two dot rails are on screen at once
   — these five, and the current feature's own four below its window. Putting them
   at opposite ends of the feature is what stops them reading as one rail; the size
   difference below is the second signal, not the only one. */
.pctrl--feat{margin-top:0;margin-bottom:clamp(4px,1vh,10px)}

/* the outer five, drawn heavier than the inner four. Same language, more weight —
   the hit area stays the inherited 44px, which is gate 8's floor. */
.dot--feat::before{height:5px;width:12px}
.dot--feat[aria-selected="true"]::before{width:34px}

/* ⚠ ≤900px: the outer frame is already one column, so there is nothing to collapse
   — but the inner players still need v22.css's own ≤900px behaviour, and they get
   it, because they are ordinary `.player`s and this rule never reaches them. */
@media (max-width:900px){
  .pctrl--feat{margin-bottom:clamp(2px,.6vh,6px)}
  .feat{gap:clamp(14px,2.2vh,20px)}
}

/* ⚠⚠ INSIDE THE CAROUSEL THE INNER STEPS TOP-ALIGN, AND OUTSIDE IT THEY DO NOT.
   v22.css:116 gives `.frame` `align-items:center`, which is right for a standalone
   player: the row is the tallest step and a short one floats pleasantly in it.
   Nested, it is wrong twice over — the row is now the tallest step of the TALLEST
   FEATURE, and the feature's heading sits directly above, so a centred short step
   opens a ~300px band of empty green between the standfirst and the step it
   belongs to. Measured at 1280 on RSVPs, whose seat-diagram step is nearly twice
   its first. Top-aligned, the emptiness all falls below the window, where it reads
   as breathing room instead of a hole. ⚠ `>` again — this must not reach further. */
.player--feat > .frame > .feat .frame{align-items:start}

/* ── the carousel's control line ─────────────────────────────────────────────
   His ask: say once, at the top, that Iris never acts without the couple's word.
   ⚠ HER REGISTER, NOT A BANNER. Cormorant italic in champagne is the voice this
   site gives Iris everywhere else (v22.css §5's `.iris__say`), and the gold left
   edge is the same 3px --rule that marks anything she authored. It is deliberately
   NOT an .eyebrow: an eyebrow labels a section, and this is a promise. */
.feat__law{border-left:3px solid var(--rule);padding:2px 0 2px 14px;margin:0 0 clamp(12px,2vh,20px);
  font-family:var(--corm);font-style:italic;font-size:clamp(15px,1.9vh,18px);line-height:1.5;
  color:var(--gold-2);max-width:62ch}
@media (max-width:900px){.feat__law{font-size:15px;padding-left:12px}}

/* ═════════════════════════════════════════════════════════════════════════════
   THE FEATURE DESKTOP — clean text left, a 1280x800 desktop right.

   ⚠⚠ POSTER ONLY, AND THAT IS HIS RULING, NOT A SHORTCUT. The brief asks for the
   LIVE app in an iframe on `landing.html`'s proven mechanism. He ruled mid-arc
   that "the ido tour is obselete right now", which strikes the live half — and
   three measurements taken before that ruling say the same thing anyway:
     · `/tour?embed=1` lands on the GUIDED-TOUR CHAPTER MENU, not the app.
     · It renders IDENTICALLY at 390 and 1280 (449 nodes, same classes) — the desk
       face is gated on `VITE_WARROOM_ENABLED`, which no repo env file sets, so a
       1280 iframe is the PHONE face on a laptop canvas.
     · `ido-tour-go` drives the narrated Showcase chapters, never the app's rooms.
   An iframe with nothing to point at is dead machinery on the page that sells the
   product (Rule 8). So the poster IS the deliverable.

   ⚠ THE LIVE SWAP STAYS ONE ADDITION AWAY. `.desk-screen` keeps landing.html's
   exact wrapper and 1280/800 ratio, and the poster sits above where the iframe
   would go. Adding `<iframe class="desk-frame">` beneath it and the `ido-tour-ready`
   handler is additive — nothing here has to be rebuilt.
   ═════════════════════════════════════════════════════════════════════════════ */

/* ⚠ THE FRAME IS TWO COLUMNS NOW, ~40/60, AND IT OVERRIDES THE ONE-COLUMN RULE
   THE CAROUSEL ARC PUT HERE. Same `>` discipline: the OUTER frames only. */
/* ⚠⚠ NO `subgrid`, AND THE FIRST VERSION USED IT AND RENDERED A RUIN. The frame
   held the two tracks and `.feat--desk` inherited them with
   `grid-template-columns:subgrid; grid-column:1/-1`. It did not resolve: the text
   column collapsed to about one word wide — "invitation, / all / the / way / there"
   down the left edge — and the desktop never painted at all. Measured at 1280 in
   Chromium, not reasoned about.
   The frame goes back to ONE column and the split lives on the element that owns
   it. One grid, no inheritance, nothing to resolve. */
.player--feat > .frame{grid-template-columns:minmax(0,1fr);gap:0;align-items:center}
/* ⚠⚠ `width:100%` IS LOAD-BEARING AND ITS ABSENCE COST A RENDER. As a grid item of
   `.frame` this should have stretched to the 1152px track and did not — measured,
   `justify-self` computed `auto`, `justify-items` computed `normal`, and the item
   still shrink-wrapped to 522px. With an indefinite container width the `.4fr/.6fr`
   tracks resolve against max-content instead of the track, so the desktop rendered
   at 290px inside a 668px column and the heading wrapped three lines early.
   The fix is not to argue with the cascade: state the width. */
.feat--desk{display:grid;width:100%;grid-template-columns:minmax(0,.4fr) minmax(0,.6fr);gap:clamp(20px,3vw,44px);align-items:center;min-width:0}
.feat__side{display:flex;flex-direction:column;gap:11px;min-width:0;max-width:46ch}
.feat__stage{min-width:0}

/* the four step titles, kept as the story. ⚠ THE "Step N of 4 ·" EYEBROWS AND THE
   LONGER NOTES ARE THE PART HE ASKED TO DROP; the titles are the story itself and
   a heading with no story under it is the "empty and text-heavy" he complained of
   wearing different clothes. Numbered by CSS counter, so no digit is typed. */
.feat__steps{list-style:none;margin:6px 0 0;padding:0;counter-reset:fstep;display:flex;flex-direction:column;gap:9px}
.feat__steps li{counter-increment:fstep;position:relative;padding-left:30px;font-size:clamp(13.5px,1.7vh,15.5px);line-height:1.45;color:var(--on-green-ivory)}
.feat__steps li::before{content:counter(fstep);position:absolute;left:0;top:-1px;width:20px;height:20px;border:1px solid var(--hair-gold-2);border-radius:50%;display:flex;align-items:center;justify-content:center;font-family:var(--play);font-size:11px;color:var(--gold-2)}

/* ── the desktop shell — landing.html:255-257, renamed to this site's convention ── */
.desk-screen{position:relative;width:100%;aspect-ratio:1280/800;overflow:hidden;border-radius:6px;background:var(--paper);
  border:1px solid var(--hair-ink-2);box-shadow:0 40px 80px -34px rgba(0,0,0,.62)}
.desk-poster{position:absolute;inset:0;background:var(--paper);color:var(--ink);overflow:hidden;
  /* ⚠⚠ EVERY DIMENSION INSIDE THE POSTER IS `em`, AND THIS ONE LINE IS THE SCALE.
     landing.html scales its desk with a JS `fit()` + ResizeObserver. This needs no
     JS at all: the screen is a container, and 1em is pinned to 1/128th of its
     width — so 1em == 10 logical px on a 1280 canvas, at ANY rendered width. The
     poster is drawn once at laptop proportions and is exact from 320px to 4K.
     ⚠ THE FALLBACK IS THE LINE ABOVE THE @supports, NOT AN ABSENCE: a browser with
     no container units gets a fixed 10px and a poster that is merely small rather
     than broken. */
  font-size:10px}
@supports (container-type:inline-size){
  .desk-screen{container-type:inline-size}
  .desk-poster{font-size:calc(100cqw / 128)}
}

/* the poster's chrome bar */
.pd{position:absolute;inset:0;display:flex;flex-direction:column;font-family:var(--sans)}
.pd__bar{display:flex;align-items:center;gap:.7em;padding:1.1em 1.4em;background:var(--chrome);border-bottom:.1em solid rgba(31,58,48,.14);flex:none}
.pd__bar i{width:.9em;height:.9em;border-radius:50%;background:rgba(31,58,48,.16);flex:none}
.pd__bar i:first-child{background:var(--rule)}
.pd__cap{margin-left:.6em;font-size:1.05em;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-mute)}
.pd__live{margin-left:auto;font-size:1em;letter-spacing:.14em;text-transform:uppercase;color:var(--pos)}
.pd__body{display:grid;grid-template-columns:15em minmax(0,1fr);flex:1;min-height:0}

/* the emerald rail, with the live room lit — the same idiom as the ps desk frame */
.pd__rail{background:var(--header);padding:1.6em 1.2em;display:flex;flex-direction:column;gap:.2em;min-width:0}
.pd__brand{font-family:var(--play);font-size:1.6em;color:var(--gold);margin-bottom:1.1em;padding-left:.5em}
.pd__room{font-size:1.25em;color:var(--on-green-ivory);padding:.65em .8em;border-radius:.55em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pd__room--on{color:var(--paper);background:rgba(240,220,166,.12);box-shadow:inset .2em 0 0 var(--gold-2)}

/* the pane */
.pd__pane{padding:1.8em 2em;display:flex;flex-direction:column;gap:1.3em;min-width:0;overflow:hidden}
/* ⚠⚠ THE LAST LINE IS PINNED TO THE FOOT OF THE SCREEN, AND IT IS A MEASUREMENT.
   The canvas is a fixed 1280x800 and the five panes filled 49-63% of it — half a
   screen of empty ivory under every room, which reads as an app that has not
   finished loading rather than a laptop showing a room. A real app fills its
   viewport, and a real room in THIS app puts her line (or its one control) at the
   foot. Pinning the last child gathers the slack into one deliberate gap instead
   of leaving it as a tail. Measured after: 100% on all five.
   ⚠ It is a no-op in the ≤900px reflow, where the pane is auto-height and there is
   no slack to gather — so no media-query exception is needed and none is written. */
.pd__pane > :last-child{margin-top:auto}
.pd__head{display:flex;justify-content:space-between;align-items:baseline;gap:1.2em;border-bottom:.1em solid var(--hair-ink);padding-bottom:1.1em}
.pd__title{font-family:var(--play);font-size:2em;color:var(--ink-head);line-height:1.2}
.pd__meta{font-size:1.2em;color:var(--ink-quiet);flex:none}
.pd__meta--pos{color:var(--pos)}
.pd__nums{display:flex;gap:2.4em;flex-wrap:wrap}
.pd__num{display:flex;flex-direction:column;gap:.3em}
.pd__num b{font-family:var(--play);font-size:3em;font-weight:500;color:var(--ink-head);line-height:1}
.pd__num span{font-size:1.1em;letter-spacing:.04em;color:var(--ink-quiet)}
.pd__num u{text-decoration:none;color:var(--pos)}
.pd__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(16em,1fr));gap:1em}
.pd__tile{border:.1em solid var(--hair-ink);border-radius:.9em;padding:1.1em 1.2em;display:flex;flex-direction:column;gap:.5em;background:var(--paper);min-width:0}
.pd__k{font-size:1em;letter-spacing:.14em;text-transform:uppercase;color:var(--gold-text)}
.pd__n{font-family:var(--play);font-size:1.55em;color:var(--ink-head);line-height:1.25}
.pd__d{font-size:1.2em;color:var(--ink-quiet);line-height:1.45}
.pd__tile--done{border-color:rgba(28,107,79,.32)}
.pd__tile--done .pd__k{color:var(--pos)}
/* a drawn row list, for the money and diary screens */
.pd__rows{display:flex;flex-direction:column}
.pd__row{display:flex;justify-content:space-between;gap:1em;padding:.85em 0;border-bottom:.1em dotted rgba(31,58,48,.16)}
.pd__row:last-child{border-bottom:0}
.pd__row b{font-weight:400;font-size:1.3em;color:var(--ink)}
.pd__row span{font-size:1.25em;color:var(--ink-quiet)}
.pd__row span.amber{color:var(--amber)}
.pd__row span.pos{color:var(--pos)}
/* her line — the same 3px gold edge and Cormorant italic as everywhere else */
.pd__iris{border-left:.3em solid var(--rule);background:var(--band);padding:1.1em 1.3em;font-family:var(--corm);font-style:italic;font-size:1.55em;line-height:1.5;color:var(--pos)}
.pd__send{align-self:flex-start;background:var(--brand);color:var(--gold);border-radius:.8em;padding:1em 1.7em;font-size:1.15em;letter-spacing:.16em;text-transform:uppercase}
/* a mail card, for the appointments screen */
.pd__mail{border:.1em solid var(--hair-ink);border-radius:.9em;overflow:hidden}
.pd__mailbar{display:flex;gap:1em;padding:.9em 1.2em;background:var(--band);font-size:1.1em;color:var(--ink-mute)}
.pd__mailbody{padding:1.1em 1.2em;font-size:1.3em;line-height:1.5;color:var(--ink)}
/* the seating plate, for RSVPs */
.pd__seats{display:flex;gap:.8em;flex-wrap:wrap}
.pd__seat{border:.1em solid var(--hair-ink);border-radius:.7em;padding:.7em 1em;font-size:1.2em;color:var(--ink);background:var(--paper)}
.pd__seat b{font-weight:400;color:var(--ink-head)}
.pd__seat u{text-decoration:none;display:block;font-size:.85em;color:var(--gold-text);letter-spacing:.08em;text-transform:uppercase}

/* ── ≤900px: the desktop goes under the text, still a desktop, never crushed ── */
/* ── ≤900px: THE POSTER REFLOWS, IT DOES NOT SCALE. ─────────────────────────────
   ⚠⚠ AND THE FIRST VERSION SCALED IT, WHICH WAS MEASURABLY WRONG. Keeping the
   1280x800 canvas and letting the container unit shrink it gives `1em = 2.66px`
   at a 342px column — body text lands at about 3.2px. That is not "a laptop seen
   small", it is mush, and no amount of calling it decorative fixes a screen a
   phone cannot read.
   So below 900px the canvas is abandoned and the poster lays out for the width it
   actually has: the rail lies down and shows only the live room, the pane fills,
   and every size goes back to real px. It is the same pattern the `ps` desk frame
   already uses at this width and that he has walked — and it is the more honest
   picture besides, because the app genuinely does reflow on a phone. */
@media (max-width:900px){
  .feat--desk{grid-template-columns:minmax(0,1fr);gap:clamp(16px,2.6vh,24px)}
  .feat__side{max-width:none}
  .feat__steps li{font-size:13.5px}

  .desk-screen{aspect-ratio:auto;container-type:normal}
  .desk-poster{position:static;font-size:10px}
  .pd{position:static}
  .pd__bar{padding:10px 12px;gap:7px}
  .pd__bar i{width:9px;height:9px}
  .pd__cap{font-size:10px;margin-left:6px}
  .pd__live{font-size:10px}
  .pd__body{grid-template-columns:minmax(0,1fr)}
  .pd__rail{flex-direction:row;align-items:center;flex-wrap:wrap;gap:4px;padding:10px 12px}
  .pd__brand{font-size:15px;margin-bottom:0;padding-left:0}
  .pd__room{font-size:12px;padding:6px 9px;border-radius:7px}
  .pd__room:not(.pd__room--on){display:none}
  .pd__room--on{margin-left:auto;box-shadow:none}
  .pd__pane{padding:16px;gap:13px}
  .pd__head{gap:10px;padding-bottom:11px}
  .pd__title{font-size:17px}
  .pd__meta{font-size:12px}
  .pd__nums{gap:16px}
  .pd__num b{font-size:23px}
  .pd__num span{font-size:11.5px}
  .pd__grid{grid-template-columns:minmax(0,1fr);gap:10px}
  .pd__tile{padding:12px 13px;gap:6px;border-radius:9px}
  .pd__k{font-size:10px}
  .pd__n{font-size:15px}
  .pd__d{font-size:12.5px}
  .pd__row{padding:9px 0}
  .pd__row b{font-size:13.5px}
  .pd__row span{font-size:13px}
  .pd__iris{font-size:15px;padding:11px 13px;border-left-width:3px}
  .pd__send{font-size:12px;padding:11px 18px;border-radius:8px}
  .pd__seat{font-size:12.5px;padding:7px 10px;border-radius:7px}
  .pd__mailbar{font-size:11px;padding:9px 12px}
  .pd__mailbody{font-size:13.5px;padding:11px 12px}
}
