/* ═══════════════════════════════════════════════════════════════════════
   VESPER DRAFT — the drafting-table sheet, the door of Direction D
   ═══════════════════════════════════════════════════════════════════════
   Owner ruling 2026-09-04: the unauthenticated door takes the drafting-table
   mock as mocked. This file is that sheet.

   ⚠️ 2026-09-05 — THE SHEET IS NO LONGER ONLY THE DOOR. It is loaded by
   SEVEN pages: the three doors (landing · login · apply) and the four public
   chrome pages behind them (pricing · settings · privacy · support). The
   reason pricing came first is in the middleware: `_middleware.js` 302s every
   signed-in user without an allowlisted tier to /pricing.html, so the screen
   immediately after a successful sign-in through the door was the OLD skin —
   for exactly the users the door converts. A door that opens onto a different
   building is not a door. What is still true: everything past THESE seven is
   the product, and nothing here reaches it.

   WHY A SHARED FILE AND NOT SEVEN INLINE COPIES (deliberate deviation, and
   the reason is in the repo's own scars): the three public doors already
   carry three hand-maintained copies of the same nav, footer and mode
   toggle, which is the shape `feedback_clone_the_file_clone_the_bugs`
   names. The sheet is ~11 KB of geometry; three copies of it would drift
   the first time one page is touched. One home, seven consumers. The
   pages keep only what is theirs (the Clerk mount's chrome on login,
   pricing and settings; the form states on apply).

   ⚠️ The same scar re-opened on 2026-09-05: `.dt-claim h1` was a
   byte-identical page-local block on login.html AND apply.html. Four more
   pages would have made it six copies of one heading, so it moved here,
   beside `.dt-claim h2` it was already a copy of.

   PALETTE. The door is pinned to the schematic palette regardless of the
   stored `vesper-palette` flag — it is the front of the new direction, and
   a visitor who has never opted in must still see it. Each page writes
   data-palette="schematic" on <html> right after vesper-boot.js. So this
   file may reason about ONE palette, unlike a product page (AGENTS.md
   §4b), and it still speaks only in TOKENS — no literal colour, no radius,
   no font family of its own.

   MODE. day/dusk both come from the SYSTEM (prefers-color-scheme) until the
   visitor picks one in the corner toggle; the toggle writes the boot's own
   vocabulary (`vesper-mode` ∈ {day,dusk} + data-mode) so the choice carries
   into the app.

   ⚠️ ONE DELIBERATE BEND OF DIRECTION D — search this file for box-shadow.
   The schematic skin bans shadow outright:
       html[data-palette="schematic"] * { box-shadow:none !important }
   The door bends it in exactly one place: the FOCUSED field cell breathes a
   soft accent glow. Because the ban is !important, the bend has to be a
   higher-specificity !important of its own (the skin's radius-0 blanket
   documents the same escape). The breathe is animated on a registered
   custom property, not on box-shadow, so the static !important declaration
   keeps winning while the radius grows 13px to 30px over 10s.
   It exists on the door and nowhere past it.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── The sheet ───────────────────────────────────────────────────────── */
body.dt{
  margin:0;
  /* the positioning context for `.dt-sheet` — see the note there. Without it
     an absolutely-positioned frame would resolve against the initial
     containing block, which is viewport-SIZED, and behave like the fixed one
     it replaces. */
  position:relative;
  min-height:100vh;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--body);
  font-weight:300;
  font-size:13px;
  line-height:1.35;
  -webkit-font-smoothing:antialiased;
}
body.dt *{box-sizing:border-box}
body.dt a{color:inherit;text-decoration:none}

/* the drawing sheet's own border, inset from the plate — decoration only */
/* the frame is capped with the plate, not with the viewport: at 2400 a
   border pinned to the screen edges is a window, not a sheet. 1532 = the
   plate's 1560 less the 14px the drawing sits inside the frame on each
   side, so the gap between rule and content is the board's own 20px at
   every width. */
.dt-sheet{
  /* ⚠️ ABSOLUTE, NOT FIXED, AND THE OWNER FOUND OUT WHY (2026-09-05).
     "the outer border line seems to be static on scroll, like stuff inside of
     it is scrolling on top of it." It was `position:fixed`, so the frame was
     pinned to the WINDOW while the page moved underneath it — the drawing
     sliding under its own border.
     Nobody saw it for a week because every page that carried this sheet was
     exactly 844 tall and did not scroll: on a page that fits the window, a
     frame fixed to the viewport and a frame drawn on the paper are the same
     picture. settings.html stopped fitting the moment the account panel's
     height became its content's (round 4), and the two came apart.
     Absolute inside `body.dt`'s relative context spans the DOCUMENT, so the
     frame is drawn ON the paper and travels with it. */
  position:absolute;
  top:14px;
  bottom:14px;
  left:14px;
  right:14px;
  max-width:1532px;
  margin:0 auto;
  border:1px solid var(--line);
  opacity:.5;
  pointer-events:none;
  z-index:0;
}

/* the plate. Left-anchored is the APP frame's rule (a rail at 240 + a 30px
   gutter); a door has no rail, so the sheet is centred and capped — at 2400
   a full-bleed drawing has no edge to read against. */
.dt-page{
  position:relative;
  z-index:1;
  max-width:1560px;
  margin:0 auto;
  padding:30px 34px 26px;
  display:flex;
  flex-direction:column;
  gap:32px;
  min-height:100vh;
}

/* ── The top rule: what this sheet is, and the mode toggle ───────────── */
.dt-top{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:20px;
  flex-wrap:wrap;
  border-bottom:1px solid var(--line);
  padding-bottom:9px;
}
.dt-top-right{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-left:auto}
.dt-follow{
  font-family:var(--mono);
  font-size:9.5px;
  letter-spacing:.06em;
  color:var(--muted);
}

/* the day/dusk toggle, drawn the way the sheet draws everything else */
.dt-tog{display:flex;border:1px solid var(--line)}
.dt-tog button{
  font-family:var(--mono);
  font-size:9px;
  letter-spacing:.16em;
  text-transform:uppercase;
  /* the unselected half of a control, so ink-soft rather than --muted for the
     same reason .dt-label carries it */
  color:var(--ink-soft);
  background:transparent;
  border:0;
  border-right:1px solid var(--line);
  padding:5px 10px;
  cursor:pointer;
  transition:background .15s,color .15s;
}
.dt-tog button:last-child{border-right:0}
.dt-tog button.on{background:var(--ink);color:var(--paper)}

/* ── The title block ─────────────────────────────────────────────────── */
/* VESPER is the sheet's TITLE, the rose beside it the drafter's mark, a
   dimension line measures the word, and the cells carry sheet · date ·
   title · scale · mode. */
.dt-block{border:1px solid var(--line)}
.dt-block-main{
  display:flex;
  align-items:flex-start;
  gap:26px;
  padding:24px 26px 22px;
}
.dt-markwrap{flex:0 0 auto;width:var(--dt-mark, 180px)}
.dt-wordwrap{min-width:0;flex:1 1 auto;padding-top:8px}

.dt-wm{
  font-family:var(--display);
  font-weight:300;
  font-size:var(--dt-wm, 132px);
  letter-spacing:-.035em;
  line-height:.78;
  margin:0;
  color:var(--accent);
  /* the word is the one place the accent carries the brand (owner ruling
     2026-09-03: accent on DATA plus one masthead word) */
}
.dt-strap{letter-spacing:.28em;margin-top:14px}

/* ── The signed-in sheet: body.dt.dt-in ──────────────────────────────────
   The doors are for a visitor who has nowhere to go yet, so their masthead is
   a label and their footer offers Apply / Log in / Privacy. The two sheets
   BEHIND the door — settings and pricing — are read by someone with an
   account, and the owner's prod drive found the obvious gap: "not seeing a
   button to go back to home from account page." These sheets carry no rail,
   so the masthead has to be the way back.
   ⚠️ Only under `.dt-in`. Making the masthead a link on a door would offer a
   signed-out visitor a trip to a gated page that bounces them straight back. */
body.dt-in .dt-home{
  /* inline-block, not block: as a block the anchor filled the whole wordmark
     column (measured 1,101px at 1440), so a click on empty sheet to the right
     of VESPER navigated. It hugs the word instead. */
  display:inline-block;
  color:inherit;
  text-decoration:none;
  /* the wordmark is 46px at its smallest, so the hit area clears the 27px
     floor on its own; the min-height states the floor rather than trusting a
     font size to keep it. */
  min-height:27px;
}
body.dt-in .dt-home:hover .dt-wm{ opacity:.82; }
body.dt-in .dt-home:focus-visible{ outline:1px solid var(--accent); outline-offset:4px; }

/* the dimension line: end ticks, a rule, and the MEASURED width above it.
   The width is measured off the rendered wordmark at runtime (fonts settle
   first) — a fluid sheet cannot carry a number pasted from a 1200px board. */
.dt-dims{margin-top:22px;display:flex;flex-direction:column;gap:12px;align-items:flex-start}
.dt-dim{position:relative;height:15px;opacity:.85;max-width:100%}
.dt-dim::before,
.dt-dim::after{
  content:'';
  position:absolute;
  top:0;
  height:100%;
  border-left:1px solid var(--ink);
}
.dt-dim::before{left:0}
.dt-dim::after{right:0}
.dt-dim i{
  position:absolute;
  left:0;right:0;top:50%;
  border-top:1px solid var(--ink);
}
.dt-dim .dt-cap{
  position:absolute;
  left:50%;
  bottom:calc(100% - 1px);
  transform:translateX(-50%);
  white-space:nowrap;
  font-family:var(--mono);
  font-size:9px;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--ink);
  background:var(--paper);
  padding:0 6px;
}
.dt-dim.small .dt-cap{font-size:8px}
/* the mark's own dimension: the r30 construction circle is 60 of the 96
   viewBox units the mark is drawn in, so it measures 62.5% of the box. */
.dt-dim-mark{
  width:calc(var(--dt-mark, 180px) * .625);
  margin:14px auto 0;
}

/* the cells along the foot of the block */
.dt-cells{
  display:grid;
  grid-template-columns:repeat(5,1fr);
  border-top:1px solid var(--line);
}
.dt-cells>div{padding:9px 12px;border-right:1px solid var(--line);min-width:0}
.dt-cells>div:last-child{border-right:0}
.dt-label{
  font-family:var(--mono);
  font-size:9.5px;
  letter-spacing:.16em;
  text-transform:uppercase;
  /* ink-soft, not the board's --muted: measured 4.23:1 on day paper, which
     the skin itself records as 0.27 UNDER the AA floor (an owner ruling that
     governs the shipped `.eyebrow.quiet`, not a new label). The door's
     yardstick asks for >=4.5:1 in both modes, and ink-soft is what the house
     `.eyebrow` base already uses — 9.70:1 day / 9.53:1 dusk. */
  color:var(--ink-soft);
  line-height:1.5;
}
.dt-val{
  font-family:var(--mono);
  font-size:11.5px;
  color:var(--ink);
  margin-top:3px;
  overflow:hidden;
  text-overflow:ellipsis;
}

/* ── The body: the claim on the left, the doors on the right ─────────── */
.dt-body{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:34px;
  align-items:start;
  /* align-content too: the body is the plate's flex:1 child, so grid ROWS
     would stretch to fill a tall viewport and open a gap between a stacked
     claim and its door (measured at 390: ~110px of nothing). The slack
     belongs at the foot of the sheet, not between two blocks. */
  align-content:start;
  flex:1 1 auto;
}
.dt-claim{max-width:540px}
/* h1 and h2 are the SAME drawing — which one a sheet uses is a document
   question (one <h1> per page), not a design one. login.html and apply.html
   each carried a byte-identical page-local copy of this block; the four
   public-chrome sheets would have made it six. One home. */
.dt-claim h1,
.dt-claim h2{
  font-family:var(--display);
  font-weight:300;
  font-size:var(--dt-h2, 40px);
  letter-spacing:-.025em;
  line-height:1.04;
  margin:14px 0 0;
  color:var(--ink);
}
/* a sheet whose body is one column: a document (privacy, support) or a
   mounted panel too wide to sit beside its claim (settings). The claim runs
   the full measure above the body rather than beside it. */
.dt-body.solo{grid-template-columns:minmax(0,1fr)}
.dt-body.solo .dt-claim{max-width:none}
.dt-lede{
  font-size:12.5px;
  line-height:1.6;
  color:var(--ink-soft);
  margin-top:11px;
  max-width:470px;
}
/* the plotter's own status line under the claim */
.dt-read{
  font-family:var(--mono);
  font-size:10px;
  letter-spacing:.08em;
  color:var(--ink-soft);
  display:grid;
  grid-template-columns:auto minmax(0,1fr);
  gap:2px 12px;
  margin-top:26px;
}
.dt-read i{
  font-style:normal;
  color:var(--muted);
  text-transform:uppercase;
  font-size:8.5px;
  letter-spacing:.16em;
  align-self:center;
}

/* ── The doors: two equal cells ──────────────────────────────────────── */
.dt-doors{display:grid;grid-template-columns:1fr 1fr;align-items:stretch}
.dt-doors.one{grid-template-columns:1fr;max-width:420px}
/* a door that HOSTS a mounted control rather than a form of its own: Clerk's
   pricing table and its account panel are both wider than a 420px door.
   ⚠️ 880 WAS A GUESS, AND THE OWNER'S 2026-09-05 DRIVE MEASURED IT WRONG.
   The account panel is a two-pane widget whose panes carry their own
   min-content widths; at ~2000 the painted content ran 455px PAST the door's
   right hairline, and with a wider pane it pushed the DOCUMENT to 2,383px
   against a 1,989px window — which is what made the sheet read left-anchored
   with empty paper to the right and clipped the mode toggle. A hosting door
   takes the sheet's own measure instead of a number: the body it sits in is
   already the plate minus its padding, so `100%` is exactly "as wide as this
   sheet allows" at every width, and never wider. */
.dt-doors.wide{max-width:100%}
.dt-door{
  border:1px solid var(--line);
  padding:14px;
  display:flex;
  flex-direction:column;
  min-width:0;
}
.dt-door+.dt-door{margin-left:-1px}
/* a door whose body is a <form> (the application) — the form is the flex
   child that has to grow, or the go control stops sitting at the foot. */
.dt-door>form{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}
/* ⚠️ …but a SINGLE-column stack shares its rule VERTICALLY. The -1px above
   is the two-across case; in a one-column grid it shifted the second door a
   pixel left instead of closing the seam. It never bit because `.one` had
   only ever held one door; settings.html stacks three. */
.dt-doors.one .dt-door+.dt-door{margin-left:0;margin-top:-1px}
.dt-dhd{
  display:flex;
  justify-content:space-between;
  align-items:baseline;
  gap:10px;
  border-bottom:1px solid var(--line);
  padding-bottom:7px;
  margin-bottom:11px;
}
.dt-dtitle{
  font-family:var(--display);
  font-weight:300;
  font-size:23px;
  letter-spacing:-.02em;
  line-height:1;
  margin:6px 0 12px;
  color:var(--ink);
}
.dt-note{
  font-size:11px;
  line-height:1.55;
  color:var(--ink-soft);
  margin-top:9px;
}
.dt-note b{font-weight:400;color:var(--ink)}
/* the secondary CTA under the claim (prod's own sentence) — the doors carry
   the ACTION, this carries the words */
.dt-cta{margin-top:14px}
/* ⚠️ THE `body.dt` PREFIX IS LOAD-BEARING, NOT DECORATION.
   `body.dt a{color:inherit}` at the top of this file is (0,1,2) — two element
   selectors and a class — so a bare `.dt-note a` is (0,1,1) and LOSES to it.
   Measured on the live doors 2026-09-05: apply.html's "Return to
   vesperfl.com →" and login.html's "Apply for access →" both painted
   `--ink-soft`, indistinguishable from the sentence around them, since the
   day this file shipped. Every link colour on the sheet carries the prefix. */
body.dt .dt-note a,
body.dt .dt-doc a{color:var(--accent)}
.dt-note a:hover,
.dt-doc a:hover{text-decoration:underline}

/* ── The document (privacy · support) ────────────────────────────────── */
/* Two of the seven sheets are prose, not a control: a policy and a support
   page, both of which an App Store reviewer opens signed out. They take the
   same sheet and set the text on it — one measure, hairline rules between
   sections, the table drawn the way the title block's cells are drawn.
   Nothing here is uppercase-mono except through the house `.eyebrow` class,
   so the section rules stay rule-6 clean. */
.dt-doc{max-width:720px;margin-top:22px}
.dt-doc h2{
  font-family:var(--display);
  font-weight:300;
  font-size:21px;
  letter-spacing:-.02em;
  line-height:1.1;
  color:var(--ink);
  margin:26px 0 9px;
  border-top:1px solid var(--line);
  padding-top:15px;
}
.dt-doc h2:first-of-type{margin-top:0;border-top:0;padding-top:0}
.dt-doc p{font-size:12.5px;line-height:1.65;color:var(--ink-soft);margin:0 0 11px}
.dt-doc p.lede{font-size:14px;line-height:1.6;color:var(--ink);margin-bottom:22px}
.dt-doc ul{margin:0 0 14px;padding-left:16px;font-size:12.5px;line-height:1.65;color:var(--ink-soft)}
.dt-doc li{margin-bottom:5px}
.dt-doc strong{font-weight:400;color:var(--ink)}
.dt-doc table{border-collapse:collapse;width:100%;margin:2px 0 16px;font-size:12.5px}
/* an element selector, so rule 6 reads it as a heading rather than a kicker
   — and it IS the table's caption row, the same drawing as `.dt-label`. */
.dt-doc th{
  text-align:left;
  font-family:var(--mono);
  font-size:9.5px;
  letter-spacing:.16em;
  text-transform:uppercase;
  font-weight:400;
  color:var(--ink-soft);
  padding:0 14px 7px 0;
  border-bottom:1px solid var(--line);
}
.dt-doc td{
  padding:9px 14px 9px 0;
  border-bottom:1px solid var(--line);
  color:var(--ink-soft);
  line-height:1.55;
  vertical-align:top;
}
.dt-doc td:first-child{color:var(--ink);white-space:nowrap}
/* the revision stamp. The mono-caps type comes from the house `.eyebrow`
   class in the markup; this rule is layout only. */
.dt-updated{margin-top:26px;padding-top:12px;border-top:1px solid var(--line)}
@media (max-width:760px){ .dt-doc td:first-child{white-space:normal} }

/* ── The field cell ──────────────────────────────────────────────────── */
/* A drafted box with its caption inside it: the label is the caption, the
   input is the value. The input carries no chrome of its own — the cell IS
   the chrome, which is why the glow can live on the cell. */
.dt-cell{
  /* the cell is a <label> so the whole box is the control's hit target —
     which makes `display:block` load-bearing, not cosmetic: a label is
     inline by default, and an inline box wrapping a block-level input
     collapses to no height. It survived on landing.html only because the
     cells happen to be flex items there (flex blockifies its children);
     inside apply.html's <form> they are not, and the two fields drew on top
     of each other. Stated once, here, so a third door cannot inherit it. */
  display:block;
  border:1px solid var(--line);
  padding:8px 10px;
  position:relative;
  margin-bottom:8px;
  background:var(--paper);
}
.dt-cell input,
.dt-cell textarea{
  display:block;
  width:100%;
  margin-top:3px;
  padding:0;
  border:0;
  background:transparent;
  color:var(--ink);
  font-family:var(--mono);
  font-size:12.5px;
  line-height:1.5;
  outline:none;
  -webkit-appearance:none;
  appearance:none;
}
/* the focus ring lives on the CELL (the glow below), so the control inside
   it does not draw a second one — the same shape the skin uses for the
   cmdbar's :focus-within wrapper. */

/* ── THE PLACEHOLDER INK (owner, prod, day mode 2026-09-05: "very faint") ──
   Two separate failures wore one symptom:
     · the sheet's own cells painted placeholders in `--muted`, measured
       4.23:1 on day paper — the value the skin itself records as 0.27 UNDER
       the AA floor. On sheet metadata that is an owner ruling; on the hint
       inside a form field it is the reader failing to see what to type.
     · Clerk's inputs had NO ::placeholder rule anywhere in the tree, so
       whatever painted them was the UA's or Clerk's, unpinned and unmeasured.
   Both are pinned here, to one value, DERIVED FROM `--ink` at a measured
   alpha — no token is touched (a separate branch is re-tuning --muted through
   the kit; this must not race it).
   65% of --ink over --paper measures **5.58:1 day · 7.22:1 dusk** against the
   CELL's own ground, which is the surface a placeholder actually sits on —
   and still reads as a hint, an order of magnitude softer than the entered
   value at 16.7:1. The `--ink-soft` line above it is the fallback for a
   browser without color-mix: 9.70 / 9.72, safe but nearly as loud as real
   text, which is why it is second choice and not first. */
:root{ --dt-placeholder: var(--ink-soft); }
@supports (color: color-mix(in srgb, red 50%, blue)) {
  :root{ --dt-placeholder: color-mix(in srgb, var(--ink) 65%, var(--paper)); }
}
.dt-cell input::placeholder,
.dt-cell textarea::placeholder{ color:var(--dt-placeholder); opacity:1; }
.dt-cell .req{color:var(--accent)}

/* the go control — the ink inversion the schematic draws a selected/primary
   state with. NOT .btn-primary: under this palette --grad-gold resolves to an
   indigo→violet GRADIENT, and the adopted board draws its primary as flat
   ink. Transcribed, not re-derived. */
.dt-go{
  border:1px solid var(--ink);
  background:var(--ink);
  color:var(--paper);
  padding:9px 12px;
  display:flex;
  justify-content:space-between;
  align-items:center;
  gap:12px;
  width:100%;
  font-family:var(--body);
  font-weight:400;
  font-size:13px;
  cursor:pointer;
  margin-top:auto;
  transition:opacity .15s;
}
.dt-go.ghost{background:transparent;color:var(--ink)}
.dt-go:hover:not(:disabled){opacity:.82}
.dt-go:disabled{opacity:.5;cursor:default}

/* ── The foot ────────────────────────────────────────────────────────── */
.dt-foot{
  border-top:1px solid var(--line);
  padding-top:9px;
  display:flex;
  justify-content:space-between;
  align-items:baseline;
  gap:16px;
  flex-wrap:wrap;
}
.dt-foot b{font-weight:400;color:var(--ink)}
.dt-foot a:hover{color:var(--ink)}

/* ═══ THE FOCUS GLOW — the one bend, stated above ═══════════════════════
   Registered so the breathe can interpolate it; the box-shadow declaration
   itself stays a static, higher-specificity !important that outranks the
   skin's blanket ban. Where @property is unsupported the glow simply does
   not interpolate — it is still drawn, still 13px. */
@property --dt-glow{
  syntax:'<length>';
  inherits:false;
  initial-value:13px;
}
@keyframes dt-breathe{
  0%{--dt-glow:13px}
  50%{--dt-glow:30px}
  100%{--dt-glow:13px}
}
/* FOUR sheets mount Clerk inside a door cell — landing and login (sign-in),
   pricing (the plan table), settings (the account panel) — so Clerk's own
   field row is a `.dt-cell` by another name and takes the same one bend.
   ⚠️ KEYED ON THE MOUNT IDS, and the reason is the CASCADE, not taste: Clerk
   injects its own <style> into <head> AT MOUNT TIME, i.e. after every sheet
   we ship, so at EQUAL specificity Clerk wins. `#clerk-signin .cl-formField`
   is (1,1,0); a marker class would be (0,2,0) and could lose a tie the id
   form wins. `:is()` takes the specificity of its most specific argument, so
   one compact selector gives every mount id strength.
   An id list IS a set you can silently fall out of — so it is closed by a
   gate rather than by discipline: `functions/_lib/door-sheet.test.js` derives
   the mounts from the `Clerk.mount*()` CALLS on every page that loads this
   sheet and requires each one to appear here. */
/* ⚠️ EVERY SELECTOR HERE IS A :focus-within. There was a bare `.dt-glow`
   opt-in class in this list and nothing carried it — an escape hatch that
   would have glowed AND breathed at rest wherever it was applied. The ruling
   is "the ONE bend, on the focused cell"; an unused hatch is how a ban
   erodes, so it is gone. If a fourth control ever needs the glow, add its
   :focus-within here, not a class. */


/* ── Motion: the mark drafts itself ONCE per load, then holds ─────────── */
/* The board loops on a 12s clock so it can be watched; a door draws once —
   the periods, delays and easing are the board's own draw phase:
     outline 2.82s · seams +0.22s at 45ms × 16 · construction +0.75s ·
     rays +1.1s at 90ms × 8 · tip dots +1.5s · dimension lines +2.05s.
   After that only the two satellites move: A on r30 26s CW, B on r18
   41s CCW. */
.dt-mark{
  display:block;
  width:100%;
  height:auto;
  overflow:visible;
  color:var(--ink);
}
.dt-mark line,
.dt-mark path,
.dt-mark circle{
  fill:none;
  stroke:currentColor;
  stroke-width:1;
  stroke-linejoin:miter;
  vector-effect:non-scaling-stroke;
}
.dt-mark .dt-dot,
.dt-mark .dt-rcap{fill:currentColor;stroke:none}
.dt-mark .dt-acc{fill:var(--accent);stroke:none}
.dt-mark .dt-halo{opacity:.5}
.dt-mark .vrose-outline{opacity:1}
.dt-mark>line[pathLength]{opacity:.5}

/* the construction geometry is DOTTED, and a dotted dash pattern and a
   dash-reveal are the same property — so construction ARRIVES by fading,
   at the board's own delays, while the mark itself is drawn stroke-first.
   (The board has the identical conflict and resolves it the same way at
   runtime: its inline dash pattern outranks the reveal's dasharray.)

   ⚠️ THE RINGS ARE THE ONE PIECE OF CONSTRUCTION CARRYING pathLength="1"
   (the axes and the rays do not), so a dash value on them is a FRACTION OF
   THE CIRCUMFERENCE, not a length. `.6 .8` therefore asked for a dash
   covering 60% of the circle followed by a gap of 80% — measured 2026-09-05,
   each ring painted as ONE BROKEN ARC at 57.7% of its own circumference,
   not as dots. State the pattern as what pathLength makes it: a dot COUNT.
   .6/N and .8/N keep the authored 6:8 dash-to-gap ratio, and N tracks the
   RADIUS (200 at r30, 120 at r18 — both 6.667 × r) so the two rings get the
   same 0.57px dot rather than a dot proportional to their circumference.
   0.57px is the 0.6px the line above was reaching for, and nothing here
   depends on the engine's measured path length. The two radii are the kit's
   own and rose-geometry.test.js pins them, so these selectors cannot go
   stale silently. See §Rings in docs/sweeps/SHEET-DRAW-2026-09-05.md. */
.dt-mark .vrose-ring{opacity:.5}
.dt-mark .vrose-ring[r="30"]{stroke-dasharray:calc(.6 / 200) calc(.8 / 200)}
.dt-mark .vrose-ring[r="18"]{stroke-dasharray:calc(.6 / 120) calc(.8 / 120)}
.dt-mark .dt-axis{stroke-dasharray:1 1.4;opacity:.4}
.dt-mark .dt-ray{stroke-dasharray:.9 1.3;opacity:.34}
.dt-mark .dt-rcap{opacity:.5}
.dt-mark .dt-xdot{opacity:.6}

/* ⚠️ THE DRAW UNIT — state the dash in the space Chrome MEASURES it in.
   The drawn shapes carry pathLength="1" so that one dash value means "the
   whole path" whatever the path is; they also carry non-scaling-stroke (the
   hairline must stay 1px at every mark size). Chrome resolves the two in the
   wrong order: it normalises the dash by pathLength into USER units and then,
   because the stroke does not scale, applies that number as CSS PIXELS —
   never multiplying by the mark's own scale. So `stroke-dasharray:1` asked
   for the full path and delivered 263.197 CSS px of it, while the outline's
   painted length is 263.197 × (mark ÷ 96 viewBox units). Measured 2026-09-05
   on all seven doors: the outline settled at 53.3% and STOPPED MID-STROKE —
   a broken star, not a faint one — with the seams at 51.5%; permanent under
   prefers-reduced-motion, which cancels the animation with the dash still in
   force. ⚠️ AT OTHER SIZES IT IS A DIFFERENT BREAK, NOT A SMALLER ONE, and
   the sheet's own breakpoints are where it changes: 68.9% at 140px, 92.7% at
   104px (one gap near the north tip), 52.0% at the plate's 807px (alternating
   arcs) — and 100% at 62px, where the dash is longer than the whole path, so
   the smallest phones never saw it. Multiply the value back into the space
   the dash lands in and one
   unit is one path again, at every size — 100.0% measured at 62 · 104 · 140 ·
   180 · 807. The 96 is the viewBox extent, the same 96 the mark's own
   dimension line divides by. ⚠️ THE UNIT FOLLOWS --dt-mark, so a consumer
   that resizes the mark must resize it through that token, not by styling the
   <svg> directly. Ledger: docs/sweeps/SHEET-DRAW-2026-09-05.md. */
@keyframes dt-draw{from{stroke-dashoffset:var(--dt-draw-unit, 1.875px)}to{stroke-dashoffset:0}}
@keyframes dt-arrive{from{opacity:0}}

.dt-mark .vrose-outline,
.dt-mark>line[pathLength]{
  --dt-draw-unit:calc(var(--dt-mark, 180px) / 96);
  stroke-dasharray:var(--dt-draw-unit, 1.875px);
  animation:dt-draw 2.82s cubic-bezier(.2,.7,.2,1) both;
}
.dt-mark>line[pathLength]{animation-delay:calc(.22s + var(--i,0) * .045s)}
.dt-mark .vrose-ring,
.dt-mark .dt-axis{animation:dt-arrive .9s ease-out .75s both}
.dt-mark .dt-ray,
.dt-mark .dt-rcap{animation:dt-arrive .7s ease-out both;animation-delay:calc(1.1s + var(--i,0) * .09s)}
.dt-mark .dt-dot,
.dt-mark .dt-xdot{animation:dt-arrive .6s ease-out 1.5s both}
.dt-dims{animation:dt-arrive .8s ease-out 2.05s both}

.dt-mark .dt-sat{
  transform-box:view-box;
  transform-origin:32px 32px;
  animation:dt-orbit linear infinite;
}
.dt-mark .dt-sat.a{animation-duration:26s}
.dt-mark .dt-sat.b{animation-duration:41s;animation-direction:reverse}
@keyframes dt-orbit{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}

/* present and still: nothing runs, and every static opacity above is what
   paints — the arrive keyframes only ever set the FROM end, so cancelling
   them leaves the settled drawing. */
@media (prefers-reduced-motion:reduce){
  .dt-mark *,
.dt-dims{ animation:none !important; }
}

/* ── Widths ──────────────────────────────────────────────────────────── */
/* Every rule above is unconditional, so it applies at EVERY width — these
   blocks change only the sizes and the number of columns. */
:root{--dt-mark:180px;--dt-wm:132px;--dt-h2:40px}

@media (max-width:1180px){
  :root{--dt-mark:140px;--dt-wm:104px}
  .dt-body{grid-template-columns:minmax(0,1fr);gap:26px}
  .dt-claim{max-width:none}
}
@media (max-width:820px){
  :root{--dt-mark:104px;--dt-wm:74px;--dt-h2:32px}
  .dt-cells{grid-template-columns:repeat(3,1fr)}
  .dt-cells>div:nth-child(3n){border-right:0}
  .dt-cells>div:nth-child(n+4){border-top:1px solid var(--line)}
  .dt-doors{grid-template-columns:1fr}
  .dt-door+.dt-door{margin-left:0;margin-top:-1px}
}
@media (max-width:600px){
  :root{--dt-mark:62px;--dt-wm:46px;--dt-h2:21px}
  .dt-sheet{top:10px;bottom:10px;left:10px;right:10px}
  /* the board's phone column runs on a 7px gap, not the desktop 32 */
  .dt-page{padding:22px 16px 10px;gap:8px}
  .dt-block-main{gap:14px;padding:10px 12px 9px;align-items:flex-start}
  .dt-wordwrap{padding-top:2px}
  .dt-strap{font-size:7.5px;letter-spacing:.19em;margin-top:8px}
  .dt-dims{margin-top:6px;gap:8px}
  .dt-cells{grid-template-columns:repeat(3,1fr)}
  .dt-cells>div{padding:4px 10px}
  .dt-label{font-size:8px}
  .dt-val{font-size:10px}
  /* the last 30px of the 844: the sheet ran 874 after the door-2 cells came
     out, so the door's own interior padding is trimmed rather than a line of
     copy dropped. Every number below was read back off the 390 render. */
  .dt-door{padding:9px 11px}
  .dt-dhd{padding-bottom:5px;margin-bottom:7px}
  .dt-dtitle{font-size:17px;margin:3px 0 7px}
  .dt-cell{padding:5px 9px;margin-bottom:6px}
  .dt-note{font-size:9.5px;margin-top:6px}
  .dt-cta{margin-top:10px}
  .dt-lede{margin-top:8px}
  .dt-strap{margin-top:6px}
  .dt-go{padding:7px 11px;font-size:12.5px;margin-top:7px}
  /* the prose sheets on the phone: the same measure, tightened rules. Nothing
     is cut — a policy page that hides a section is not a policy page. */
  .dt-doc{margin-top:12px}
  .dt-doc h2{font-size:16px;margin:16px 0 7px;padding-top:11px}
  .dt-doc p{font-size:12px;margin-bottom:9px}
  .dt-doc p.lede{font-size:13px;margin-bottom:14px}
  .dt-doc ul{font-size:12px;margin-bottom:11px}
  .dt-doc table{font-size:12px}
  .dt-doc td{padding:7px 10px 7px 0}
  .dt-updated{margin-top:18px}
  /* the board's phone artboard carries no plotter readout — it is drafting
     flavour, not copy, and it is 44px of a 844px sheet */
  .dt-read{display:none}
  .dt-dim-mark{display:none}
  /* the phone artboard carries three cells, not five: TITLE repeats the
     strap two lines above it and SCALE is drafting flavour — both wrap to
     three lines at this width and buy nothing. */
  .dt-sm-off{display:none}
  .dt-cells{grid-template-columns:repeat(3,1fr)}
  .dt-cells>div{border-top:0}
  .dt-val{white-space:nowrap}
  .dt-top .eyebrow{font-size:8.5px;letter-spacing:.16em}
  .dt-foot{gap:8px}
  .dt-foot .eyebrow{font-size:8.5px;letter-spacing:.14em}
  .dt-follow{display:none}
  /* ⚠️ 16px is not a type choice — iOS Safari ZOOMS the page when a focused
     input is under 16px, which throws the sheet sideways mid-form. The
     board is a 390px artboard and never had to answer for that; a door
     does. The mono face keeps the cell reading as a plotted value. */
  .dt-cell input,
  .dt-cell textarea{font-size:16px}
  /* touch targets: the board draws a 22px toggle and a 36px go control.
     Prod's own phone rule already bumped this toggle for the same reason. */
  .dt-tog button{padding:9px 14px}
  .dt-go{padding:11px 12px;font-size:13px}
}


/* ═══════════════════════════════════════════════════════════════════════
   THE PLATE — landing.html, and only landing.html (`body.dt-plate`)
   ═══════════════════════════════════════════════════════════════════════
   Owner ruling 2026-09-05 ("R2 good"): the LANDING becomes the plate drawn on
   canvas row R2 — ONE engineering drawing filling the sheet, with the two
   doors as the drafted DETAIL callouts leadered off the mark, the title block
   along the foot. `login.html` and `apply.html` keep the drafting-table sheet
   above: they are the DIRECT doors, and a direct door does not need a drawing
   to explain itself.

   ⚠️ THE ONE STRUCTURAL RULE — "the drawing never crosses a callout" is held
   by the STRUCTURE, not by a number. The drawing and the callouts are two
   grid columns, and the drawing is an <svg> with a viewBox: an SVG viewport
   clips, so at every width the strokes are inside the drawing column and the
   callouts are inside theirs. That is why `.dt-mark`'s own `overflow:visible`
   is overridden here — and the same clip is what lets the narrow layout crop
   the frame back down to the mark alone.

   ⚠️ AND WHY THE ROSE IS NOT WRAPPED IN A SCALING <g>. The obvious way to put
   a 64-unit mark inside a 158-unit drawing frame is a transform group. Two
   things forbid it: `functions/_lib/rose-geometry.test.js` reads the door's
   seams as `<line pathLength="1" … x1="32" y1="32" …>` DIRECT children, and
   the sheet's own draw rule is `.dt-mark>line[pathLength]`. So the frame is
   widened AROUND the mark instead and every leader, dimension and bubble is
   expressed in the mark's own units. Nothing about the mark moved.
   ═══════════════════════════════════════════════════════════════════════ */
body.dt-plate{
  --dt-wm:89px;
  /* the rendered size of the 96-unit mark in the NARROW layout, where the
     frame is cropped back to it. Read by `.dt-frame` and by the dimension
     line under it, so the two cannot disagree. */
  --pl-r:320px;
}
/* 14, not the sheet's 32, and a tighter foot: the board's plate spends its 900
   on the DRAWING — header 23, drawing 606, title block 132, footer 23 — and a
   32px gap three times over is 96 of them. The bottom padding is 18 because
   the plate's footer sits on the sheet's own 14px rule rather than floating
   above it. Measured back at 1440x900, the board's own artboard: 916 -> 898
   with these, against 1001 on the sheet's own numbers. */
body.dt-plate .dt-page{gap:14px;padding:26px 34px 18px}
/* the drafting order: the top rule, the drawing beside its callouts, then the
   title block along the foot, then the sheet's own footer. The title block is
   FIRST in the document (it carries the <h1>) and last on the plate — the
   flex order moves it, so reading order stays title-then-content and no
   focusable element changes position. */
body.dt-plate .dt-top{order:0}
body.dt-plate .dt-plate-body{order:2}
body.dt-plate .dt-block{order:3}
body.dt-plate .dt-foot{order:4}

.dt-plate-body{
  display:grid;
  /* the board's own proportions at 1440: an 814px drawing beside a 558px
     callout column inside the plate's 1372px measure. Fractions and not
     pixels, so the pair keeps its shape at every width — and the plate's
     existing 1560 cap is what stops a 2494 viewport from drawing a 900px-wide
     sign-in card. */
  grid-template-columns:814fr 558fr;
  align-items:start;
  /* align-content too, and for the reason `.dt-body` already records: the
     plate body is the page's flex:1 child, so a stacked drawing-under-door
     grid would distribute the tall viewport's slack BETWEEN the two rows.
     Measured at 390: 78px of nothing between the door and the mark. The
     slack belongs at the foot of the sheet. */
  align-content:start;
  flex:1 1 auto;
}
.dt-drawing{min-width:0}
/* the frame the drawing is drawn in. At the plate's own widths it is just the
   SVG's box; at the narrow ones it is the crop window (see the media block). */
.dt-frame{min-width:0}
.dt-callouts{min-width:0;display:flex;flex-direction:column}
body.dt-plate .dt-mark{overflow:hidden}

/* the plotted annotation: leaders, bubbles, dimension lines and their
   captions. It arrives on the sheet's own dimension beat (2.05s), which is
   what `.dt-dims` takes — the drawing and the numbers that measure it land
   together. */
/* ⚠️ THE TWO CONSTRUCTION RINGS ARE SOLID ON THE PLATE — SCHEMATIC ONLY.
   The bug this rule was first written against is GONE — the sheet-draw round
   root-fixed the rings' dash on 2026-09-05 (`calc(.6 / 200)` / `calc(.6 / 120)`,
   a size-independent 0.57px dot). What is left is a scale question, and the
   board answers it: at the door's 180px a 0.57px dot on a 113px circumference
   is a dotted circle; at the plate's 496 it is ~850 sub-pixel dots on a 973px
   circumference, which paints as a haze rather than as construction geometry.
   `gen_landing.rose()` draws both rings SOLID at half ink and the plate follows
   it. Nothing about the sheet's rule is wrong — the plate simply is not the
   size it is right at.
   ⚠️ SF-59 / BUG-040 (2026-09-11): under Silk the frame crops to the bare
   64-78px mark (`.dt-frame` — the narrow layout and the Silk lockup below),
   no annotation dots, no scale excuse — and a SOLID ring shares its radius
   with the cardinal tips by design (ROSE-KIT: tips 30/18, rings 30/18). A
   continuous stroke crossing a sharp point head-on reads as a flat cut at
   12/3/6/9 o'clock; a dotted one does not, because a 0.57px dot rarely lands
   on the exact pixel the tip does. Measured both ways at 2x: solid flattens
   every cardinal tip, dotted does not, at every Silk width tested
   (390/600/900/1440). So Silk keeps the kit's own dotted rings and only
   schematic's large plate goes solid. */
html body.dt-plate .dt-mark .vrose-ring{stroke-dasharray:none}

/* ⚠️ THE PLATE'S MARK *ARRIVES*; IT DOES NOT DASH-DRAW — AND THIS RULE IS
   STILL LOAD-BEARING AFTER THE SHEET-DRAW FIX. Re-measured 2026-09-06 on the
   landed sheet, at 1440:

       .dt-mark rendered width   807.5px   (the plate's grid column)
       --dt-mark                 180px     (the sheet's own token, untouched)
       --dt-draw-unit            calc(180px / 96) = 1.875px
       px per user unit here     5.1252

   The sheet's fix states the dash in the space Chrome measures it in, and it
   derives that space from `--dt-mark` — its own comment says so: "a consumer
   that resizes the mark must resize it through that token, not by styling the
   <svg> directly". THE PLATE IS THAT CONSUMER. Its mark is sized by the grid
   column (and by `--pl-r` in the narrow crop), so the unit the sheet computes
   is 1.875 where this mark needs 5.125: delete the two declarations below and
   the plate draws 36.6% of a star again. That is why they stay.
   ⚠️ So what the owner watched "get drawn" on prod is the ARRIVE sequence,
   not a travelling stroke: the outline lands, then the sixteen seams one at a
   time on the sheet's own 45ms stagger, then the construction, the rays, the
   dots and the dimension lines. Longhands, not the shorthand, so the easing,
   the fill and the per-seam delay still come from the sheet.
   ⚠️ The real repair is to make the plate drive `--dt-mark`, which needs the
   rendered width and so needs JS — and a JS-set unit fails OPEN to a broken
   star when scripting is off, where this fails closed to a complete one.
   Boarded, not improvised here. */
body.dt-plate .dt-mark .vrose-outline,
body.dt-plate .dt-mark>line[pathLength]{
  stroke-dasharray:none;
  animation-name:dt-arrive;
  animation-duration:.9s;
}

.dt-plot{animation:dt-arrive .8s ease-out 2.05s both}
.dt-mark .dt-lead{opacity:.7}
.dt-mark .dt-bub{fill:var(--paper);stroke:currentColor}
.dt-mark .dt-plot text{
  fill:currentColor;
  stroke:none;
  font-family:var(--mono);
  opacity:.8;
}
/* the bubble letters are LABELS, not annotation — full ink. The selector has
   to out-specify `.dt-mark .dt-plot text` above it; `.dt-mark .dt-bubt` does
   not, and the measured result was A and B painting at .8 like the dimension
   captions. */
.dt-mark .dt-plot text.dt-bubt{opacity:1}
/* ⚠️ THE SATELLITES TAKE THE SHEET'S OWN ORIGIN — THERE IS NO OVERRIDE HERE,
   AND THE FIRST ROUND WAS WRONG TO THINK ONE WAS NEEDED. It read
   `getBoundingClientRect()` off the transformed <g> mid-animation, concluded a
   satellite was orbiting 405px from a centre it should orbit at 154, and cut
   them. Re-measured 2026-09-06 the honest way — animations PAUSED at
   currentTime 0 / 25% / 50%, reading the CIRCLE's box, not the group's:

       mark centre (32,32) on screen     337.5, 351.9
       sat A at 0 / .25 / .5             337.5,198.1 · 491.3,351.9 · 337.5,505.7
       orbit radius                      153.8px = 30 × 5.1252  → exactly r30

   `transform-origin` lengths under `transform-box:view-box` resolve as USER
   COORDINATES, not as an offset from the viewBox's min corner — which is why
   the sheet's `32px 32px` is the mark's centre on the door's `-16 -16 96 96`
   and on the plate's `-27.2 -22.2 157.5 117.3` alike. Measured, not reasoned:
   a corner-relative origin would have put the centre at x=198, and it is at
   337.5. So the plate inherits `.dt-mark .dt-sat` untouched — no rule here at
   all. The one thing the plate changes is the RADII, and those live where the
   door's do: in the mark's own markup, with the arithmetic beside them. */

/* the callouts. A DETAIL callout is drawn heavier than the sheet's hairlines
   — it is the thing the leader points at. */
body.dt-plate .dt-door{border-color:var(--ink)}
/* the door's own dimension strip, between the two callouts: the sheet
   dimensions what it drew, and what it drew here is the shipped Clerk door.
   The caption is MEASURED off the rendered control at runtime (landing.html)
   for the same reason the wordmark's is — a fluid sheet cannot carry a number
   pasted off a fixed artboard. */
.dt-dim-door{width:100%;margin:22px 0 16px}

/* Detail B draws its two cells SIDE BY SIDE, as the board does — a callout
   the height of the board's 190px has no room for a stack, and the drafting
   table's own door had the vertical run of a full column to spend. The gap
   replaces `.dt-cell`'s own margin so the two rules cannot double up. */
.dt-apfields{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.dt-apfields .dt-cell{margin-bottom:0}

/* the title block along the foot: no mark column (the mark IS the drawing),
   the wordmark and its dimension on the left, the claim on the right.
   ⚠️ THE CELL IS SIZED BY THE WORDMARK, AND THE WORDMARK GREW 1.35x
   (owner, prod dusk 2026-09-06: "can we make the vesper cell a little bigger
   (taller and scale accordingly?)"). The block was drawn to the board's 132px
   of a 900px sheet and read as a FOOTER STRIP under a 601px drawing — 103.5px
   of cell for the sheet's own title. The wordmark goes 66 -> 89 (Hanken
   Grotesk's cap height is ~.72em, so 47.5 -> 64.1 measured cap, x1.35), the
   padding and the dimension's gap open with it, and the claim column takes
   the same step so the two halves still read as one cell rather than a big
   word beside small print. THE FIVE-CELL SHEET STRIP DOES NOT MOVE — it is
   metadata, not title, and its 47.8px is what keeps this a title BLOCK. */
.dt-block-plate{gap:44px;align-items:flex-start;padding:20px 26px 18px}
.dt-block-plate .dt-wordwrap{flex:0 0 auto;padding-top:0}
/* 760, not 660: the lede steps 12.5 -> 14 with the rest, and at 660 that is a
   third line — measured. 760 keeps it at two, and the column has 985px of
   room beside a 301px wordmark, so nothing is squeezed to buy it. */
.dt-block-plate .dt-claim{flex:1 1 auto;min-width:0;max-width:760px;padding-top:3px}
.dt-block-plate .dt-dims{margin-top:16px}
/* the claim scales WITH the word, or the cell just grows around unchanged
   print. The eyebrow keeps the house `.eyebrow` drawing and takes one step of
   size; nothing here redefines a component. */
body.dt-plate .dt-block .eyebrow.accent{font-size:11px;letter-spacing:.2em}
body.dt-plate .dt-lede{margin-top:10px;max-width:760px;font-size:14px;line-height:1.6}
body.dt-plate .dt-cells>div{padding:7px 12px}
/* the strap and the claim are the SAME sentence twice over, and the board
   draws exactly one of them per artboard: the desktop plate carries the
   accented eyebrow and the lede, the phone carries the strap alone. */
body.dt-plate .dt-strap{display:none}

/* the drawing's own dimension, for the widths where the frame is cropped to
   the mark and the plotted one is out of view. Same number, same words as the
   drafting-table sheet's `.dt-dim-mark`, on the same 62.5% of the box. */
.dt-dims-draw{display:none;align-items:center;margin-top:16px}
.dt-dims-draw .dt-dim{width:calc(var(--pl-r) * .625)}

@media (max-width:1180px){
  body.dt-plate{--dt-wm:78px}
  /* one column, and the CALLOUTS come first — the door is the job. The
     drawing follows as the mark alone: the board's own phone artboard drops
     the leaders and the dimension notes, and it is right to, because the
     frame's 8.5px mono renders at 4px once 158 units are scaled to a phone. */
  .dt-plate-body{grid-template-columns:minmax(0,1fr);gap:22px}
  /* capped and centred over the mark below it. Uncapped, a 1180 window drew a
     791px-wide email field — measured — which is not a door, it is a banner. */
  .dt-callouts{order:1;max-width:620px;margin:0 auto;width:100%}
  .dt-drawing{order:2}
  .dt-frame{position:relative;height:var(--pl-r);overflow:hidden}
  /* the frame cropped to the mark. The 96-unit mark is 496 of the frame's 814
     units of width with its box at (58,32) from the frame's top-left, so with
     k = --pl-r / 496 every offset below is one fraction of --pl-r and nothing
     is a magic pixel. */
  body.dt-plate .dt-mark{
    position:absolute;
    left:50%;
    top:calc(var(--pl-r) * -32 / 496);
    width:calc(var(--pl-r) * 814 / 496);
    /* ⚠️ WITHOUT THIS THE CROP DOES NOT HAPPEN AND NOTHING ERRORS.
       `vesper-theme.css` line 129 is `img,svg{display:block;max-width:100%}`,
       so the calc above was clamped straight back to the column: measured
       322px where it asks for 364.4, i.e. the mark drawn at 0.88 of its size
       and off-centre, looking merely "a bit small". */
    max-width:none;
    height:auto;
    margin-left:calc(var(--pl-r) * -306 / 496);
  }
  .dt-plot{display:none}
  .dt-dims-draw{display:flex}
  .dt-dim-door{margin:18px 0 14px}
  .dt-block-plate{gap:26px}
}
@media (max-width:600px){
  /* 300, where the board drew 222: the board's Detail A is a 300px PICTURE of
     the door and the real Clerk mount settles at 218, so the mark takes the
     room back rather than leaving it as paper above the footer. Measured at
     390x844: the drawing closes at 703 against a footer at 812. */
  body.dt-plate{--dt-wm:70px;--pl-r:300px}
  /* ⚠️ THE PADDING HAS TO BE RESTATED, not inherited from the sheet's own
     phone rule. `body.dt-plate .dt-page` is (0,2,1) and the sheet's phone
     block is a bare `.dt-page` — a media query adds no specificity, so the
     plate's desktop 34px side padding won at 390 and cost the drawing 36px
     of its column. Measured: the mark's frame 322 wide instead of 358. */
  body.dt-plate .dt-page{gap:10px;padding:22px 16px 10px}
  /* the board's phone artboard is a wordmark, a strap, ONE callout, the
     invite line and the mark. The title block loses its rule and its cells —
     they are the drafting sheet's metadata, and the sheet the phone draws is
     the mark and the door. */
  body.dt-plate .dt-block{order:1;border:0}
  body.dt-plate .dt-block-main{padding:2px 0 0}
  body.dt-plate .dt-cells{display:none}
  body.dt-plate .dt-claim{display:none}
  .dt-block-plate{display:block}
  .dt-block-plate .dt-dims{display:none}
  body.dt-plate .dt-strap{display:block;margin-top:9px}
  /* ② is a LINK at this width, exactly as the board draws it: door ①'s own
     invite line carries `Apply for access →`, the footer carries `Apply`, and
     apply.html is the page that owns the flow. The cells stay in the document
     (hidden), so /vesper-apply.js keeps its contract. */
  body.dt-plate #door-apply{display:none}
  .dt-dim-door{display:none}
  .dt-dims-draw{margin-top:10px}
  body.dt-plate .dt-plate-body{gap:14px}
}


/* ═══════════════════════════════════════════════════════════════════════
   THE SIGN-IN DOOR — Clerk's own DOM, drawn as a drafted field cell
   ═══════════════════════════════════════════════════════════════════════
   Moved out of login.html 2026-09-05, when the LANDING's cell became a Clerk
   mount too (the owner's prod drive found the old hand-off costing two
   Continues for one act). Two pages that must look and behave identically at
   an auth boundary do not get two copies of the recipe.

   Clerk renders the control, so the cell is drawn ON its elements rather than
   around them. Every `cl-` class below is one of Clerk's documented appearance
   element keys, read off the LIVE clerk-js v5 mount, not guessed. The
   behaviour that pairs with it is /vesper-door.js.

   `@keyframes spin` was renamed `dt-spin` on the way in: a bare `spin` in a
   shared sheet is a name three pages could collide on (apply.html already
   carries its own `ap-spin`).
   ═══════════════════════════════════════════════════════════════════════ */

/* the claim heading lives with `.dt-claim h2` up in the body section — it is
   the same drawing, and two copies of it in one file is how a sheet drifts
   from itself. */

/* No min-height here. `.boot-loading` and `.boot-error` each carry their own
   120px, so the placeholder holds the box on its own while the SDK loads —
   and a floor on the MOUNT outlives the placeholder: measured on the phone
   board, Clerk's settled form was 103px inside a 120px box, 17px of nothing
   that a fixed 844 artboard cannot spare. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile),
:is(#clerk-signin, #clerk-pricing, #clerk-profile) button,
:is(#clerk-signin, #clerk-pricing, #clerk-profile) input{ font-family:var(--body); }

/* clerk-js v5 wraps `card` in an outer `cardBox` carrying Clerk's own
   drop-shadow, hairline, radius and overflow:hidden — a card inside the
   door that pops past its rule and clips the field label. The appearance
   config neutralises it too; Clerk's hashed class can win on specificity,
   so pin it here as well. overflow:visible keeps the label unclipped. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-cardBox{
  box-shadow:none !important;
  border:none !important;
  background:transparent !important;
  max-width:100% !important;
  overflow:visible !important;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-card{ background:transparent; padding:0; box-shadow:none; }
/* ⚠️ THE FLEX MIN-CONTENT FLOOR — why `max-width` alone could not hold it.
   Clerk lays the account panel out as a flex row, and a flex item's default
   `min-width:auto` refuses to shrink below its content. So the panes kept
   their intrinsic width, painted straight through the door's hairline, and
   extended the document; capping the CARD did nothing because it is the
   CHILDREN that overflow. `min-width:0` lets them shrink to the box they are
   given, which is what makes the widget responsive inside a drafted door
   rather than merely clipped by one. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-card > *,
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-navbar,
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-pageScrollBox, .cl-scrollBox, .cl-page, .cl-pageContent){ min-width:0; }
/* and long unbroken values (a device string, a session id) wrap instead of
   propagating a min-content width back up the tree. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-profileSectionContent, .cl-profileSectionPrimaryButton, .cl-userPreviewSecondaryIdentifier){
  overflow-wrap:anywhere;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile),
:is(#clerk-signin, #clerk-pricing, #clerk-profile) > div,
:is(#clerk-signin, #clerk-pricing, #clerk-profile) form,
:is(#clerk-signin, #clerk-pricing, #clerk-profile) input,
:is(#clerk-signin, #clerk-pricing, #clerk-profile) button{ max-width:100% !important; box-sizing:border-box !important; }

/* THE FIELD CELL. `.cl-formField` is the label+input group, so it is the
   drafted box; the glow on its :focus-within lives in /vesper-draft.css
   beside the sheet's own `.dt-cell` (one bend, one home). */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formField{
  border:1px solid var(--line);
  background:var(--paper);
  padding:8px 10px;
  position:relative;
  gap:0;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formFieldRow{ margin-bottom:8px; }
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formFieldLabelRow{ margin:0; padding:0; }
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formFieldLabel{
  font-family:var(--mono);
  font-size:9.5px;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--ink-soft);
  line-height:1.5;
  padding:0;
}
/* NOT `.cl-required` on its own: clerk-js v5 puts that class on the LABEL
   element itself (measured — `class="cl-formFieldLabel … cl-required …"`), so
   an accent rule there repaints the whole caption, not an asterisk. Scope it
   to a child, which is where the marker lives when Clerk renders one. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formFieldLabel .cl-required{ color:var(--accent); }
/* Clerk's input takes the sheet's placeholder ink too. `#id .class` is
   (1,1,0) and Clerk's emotion class is (0,1,0), so this wins the tie Clerk
   would otherwise take by injecting its <style> after ours. `opacity:1`
   because a UA placeholder is dimmed by opacity as well as colour, and a
   measured ratio means nothing if something halves it afterwards. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(input, textarea)::placeholder{
  color:var(--dt-placeholder) !important;
  opacity:1 !important;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formFieldInput{
  border:0 !important;
  background:transparent !important;
  box-shadow:none !important;
  outline:none;
  padding:0 !important;
  margin-top:3px;
  font-family:var(--mono);
  font-size:12.5px;
  line-height:1.5;
  color:var(--ink);
}
/* the six code boxes on the emailed-code step, drawn as the board draws
   them: a hairline square each, mono, no fill. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-otpCodeField{
  border:0;
  background:transparent;
  padding:0;
  gap:7px;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-otpCodeFieldInput{
  border:1px solid var(--line) !important;
  background:var(--paper) !important;
  box-shadow:none !important;
  height:34px;
  font-family:var(--mono);
  font-size:14px;
  color:var(--ink);
  /* ── THE CURSOR (owner, prod 2026-09-05: "in the six code cells he cannot
     see the flashing cursor") ──────────────────────────────────────────────
     We styled the six boxes and never said where the caret goes, so whatever
     Clerk sets stood — and a segmented code field is exactly the control a
     component library hides the caret in, because it usually draws its own.
     Declared here, in the accent, and !important: the failure mode is a
     reader typing into a box with no sign of where they are, and Clerk is
     free to move its own rule (or reach for an inline style) on any release.
     `caret-color` is not on the skin's box-shadow/radius blanket, so nothing
     else fights it. */
  caret-color:var(--accent) !important;
}
/* ── THE FOCUSED BOX (same finding) ────────────────────────────────────────
   There was no ring at all: measured `outline-style: none` on the settled
   box. `:focus` AND `:focus-visible`, deliberately — on a code field the
   reader is typing, so "did the keyboard get you here" is the wrong question
   to gate a cursor's home on. `outline` and not `box-shadow`, because the
   schematic blanket bans shadow with !important and an outline is the more
   blueprint-honest mark anyway; `outline-offset:-1px` keeps the 2px ring
   inside the 34px box so the six do not shuffle when one takes focus. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-otpCodeFieldInput:focus,
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-otpCodeFieldInput:focus-visible{
  outline:2px solid var(--accent) !important;
  outline-offset:-1px;
  border-color:var(--accent) !important;
}
/* ── THE RESEND LINE (same finding: "faint lavender on paper") ─────────────
   `Didn't receive a code? Resend (28)`. Clerk paints the link from its own
   scale; the sheet never claimed it. Pinned to the accent — 5.68:1 day /
   7.73:1 dusk on the cell's ground — with the sentence around it at
   ink-soft, and opacity:1 because a countdown state dims the link and a
   measured ratio means nothing if something halves it afterwards. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formResendCodeLink{
  color:var(--accent) !important;
  opacity:1 !important;
  font-weight:400;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formResendCodeLink:disabled{
  color:var(--ink-soft) !important;   /* the (28) countdown, still readable */
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-footerActionText, .cl-identityPreviewText, .cl-formHeaderSubtitle){
  color:var(--ink-soft) !important;
  opacity:1 !important;
}

/* THE GO CONTROL — the same ink inversion the sheet's own `.dt-go` draws,
   restated here because Clerk owns this button and cannot take a class. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formButtonPrimary{
  border:1px solid var(--ink);
  background:var(--ink);
  color:var(--paper);
  padding:9px 12px;
  font-family:var(--mono);
  font-size:11px;
  font-weight:400;
  letter-spacing:.12em;
  text-transform:uppercase;
  box-shadow:none;
  width:100%;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formButtonPrimary:hover{ background:var(--ink); opacity:.82; }
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-footerActionLink,
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-alternativeMethodsBlockButton{ color:var(--accent); }

/* ══ THE ACCOUNT PANEL — Clerk's <UserProfile/> on the sheet ═══════════
   From the owner's prod screenshots, 2026-09-05 (schematic, 1440):
     · the left navbar rendered as a SOLID GREY column ~1,350px tall under
       three items, so the door was a mostly-empty grey box;
     · the right pane held ~300px of content but sat in a fixed-height
       `pageScrollBox` with its OWN inner scrollbar — on the Security tab,
       seven device rows scrolling inside the door;
     · the Billing tab's plan card sat on Clerk's grey card ground with a
       grey button.
   None of that is the sheet: there is no grey in this palette, no elevation
   to scroll inside, and no radius. The rules below are keyed on the shared
   :is(#clerk-signin, #clerk-pricing, #clerk-profile)
   list on purpose — `.cl-navbar` and friends exist only on UserProfile, so
   the sign-in and pricing mounts inherit exactly the rules that apply to
   them and nothing else.

   ⚠️ UNVERIFIED BY ANY AGENT. Billing is disabled on the dev Clerk instance
   and UserProfile needs a real session, so every selector here is written
   from clerk-js v5's documented appearance element keys, not read off a live
   render. The owner drives it. ══════════════════════════════════════════ */

/* the navbar is a column of the sheet, not a filled sidebar: paper ground,
   one hairline on the edge it shares with the pane. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-navbar{
  background:var(--paper) !important;
  border:0;
  border-right:1px solid var(--line);
  /* the divider runs the full height of the pane beside it, the way every
     other rule on this sheet does. Clerk lays the card out as a flex row, so
     stretch is what makes the hairline a COLUMN rule rather than a 53px stub
     beside a 659px pane. */
  align-self:stretch;
  box-shadow:none;
  padding:10px 0 10px 0;
  min-width:0;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-navbarButtons{ gap:0; }
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-navbarButton{
  font-family:var(--body);
  font-weight:300;
  font-size:12.5px;
  color:var(--ink-soft);
  background:transparent !important;
  border:0;
  border-radius:0 !important;
  box-shadow:none;
  padding:8px 14px;
  justify-content:flex-start;
  gap:9px;
  opacity:1;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-navbarButton:hover{ color:var(--ink); }
/* the selected item takes the sheet's ONE selected-state word — the ink
   inversion `.on` draws everywhere else. Clerk spells "active" three ways
   across v5 builds, so all three are named rather than guessed at. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-navbarButton__active, .cl-navbarButton[data-active], .cl-navbarButton[aria-current]){
  background:var(--ink) !important;
  color:var(--paper) !important;
  border-radius:0 !important;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-navbarButton__active, .cl-navbarButton[data-active], .cl-navbarButton[aria-current]) .cl-navbarButtonIcon{
  color:var(--paper);
}
/* the phone's menu button is a drafted control, not a grey pill */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-menuButton{
  background:transparent;
  border:1px solid var(--line);
  border-radius:0 !important;
  color:var(--ink);
  box-shadow:none;
}

/* ⚠️ THE HEIGHT IS THE CONTENT'S, AND NOTHING SCROLLS INSIDE THE DOOR.
   Clerk gives the scroll box a fixed height and its own scrollbar; on the
   sheet the door grows to the taller of its two panes and the DOCUMENT
   scrolls. A pane that scrolls inside a drawn box is an elevation this
   palette does not have. */
/* ⚠️ ROUND 4 MEASURED THIS ON A FACSIMILE AND CALLED IT DONE. The owner then
   drove the REAL widget: "a little scroll jank when the Clerk window has its
   own scroll box, like lots of devices" — a grey scrollbar still inside the
   door on the Security tab. A stub that never had a scroller cannot prove one
   is gone. So the named set below is Clerk v5's own UserProfile element
   descriptors (`scrollBox`, `pageScrollBox`, `page`, `navbar`,
   `navbarMobileMenuRow`) rather than the two classes the stub happened to
   carry — and because Clerk also emits HASHED `cl-internal-*` classes that no
   stylesheet can name, `VesperClerk.fitDoor()` releases any surviving
   scroller in JS and re-measures. Belt and braces, because the braces are
   invisible from here. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-scrollBox, .cl-pageScrollBox, .cl-navbarMobileMenuRow){
  overflow:visible !important;
  overflow-y:visible !important;
  overflow-x:visible !important;
  max-height:none !important;
  min-height:0 !important;
  height:auto !important;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-scrollBox, .cl-pageScrollBox){ padding:16px 20px; }
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-cardBox, .cl-card, .cl-page, .cl-pageContent, .cl-navbar, .cl-rootBox){
  min-height:0 !important;
  height:auto !important;
  max-height:none !important;
  overflow:visible !important;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-page{ padding:0; }

/* the right pane's own sections, drawn on the sheet's hairline */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-profileSection{
  border-bottom:1px solid var(--line);
  padding:14px 0;
  background:transparent;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-profileSection:last-of-type{ border-bottom:0; }
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-profileSectionTitle, .cl-profileSectionTitleText){
  font-family:var(--mono);
  font-size:9.5px;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--ink-soft);
  border:0;
  padding:0;
}
/* the VALUES under a caption are the sheet's plotted values */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-profileSectionContent, .cl-userPreviewSecondaryIdentifier){
  font-family:var(--mono);
  font-size:12.5px;
  color:var(--ink);
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-profileSectionPrimaryButton{
  color:var(--accent);
  background:transparent;
  border:0;
  border-radius:0 !important;
  padding:4px 0;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-headerTitle, .cl-userPreviewMainIdentifier){
  font-family:var(--display);
  font-weight:300;
  font-size:19px;
  letter-spacing:-.02em;
  color:var(--ink);
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-headerSubtitle{
  font-family:var(--body);
  font-weight:300;
  font-size:12px;
  color:var(--ink-soft);
}
/* a badge is a hairline tag here, never a filled pill */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-badge{
  background:transparent !important;
  border:1px solid var(--line);
  border-radius:0 !important;
  color:var(--ink-soft);
  font-family:var(--mono);
  font-size:9px;
  letter-spacing:.14em;
  text-transform:uppercase;
  padding:2px 6px;
  box-shadow:none;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-avatarBox{ border-radius:0 !important; }

/* THE PLAN CARD (the Billing tab, and the same card the pricing sheet
   mounts): paper ground, a hairline box, and the button in the sheet's own
   button grammar rather than Clerk's grey. */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-pricingTableCard, .cl-planCard, .cl-subscriptionCard){
  background:var(--paper) !important;
  border:1px solid var(--line) !important;
  border-radius:0 !important;
  box-shadow:none !important;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-pricingTableCardHeader, .cl-pricingTableCardBody, .cl-pricingTableCardFooter){
  background:transparent !important;
  border-color:var(--line) !important;
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-pricingTableCardTitle, .cl-planCardTitle){
  font-family:var(--display);
  font-weight:300;
  font-size:19px;
  letter-spacing:-.02em;
  color:var(--ink);
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-pricingTableCardFee, .cl-pricingTableCardFeePeriod, .cl-pricingTableCardFeePeriodNotice){
  font-family:var(--mono);
  color:var(--ink);
}
:is(#clerk-signin, #clerk-pricing, #clerk-profile) :is(.cl-pricingTableCardFeePeriod, .cl-pricingTableCardFeePeriodNotice){
  font-size:11px;
  color:var(--ink-soft);
}

/* ── Hide every "Sign up" CTA Clerk surfaces ────────────────────
   Belt-and-suspenders alongside elements.footerAction in CLERK_APPEARANCE.
   Sign-up is API-disabled in Clerk Dashboard; the "No account? Sign up"
   link only leads to a dead-end form that errors on submit.
   Narrowed deliberately (it was [class*=cl-footerAction], too broad): hide
   ONLY the sign-up CTA, never the whole footer-action area, which also holds
   Clerk's "Use another method" switcher (verification code <-> link). */
:is(#clerk-signin, #clerk-pricing, #clerk-profile) [class*="footerAction__signUp"],
:is(#clerk-signin, #clerk-pricing, #clerk-profile) a[href*="sign-up"],
:is(#clerk-signin, #clerk-pricing, #clerk-profile) a[href*="signup"],
:is(#clerk-signin, #clerk-pricing, #clerk-profile) [data-localization-key*="signUpAction"]{
  display:none !important;
}

/* "Secured by Clerk" badge repositioning was removed once we hit Clerk Pro
   and toggled "Remove Secured by Clerk branding". If you ever downgrade back
   to Hobby (which forces the badge), restore from git: search for
   "cl-internal", "cl-badge", "cl-poweredBy", "cl-footerLogoLink". */

.boot-loading{
  display:flex;align-items:center;justify-content:center;min-height:120px;
  color:var(--ink-soft);font-family:var(--mono);font-size:10px;letter-spacing:0.14em;
  text-transform:uppercase;padding:12px;
}
.boot-loading::after{
  content:'';width:14px;height:14px;margin-left:10px;
  border:2px solid var(--line);border-top-color:var(--accent);border-radius:50%;
  animation:dt-spin .7s linear infinite;
}
.boot-error{
  display:flex;align-items:center;justify-content:center;min-height:120px;
  padding:12px;text-align:center;
  color:var(--neg);font-family:var(--mono);font-size:10px;letter-spacing:0.14em;
  text-transform:uppercase;line-height:1.6;
}
@keyframes dt-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){ .boot-loading::after{animation:none} }

/* ── STALE-SESSION RECOVERY (login loop-breaker) ──────────────────
   Shown when the page has bounced back from a gated route several times in a
   few seconds — the server can't validate the session but the client still
   thinks it's signed in. Breaks the white-flash redirect loop and gives the
   user a one-tap way out. */
.stale-recovery{ display:flex;flex-direction:column;gap:10px;padding:4px 0 0; }
.stale-recovery h2{
  font-family:var(--display);font-weight:300;font-size:19px;color:var(--ink);margin:0;
}
.stale-recovery p{ font-size:12px;color:var(--ink-soft);line-height:1.55;margin:0; }
.stale-recovery .sr-actions{ display:flex;flex-direction:column;gap:7px;margin-top:6px; }

/* ── UNKNOWN-EMAIL NOTICE (Vesper-branded fallback for Clerk's generic
       "couldn't find account" error, expected now that sign-up is off) ── */
.vesper-no-account-notice{ display:none; }
.vesper-no-account-notice.show{ display:block; }

/* ⚠️ CLERK'S OWN KEYFRAMES. The sheet's reduced-motion block covers what the
   sheet draws; measured with the media query forced on, ONE animation was
   still running inside :is(#clerk-signin, #clerk-pricing, #clerk-profile) (an emotion-hashed keyframe on a
   cl-internal element). A user who asks the OS for no motion is asking the
   whole door, not the half we authored. */
@media (prefers-reduced-motion:reduce){
  :is(#clerk-signin, #clerk-pricing, #clerk-profile) *{ animation:none !important; transition:none !important; }
}

/* the same iOS zoom guard the sheet applies to its own cells — Clerk owns
   this input, so it has to be said here too. */
@media (max-width:600px){
  :is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formFieldInput{ font-size:16px; }
  :is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-otpCodeFieldInput{ height:40px; font-size:16px; }
  :is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formButtonPrimary{ padding:12px; }
  /* ⚠️ CLERK'S OWN SPACING, ON A RULED BOARD. The landing's phone artboard is
     a fixed 390x844 and door ① is now Clerk's component; Clerk lays its form
     out on a 32px `cl-form` gap plus an 8px label/input gap, which is
     generous on a desktop card and 35px too much on this board (measured:
     879 against the ruled 844). Every one of the 35 comes out of the NEW
     element's own whitespace — nothing on the sheet was cut for it:
        cl-form gap             32 -> 12   (-20)
        cl-formFieldRow margin   8 -> 0     (-8)
        cl-formField padding     8 -> 6     (-4)
        label/input gap          8 -> 2     (-6)
        :is(#clerk-signin, #clerk-pricing, #clerk-profile) min-height 120 -> none (-17, above)
     -55 available, -35 needed; the surplus went back into the two gaps
     rather than being pocketed, so the phone form breathes MORE than a
     minimal fit would. The button keeps its 12px padding: 41px is the
     touch target. Measured back: 844 exactly. */
  :is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-form{ gap:12px; }
  :is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formFieldRow{ margin-bottom:0; }
  :is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formField{ padding:6px 10px; }
  :is(#clerk-signin, #clerk-pricing, #clerk-profile) .cl-formField > div{ gap:2px; }
}


/* ═══════════════════════════════════════════════════════════════════════
   THE APPLICATION'S STATES — pending, error, received
   ═══════════════════════════════════════════════════════════════════════
   Moved out of apply.html 2026-09-05, when the LANDING's ② cell became the
   form itself (the owner's prod drive: "apply for access has the same two
   page issue"). Two pages that must report an insert identically do not get
   two copies of the recipe. The behaviour that pairs with it is
   /vesper-apply.js.
   ═══════════════════════════════════════════════════════════════════════ */

/* the submit control is the sheet's own .dt-go; only the pending state and
   the disabled state are this page's */
.ap-spin{
  width:12px;height:12px;flex-shrink:0;display:none;
  border:1px solid var(--paper);border-top-color:transparent;border-radius:50%;
  animation:ap-spin .7s linear infinite;
}
.dt-go.loading .ap-spin{display:block}
.dt-go.loading .ap-label{opacity:.6}
@keyframes ap-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){ .ap-spin{animation:none} }

/* the inline error — a drafted box, not a tinted card */
.ap-error{
  display:none;
  border:1px solid var(--neg);
  padding:8px 10px;
  margin-bottom:8px;
  font-family:var(--mono);
  font-size:11px;
  line-height:1.5;
  color:var(--neg);
}
.ap-error.on{display:block}

/* the received state replaces the form inside the same door */
.ap-done{display:none}
.ap-done.on{display:block}
.ap-done .ap-check{
  width:34px;height:34px;
  border:1px solid var(--accent);
  color:var(--accent);
  display:flex;align-items:center;justify-content:center;
  margin-bottom:14px;
}
.ap-done h2{
  font-family:var(--display);font-weight:300;font-size:23px;letter-spacing:-.02em;
  line-height:1;margin:0 0 10px;color:var(--ink);
}
.ap-done em{font-style:normal;color:var(--ink);font-weight:400;overflow-wrap:anywhere}
