/* ==========================================================================
   adminless.ai, THE ROOT CARD — the letterhead sheet.
   ROUND 9, 2026-09-18. Rebuilt from the locked mockup
   `_sandbox/root-card-mockups/a5-tilt-drafting/mockup.css` (direction A,
   third iteration: A → A2/A3/A4 → A5). The round-6 composition this file
   used to hold — a centred page with two outlined door cards, six revealed
   bands and a 38px h1 — is gone, not retuned.

   THE CONTRACT, unchanged since the card was promoted on 2026-09-14 and the
   reason most of the numbers below are what they are: the whole card fits
   1280 x 800 with no scroll, in either axis. Re-measured this round at
   1280x800, 1440x900 and 1920x1080.

   WHAT THE COMPOSITION IS NOW. The window is a desk and the card is a sheet
   of company stationery lying on it: ivory stock on a darker desk, one low
   offset shadow, a 1px cut edge, registration crosshairs at the SHEET's four
   corners. Everything is set the way a letterhead is set — a printed wordmark
   over one 2px navy rule, ruled editorial columns instead of boxes, a
   justified foot band. The sheet is also an OBJECT: under a fine pointer it
   rotates at most 1.2 degrees toward the cursor and two hairlines cross under
   it, both driven by card.js, both dead on a touch screen, under reduced
   motion and with JS off. Static is the designed state.

   WHAT THIS FILE DEPENDS ON, all inside this directory (the site never
   reaches outside its own folder at runtime):
     fonts.css   the five self-hosted faces, INCLUDING Fraunces italic and
                 the opsz 25-38 cut the h1 and the door names ask for. No
                 @font-face is declared here; the mockup carried its own
                 copies only because it was standalone.
     tokens.css  the family scale. Loaded, never redefined — and, from this
                 round, not consumed either: see the note on the type scale
                 below.
     card.js     the tilt and the drafting cross (sections 3b and 3c)
     motion.js   loaded, with nothing to observe (section 3a)

   ⚠ THE TYPE SCALE IS THE SHEET'S OWN, AND THAT IS A DECLARED DEPARTURE.
   tokens.css holds the family scale and may not be redefined; this file does
   not redefine one token of it, it sets a printed scale in page-local
   properties instead (`--size-h1` … `--size-mono`, the letterhead's
   72/43/34/22/18/13.5/13/12/11). The old declared exception — "root keeps h1
   at 38px because a one-screen card cannot spend 72" — is RETIRED: the h1 is
   the slogan now, it is the sentence a stranger is meant to remember, and it
   clamps to exactly the family's 72px at the top of its range. tokens.css
   itself is untouched; its `--h1: 72px` and this sheet's ceiling agree.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Palette and scale

   --desk and --brass are the two colours this round adds. The desk is a
   measured 1.18:1 step off the paper: enough that the sheet has an edge, too
   little to read as a band. --brass exists for exactly one state, the
   hostname inside a navy-filled door; on #14213D it measures 6.6:1, so it
   carries 13px mono legitimately, and navy is not a surface anywhere else on
   the sheet, so brass never appears anywhere else either.

   Four properties the old file carried are gone with the composition they
   served: --logo-filter and --logo-blend (there is one palette, the art is
   flat black and multiplies into the stock), and --grain-blend /
   --grain-opacity (the grain is printed on the sheet now, at one value, in
   one rule). --on-accent is --paper: the ivory type on the navy fill and the
   ivory of the stock are the same ink.
   -------------------------------------------------------------------------- */

:root {
  --desk: #E4DDCC;
  --paper: #F3EFE4;       /* also the ivory type ON the navy: 13.90:1 */
  --ink: #1B1B1F;         /* 14.9:1 on paper, measured */
  --accent: #14213D;      /* 13.9:1 on paper, measured */
  --brass: #C8A253;       /* 6.6:1 on the accent, measured — hover only */
  --muted-ink: #5E594F;   /* 6.1:1 on paper, measured — the smallest type */
  --muted: #7A7468;       /* decoration only: crosshairs, the muted period */
  --rule: #D9D4C7;

  --font-display: Fraunces, Georgia, serif;
  --font-body: "IBM Plex Sans", system-ui, sans-serif;
  --font-label: "IBM Plex Mono", ui-monospace, monospace;

  /* display type is tracked in, never past -0.03em */
  --display-track: -0.018em;

  --size-h1: clamp(30px, 5.4vw, 72px);
  --size-mark: clamp(30px, 3.3vw, 43px);
  --size-door: clamp(25px, 2.7vw, 34px);
  --size-prop: clamp(18px, 1.72vw, 22px);
  --size-line: clamp(16px, 1.38vw, 18px);
  --size-mono: 13px;

  --door-pad: clamp(20px, 3.4vw, 54px);

  --ease: cubic-bezier(.23, 1, .32, 1);
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

h1, h2, p, ul { margin: 0; }
ul { padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }

/* Nothing on the card is selectable (Jake, 2026-09-18): it is a printed
   object, not a document, and a drag across it should tilt the sheet, not
   paint blue over the type. The two email addresses are the one exception,
   because a visitor copies them. ::selection is kept for those. */
body { -webkit-user-select: none; user-select: none; }
a[href^="mailto:"] { -webkit-user-select: text; user-select: text; }
::selection { background: var(--accent); color: var(--paper); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 6px;
}

/* anchors.js sends the reader to the sheet and moves focus there so the next
   Tab continues from the right place. The ring above is for things you
   operate; drawn round the whole sheet it is noise, so the scroll target, and
   only the scroll target, goes without one. */
[data-anchored]:focus,
[data-anchored]:focus-visible { outline: none; }

/* The zero-size <svg> that holds the door arrow's one path. Out of flow, and
   positioned HERE rather than by a style attribute on the element: nginx
   serves this site under `style-src 'self'` with no 'unsafe-inline', so an
   inline style attribute is dropped and the defs would take part in the
   layout. */
.defs { position: absolute; width: 0; height: 0; }

/* Skip link: out of flow until focused, then the first stop in the tab order.
   It carries the card's 44px hit box; the chip measured 166.61 x 37 on its
   14px line before round 8 gave it one. Left edge on the desk's own padding,
   so a focused chip lines up with the sheet it is about to hand you. */
.skip {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  position: absolute;
  left: clamp(14px, 2.2vw, 48px);
  top: -56px;
  z-index: 4;
  padding: 8px 14px;
  background: var(--accent);
  color: var(--paper);
  font-family: var(--font-label);
  font-size: var(--size-mono);
  letter-spacing: 0.06em;
  transition: top 140ms ease;
}
.skip:focus { top: 12px; }

/* --------------------------------------------------------------------------
   2. The desk

   The window is the desk and the sheet is centred on it. The padding is in
   vh/vw so a taller window gives the sheet more desk rather than stretching
   an empty grid row inside it: at 1920x1080 this is ~120px of desk above and
   below a 1240px sheet, which is the composition, not slack. It is also what
   replaced round 8's `max-height: 900px` cap — the cap existed because the
   old page grid handed a tall monitor's slack to one interior row; a sheet
   centred on a desk has no interior slack to hand out.

   The desk is also where the perspective lives — see section 3b. A tilt is a
   relationship between a sheet and the surface it lies on, so the vanishing
   point belongs to the desk and not to the paper.
   -------------------------------------------------------------------------- */

body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: clamp(14px, 2.2vh, 40px) clamp(14px, 2.2vw, 48px);
  background: var(--desk);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--size-line);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* --------------------------------------------------------------------------
   3. The sheet

   One offset shadow plus a 1px hairline edge. Not a ghost card: the shadow
   is short, low and pushed DOWN (0 24px 60px -30px), the way a sheet of
   stock sits, and the hairline is the cut edge of the paper rather than a
   drawn border around empty space.

   `isolation: isolate` keeps the paper grain multiplying into the sheet and
   not into the desk — the desk is a surface, the sheet is paper.

   `overflow: hidden` is load-bearing, not hygiene: it is what clips the
   drafting hairlines to the paper. The layer is one element moved by one
   transform, so a clip on the layer would travel with it; the clip has to
   live on the thing standing still, and the thing standing still — even
   under the tilt, which moves the sheet's rendering and not its box — is
   the sheet.
   -------------------------------------------------------------------------- */

.sheet {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  width: min(100%, 1240px);
  padding: clamp(24px, 3.4vh, 46px) clamp(24px, 3.2vw, 60px);
  background: var(--paper);
  box-shadow:
    0 0 0 1px rgba(20, 33, 61, .10),
    0 24px 60px -30px rgba(20, 33, 61, .35);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  /* 5.2vh, measured: the headline is 22px taller than the letterhead base's
     at 1280 and the foot lost its sites row, so the sheet re-balances half a
     step down from 5.7vh. At 1280x800 this sets the sheet at 747 in 764.8px
     of desk. */
  row-gap: clamp(18px, 5.2vh, 52px);
}

/* the family's one texture: a 160px turbulence tile at 5%, multiplied into
   the stock. Fixed cost, no layout cost. INSIDE the sheet, so it tilts with
   it: the grain is printed on the paper, not projected onto it — which is
   why the old fixed `.grain` overlay on <body> is gone from the markup. */
.sheet::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: .05;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* four crosshairs at the corners of the SHEET */
.regs { position: absolute; inset: 14px; pointer-events: none; z-index: 3; }
.regs i { position: absolute; width: 11px; height: 11px; opacity: .6; }
.regs i::before,
.regs i::after { content: ""; position: absolute; background: var(--muted); }
.regs i::before { left: 0; right: 0; top: 5px; height: 1px; }
.regs i::after { top: 0; bottom: 0; left: 5px; width: 1px; }
.regs i:nth-child(1) { top: 0; left: 0; }
.regs i:nth-child(2) { top: 0; right: 0; }
.regs i:nth-child(3) { bottom: 0; left: 0; }
.regs i:nth-child(4) { bottom: 0; right: 0; }

/* --------------------------------------------------------------------------
   3a. THE ONE AUTHORED MOMENT, and what it replaced

   The sheet's entrance is the letterhead rule drawing left to right, once,
   in CSS (section 4). There is no `[data-reveal]` on this page any more and
   there is nothing left for the shared `motion.js` to observe — the tag
   stays because that file is byte-identical family plumbing and it stamps
   `html.js`, which costs one class and nothing else.

   That is the round-8 P0 answered at the root instead of per band: a
   scroll-triggered reveal on a page that by contract never scrolls has
   nothing to reveal on, and two of the six bands used to sit at `opacity: 0`
   for ever on a 1440x900 monitor because of it. Nothing on this sheet now
   starts invisible, so nothing can be left invisible.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   3b. THE TILT

   `.tilt` is added to <html> by card.js, and only after it has checked the
   pointer and the motion preference. So the whole of this block is dead with
   JS off, on a touch screen, and for a reader who asked for stillness — and
   dead means the sheet is never transformed at all, not that it is
   transformed by zero. A 3D transform rasterises the text it contains; the
   flat sheet should never pay for a feature it is not using.

   2000px of perspective at 1.2 degrees is a 17px swing in z across a 1240px
   sheet. That is the budget: the far corner gains about half a percent of
   scale, the near corner loses it, and the h1's box moves under 6px. The
   angle is the whole effect, not the displacement.

   --tilt-ms is 0ms while the pointer drives it (the lerp in card.js IS the
   smoothing, and a transition on top of it would be a second one) and 600ms
   for the one return to flat when the pointer leaves the window.
   -------------------------------------------------------------------------- */

/* `min-width: 761px` is not decoration: below it the sheet becomes the page
   (section 9), and `.tilt .sheet` outranks a bare `.sheet` on specificity, so
   a narrow window has to be kept out of this block rather than corrected
   inside that one. It is the third of card.js's three gates, and the only one
   that lives in CSS. */
@media (hover: hover) and (pointer: fine) and (min-width: 761px) {
  .tilt body { perspective: 2000px; }

  .tilt .sheet {
    --rx: 0deg;
    --ry: 0deg;
    --tilt-ms: 0ms;
    transform: rotateX(var(--rx)) rotateY(var(--ry));
    transition: transform var(--tilt-ms) var(--ease);
    will-change: transform;
  }
}

/* --------------------------------------------------------------------------
   3c. THE DRAFTING CURSOR

   ONE element with no size: an origin pinned to the sheet's top-left corner,
   carrying two long hairlines as its own ::before and ::after. card.js writes
   --x and --y once per animation frame and those two properties feed nothing
   but the single transform below, so a pointer move costs one composited
   translate and no layout at all.

   They are written on the SHEET, not on this layer, because the sheet is
   already the element the tilt mutates every frame: one style write per
   frame instead of two.

   `z-index: -1` inside the sheet's stacking context puts the cross ABOVE the
   stock and BELOW every word printed on it — a hairline drawn on the paper,
   under the ink. It is therefore also under a navy door face (z-index 2),
   which is the answer to "ensure line effect renders behind things" (Jake,
   2026-09-18) and is why the face carries no second pair: see section 6.

   Gated exactly as the tilt is: a real hover, a fine pointer, and 761px,
   below which the sheet stops being an object on a desk. Under reduced
   motion it is not merely still, it is not there (section 10), and with JS
   off it is never moved and never shown.
   -------------------------------------------------------------------------- */

.xhair {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  z-index: -1;
  display: none;
  opacity: 0;
  pointer-events: none;
  transform: translate(var(--x, 0px), var(--y, 0px));
  transition: opacity 200ms var(--ease);
  will-change: transform;
}
.xhair::before,
.xhair::after { content: ""; position: absolute; background: var(--rule); }
.xhair::before { left: 0; top: -150vh; width: 1px; height: 300vh; }
.xhair::after { top: 0; left: -150vw; height: 1px; width: 300vw; }
.xhair.is-on { opacity: 1; }

@media (hover: hover) and (pointer: fine) and (min-width: 761px) {
  .xhair { display: block; }
}

/* --------------------------------------------------------------------------
   4. The mast
   -------------------------------------------------------------------------- */

.mast {
  display: grid;
  grid-template-columns: auto auto;
  justify-content: space-between;
  align-items: last baseline;
  column-gap: 28px;
}

/* The wordmark is PRINTED, not clicked. It is a <span>: a link from the card
   to the card is a target that does nothing, and on a phone it was a 331px
   invisible hit area across the top of the sheet.

   "AdminLess.ai" is set in one face, ".ai" included, and in mixed case: the
   mono ".ai" the family's sites have worn is a second voice inside a
   wordmark, the all-caps ADMINLESS.AI was a label rather than a name, and
   the navy period is already the one idea the logo needs. */
.mast__mark {
  font-family: var(--font-display);
  font-size: var(--size-mark);
  line-height: 1;
  letter-spacing: var(--display-track);
  color: var(--ink);
}
.mast__dot { color: var(--accent); }

/* The underline is the text's own, not a border on a 44px box: with the box
   the hairline sat 20px under the address and read as a stray rule. Padding
   builds the 44px target (14 + 16 + 14) and leaves the baseline alone, so the
   address still sits on the wordmark's line. */
.mast__mail {
  justify-self: end;
  font-family: var(--font-label);
  font-size: var(--size-mono);
  padding-block: 14px;
  color: var(--muted-ink);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--rule);
  text-underline-offset: 4px;
  transition: color 140ms var(--ease), text-decoration-color 140ms var(--ease);
}

/* THE ONE MOVEMENT. The letterhead rule draws left to right, once, on load.
   It is CSS, so it runs with JS off; it is one element, so nothing else on
   the sheet animates in. The tilt is not a second entrance: it only ever
   answers a pointer that is already on the desk. */
.mast__rule {
  grid-column: 1 / -1;
  height: 2px;
  margin-top: 14px;
  background: var(--accent);
  transform-origin: left center;
  animation: draw 600ms var(--ease) both;
}
@keyframes draw {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* --------------------------------------------------------------------------
   5. The statement

   The slogan is the h1 and it is printed at 72px, because it is the sentence
   a stranger is meant to remember. The second sentence turns to the accent
   italic in place: one sentence stating the shared complaint, one answering
   it, and the turn is carried by the face rather than by a new line.

   The proposition follows in ink at 22px Plex Sans — not grey, not a
   caption. It is the fact the joke is standing on, and it has to be the
   second thing read, not the fourth.

   Both sentences set on ONE line each at every desktop width, measured: the
   slogan asks for 1025.7px of the 1120 the narrowest sheet gives, the
   proposition 790. That is the whole reason there is no measure cap here —
   at 46ch the proposition broke after "hire a" and left "tech team." alone
   on a line under a headline that had not broken at all.
   -------------------------------------------------------------------------- */

.say__h {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--size-h1);
  font-weight: 400;
  line-height: 1.04;
  letter-spacing: var(--display-track);
  text-wrap: balance;
  color: var(--ink);
}
.say__turn {
  font-style: italic;
  color: var(--accent);
}

.say__prop {
  margin: 20px 0 0;
  font-size: var(--size-prop);
  line-height: 1.45;
  color: var(--ink);
}

/* --------------------------------------------------------------------------
   6. The doors

   Two ruled columns, one vertical hairline. The whole column is the anchor,
   so the target is the column: ~550x180 on desktop, full width on a phone.

   The padding lives on `.door__inner`, not on `.door`, and that is load
   bearing: `.door__face` is absolutely positioned against the door and holds
   a second `.door__inner`, so both copies get their measure from the same
   rule and cannot drift apart by a pixel.
   -------------------------------------------------------------------------- */

.doors {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

/* The hairline is drawn on the GUTTER, not on the second column's edge. As a
   border-left on the door it sat exactly on the 1fr boundary, so the focus
   ring — a 2px navy rectangle at 10px offset, and the whole column is the
   link — crossed it on both sides. Centred, with 14px of margin holding each
   door off it, the ring clears the rule by 4px on either side, and the two
   navy fills leave a 28px ivory channel with the rule down the middle. */
.doors::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  background: var(--rule);
  z-index: 1;
}

.door {
  position: relative;
  display: grid;
  text-decoration: none;
  color: var(--ink);
  transition: transform 180ms var(--ease);
}
.door:first-child { margin-right: 14px; }
.door + .door { margin-left: 14px; }
.door:active { transform: scale(.985); }
.door:focus-visible { outline-offset: 10px; }

/* `auto minmax(0,1fr) auto` is the whole reason the two columns look set
   rather than pasted: one line runs to two lines and the other to two at a
   different length, and with an auto middle row the two hostnames sat 26px
   apart on different levels. The 1fr takes the difference, so both hostnames
   print on one line across the sheet. */
.door__inner {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: 12px;
}
.door:first-child .door__inner { padding-right: var(--door-pad); }
.door + .door .door__inner { padding-left: var(--door-pad); }

.door__name {
  font-family: var(--font-display);
  font-size: var(--size-door);
  line-height: 1.05;
  letter-spacing: 0.008em;
  color: var(--accent);
}
.door__dot { color: var(--muted); }

/* 42ch, measured, and it is the copy that sets it. Given the whole column
   (513px at 1280) the agencies line sets 2 and the companies line sets 1, so
   the second column carried a 27px hole above a hostname that was already
   bottom-aligned. At 42ch (437px) the agencies line breaks in exactly the
   same place it did before and the companies line takes its last word down:
   2 against 2 at every desktop width, which is what makes the two columns
   look set rather than pasted. Both lines already ragged 90-125px short of
   the hairline, so the cap costs no filled measure — it only stops the
   shorter sentence from running on alone. */
.door__line {
  max-width: 42ch;
  font-size: var(--size-line);
  line-height: 1.5;
  color: var(--ink);
}

.door__go {
  display: inline-flex;
  align-self: end;
  align-items: center;
  gap: 12px;
  min-height: 24px;
  font-family: var(--font-label);
  font-size: var(--size-mono);
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--accent);
}
.door__url {
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
  transition: border-bottom-color 140ms var(--ease);
}
.door__arrow {
  flex: none;
  fill: none;
  transition: transform 180ms var(--ease);
}

/* --- the navy face ------------------------------------------------------
   The same column, set in ivory and brass on navy, held at zero height by
   `clip-path: inset(100% 0 0 0)` and wiped up from the bottom in 320ms. It
   bleeds 18px past the door on three sides so the fill reads as a printed
   column of ink rather than a rectangle drawn round the words, and the
   second `.door__inner` carries that 18px back as padding, so the ivory type
   lands exactly on top of the ink type it replaces.

   Not rendered below 761px and not rendered without a fine pointer: a state
   a finger cannot enter is markup a phone should not download the cost of
   laying out. */
.door__face {
  display: none;
  --bleed: 18px;
  position: absolute;
  top: calc(-1 * var(--bleed));
  bottom: calc(-1 * var(--bleed));
  left: 0;
  right: 0;
  z-index: 2;
  background: var(--accent);
  color: var(--paper);
  pointer-events: none;
  clip-path: inset(100% 0 0 0);
  transition: clip-path 320ms var(--ease);
}
.door__face .door__inner { padding-block: var(--bleed); }
.door:first-child .door__face { left: calc(-1 * var(--bleed)); }
.door:first-child .door__face .door__inner { padding-left: var(--bleed); }
.door + .door .door__face { right: calc(-1 * var(--bleed)); }
.door + .door .door__face .door__inner { padding-right: var(--bleed); }

.door__face .door__name,
.door__face .door__line { color: var(--paper); }
.door__face .door__go,
.door__face .door__dot { color: var(--brass); }

/* --- the cross and the navy -----------------------------------------------
   The drafting layer sits at z-index -1 inside the sheet: on the paper, under
   everything printed. A filled door is printed, so the cross goes BEHIND it
   and reappears where the fill ends, the way a ruled line disappears under
   a pasted-on label (Jake, 2026-09-18: "ensure line effect renders behind
   things"). An earlier cut redrew the pair inside the fill at 38% on
   `.door__face::before/::after`; it read as a scratch through the
   reversed-out type and is gone — the face carries no pseudo-elements at
   all, which is also what the round-9 acceptance measures. */

@media (hover: hover) {
  .mast__mail:hover { color: var(--accent); text-decoration-color: var(--accent); }
  .door:hover .door__url { border-bottom-color: var(--accent); }
  .door:hover .door__arrow { transform: translateX(6px); }
  .foot__company a:hover {
    color: var(--accent);
    text-decoration-color: var(--accent);
  }
}

@media (hover: hover) and (pointer: fine) and (min-width: 761px) {
  .door__face { display: grid; }
  .door:hover .door__face { clip-path: inset(0 0 0 0); }
  /* on navy, the hostname's rule is brass too: the navy one the resting
     column uses would be a line drawn in the background colour. */
  .door:hover .door__face .door__url { border-bottom-color: var(--brass); }
}

/* --------------------------------------------------------------------------
   7. The bottom band: the two credential groups

   One hairline opens the band, and the band is justified the way a
   letterhead foot is: the four funders under their label on the left, the
   three programmes under theirs on the right, flush to the edge.

   ⚠ THE HEIGHTS ARE OPTICAL, NOT UNIFORM, AND THAT IS THE POINT. It is the
   same house rule this file has carried since the card shipped: hanging
   every mark off one box height is the wrong reading of "normalised to 34px
   optical", because Google for Startups and Microsoft are single-line
   wordmarks that spend 34px on cap height and shout, while SUTD,
   Create4Good, raiSE and Enterprise Singapore are stacked lockups spending
   the same 34px across two or three lines and vanish. (Jake, 2026-09-14:
   "the logos and the sizing of the other organisations are quite glaring".)
   So the funders are the art and sit at 34-44px; the programmes are the
   second group and sit at 20-30px, with the stacked NVIDIA badge above the
   two wordmarks inside that group.

   The one-dial `--mark-scale` of the old file is gone with the old row: the
   phone layout is a 2x2 per group (section 9) rather than a shrinking
   seven-across line, so the narrow heights are stated once, there, instead
   of being derived from a dial that had to be re-tuned at every step.
   -------------------------------------------------------------------------- */

/* THE RHYTHM IS UNIFORM AROUND THE DOORS (Jake, 2026-09-18). The sheet's
   row-gap sets statement → doors and doors → the closing hairline at the
   same interval, so the two door columns sit in an even band of paper. An
   earlier cut held the hairline off by the row-gap PLUS its own 4.6vh margin
   to mark a change of subject; measured at 1280x800 that read as the doors
   pulled up against the proposition with a hole beneath them. The band's own
   padding-top stays, because that is the distance from the rule to the marks,
   not from the doors to the rule. */
.creds {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  justify-content: space-between;
  gap: 24px 40px;
  margin-top: 0;
  /* rule → marks equals marks → record: both are the sheet's own row-gap,
     so the band reads as one even interval, not a tight top and a loose
     bottom (Jake, 2026-09-18). The record's extra margin came off with it. */
  padding-top: clamp(18px, 5.2vh, 52px);
  border-top: 1px solid var(--rule);
}
/* Both labels sit on one line, both mark rows on one baseline: each group is
   a column with the label pinned to the top and the marks to the bottom, so
   the taller funder ladder no longer pushes BACKED BY above IN THE PROGRAMMES
   OF. */
.creds__group { display: flex; flex-direction: column; justify-content: space-between; }

.creds__label {
  margin: 0 0 14px;
  font-family: var(--font-label);
  font-size: 12px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted-ink);
}
.creds__group--pro { text-align: right; }

.creds__marks {
  display: flex;
  align-items: flex-end;
  gap: clamp(22px, 2.9vw, 46px);
  margin: 0;
  padding: 0;
  list-style: none;
}
.creds__marks li { display: flex; align-items: flex-end; }
.creds__marks--pro { justify-content: flex-end; gap: clamp(20px, 2.4vw, 38px); }

.mark { width: auto; display: block; mix-blend-mode: multiply; }
/* the four funders: stacked lockups, levelled by eye */
.mark--sutd  { height: 40px; }
.mark--c4g   { height: 36px; }
.mark--raise { height: 44px; }
.mark--esg   { height: 34px; }
/* the three programmes: the same ladder, one group down */
.mark--inception { height: 30px; }
.mark--microsoft { height: 21px; }
.mark--gfs       { height: 20px; }

/* --------------------------------------------------------------------------
   8. The record

   One line, and it is the legal record: name, UEN, registered address, the
   address you write to, Privacy, Terms. No sites directory — a card whose
   foot lists its own subdomains has answered the doors before the reader
   chose one, and the two doors are the whole navigation this page has. So
   `.foot__sites` and its three hostnames are gone from this file with the
   markup that held them.

   11px at 0.065em, measured: at 11.5/0.075 the line set in one run at 1280
   (1141.8px of content) and broke in two at 1920, where the capped sheet
   only gives 1120. A record line that reflows between two monitors is a
   record line nobody set.
   -------------------------------------------------------------------------- */

.foot { display: grid; margin-top: 0; }

.foot__company {
  margin: 0;
  font-family: var(--font-label);
  font-size: 11px;
  line-height: 1.6;
  letter-spacing: 0.065em;
  text-transform: uppercase;
  color: var(--muted-ink);
}
/* Underlines here are the text's own, so the 6px of padding that lifts an
   11px label to a 25-28px hit box cannot drag a border away from it. A
   border draws on the border box and would move with the padding; a
   decoration is drawn on the text and stays where it was (ROOT-6). */
.foot__company a {
  padding-block: 6px;
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--rule);
  text-underline-offset: 4px;
  transition: color 140ms var(--ease), text-decoration-color 140ms var(--ease);
}
/* the middot between the record's parts. It carries the glyph, not just the
   air: the family spec fixes the wording of this line down to its separators,
   and an empty padded span rendered the spacing without the character. */
.foot__sep { padding: 0 8px; color: var(--muted); }
.foot__sep--in { padding: 0 6px; }

/* --------------------------------------------------------------------------
   9. Phones: the sheet becomes the page

   No desk, no shadow, no perspective, no tilt and no navy fill — a phone
   screen is not a desk, and a hover state a finger cannot enter is a state
   that should not exist. What is left is the letterhead stack carrying the
   same copy. The sheet keeps its grain and its crosshairs, because those are
   the stationery, not the staging.

   ONE breakpoint, at 760. The old file turned at 880, 560 and 349 because it
   was folding a seven-across credential row and a two-column site list down
   in stages; neither exists now, and the marks go 2x2 per group in one step.
   -------------------------------------------------------------------------- */

@media (max-width: 760px) {
  body {
    padding: 0;
    background: var(--paper);
    place-items: stretch;
  }

  .sheet {
    width: 100%;
    min-height: 100vh;
    padding: 22px 22px 34px;
    box-shadow: none;
    /* 3vh, measured: the second door's bottom is the whole constraint at
       375x667. This lands it at 613, clear of the fold with the mast, the
       slogan and the proposition stacked above it. */
    row-gap: clamp(18px, 3vh, 34px);
    grid-template-rows: auto auto auto auto 1fr;
  }

  .regs { inset: 10px; }

  /* the mast stacks: wordmark, then the address under it */
  .mast { grid-template-columns: minmax(0, 1fr); }
  .mast__mail { justify-self: start; margin-top: 0; padding-block: 12px 14px; }
  .mast__rule { margin-top: 12px; }

  /* ONE SENTENCE A LINE, and neither wrapping algorithm will do it: the
     slogan needs two lines on a 331px column, greedy breaking set "We hate
     admin too. Let's" and dropped "lessen it." under it, and `balance` set
     "We hate admin" / "too. Let's lessen it." — both cut a sentence in half
     at the one place the voice changes. The turn becomes its own block
     instead, which is a fact about the sentence and not a guess about the
     measure: "We hate admin too." sets 245px and "Let's lessen it." 200px at
     28.5px, so each holds one line down to 320. No <br> in the markup, so
     the desktop headline is still one run on one line. */
  .say__h {
    font-size: clamp(26px, 7.6vw, 34px);
    line-height: 1.1;
  }
  .say__turn { display: block; }
  .say__prop { margin-top: 14px; }

  /* the doors stack as two ruled rows */
  .doors { grid-template-columns: minmax(0, 1fr); }
  .doors::before { display: none; }   /* the gutter rule becomes a row rule */
  .door:first-child { margin-right: 0; padding-bottom: 22px; }
  .door + .door {
    border-top: 1px solid var(--rule);
    padding-top: 22px;
    margin-left: 0;
  }
  .door__inner { gap: 10px; grid-template-rows: auto auto auto; }
  .door:first-child .door__inner { padding-right: 0; }
  .door + .door .door__inner { padding-left: 0; }
  .door__line { max-width: none; }
  .door__go { min-height: 44px; }

  /* both credential groups go 2x2, left flush, at the narrow rung of the
     ladder: the funders keep their lead over the programmes. All seven marks
     stay — the old file switched the three programme lockups off below 560
     and printed their names as a sentence instead, because a seven-across
     row shrank them to a 14-23px smudge. At 2x2 they are 17-25px tall art
     with room around them, so the stand-in sentence is gone with the row
     that needed it. */
  .creds {
    align-items: flex-start;
    gap: 22px;
    /* the desktop double-gap comes off: on a page that scrolls, the hairline
       is enough of a change of subject on its own. */
    margin-top: 6px;
    padding-top: 24px;
  }
  .creds__group--pro { text-align: left; }
  .creds__marks,
  .creds__marks--pro {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: center;
    justify-items: start;
    justify-content: start;
    gap: 22px 20px;
  }
  .creds__marks li { min-height: 44px; align-items: center; }
  .mark--sutd  { height: 34px; }
  .mark--c4g   { height: 30px; }
  .mark--raise { height: 40px; }
  .mark--esg   { height: 29px; }
  .mark--inception { height: 25px; }
  .mark--microsoft { height: 18px; }
  .mark--gfs       { height: 17px; }

  .foot { align-self: end; margin-top: 4px; }

  /* every link in the record becomes its own 44px row, and the printed
     separators come off: a middot between two stacked rows is noise. */
  .foot__company {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    line-height: 1.55;
  }
  .foot__company > .foot__sep { display: none; }
  .foot__company a {
    display: flex;
    align-items: center;
    min-height: 44px;
    min-width: 44px;   /* "Terms" sets 36.6px wide; the box, not the word */
    padding-block: 0;
  }
  .foot__legal { display: flex; align-items: center; gap: 0; }
  .foot__legal .foot__sep { display: inline; }
}

/* --------------------------------------------------------------------------
   10. Reduced motion

   The rule is drawn, it just does not draw itself. Neither desk affordance
   exists: card.js returns before binding, so `.tilt` is never added and the
   sheet is never transformed rather than transformed by zero, and the
   drafting cursor is `display: none` rather than a still layer at opacity 0.
   This block is the belt to that brace if the preference is switched on
   after load. The navy door fill STAYS — it is a state, not an animation —
   it simply arrives without the wipe.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .mast__rule { animation: none; }
  .door, .door__arrow, .door__url, .door__face, .mast__mail,
  .foot__company a, .skip { transition: none; }

  .tilt body { perspective: none; }
  .tilt .sheet { transform: none; will-change: auto; }
  .xhair { display: none; }
}
