/* ============================================================================
   Lucent Showrooms — styles.css

   White, black and grey. Archivo for everything you read, Geist Mono for the
   small labels. No glass, no gradients as decoration, colour only ever in a
   photograph (and the photographs carry their own, in daylight).

   Order: tokens · reset · type · buttons · masthead · the
   first screen · the statement · the morph
   · booking · the deal · pricing · start · reveal · responsive
   · reduced motion · no script.
   ========================================================================== */

/* ------------------------------------------------------------------ tokens */
:root {
  --ink:        #0b0b0b;
  --ink-2:      #131313;
  --paper:      #f4f4f4;
  --white:      #ffffff;
  --text:       #121212;
  --dim:        #6a6a6a;
  --bone:       #f4f4f4;
  --bone-dim:   #a6a6a6;
  --bone-rule:  rgba(255, 255, 255, .17);
  --rule:       rgba(11, 11, 11, .16);
  --rule-soft:  rgba(11, 11, 11, .09);
  --rule-strong: rgba(11, 11, 11, .4);

  --pad:   clamp(20px, 4vw, 56px);
  --wrap:  1280px;
  --nav-h: 64px;

  --ease:     cubic-bezier(.22, .61, .28, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-in-out: cubic-bezier(.65, 0, .15, 1);
}

@font-face {
  font-family: "Archivo";
  src: url("assets/fonts/archivo-var-latin.woff2") format("woff2");
  font-weight: 100 900; font-stretch: 62% 125%; font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("assets/fonts/geist-mono-var-latin.woff2") format("woff2");
  font-weight: 100 900; font-display: swap;
}

/* ------------------------------------------------------------------- reset */
*, *::before, *::after { box-sizing: border-box; }
html { background: var(--ink); scroll-behavior: smooth; scroll-padding-top: var(--nav-h); -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--paper); color: var(--text);
  font-family: "Archivo", system-ui, sans-serif; font-variation-settings: "wght" 430, "wdth" 100;
  font-size: 16px; line-height: 1.5; -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; height: auto; display: block; }
figure { margin: 0; }
h1, h2, h3, p { margin: 0; }
[hidden] { display: none !important; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
::selection { background: #2b2b2b; color: var(--paper); }

/* -------------------------------------------------------------------- type */
.mono {
  font-family: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variation-settings: "wght" 500; font-size: 11.5px; letter-spacing: .12em;
  text-transform: uppercase; line-height: 1.55;
}
.kick { color: var(--dim); }
[data-tone="dark"] .kick { color: var(--bone-dim); }
.h2 {
  font-family: "Archivo", sans-serif; font-variation-settings: "wght" 720, "wdth" 94;
  font-size: clamp(28px, 3.4vw, 46px); line-height: 1.06; letter-spacing: -.022em;
  margin-top: 14px; text-wrap: balance;
}
.wrap { width: min(var(--wrap), 100% - 2 * var(--pad)); margin-inline: auto; }
a { color: inherit; }

/* ----------------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 50px; padding: 0 26px; border-radius: 999px;
  font: inherit; font-size: 15px; font-variation-settings: "wght" 560; letter-spacing: -.005em;
  text-decoration: none; cursor: pointer; border: 1px solid transparent; color: inherit;
  transition: background .25s var(--ease), color .25s var(--ease), border-color .25s var(--ease), translate .25s var(--ease);
}
.btn:active { translate: 0 1px; }
.btn--ink { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.btn--ink:hover { background: #262626; border-color: #262626; }
.btn--line { border-color: var(--rule-strong); background: transparent; }
.btn--line:hover { border-color: currentColor; }
.btn--paper { background: var(--paper); color: var(--ink); border-color: var(--paper); }
.btn--paper:hover { background: var(--white); border-color: var(--white); }
.btn--paper:focus-visible { outline-color: var(--paper); }   /* every paper button sits on a dark ground (hero, pricing, Start): an ink ring vanished */
.btn--ink:focus-visible { outline-color: var(--ink); }       /* and the ink button on the paper page: its paper ring vanished the same way */
.skip { position: absolute; left: var(--pad); top: -60px; z-index: 70; min-height: 44px; display: inline-flex; align-items: center; padding: 0 16px; background: var(--paper); color: var(--ink); }
.skip:focus { top: 12px; }
:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }

/* ==========================================================================
   MASTHEAD — dresses for what is under it, part by part (data-dress on the
   mark, each link and the button, from js/site.js; data-nav-dark on the bar
   is the dress under most of the screen), and arrives with the first screen's
   bill. With no script it stands on the hero: paper.
   ========================================================================== */
.nav {
  position: fixed; inset: 0 0 auto; z-index: 40; height: var(--nav-h);
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  padding-inline: var(--pad); color: var(--ink);
  transition: color .35s var(--ease), opacity .7s .05s var(--ease), translate .7s .05s var(--ease);
}
.nav[data-nav-dark] { color: var(--paper); }
.nav__mark, .nav__links a { transition: color .35s var(--ease); }
.nav [data-dress="light"] { color: var(--ink); }
.nav [data-dress="dark"] { color: var(--paper); }
/* an edge under a part (the booking phone's, a pricing card's): the words take both dresses, split
   exactly there (--mix is a hard-stopped fill, never a blend), and the button a solid paper pill */
.nav [data-dress="mix"] { color: transparent; background-image: var(--mix); -webkit-background-clip: text; background-clip: text; }
.nav__links a[data-dress="mix"]::after { background: var(--mix); }
html[data-arrive] .nav { opacity: 0; translate: 0 -10px; pointer-events: none; }
.nav__mark { grid-column: 1; justify-self: start; display: flex; align-items: baseline; gap: 12px; min-height: 44px; padding-top: 12px; text-decoration: none; }
.nav__mark b {
  font-family: "Archivo", sans-serif; font-variation-settings: "wght" 760, "wdth" 88;
  font-size: 17px; letter-spacing: .1em;
}
.nav__mark .mono { opacity: .72; }
.nav__links { grid-column: 2; display: flex; gap: 32px; list-style: none; margin: 0; padding: 0; }
.nav__links a { position: relative; display: inline-flex; align-items: center; min-height: 44px; min-width: 44px; text-decoration: none; }
.nav__links a::after {
  content: ""; position: absolute; left: 0; right: .15em; bottom: 10px; height: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: 0 50%; transition: transform .35s var(--ease);
}
.nav__links a:hover::after { transform: scaleX(1); opacity: .5; }
.nav__links a.is-here::after { transform: scaleX(1); }
.nav__cta {
  grid-column: 3; justify-self: end; display: inline-flex; align-items: center; min-height: 44px; padding: 0 18px;
  border: 1px solid currentColor; border-radius: 999px; text-decoration: none;
  transition: background .25s var(--ease), color .25s var(--ease);
}
.nav__cta[data-dress="mix"] { background: var(--paper); color: var(--ink); border-color: var(--ink); -webkit-background-clip: border-box; background-clip: border-box; }
.nav__cta:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.nav__cta[data-dress="dark"]:hover { background: var(--paper); color: var(--ink); border-color: var(--paper); }

/* ==========================================================================
   THE REST CUE — while a held arrow key rests on a chapter (js/site.js), the
   hero's scroll cue's shape mirrored to the foot, left: a 48 px hairline that
   drains as the rest runs out (--r, the share left), then the words. Left,
   because the deal's foot line runs to the centre; on Booking the cue takes the
   tag's corner, and the tag fades while it is up. Dressed (data-tone) for whatever chapter is under it once the
   page is still. Never under reduced motion: the hold never rests.
   Subtle, but not missable (founder, 2026-09-10, night: "some people may not even notice
   it"): the words at full ink, not dim; a 72 px line; a 10 px rise on arrival; and the words
   breathe twice as it lands (opacity only, one pulse a second and a half), then hold.
   ========================================================================== */
.rest {
  position: fixed; left: var(--pad); bottom: 30px; z-index: 40;
  display: inline-flex; align-items: center; gap: 14px; margin: 0;
  color: rgba(244, 244, 244, .92); opacity: 0; translate: 0 10px; pointer-events: none;
  transition: opacity .4s var(--ease), translate .4s var(--ease);
}
.rest i { position: relative; display: block; width: 72px; height: 1px; background: rgba(244, 244, 244, .3); overflow: clip; }
.rest i::after { content: ""; position: absolute; inset: 0; background: var(--paper); transform-origin: left; transform: scaleX(var(--r, 1)); }
.rest[data-tone="light"] { color: var(--text); }
.rest[data-tone="light"] i { background: var(--rule); }
.rest[data-tone="light"] i::after { background: var(--text); }
.rest[data-on] { opacity: 1; translate: none; }
.rest[data-on] span { animation: rest-breathe 1.5s var(--ease-in-out) .4s 2; }
@keyframes rest-breathe { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }

/* ==========================================================================
   THE FIRST SCREEN — the chosen photograph, full bleed, sticky under the
   paper page that follows. One bill stands on the floor's shadow, bottom
   left where the floor is empty: a two-line headline, one sentence under
   it, one button. The veil is shaped for a daylight photograph: a band under
   the masthead and a pool behind its button, then from 1280 px a pool in the
   bill's corner and a strip along the foot, the rest of the room left clear;
   under 1280, where the bill spans too much of the width, a band across the foot.
   The edges keep their mono lines. On a fresh visit the masthead, the bill
   and the edges arrive once the photograph has decoded (html[data-arrive],
   set and lifted by the head script in index.html).
   ========================================================================== */
.curtain { position: relative; }
.hero {
  position: sticky; top: 0; z-index: 0; height: 100svh; min-height: 520px;
  overflow: clip; background: var(--ink); isolation: isolate; color: var(--paper);
}
.hero__photo {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 58%;
  transform: scale(var(--hs, 1)); will-change: transform;
}
/* the photograph darkens as the paper page climbs over it: an ink veil's opacity, never a
   filter on the picture (a brightness() here re-rastered the whole screen every scroll frame,
   21 ms a frame in software). */
.hero__shade {
  position: absolute; inset: 0; pointer-events: none; background: var(--ink);
  opacity: calc(1 - var(--hb, 1)); will-change: opacity;
}
/* the band under the masthead is darker for the Victorian-flat hero (2026-09-14): its upper windows
   and pale wall sit behind the links, which read 3.4:1 at 1366×657 under the old .58 / .46 band. A
   pool behind the links alone fixed them and left "Showrooms" beside the mark at 4.1:1, so the whole
   band moved. */
/* Under 1280 the foot band reaches a little higher since the promise line came in (2026-10-13): the bill grew
   by a line and lifted the kicker to 3.9:1 at 1024×768, 3.6 at 900×700 and 4.4 at 390×844 under the old .42-at-36%
   band; this one measures 5.0:1 or better at 1024×768, 900×700, 768×1024, 414×896, 390×844, 390×664, 375×812,
   375×667 and 360×780. */
.hero__veil {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(0, 0, 0, .68), rgba(0, 0, 0, .58) 6%, transparent 16%),
    radial-gradient(38% 34% at 100% 0%, rgba(0, 0, 0, .3), transparent 100%),
    linear-gradient(0deg, rgba(0, 0, 0, .78), rgba(0, 0, 0, .54) 38%, rgba(0, 0, 0, .24) 58%, transparent 80%),
    radial-gradient(52% 44% at 12% 100%, rgba(0, 0, 0, .62), transparent 100%);
}
/* from 1280 px the bill is a caption in the corner, so the shade is too and the room stays clear
   (narrower, the kicker runs out of the pool: 3.8:1 at 1024×768, 2.8 at 768×1024). Tuned by
   measurement, not by eye: each word on the photograph against the ground under it, at the seven
   sizes in CLAUDE.md (~/.cache/lucent-hero/veil.py). */
@media (min-width: 1280px) {
  .hero__veil {
    background:
      linear-gradient(180deg, rgba(0, 0, 0, .68), rgba(0, 0, 0, .58) 6%, transparent 16%),
      radial-gradient(38% 34% at 100% 0%, rgba(0, 0, 0, .3), transparent 100%),
      linear-gradient(0deg, rgba(0, 0, 0, .42), transparent 16%),
      radial-gradient(90% 90% at 0% 100%, rgba(0, 0, 0, .84), rgba(0, 0, 0, .68) 42%, rgba(0, 0, 0, .32) 74%, transparent 100%);
  }
}

/* the bill: a mono kicker (what Lucent is), one sentence in two lines and one button, kept
   small. The photograph is the first screen; the bill is a caption standing on its floor, low,
   under the island counters at 16:9 as well as 16:10 (hence the height cap on the size). The
   kicker can sit above the sentence now because the bill starts low enough that it lands on
   clear floor; the earlier, taller bill put it on a counter. The turn is weight: the set-up in a
   regular weight, the payoff heavy and a touch narrower. */
.hero__bill {
  position: absolute; left: var(--pad); right: var(--pad); bottom: clamp(84px, 10.5vh, 100px);
  display: grid; justify-items: start;
}
.hero__kick {
  margin-bottom: clamp(10px, 1.6vh, 16px); font-size: 12px; color: rgba(244, 244, 244, .84);   /* .78 until the promise line lifted it: 4.48:1 at 1400×769 in Firefox */
  text-shadow: 0 1px 2px rgba(0, 0, 0, .5);
}
.hero__h {
  font-family: "Archivo", sans-serif; font-variation-settings: "wght" 480, "wdth" 100;
  font-size: clamp(19px, min(2.3vw, 3.9vh), 32px); line-height: 1.18; letter-spacing: -.014em;
  color: rgba(244, 244, 244, .88); max-width: 34em;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 3px 14px rgba(0, 0, 0, .4);
}
.hero__l { display: block; padding-bottom: .04em; }
.hero__l--turn { font-variation-settings: "wght" 720, "wdth" 96; letter-spacing: -.02em; color: var(--paper); }
/* the promise (founder, 2026-10-13: "a fading in animation on the front page no scrolling needed", "a 'we increase
   your conversions so you get more sales' kinda line"): one plain line under the sentence, with no number in it
   (Lucent has no client results to quote). It fades in on its own beat once the sentence has landed, slower than
   the rest, so it reads as the answer to "Your website decides whose." */
.hero__sub {
  margin-top: clamp(8px, 1.4vh, 14px); max-width: 40em;   /* one line from 768 px; balanced over two on a phone */
  font-size: clamp(15px, min(1.35vw, 2.4vh), 19px); line-height: 1.4; letter-spacing: -.005em;
  color: rgba(244, 244, 244, .86); text-wrap: balance;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 3px 14px rgba(0, 0, 0, .4);
  transition: opacity 1.5s var(--d, 0s) var(--ease), translate 1.5s var(--d, 0s) var(--ease);
}
html[data-arrive] .hero__sub { opacity: 0; translate: 0 8px; }
.hero__row { display: flex; flex-wrap: wrap; gap: 14px; margin-top: clamp(16px, 2.2vh, 22px); }
/* arrival: the kicker rises, each line wipes in from the left, then the button rises */
.hero__kick, .hero__row {
  transition: opacity .8s var(--d, 0s) var(--ease), translate .8s var(--d, 0s) var(--ease);
}
.hero__l {
  clip-path: inset(-.3em -.5em -.45em -.25em);   /* room for the descenders and the shadow */
  transition: clip-path 1.1s var(--d, 0s) var(--ease-out), translate 1.1s var(--d, 0s) var(--ease-out);
}
html[data-arrive] .hero__kick, html[data-arrive] .hero__row { opacity: 0; translate: 0 14px; }
html[data-arrive] .hero__l { clip-path: inset(-.3em 100% -.45em -.25em); translate: -12px 0; }

/* the edges */
.hero__foot {
  position: absolute; left: var(--pad); right: var(--pad); bottom: 30px;
  display: grid; grid-template-columns: 1fr auto; align-items: end; gap: 20px;
  transition: opacity .8s .2s var(--ease), translate .8s .2s var(--ease);
}
html[data-arrive] .hero__foot { opacity: 0; translate: 0 14px; }
.hero__tag { color: rgba(244, 244, 244, .64); }
.hero__scroll { justify-self: end; display: inline-flex; align-items: center; gap: 12px; }
.hero__scroll i { position: relative; display: block; width: 48px; height: 1px; background: rgba(244, 244, 244, .35); overflow: clip; }
.hero__scroll i::after { content: ""; position: absolute; inset: 0; background: var(--paper); transform: translateX(-100%); animation: cue 2s var(--ease-in-out) infinite; }
@keyframes cue { 0% { transform: translateX(-100%); } 55%, 100% { transform: translateX(100%); } }

/* ==========================================================================
   THE STATEMENT — paper climbs over the photograph; the headline fills with
   ink as it arrives (--p is written by js/site.js, and is 1 without it).
   ========================================================================== */
/* the sheet is symmetric top to bottom: the head rule sits as far from the top edge as the foot
   rule from the bottom (--pt plus the kicker's line, which is why the bottom pads 7 px more: the
   foot rule runs through the buttons' middle, 25 px down), and --gap separates each rule from
   the words beside it */
.state {
  position: relative; z-index: 2; background: var(--paper); color: var(--text);
  --pt: clamp(28px, 5vh, 52px); --gap: clamp(56px, 10vh, 112px);
  padding-block: var(--pt) calc(var(--pt) + 7px);
  border-radius: 22px 22px 0 0; box-shadow: 0 -40px 80px -40px rgba(0, 0, 0, .5);
}
.state .wrap { text-align: center; }
/* the sheet's head: the kicker spread to the two edges over one hairline, as on a spec sheet */
.state__kick {
  display: flex; justify-content: space-between; gap: 16px;
  color: var(--dim); padding-bottom: 14px; border-bottom: 1px solid var(--rule);
  margin-bottom: var(--gap);
}
.state__h {
  font-family: "Archivo", sans-serif; font-variation-settings: "wght" 780, "wdth" 88;
  font-size: clamp(40px, 8.4vw, 128px); line-height: .92; letter-spacing: -.04em; word-spacing: .05em;
  margin-inline: auto; text-wrap: balance;
}
.state__l {
  display: block; padding-bottom: .04em;
  background: linear-gradient(90deg, var(--ink) calc(var(--p, 1) * 100%), #c6c6c6 calc(var(--p, 1) * 100% + 7%));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
/* the turn: one word set at the site's regular weight and full width, nothing else changes */
.state__em { font-weight: inherit; font-variation-settings: "wght" 480, "wdth" 100; letter-spacing: -.015em; }
/* on the page's axis: the two sentences stack with a hairline tick between them. (Side by side
   they could never be symmetric: the halves differ in length, so either the hairline or the pair
   sat off centre.) */
.state__sub {
  font-size: clamp(18px, 1.7vw, 24px); font-variation-settings: "wght" 480; letter-spacing: -.01em;
  margin-top: clamp(32px, 5vh, 52px);
}
.state__sub span { display: block; }
.state__sub span:first-child::after { content: ""; display: block; width: 1px; height: 1.1em; margin: .4em auto; background: var(--rule-strong); }
/* the sheet's foot mirrors its head: one hairline to both edges, broken by the two buttons, which
   share one width so the break sits on the axis */
.state__row {
  display: grid; grid-template-columns: 1fr auto auto 1fr; align-items: center; column-gap: 14px;
  margin-top: calc(var(--gap) - 25px);
}
.state__row::before, .state__row::after { content: ""; height: 1px; background: var(--rule); }
.state__row::before { margin-right: 18px; }
.state__row::after { margin-left: 18px; }
.state__row .btn { min-width: 184px; }

/* ==========================================================================
   THE MORPH — LUCENT throws six showrooms (a lit sign, a floor, a camera that tilts down)
   ========================================================================== */
.morph {
  position: relative;
  height: 338vh;   /* its span is 260vh × SPAN_SHARE in js/morph.js (the rise runs quicker): re-derive with RISE_PACE */
  color: var(--bone);
  background: linear-gradient(180deg, var(--ink-2), var(--ink) 22%, var(--ink) 78%, var(--ink-2));
}
.morph__stage {
  position: sticky; top: 0; height: 100svh; overflow: clip;
  display: grid; place-items: center;
  padding-bottom: clamp(48px, 9vh, 108px);
  perspective: 1500px; perspective-origin: 50% 46%;
}
.morph__glow {
  position: absolute; inset: 0;
  background: radial-gradient(58% 42% at 50% 46%, rgba(255, 255, 255, .1), transparent 68%);
  opacity: var(--glow, .5); transition: opacity .4s linear;
}
.morph__stage[data-failing] .morph__glow { transition: none; }   /* the sign failing: the glow steps with it (js/morph.js) */
.morph__caption {
  position: absolute; top: clamp(76px, 10vh, 112px); left: 50%; translate: -50% 0;
  display: grid; text-align: center; white-space: nowrap; color: var(--bone-dim);
}
.morph__caption span { grid-area: 1 / 1; opacity: 0; transition: opacity .45s var(--ease); }
.morph__caption span.is-on { opacity: 1; }

/* the scene: the word is a 3D space (the stage's perspective reaches into it).
   The world turns about the sign's centre line as the camera tilts down; the
   wall carries the six glyphs at z = 0; the floor carries the six panes and
   is pitched off the wall at the sign's foot (js/morph.js writes both
   transforms every frame, and none once the grid has landed). */
.word {
  position: relative; width: min(1180px, 92vw); height: min(64vh, 660px);
  margin-top: 3vh; transform-style: preserve-3d; will-change: transform;
}
.morph__world, .morph__wall, .morph__floor {
  position: absolute; left: 50%; top: 50%; width: 0; height: 0;
  transform-style: preserve-3d;
}
.morph__world, .morph__floor { will-change: transform; }
.mark { position: absolute; width: 0; height: 0; pointer-events: none; }

/* a glyph: the room cut by the letter. The mask sits on the box; inside it the
   picture, an ink veil whose opacity is the darkness (the light is opacity, so
   no filter animates on a photograph) and a white flare for the throw. */
.glyph {
  position: absolute; display: block; overflow: clip;
  -webkit-mask-image: var(--mask-letter); mask-image: var(--mask-letter);
  -webkit-mask-size: var(--mask-size, auto 95.5%); mask-size: var(--mask-size, auto 95.5%);
  -webkit-mask-position: var(--anchor-x) var(--anchor-y); mask-position: var(--anchor-x) var(--anchor-y);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  backface-visibility: hidden;
}
.glyph i { position: absolute; inset: 0; display: block; }
.glyph:not(.glyph--mirror) { will-change: transform; }   /* the kick as it throws (js/morph.js) */
.glyph__plate { background: rgba(255, 255, 255, .075); }   /* the unlit sign, seen once the room has left the letter */
.glyph__img {
  background-image: var(--src); background-size: cover; background-position: 50% 50%;
  will-change: transform;   /* it slides down out of the letter with the throw (js/morph.js) */
}
.glyph__veil { background: var(--ink); opacity: .62; will-change: opacity; }
.glyph__flare { background: var(--white); opacity: 0; will-change: opacity; }
/* the light the sign spills on the wall: the letter in white, blurred. The
   blur is on the box and the mask on its child: filter runs before mask, so
   a blur on the masked box itself would be clipped by its own letter. */
.halo {
  position: absolute; display: block; opacity: 0; pointer-events: none;
  filter: blur(22px); will-change: opacity;
}
.halo__in {
  position: absolute; inset: 0; display: block; background: var(--white);
  -webkit-mask-image: var(--mask-letter); mask-image: var(--mask-letter);
  -webkit-mask-size: var(--mask-size, auto 95.5%); mask-size: var(--mask-size, auto 95.5%);
  -webkit-mask-position: var(--anchor-x) var(--anchor-y); mask-position: var(--anchor-x) var(--anchor-y);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
/* the polished floor: the sign again below the foot line, upside down, in the wall's own plane,
   fading with depth. One box for the six, so the fade is one mask and the light one opacity. */
.morph__mirror {
  position: absolute; opacity: 0; pointer-events: none; will-change: opacity;
  -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, .9), transparent 82%);
  mask-image: linear-gradient(to bottom, rgba(0, 0, 0, .9), transparent 82%);
}
.glyph--mirror { transform: scaleY(-1); }
.glyph--mirror .glyph__img { filter: brightness(.8); }
.glyph[data-letter="L"], .halo__in[data-letter="L"] { --mask-letter: url("assets/masks/L.svg"); }
.glyph[data-letter="U"], .halo__in[data-letter="U"] { --mask-letter: url("assets/masks/U.svg"); }
.glyph[data-letter="C"], .halo__in[data-letter="C"] { --mask-letter: url("assets/masks/C.svg"); }
.glyph[data-letter="E"], .halo__in[data-letter="E"] { --mask-letter: url("assets/masks/E.svg"); }
.glyph[data-letter="N"], .halo__in[data-letter="N"] { --mask-letter: url("assets/masks/N.svg"); }
.glyph[data-letter="T"], .halo__in[data-letter="T"] { --mask-letter: url("assets/masks/T.svg"); }

/* a pane: the room as a pool of light on the floor, then a card on the wall.
   A hard rectangle, never a soft radial. The picture develops by opacity;
   the flash is the light landing. */
.pane {
  position: absolute; width: var(--cw, 240px); height: var(--ch, 240px);
  overflow: clip; backface-visibility: hidden; visibility: hidden;
  will-change: transform;   /* the rise: it sits up off the floor about its centre, then lies back (js/morph.js) */
}
.pane__img {
  position: absolute; inset: 0; opacity: 0;
  background-image: var(--src); background-size: cover; background-position: 50% 50%;
  will-change: opacity, clip-path;
  clip-path: inset(0 0 calc((1 - var(--dev, 0)) * 100%) 0);   /* the light pours from the wall's foot */
  transition: transform .6s var(--ease);
}
/* once the grid has landed, a card answers the pointer: the room eases in a
   little and the edge comes up. Only then, and only for a fine pointer. */
@media (hover: hover) and (pointer: fine) {
  /* data-over, not :hover: under the pointer parallax the 3D hit test misses parts of a card, so js/rooms.js marks
     the card whose box holds the pointer (and the stage, for the cursor) */
  .morph__stage[data-landed] .pane[data-over] .pane__img { transform: scale(1.04); }
  .morph__stage[data-landed] .pane[data-over] .pane__sheen { border-color: rgba(255, 255, 255, .42); }
  .morph__stage[data-landed][data-over] { cursor: pointer; }
  .pane__sheen { transition: border-color .4s var(--ease); }
}
.pane__flash { position: absolute; inset: 0; background: var(--white); opacity: 0; pointer-events: none; will-change: opacity; }
.pane__front {
  position: absolute; left: 0; right: 0; top: calc(var(--dev, 0) * 100% - 1px); height: 2px;
  background: var(--white); opacity: 0; pointer-events: none; will-change: opacity, top;
}
.pane__sheen {
  position: absolute; inset: 0; pointer-events: none;
  opacity: var(--card, 0);
  border: 1px solid rgba(255, 255, 255, .16);
  background: linear-gradient(0deg, rgba(11,11,11,.82), transparent 44%);
}
.pane__cap {
  position: absolute; left: 16px; right: 16px; bottom: 14px;
  display: flex; flex-direction: column; gap: 3px;
  opacity: var(--cap, 0); transform: translateY(calc((1 - var(--cap, 0)) * 10px));
}
.pane__cap b {
  font-family: "Archivo", sans-serif; font-variation-settings: "wght" 640, "wdth" 100;
  font-size: clamp(.82rem, 1.2vw, 1.02rem); letter-spacing: -.02em;
}
.pane__cap .mono { font-size: clamp(9px, .8vw, .6rem); color: var(--bone-dim); line-height: 1.4; }   /* 8 px up to 1024 wide was the smallest type on the page by far */

/* the beams: one canvas over the stage, drawn only while a letter throws */
.morph__beams { position: absolute; left: 0; top: 0; pointer-events: none; }
.morph__foot {
  position: absolute; bottom: clamp(30px, 6vh, 64px); left: 50%; translate: -50% 0;
  width: min(60ch, 88vw); text-align: center; color: var(--bone-dim);
  font-size: .95rem; line-height: 1.55; text-wrap: pretty;   /* never a lone word on the last line */
  opacity: var(--foot, 0); transition: opacity .4s linear;
}
.noscript-grid { display: grid; gap: 20px; padding-block: 60px; }

/* ==========================================================================
   A ROOM'S HOMEPAGE (js/rooms.js) — once the grid has landed, a card opens the
   homepage we'd build around its room: the photograph flies from the card to
   the homepage's picture, a paper page climbs up behind it (the statement's
   gesture) and the words set in. Its own bar, ink over the top, says it is a
   concept, steps through the six and closes it. The homepage keeps the page's
   paper, ink and grey in a voice of its own: a wide tracked mark, a lighter
   display weight. The photographs are 960 px squares, so the picture is at most
   55% of the width and never runs past 1.1× at 1920.
   ========================================================================== */
/* the card: the whole square is the button, and a small pill says what it opens. Never before the landing */
.pane__open {
  position: absolute; inset: 0; z-index: 1; width: 100%; margin: 0; padding: 0; border: 0;
  background: none; color: var(--paper); font: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.morph__stage:not([data-landed]) .pane__open { visibility: hidden; }
.pane__open:focus-visible { outline: 2px solid var(--paper); outline-offset: -6px; }   /* inside the card: the pane clips */
.pane__go {
  position: absolute; top: 12px; right: 12px; display: inline-flex; align-items: center; gap: 8px;
  height: 32px; padding: 0 13px 0 11px; border: 1px solid rgba(255, 255, 255, .36); border-radius: 999px;
  background: var(--ink); font-size: 10.5px; line-height: 1; opacity: var(--cap, 0);
  transition: background .3s var(--ease), color .3s var(--ease), border-color .3s var(--ease);
}
.pane__open:focus-visible .pane__go { background: var(--paper); color: var(--ink); border-color: var(--paper); }
@media (hover: hover) and (pointer: fine) {
  .morph__stage[data-landed] .pane[data-over] .pane__go { background: var(--paper); color: var(--ink); border-color: var(--paper); }
}
@media (max-width: 900px) {
  .pane__go { top: 8px; right: 8px; width: 32px; padding: 0; justify-content: center; }
  .pane__go span { display: none; }
}
.pane[data-away] .pane__img { visibility: hidden; }   /* its photograph is in flight */

/* the view: a modal dialog over everything, the masthead included (js/rooms.js holds the page still under it) */
.site {
  --bar: 56px; --head: 72px;
  position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none;
  margin: 0; padding: 0; border: 0; overflow: hidden; background: transparent; color: var(--text);
}
.site::backdrop { background: transparent; }
.site[data-wait] { opacity: 0; }   /* the frame or two while the photograph decodes */
.site__ground { position: absolute; inset: 0; background: var(--ink); }

/* the bar is Lucent's, not the homepage's: what this is, the six, and the way out */
.site__bar {
  position: absolute; inset: 0 0 auto; z-index: 3; height: var(--bar);
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding-inline: var(--pad); background: var(--ink); color: var(--bone); border-bottom: 1px solid var(--bone-rule);
}
.site__what { display: flex; align-items: center; gap: 14px; min-width: 0; white-space: nowrap; }
.site__what .mono { color: var(--bone-dim); }
.site__room { overflow: hidden; text-overflow: ellipsis; font-size: 14px; font-variation-settings: "wght" 480; }
.site__room::before { content: ""; display: inline-block; width: 1px; height: 12px; margin-right: 14px; background: var(--bone-rule); vertical-align: -1px; }
.site__ctrl { display: flex; align-items: center; gap: 4px; }
.site__count { min-width: 5.4em; text-align: center; color: var(--bone-dim); }
.site__step, .site__close {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px; min-width: 44px; height: 44px;
  padding: 0; border: 1px solid transparent; border-radius: 999px; background: none; color: var(--bone); cursor: pointer;
  transition: background .25s var(--ease), color .25s var(--ease), border-color .25s var(--ease);
}
.site__step:hover { border-color: rgba(255, 255, 255, .4); }
.site__close { margin-left: 10px; padding: 0 18px; border-color: rgba(255, 255, 255, .4); }
.site__close:hover { background: var(--paper); color: var(--ink); border-color: var(--paper); }

/* the homepage */
.site__page { position: absolute; inset: var(--bar) 0 0; overflow: hidden auto; overscroll-behavior: contain; }
.site__doc { position: relative; display: flex; flex-direction: column; min-height: 100%; }
.site__sheet { position: absolute; inset: 0; background: var(--paper); }
.site__head {
  position: relative; flex: none; height: var(--head);
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 20px;
  padding-inline: var(--pad); border-bottom: 1px solid var(--rule);
}
.site__mark { justify-self: start; font-variation-settings: "wght" 760, "wdth" 112; font-size: 15px; letter-spacing: .2em; text-transform: uppercase; color: var(--ink); }
.site__links { display: flex; gap: 30px; color: var(--dim); }
.site__tel { justify-self: end; color: var(--text); }
.site__hero { position: relative; flex: 1 0 auto; min-height: 480px; display: grid; grid-template-columns: 9fr 11fr; }
.site__copy { align-self: center; display: grid; justify-items: start; padding: 36px clamp(28px, 4vw, 72px) 36px var(--pad); }
.site__kick { color: var(--dim); }
.site__h {
  margin-top: 18px; color: var(--ink);
  font-variation-settings: "wght" 560, "wdth" 94; font-size: clamp(34px, min(3.6vw, 7.4vh), 72px);   /* each line is at most 20 characters, which fits the column at 3.6vw */
  line-height: 1.02; letter-spacing: -.03em; text-wrap: balance;
}
.site__l { display: block; padding-bottom: .05em; }
.site__say { margin-top: 22px; max-width: 38ch; font-size: 18px; line-height: 1.5; color: #4a4a4a; text-wrap: pretty; }
.site__cta { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 22px; margin-top: 32px; }
.site__book {
  display: inline-flex; align-items: center; justify-content: center; min-height: 54px; padding: 0 28px;
  border: 1px solid var(--ink); border-radius: 999px; background: var(--ink); color: var(--paper);
  font-size: 16px; font-variation-settings: "wght" 560; text-decoration: none; white-space: nowrap;
  transition: background .25s var(--ease), border-color .25s var(--ease);
}
.site__book:hover { background: #262626; border-color: #262626; }
.site__book:focus-visible { outline-color: var(--ink); }
.site__book--line { background: none; color: var(--ink); border-color: var(--rule-strong); }
.site__call { font-size: 16px; }
.site__next { margin-top: 14px; font-size: 16px; color: var(--dim); }
.site__foot {
  display: flex; justify-content: space-between; align-items: center; gap: 16px; width: 100%;
  margin-top: clamp(28px, 6vh, 56px); padding-top: 16px; border-top: 1px solid var(--rule);
}
.site__open { display: inline-flex; align-items: center; gap: 10px; font-size: 15px; }
.site__open::before { content: ""; flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--ink); }
.site[data-closed] .site__open::before { background: none; box-shadow: inset 0 0 0 1.5px var(--ink); }
.site__tag { color: var(--dim); }
/* the picture: the photograph's whole square, covering its frame and clipped to it (--s, --ix, --iy
   from the script), so the flight opens the clip out of the card's own square */
.site__photo { position: relative; margin: 0; min-height: 0; }
.site:not([data-flying]) .site__photo { overflow: clip; }   /* the square overhangs its frame, and a clip-path leaves the overhang scrollable */
.site__img {
  position: absolute; left: 50%; top: 50%; width: var(--s, 100%); height: var(--s, 100%); max-width: none;
  translate: -50% -50%; clip-path: inset(var(--iy, 0px) var(--ix, 0px)); object-fit: cover; will-change: transform, clip-path;
}
.site__sticky { display: none; }

/* the words set in once the paper is up, and go at once when it leaves or the room changes */
.site__head, .site__kick, .site__say, .site__cta, .site__next, .site__foot, .site__sticky {
  opacity: 0; translate: 0 12px; transition: opacity .16s linear, translate .16s linear;
}
.site__l { clip-path: inset(-.3em 100% -.35em -.1em); transition: clip-path .16s linear; }
.site[data-shown] :is(.site__head, .site__kick, .site__say, .site__cta, .site__next, .site__foot, .site__sticky) {
  opacity: 1; translate: none;
  transition: opacity .7s var(--d, 0s) var(--ease), translate .7s var(--d, 0s) var(--ease);
}
.site[data-shown] .site__l { clip-path: inset(-.3em -.5em -.35em -.1em); transition: clip-path 1s var(--d, 0s) var(--ease-out); }
.site__kick { --d: .04s; }
.site__l { --d: .1s; }
.site__l + .site__l { --d: .22s; }
.site__say { --d: .32s; }
.site__cta { --d: .42s; }
.site__next { --d: .48s; }
.site__foot, .site__sticky { --d: .56s; }

@media (max-width: 899px) {
  /* one column: the picture on top, the words under it, and the sticky bar with Book and Call */
  .site { --bar: 52px; --head: 60px; }
  .site__room { display: none; }
  .site__count { position: absolute; width: 1px; height: 1px; min-width: 0; overflow: hidden; clip: rect(0 0 0 0); }   /* still read out */
  .site__close { width: 44px; padding: 0; font-size: 0; letter-spacing: 0; }
  .site__head { grid-template-columns: 1fr auto; }
  .site__links { display: none; }
  .site__hero { display: flex; flex-direction: column; min-height: 0; }
  .site__photo { order: -1; flex: none; aspect-ratio: 3 / 2; }
  .site__copy { align-self: stretch; padding: 30px var(--pad) 40px; }
  .site__h { font-size: clamp(30px, 8.4vw, 46px); }
  .site__foot { margin-top: 32px; }
  .site__sticky {
    display: grid; grid-template-columns: 1fr auto; gap: 10px; flex: none;
    position: sticky; bottom: 0; z-index: 1; margin-top: auto;
    padding: 10px var(--pad) calc(10px + env(safe-area-inset-bottom)); background: var(--paper); border-top: 1px solid var(--rule);
  }
  .site__sticky .site__book { min-height: 50px; padding: 0 22px; }
}
@media (max-width: 599px) {
  .site__photo { aspect-ratio: 1; }
  .site__cta .site__book { flex: 1 1 100%; }
  .site__tel { font-size: 10.5px; letter-spacing: .08em; }
}
@media (max-width: 380px) {
  .site__what .mono, .site__mark { font-size: 10.5px; letter-spacing: .08em; }
  .site__ctrl { gap: 0; }
  .site__close { margin-left: 4px; }
}
@media (prefers-reduced-motion: reduce) {
  .site__head, .site__kick, .site__l, .site__say, .site__cta, .site__next, .site__foot, .site__sticky, .pane__go { transition: none; }
}

/* ==========================================================================
   BOOKING — the demonstration, on a phone, standing in the room it books.
   The phone is real size, ink, with a lock screen that shows the visitor's
   own time and one notification; tapping it opens a thread in which the site
   asks the four questions (room, budget, a real date, a slot) and the visitor
   answers with quick replies. Every bubble is timestamped to the second.
   Built from the founder's picks (2026-09-10: "I love 1 and 3" of the
   fifteen cuts in Documents/Code/temp). No wallpaper on the lock screen: the
   phone is one ink object.
   The chapter is one photograph, full bleed (2026-09-14, the third staging:
   the founder called the phone laid over a framed room "gimmicky ... phone
   overlay on an image with white borders around it"): a product shot of a
   phone standing on the display kitchen's stone worktop at night, its screen
   lighting the stone, the showroom out of focus behind. The live phone is
   laid exactly over the photographed one: js/site.js (fit) sizes the phone
   to the screen, then scales and places the photograph to match (--iw, --ih,
   --ix, --iy; the phone's --px, --py, --pw, --ph, --pz), so the contact, the
   light on the stone, the scale and the handset itself are the photograph's:
   only the screen is drawn. From 1024 px
   (RESPONSIVE) the stage is a screen tall, the words on it round the phone.
   When the thread books, the diary card lands on the diary's hour. The
   photograph stays night throughout (2026-09-14, the founder: "dont split the
   day and night, do one or the other, preferably night"). On one column the
   words stand on the ink above the photograph.
   ========================================================================== */
.book { position: relative; background: var(--ink); color: var(--paper); overflow-x: clip; }
.book__grid {
  --gut: clamp(28px, 3vw, 48px); --stage-h: calc(100vw * 1440 / 1020);   /* before the fit, and with no script: the tall crop's own shape */
  --iw: 100%; --ih: 100%; --ix: 0px; --iy: 0px; --px: 0px; --py: 0px; --pw: 375px; --ph: 740px; --pz: 1; --mx: 0px; --my: 0px;
  position: relative; display: grid; grid-template-columns: minmax(0, 1fr);
}
.book__copy { grid-row: 1; padding: clamp(84px, 13vh, 150px) var(--pad) clamp(32px, 5vh, 52px); }
.book__l { display: block; text-wrap: balance; }
.book__l--turn { font-variation-settings: "wght" 480, "wdth" 100; letter-spacing: -.014em; }   /* the turn, in weight and width, as the hero's bill and Start's reply */
.book__demo { color: var(--bone-dim); margin-top: 28px; }

/* the photograph: the night, with its sign and tag. The night, the diary card and the phone share one
   grid cell. The night develops out of the dark as the chapter arrives and the phone's screen wakes over
   the photographed one after it; neither slides, because the photograph must never move under the phone
   laid on it. */
.night, .night__slip, .book__grid > .phone { grid-row: 2; grid-column: 1; }
.night { position: relative; isolation: isolate; background: var(--ink); }
.night__frame { position: relative; display: block; height: var(--stage-h); overflow: clip; }   /* the photograph's box; on one column the caption follows it */
.night__img { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; object-fit: cover; object-position: 49.9% 0%; }
.book__grid[data-fit] .night__img { inset: auto; left: var(--ix); top: var(--iy); width: var(--iw); height: var(--ih); }
.night .night__img { transition: opacity 1.4s .45s var(--ease); }
html[data-js] .night[data-rise] { translate: none; }
html[data-js] .night[data-rise]:not(.is-in) .night__img { opacity: 0; }
/* the screen's light on the stone, over the photograph's own: it swells as the notification's halo
   goes out (the same 2.8 s from the same 0.8 s) and holds while the thread is open */
.night__glow {
  position: absolute; z-index: 1; pointer-events: none;
  left: calc(var(--px) + var(--pw) / 2); top: calc(var(--py) + var(--ph) - 2px); translate: -50% 0;
  width: calc(var(--pw) * 1.7); height: calc(var(--ph) * .36);
  background: radial-gradient(50% 100% at 50% 0%, rgba(255, 255, 255, .24), rgba(255, 255, 255, .08) 55%, rgba(255, 255, 255, 0));
  mix-blend-mode: screen; opacity: .45; animation: night-glow 2.8s linear .8s infinite;
}
.book__grid:has(.phone__chat:not([hidden])) .night__glow { animation: none; opacity: .75; }
html:not([data-js]) .night__glow { display: none; }
@keyframes night-glow { 0%, 100% { opacity: .45; } 9% { opacity: 1; } }
/* the showroom's sign and the photograph's tag. On the stage (RESPONSIVE) they stand on its edges as the
   first screen's do, in paper. On one column the photograph carries only the phone: on its stone the tag
   and the sign measured 2.3 to 4.2:1, so the tag is the photograph's caption on the ink under it, and the
   sign is the stage's alone */
.night__sign {
  position: absolute; right: var(--pad); z-index: 3; margin: 0;
  display: none; align-items: center; gap: 10px; color: var(--paper);
}
.night__sign i { width: 6px; height: 6px; border-radius: 50%; background: #6a6a6a; }
.night__at { color: var(--bone-dim); }
.night__tag { margin: 0; padding: 14px var(--pad) 0; color: var(--bone-dim); transition: opacity .3s var(--ease); }
html:has(.rest[data-on]) .night__tag { opacity: 0; }   /* a keyboard rest's cue takes the tag's corner while it is up */
.night__slip {   /* the diary: what the visitor picked, as the showroom finds it at nine; on the stage only */
  position: relative; z-index: 4; display: none; align-self: end; justify-self: end;
  gap: 2px; padding: 13px 16px 14px; border-radius: 6px; background: var(--paper); color: var(--ink);
  box-shadow: 0 24px 48px -22px rgba(0, 0, 0, .7);
  opacity: 0; translate: 0 12px; visibility: hidden;
  transition: opacity .25s var(--ease), translate .25s var(--ease), visibility 0s .25s;
}
.night__slip .mono { color: var(--dim); font-size: 10.5px; }
.night__slip b { margin-top: 4px; font-variation-settings: "wght" 700, "wdth" 94; font-size: 20px; line-height: 1.15; letter-spacing: -.015em; }
.night__slip-what { margin-top: 2px; font-size: 14.5px; line-height: 1.4; }
.night__slip-at { font-size: 13px; color: var(--dim); line-height: 1.4; }
.book__grid[data-lit] .night__slip { opacity: 1; translate: none; visibility: visible; transition: opacity .5s .5s var(--ease), translate .7s .5s var(--ease-out), visibility 0s .5s; }   /* a beat after the confirmation lands on the phone */

/* the ledgers either side of the phone (2026-09-14, the founder: "have some animations on the side fill it up
   make it look nice and full"). Under the evening's words, the four answers; under the morning's, the day's
   diary from 9:00 to 17:00, an hour to a line, the answers' rules level with every other hour. They follow
   the thread (js/site.js, ledgers): an answer rolls into its row and, a beat later across the phone, the day
   rolls into the diary's head and the time slides the entry to its hour (--at). With the lock screen up and
   the stage on screen they play an example through, a row at a time. They are paper on the photograph;
   once it books the entry fills and the diary card lands on its hour. The labels are #c9c9c9: #a6a6a6 on
   the first photograph's tap and vase measured 3.6:1. Every rule draws out from the phone as the
   screen wakes. On the stage only (RESPONSIVE) */
.ledger { display: none; }
html:not([data-js]) .ledger { display: none; }
.ledger__hd { align-self: start; color: var(--paper); }
.ledger__row, .ledger__hours li { position: relative; }
.ledger__row::before, .ledger__hours li::before, .ledger::after {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px; background: rgba(255, 255, 255, .22); transform-origin: right;
}
.ledger--diary .ledger__hours li::before, .ledger--diary::after { transform-origin: left; }
.ledger::after { --i: 8; top: auto; bottom: 0; }
.ledger__row { display: grid; align-content: center; justify-items: start; gap: 3px; }   /* the label and its answer as one pair between the rules: at the rules' two ends the answer read as the next label's */
.ledger__k { position: relative; color: #c9c9c9; font-size: 10.5px; }
.ledger__v { display: inline-grid; overflow: clip; vertical-align: top; }
.ledger__v > i { grid-area: 1 / 1; font-style: normal; white-space: nowrap; }
.ledger__row .ledger__v { font-size: calc(var(--hfs, 26px) * .7); font-variation-settings: "wght" 480, "wdth" 100; line-height: 1.2; letter-spacing: -.014em; }
.ledger__row[data-now] .ledger__k::before {   /* the question the phone is asking, with the notification's dot */
  content: ""; position: absolute; left: -16px; top: calc(50% - 3px); width: 6px; height: 6px; border-radius: 50%;
  background: var(--paper); animation: note-dot 1.8s var(--ease-in-out) infinite;
}
.ledger--diary .ledger__hd { justify-self: end; }
.ledger--diary .ledger__hd .ledger__v > i { text-align: right; }   /* a wider date rolling in leaves the old one where it was */
.ledger__day { position: relative; }
.ledger__hours { display: grid; grid-template-rows: repeat(8, 1fr); height: 100%; margin: 0; padding: 0; list-style: none; color: #c9c9c9; font-size: 10.5px; }
.ledger__hours li { padding-top: 6px; text-align: right; }   /* at the outer end, as the answers' labels are: at the inner end, on the lit oak panel's spill, "12:00" measured 2.7:1 */
.ledger__slot {   /* the entry: an hour tall, inside the hours, carried to its hour by --at (hours from 9:00) */
  position: absolute; top: 0; left: 0; right: 64px; height: calc(100% / 8); margin: 0; padding: 5px 0;
  opacity: 0; translate: 0 calc(var(--at, 2.5) * 100%);
  transition: translate .9s var(--ease-out), opacity .4s var(--ease);
}
.book__grid[data-slot] .ledger__slot { opacity: 1; }
.ledger__entry {
  display: flex; align-items: center; height: 100%; padding: 0 12px; border-radius: 6px;
  box-shadow: inset 0 0 0 1px rgba(244, 244, 244, .55); color: var(--paper);
  font-size: 13.5px; font-variation-settings: "wght" 480; line-height: 1.2;
  transition: background .3s var(--ease), color .3s var(--ease);
}
.ledger__entry .ledger__v { flex: 1; min-width: 0; }
.ledger__entry .ledger__v > i { overflow: hidden; text-overflow: ellipsis; }
.book__grid[data-lit] .ledger__entry { background: var(--paper); color: var(--ink); }
/* the arrival: once the screen has woken (the phone's .8 s from 1.1 s), each rule draws out from the phone with
   its words, the evening's two hours at a time and the diary's an hour at a time (--i) */
.book__copy.is-in .ledger__hd, .book__copy.is-in .ledger__row, .book__copy.is-in .ledger__hours li { animation: ledger-in .9s var(--ease) calc(1.65s + var(--i, 0) * .07s) backwards; }
.book__copy.is-in .ledger__row::before, .book__copy.is-in .ledger__hours li::before, .book__copy.is-in .ledger::after { animation: ledger-rule 1.2s var(--ease-out) calc(1.5s + var(--i, 0) * .07s) backwards; }
.book__copy.is-in .ledger__entry { animation: ledger-in .9s var(--ease) 2.3s backwards; }
@keyframes ledger-in { from { opacity: 0; } }
@keyframes ledger-rule { from { transform: scaleX(0); } }
@media (prefers-reduced-motion: reduce) {
  .book__copy.is-in .ledger__hd, .book__copy.is-in .ledger__row, .book__copy.is-in .ledger__hours li, .book__copy.is-in .ledger__entry,
  .book__copy.is-in .ledger__row::before, .book__copy.is-in .ledger__hours li::before, .book__copy.is-in .ledger::after,
  .ledger__row[data-now] .ledger__k::before { animation: none; }
  .ledger__slot, .ledger__entry { transition: none; }
}

/* the live phone on the photographed one, placed by the fit. Its margins are in its own zoomed px (a
   margin on a zoomed element zooms with it), and its box is the photographed phone's shape (375, not
   360). Only the screen is drawn (2026-09-14, the founder: the drawn handset read as "an overlayed
   png"): the frame and its rim of light, the keys, and the foot on the stone are the photograph's, and
   the padding lays the live screen on the photographed one. Those insets are the photographed display's
   edges in the box's px (photograph px × 740 / 958, ~/.cache/lucent-stage/screen.py): 17.6 top, 16.5
   right, 17.3 bottom, 17.55 left. Its corners are fitted one by one (the photograph's are not all
   equal): 37.7 top left, 38 top right, 36.3 bottom right, 34 bottom left. The screen's edge softens
   over 2 px */
.book__grid > .phone {
  z-index: 5; align-self: start; justify-self: start; margin: var(--my) 0 0 var(--mx);
  width: 375px; aspect-ratio: 375 / 740; zoom: var(--pz);
  padding: 17.6px 16.5px 17.3px 17.55px; background: none; box-shadow: none;
}
.book__grid > .phone::before {   /* the island 10 px into the screen, as on the drawn handset, on the screen's axis: the padding is uneven */
  top: 27.6px; left: calc(17.55px + (100% - 17.55px - 16.5px) / 2);
}
.book__grid > .phone .phone__key { display: none; }
.book__grid > .phone .phone__screen { border-radius: 37.7px 38px 36.3px 34px; }
.book__grid > .phone .phone__screen::after {
  content: ""; position: absolute; inset: 0; z-index: 4; border-radius: inherit; pointer-events: none;
  box-shadow: inset 0 0 2px 1px rgba(0, 0, 0, .6);
}
.book__grid:not([data-fit]) > .phone { visibility: hidden; }
html[data-js] .book__grid > .phone[data-rise] { translate: none; transition: opacity .8s 1.1s var(--ease); }

/* the handset */
.phone {
  --island: 96px;
  position: relative; justify-self: center; width: min(360px, 88vw); aspect-ratio: 360 / 740;
  border-radius: 48px; background: var(--ink); padding: 12px;
  box-shadow: 0 60px 90px -50px rgba(0, 0, 0, .7), inset 0 0 0 1px #343434, inset 0 0 0 3px #1a1a1a;
}
.phone::before {   /* the island */
  content: ""; position: absolute; left: 50%; top: 22px; translate: -50% 0; z-index: 3;
  width: var(--island); height: 28px; border-radius: 999px; background: var(--ink);
}
.phone__key {   /* the keys on the frame: mute, volume, side */
  position: absolute; width: 3px; border-radius: 2px; background: #2e2e2e; box-shadow: inset 0 0 0 1px #3a3a3a;
}
.phone__key--mute { left: -3px; top: 16%; height: 28px; }
.phone__key--up { left: -3px; top: 23%; height: 52px; }
.phone__key--down { left: -3px; top: 31.5%; height: 52px; }
.phone__key--side { right: -3px; top: 26%; height: 80px; }
.phone__screen { position: relative; width: 100%; height: 100%; border-radius: 38px; overflow: clip; background: var(--ink-2); color: var(--paper); }

/* the status bar: the visitor's time, then signal, wi-fi and a full battery with its percentage printed in
   the cell. As on a phone with an island, the row runs on the island's centre line (the island's own 10 px
   and 28 px) and each side is centred in the ear beside it: the group (76 px in a 120 px ear) clears the
   island by 24 px. Print the percentage beside the cell and it runs under the island again
   (/var/tmp/lucent-stage/bar.py measures it) */
.phone__bar {
  position: absolute; left: 0; right: 0; top: 10px; z-index: 2; height: 28px;
  display: grid; grid-template-columns: 1fr var(--island) 1fr; justify-items: center; align-items: center;
}
.phone__bar-t { font-family: "Archivo", sans-serif; font-variation-settings: "wght" 620; font-size: 15px; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
.phone__bar-r { grid-column: 3; display: inline-flex; align-items: center; gap: 6px; fill: var(--paper); }
.phone__batt { position: relative; display: inline-flex; margin-right: 3px; }   /* the margin is the terminal's, so the group centres with it */
.phone__batt-i {
  position: relative; display: inline-block; width: 27px; height: 13px; border-radius: 4.2px;
  box-shadow: inset 0 0 0 1px rgba(244, 244, 244, .45);
}
.phone__batt-i::after {   /* the terminal */
  content: ""; position: absolute; right: -3px; top: 4px; width: 2px; height: 5px; border-radius: 0 2px 2px 0; background: rgba(244, 244, 244, .45);
}
.phone__batt-i b { position: absolute; inset: 0; width: calc(100% * var(--pc, 1)); border-radius: inherit; background: var(--paper); }
.phone__batt-pc {   /* the number on the cell, in the screen's own ink */
  position: absolute; inset: 0; display: grid; place-items: center; color: var(--ink-2);
  font-family: "Archivo", sans-serif; font-variation-settings: "wght" 700; font-size: 10px; letter-spacing: -.02em; line-height: 1; font-variant-numeric: tabular-nums;
}
.phone__home { position: absolute; left: 50%; bottom: 8px; translate: -50% 0; z-index: 2; width: 120px; height: 5px; border-radius: 999px; background: var(--paper); opacity: .5; }
.phone__view { position: absolute; inset: 0; display: grid; grid-template-rows: 1fr auto; transition: opacity .45s var(--ease), translate .55s var(--ease-out), visibility 0s; }
.phone__view[hidden] { display: grid !important; opacity: 0; pointer-events: none; visibility: hidden; translate: 0 24px; transition: opacity .45s var(--ease), translate .55s var(--ease-out), visibility 0s .55s; }

/* the lock screen. The whole screen opens the thread, so a tap anywhere
   works; the notification is still the button, still the thing to press */
.phone__lock { cursor: pointer; }
.phone__lockin { padding: 74px 22px 20px; display: grid; align-content: start; gap: 2px; text-align: center; }
.phone__date { font-family: "Archivo", sans-serif; font-variation-settings: "wght" 480; font-size: 16px; color: #d8d8d8; letter-spacing: -.005em; }
.phone__time {
  font-family: "Archivo", sans-serif; font-variation-settings: "wght" 300, "wdth" 96;
  font-size: 84px; line-height: 1; letter-spacing: -.04em; font-variant-numeric: tabular-nums;
}
.phone__note {   /* one left edge for everything: header row, title, body, pill */
  position: relative; margin: 30px 10px 0; text-align: left; background: #262626; border: 1px solid #2f2f2f; border-radius: 24px;
  padding: 15px 18px 15px; cursor: pointer; color: var(--paper); font: inherit;
  display: grid; gap: 0;
  box-shadow: 0 0 0 0 rgba(244, 244, 244, .5);
  transition: background .2s var(--ease), scale .2s var(--ease);
  animation: note-halo 2.8s var(--ease-out) .8s infinite;
}
.phone__note:hover, .phone__lock:hover .phone__note { background: #2c2c2c; }
.phone__note:active, .phone__lock:active .phone__note { scale: .985; }
.phone__note:focus-visible { outline: 2px solid var(--paper); outline-offset: 3px; }
.phone__note-k { display: flex; justify-content: space-between; font-size: 12.5px; font-variation-settings: "wght" 480; color: #9a9a9a; line-height: 1.2; }
.phone__note-now { display: inline-flex; align-items: center; gap: 6px; }
.phone__note-now i {   /* the unread dot */
  width: 6px; height: 6px; border-radius: 50%; background: var(--paper); animation: note-dot 1.8s var(--ease-in-out) infinite;
}
@keyframes note-dot { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
.phone__note b { margin-top: 9px; font-variation-settings: "wght" 560; font-size: 16.5px; line-height: 1.3; letter-spacing: -.01em; color: #f4f4f4; }
.phone__note-b { margin-top: 3px; font-size: 14px; font-variation-settings: "wght" 400; color: #c9c9c9; line-height: 1.5; }
.phone__note-go {   /* the instruction, as the site's paper button, inside the thing to press */
  display: flex; justify-content: center; align-items: center; gap: 7px;
  margin-top: 13px; min-height: 40px; padding: 0 16px; border-radius: 999px;
  background: var(--paper); color: var(--ink); font-size: 14px; font-variation-settings: "wght" 560;
  box-shadow: 0 1px 0 rgba(0, 0, 0, .35);
  transition: background .2s var(--ease), translate .25s var(--ease-out), box-shadow .25s var(--ease-out);
}
.phone__note-go svg { animation: note-go 2s var(--ease-in-out) infinite; }
@keyframes note-go { 0%, 100% { translate: 0 0; } 50% { translate: 3px 0; } }
.phone__note:hover .phone__note-go, .phone__lock:hover .phone__note-go { background: #ffffff; translate: 0 -1px; box-shadow: 0 3px 0 rgba(0, 0, 0, .35); }
.phone__note:active .phone__note-go, .phone__lock:active .phone__note-go { translate: 0 0; box-shadow: 0 1px 0 rgba(0, 0, 0, .35); }
@keyframes note-halo {
  0% { box-shadow: 0 0 0 0 rgba(244, 244, 244, .55); }
  45%, 100% { box-shadow: 0 0 0 14px rgba(244, 244, 244, 0); }
}
.phone__lockfoot {   /* the second way in, in plain words, with a hand */
  justify-self: center; display: inline-flex; align-items: center; gap: 8px;
  padding-bottom: 30px; font-size: 14px; color: var(--bone-dim);
}
.phone__lockfoot svg { flex: none; animation: hint-tap 1.6s var(--ease-in-out) infinite; }
@keyframes hint-tap { 0%, 100% { translate: 0 0; } 50% { translate: 0 -4px; } }
@media (prefers-reduced-motion: reduce) {
  .phone__note { animation: none; box-shadow: 0 0 0 2px rgba(244, 244, 244, .3); }
  .phone__lockfoot svg, .phone__note-go svg, .phone__note-now i { animation: none; }
}

/* the thread */
.phone__chat { grid-template-rows: auto 1fr auto; }
.phone__head { display: flex; justify-content: space-between; color: var(--bone-dim); padding: 62px 20px 12px; border-bottom: 1px solid var(--bone-rule); }
/* the thread scrolls by wheel and touch and follows the newest bubble itself, so the browser's
   own bar (a white track with arrows on Linux and Windows) is not drawn on the ink screen */
.phone__thread { overflow: auto; padding: 16px 16px 8px; display: grid; gap: 12px; align-content: start; scroll-behavior: smooth; scrollbar-width: none; }
.phone__thread::-webkit-scrollbar { display: none; }
.phone__msg { max-width: 82%; display: grid; gap: 4px; animation: msg-in .45s var(--ease-out) both; }
.phone__msg p { padding: 10px 14px; border-radius: 18px; line-height: 1.4; font-size: 15px; }
.phone__msg .mono { color: var(--bone-dim); font-size: 10px; }
.phone__msg--site p { background: #262626; color: var(--paper); border-bottom-left-radius: 4px; }
.phone__msg--you { justify-self: end; text-align: right; }
.phone__msg--you p { background: var(--paper); color: var(--ink); border-bottom-right-radius: 4px; font-variation-settings: "wght" 560; }
@keyframes msg-in { from { opacity: 0; translate: 0 10px; } }
.phone__typing { display: inline-flex; gap: 4px; padding: 13px 14px; background: #262626; border-radius: 18px; border-bottom-left-radius: 4px; }
.phone__typing i { width: 6px; height: 6px; border-radius: 50%; background: var(--bone-dim); animation: dot 1s infinite; }
.phone__typing i:nth-child(2) { animation-delay: .15s; }
.phone__typing i:nth-child(3) { animation-delay: .3s; }
@keyframes dot { 0%, 100% { opacity: .3; } 50% { opacity: 1; } }
.phone__replies { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; animation: msg-in .45s .2s var(--ease-out) both; }
.phone__reply {
  min-height: 44px; padding: 0 16px; border-radius: 999px; border: 1px solid var(--bone-dim); background: transparent;
  color: var(--paper); font: inherit; font-size: 14px; cursor: pointer; transition: background .2s var(--ease), color .2s var(--ease);
}
.phone__reply:hover { background: var(--paper); color: var(--ink); }
.phone__card { max-width: 92%; background: var(--paper); color: var(--ink); border-radius: 18px; padding: 14px 16px; display: grid; gap: 4px; animation: msg-in .5s var(--ease-out) both; }
.phone__card b { font-family: "Archivo", sans-serif; font-variation-settings: "wght" 720, "wdth" 94; font-size: 20px; letter-spacing: -.02em; line-height: 1.1; }
.phone__card .mono { color: var(--dim); font-size: 10px; }
.phone__foot { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 20px 26px; border-top: 1px solid var(--bone-rule); color: var(--bone-dim); }
.phone__reset { min-height: 44px; padding: 0; background: none; border: 0; color: var(--bone-dim); cursor: pointer; }
.phone__reset:hover { color: var(--paper); }

/* ==========================================================================
   THE DEAL — the claim holds the centre and never moves. The four numbers
   the page may quote sit under it as a stack (each a little further back,
   its edge peeking out on the side it will leave by) and the hand opens in
   two legs as you scroll, on one axis, no lift: first the inner pair slides
   straight out from under the claim, one slot each way; then the outer
   pair, which rode behind the inner pair for the first leg, slides on out
   from behind them to the second slot. Each card turns to face the middle
   as it goes and settles a step further back than the card before it, so
   the hand is a symmetric fan with the claim nearest. --p (0 stacked → 1
   open) is written on the section by js/site.js from the pinned scroll;
   without it, 1. --q1 is the first leg's share of it (0 → .55), --q2 the
   second's (.5 → 1); a card's --k1 is its first-leg slot (±1, the claim 0),
   --k2 its second-leg step (±1 for the outer pair). The only thing off the
   axis is a tip of 1.5deg at the middle of each leg, like a card pushed
   across a table. Depth is monotone in the slot (centre 0, inner −10→−40,
   outer −60→−70→−120) so no card ever passes through another. The left
   cards keep their words at the left edge, the right cards at the right,
   the claim in the middle: whatever a neighbour overlaps is the edge with
   no words on it. The figure on each card is its number, set like the
   pricing figures (800/90). Under 1024px the pin goes and the cards are a
   list (RESPONSIVE: the fan's strips are too narrow below that).

   The light (2026-09-11, "that same animation a bit cooler": a first cut
   that curved the hand and gave the push an overshoot was turned down, "I
   prefer it when they were sliding like before"). Every card's path above is
   untouched; only light and the pointer are added.
   - Shadow. The card in front throws a soft shadow on the card behind (the
     second box-shadow; on the ink ground it is invisible), so a card slides
     out of shadow into the light, and a card shades a touch as it turns from
     the light (::after, ink at --shade for the outer pair's 14deg).
   - The claim's light. The claim's own edge lights, drawn by the deal: out
     both ways from the top centre, down both sides, and closed at the bottom
     centre on the frame the outer pair lands (--p = 1). The edge is on the
     edge, not 10px inside it as on the pricing cards: inset, it crowded the
     cite and read as a second border. So .card--in's border is transparent
     and .card__light draws the unlit edge in its place, a faint rule (the
     card's ink runs under the border and matches the ground; grey to paper
     at 1px did not read as light, the rule to paper does), with
     five 1px paper bars over it scaled by transform, shares by length (top
     .21, sides .58, bottom .21): one speed, nothing re-rasters. The list
     (under 1024px) gets the plain border back. Finished page: drawn.
   - Pointer. Once the hand is dealt (data-dealt, from js/site.js), the card
     under a fine pointer comes up (--up): halfway to the picture plane, three
     quarters square to you, its shade gone, still behind the card in front of
     it (inner −20, outer −60); its words stay clear of that card from 1024px
     up (3.6px at 1400×769, the tightest). A pointer-driven eye (the space's
     perspective-origin following the mouse) was tried and dropped: the words
     sit only 6–13px clear of the card in front, and any eye worth having slid
     them under it.
   ========================================================================== */
@property --up { syntax: "<number>"; inherits: false; initial-value: 0; }   /* not inherited: only the card and its shade read it, and an inherited typed property is computed on every descendant each scroll frame */
.deck {
  position: relative; height: 220vh; background: var(--ink); color: var(--paper);
  --pw: min(300px, 26vw); --sx: min(300px, 18vw); --ry: -7deg; --shade: .07;
}
.deck__pin {
  position: sticky; top: 0; height: 100svh; overflow: clip;
  display: grid; grid-template-rows: auto 1fr auto;
  padding-top: calc(var(--nav-h) + clamp(20px, 5vh, 56px)); padding-bottom: clamp(24px, 5vh, 56px);
}
.deck__head { text-align: center; }
.deck__head .h2 { margin-inline: auto; }
.deck__space {
  position: relative; perspective: 2000px; perspective-origin: 50% 40%; transform-style: preserve-3d;
  pointer-events: none;   /* the space is a plane at z = 0 in the cards' own 3D context: it would take every hit meant for a card behind it */
}
.deck__foot {
  text-align: center; font-size: 17px; max-width: 60ch;
  opacity: clamp(0, calc((var(--p, 1) - .8) / .2), 1);
}
.deck__foot span { display: block; text-wrap: balance; }   /* one sentence a line (run together, the wrap splits a sentence); balanced, so a phone never leaves one word alone */
.card {
  --q1: clamp(0, calc(var(--p, 1) / .55), 1);
  --q2: clamp(0, calc((var(--p, 1) - .5) / .5), 1);
  --tip: calc(sin(calc(var(--q1) * pi)) * var(--k1, 0) + sin(calc(var(--q2) * pi)) * var(--k2, 0));
  --turn: calc(var(--q1) * var(--k1, 0) + var(--q2) * var(--k2, 0));   /* slots turned, signed */
  position: absolute; left: 50%; top: 50%; width: var(--pw); aspect-ratio: 3 / 4;
  margin: 0; padding: 18px 20px 16px; box-sizing: border-box;
  background: var(--white); color: var(--text); border: 1px solid var(--rule); overflow: clip;
  box-shadow: 0 40px 70px -40px rgba(0, 0, 0, .8), 0 0 44px rgba(0, 0, 0, .3);
  display: grid; grid-template-rows: auto auto 1fr auto; gap: 14px; align-items: start;
  transform:
    translate(-50%, -54%)
    translate3d(
      calc((1 - var(--q1)) * var(--cx, 0px) + (var(--q1) * var(--k1, 0) + var(--q2) * var(--k2, 0)) * var(--sx)),
      0px,
      calc((var(--cz, 0px) + var(--q1) * (var(--z1, 0px) - var(--cz, 0px)) + var(--q2) * (var(--z2, var(--z1, 0px)) - var(--z1, 0px))) * (1 - .5 * var(--up))))
    rotateY(calc(var(--turn) * var(--ry) * (1 - .75 * var(--up))))
    rotateZ(calc(var(--tip) * 1.5deg));
  transform-style: preserve-3d; backface-visibility: hidden; will-change: transform;
  transition: --up .55s var(--ease-out); pointer-events: auto;
}
.card::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; background: var(--ink);
  --up: inherit;
  opacity: calc(var(--turn) * var(--k1, 0) / 2 * var(--shade) * (1 - var(--up)));
  will-change: opacity;
}
@media (hover: hover) and (pointer: fine) {
  .deck[data-dealt] .card:not(.card--in):hover { --up: 1; }
}
.card--in { background: var(--ink); color: var(--paper); border-color: transparent; text-align: center; }
.card--in::after { display: none; }
.card__light { position: absolute; inset: 0; pointer-events: none; box-shadow: inset 0 0 0 1px var(--bone-rule); }
.card__light b { position: absolute; background: var(--paper); will-change: transform; }
.card__light b:nth-child(1) { left: 0; right: 0; top: 0; height: 1px; transform: scaleX(clamp(0, calc(var(--p, 1) / .21), 1)); }
.card__light b:nth-child(2),
.card__light b:nth-child(3) { top: 0; bottom: 0; width: 1px; transform-origin: 50% 0; transform: scaleY(clamp(0, calc((var(--p, 1) - .21) / .58), 1)); }
.card__light b:nth-child(2) { left: 0; }
.card__light b:nth-child(3) { right: 0; }
.card__light b:nth-child(4),
.card__light b:nth-child(5) { bottom: 0; width: 50%; height: 1px; transform: scaleX(clamp(0, calc((var(--p, 1) - .79) / .21), 1)); }
.card__light b:nth-child(4) { left: 0; transform-origin: 0 50%; }
.card__light b:nth-child(5) { right: 0; transform-origin: 100% 50%; }
.card--r { text-align: right; }
.card__k { color: var(--dim); }
.card--in .card__k, .card--in .card__cite { color: var(--bone-dim); }
.card__t {
  font-family: "Archivo", sans-serif; font-variation-settings: "wght" 800, "wdth" 90;
  font-size: clamp(38px, 3.6vw, 52px); line-height: 1; letter-spacing: -.03em; white-space: nowrap;
}
.card__say { font-size: 15px; line-height: 1.4; max-width: min(26ch, calc(var(--sx) - 44px)); }   /* stays inside the strip the next card leaves clear */
.card__cite { color: var(--dim); align-self: end; padding-top: 12px; border-top: 1px solid var(--rule); min-height: 1em; width: calc(var(--sx) - 44px); }   /* a width, not a cap: the rule is one length on every card (capped, the right-hand pair's shrank to their words) */
.card--r .card__say, .card--r .card__cite { justify-self: end; }
.card--in .card__say { justify-self: center; max-width: 26ch; }
.card--in .card__cite { position: relative; width: auto; border-top-color: transparent; }   /* the claim's words keep the card's width (at the rule's width they break below 1317px), so its rule is drawn */
.card--in .card__cite::before { content: ""; position: absolute; top: -1px; left: 50%; width: calc(var(--sx) - 44px); height: 1px; translate: -50% 0; background: var(--bone-rule); }

/* ==========================================================================
   PRICING
   ========================================================================== */
.price { background: var(--paper); color: var(--text); padding-block: clamp(90px, 14vh, 170px); }
.price__grid { display: grid; gap: 20px; margin-top: 30px; }
/* two charcoal cards (founder, 2026-09-10: picked from the temp/pricing.html gallery). Each opens
   with a concept room and carries its price on the photograph; the list and the button sit in the
   charcoal beneath. Both cards are the same tone, so neither reads as the recommended one. */
.offer {
  position: relative; background: #1e1e1e; color: var(--paper); border-radius: 6px; overflow: hidden;
  display: grid; grid-template-rows: auto 1fr; align-content: start;
}
/* the edge is one light. As the card rises in, a white line draws itself round it the way the light
   will run, easing down to the light's own speed; the moment it closes, the light carries on from
   that point for good and the line dims to a faint track behind it. The light is three dashes on
   the same path, short, bright and heaviest at the head, longer, fainter and finer behind, so it
   reads as a comet with a tail (pathLength 1, so the dash maths is the same whatever the card's
   size). The care card's edge is turned half a turn, so its light starts from the opposite corner,
   half a lap from the build's, and the pair draws in from their outer edges. The path runs 10 px
   inside the card (x, y in index.html; the rect is 20 px short of the box): on the card's own edge
   the white met the paper page and read as the card losing a pixel, not as a light (founder,
   2026-09-11: "more obvious"). Every entrance here fills backwards only, so the card at rest is
   its plain rules (and the finished card with no script) and the hover can lift the track. A fine
   pointer also charges the light (js/site.js, "pricing light") and eases the room in. */
.offer { --t0: 0s; }
.offer:nth-child(2) { --t0: .2s; }
.offer__edge { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; z-index: 2; stroke-width: 1.5; }
.offer:nth-child(2) .offer__edge { rotate: 180deg; }
.offer__edge rect { width: calc(100% - 20px); height: calc(100% - 20px); fill: none; stroke: #fff; stroke-linecap: round; }
.offer__line { stroke-dasharray: 1; opacity: .2; }
.offer.is-in .offer__line { animation: offer-draw 2.5s linear calc(.2s + var(--t0)) backwards; }
/* closes at 64% (1.6 s) on a curve whose last slope, .13 / .45, is 1.6 s over the 5.5 s lap: the
   light leaves at the speed the line arrived. Then .9 s to dim. The orbit waits the same 1.8 s. */
@keyframes offer-draw {
  0%   { stroke-dashoffset: -1; opacity: 1; animation-timing-function: cubic-bezier(.3, .75, .55, .87); }
  64%  { stroke-dashoffset: 0; opacity: 1; animation-timing-function: cubic-bezier(.22, .61, .28, 1); }
  100% { stroke-dashoffset: 0; opacity: .2; }
}
.offer__glint { visibility: hidden; }
.offer.is-in .offer__glint {
  visibility: visible;
  animation: offer-orbit 5.5s linear calc(1.8s + var(--t0)) infinite, offer-wait calc(1.8s + var(--t0)) step-end;
}
.offer__glint--head { stroke-width: 2.5; stroke-dasharray: .06 .94; opacity: 1; }
.offer__glint--mid  { stroke-width: 2; stroke-dasharray: .14 .86; opacity: .55; }
.offer__glint--tail { stroke-dasharray: .3 .7; opacity: .22; }
@keyframes offer-orbit { from { stroke-dashoffset: 0; } to { stroke-dashoffset: 1; } }
@keyframes offer-wait { from, to { visibility: hidden; } }
/* the figure wipes in from the left, as the first screen's bill does; the list's rules draw across
   one after another as their lines come up */
.offer.is-in .offer__n { animation: offer-wipe 1.1s var(--ease-out) calc(.45s + var(--t0)) backwards; }
@keyframes offer-wipe {
  from { clip-path: inset(-1em 100% -1em 0); translate: -14px 0; }
  to   { clip-path: inset(-1em); translate: 0 0; }
}
.offer.is-in .offer__list li { animation: offer-row .8s var(--ease-out) calc(.7s + var(--i) * .09s + var(--t0)) backwards; }
.offer.is-in .offer__list li::before { animation: offer-rule 1s var(--ease-out) calc(.7s + var(--i) * .09s + var(--t0)) backwards; }
@keyframes offer-row { from { color: transparent; translate: 0 6px; } }
@keyframes offer-rule { from { scale: 0 1; } }
@media (hover: hover) and (pointer: fine) {
  .offer .offer__line { transition: opacity .5s var(--ease); }
  .offer:hover .offer__line { opacity: .6; }
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .offer__ph img { transition: scale .9s var(--ease); }
  .offer:hover .offer__ph img { scale: 1.04; }
}
.offer__ph { position: relative; aspect-ratio: 16 / 10; overflow: hidden; background: var(--ink); }
.offer__ph img { width: 100%; height: 100%; object-fit: cover; filter: brightness(.72); }
.offer__tag { position: absolute; left: 26px; top: 22px; color: rgba(244, 244, 244, .78); text-shadow: 0 1px 12px rgba(0, 0, 0, .6); }
.offer__n {
  position: absolute; left: 30px; bottom: 26px; margin: 0;
  font-family: "Archivo", sans-serif; font-variation-settings: "wght" 820, "wdth" 90;
  font-size: clamp(48px, 5.2vw, 76px); line-height: 1; letter-spacing: -.035em;
  color: var(--paper); text-shadow: 0 1px 22px rgba(0, 0, 0, .55);
}
.offer__n span { font-size: .3em; font-variation-settings: "wght" 500, "wdth" 100; letter-spacing: -.01em; margin-left: .32em; }
.offer__n .offer__pre { margin: 0 .32em 0 0; }
.offer__body { padding: clamp(28px, 3.4vw, 46px); display: grid; align-content: start; }
.offer__body > .mono { color: var(--bone-dim); }
.offer__list { list-style: none; margin: 26px 0 0; padding: 0; display: grid; }
.offer__list li { --i: 0; position: relative; padding: 13px 0; line-height: 1.4; font-variation-settings: "wght" 500, "wdth" 100; }
.offer__list li:nth-child(2) { --i: 1; }
.offer__list li:nth-child(3) { --i: 2; }
.offer__list li:nth-child(4) { --i: 3; }
.offer__list li::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px; background: var(--bone-rule); transform-origin: 0 50%; }
.offer__btn { margin-top: 34px; justify-self: start; }
.offer__btn--line { border-color: rgba(255, 255, 255, .4); color: var(--paper); }
.offer__btn--line:hover { border-color: var(--paper); }

/* ==========================================================================
   START
   ========================================================================== */
/* the last chapter and the sign-off under it fill the last screen (lvh on a phone, so no strip shows
   with the bar hidden): shorter, the page ended on the foot of the pricing cards with the masthead
   over their buttons, and "Book a call" could not bring Start to the top. Its content sits in the
   middle of the screen: the bottom pads less by the sign-off's band (--sign-h: the masthead's height,
   and under 1024 the legal buttons' row as well), but never under 40 px, or the email's underline runs
   into the sign-off's hairline. */
:root { --sign-h: var(--nav-h); }
.ask {
  --ask-p: clamp(90px, 14vh, 170px);
  background: var(--ink-2); color: var(--paper);
  padding-block: var(--ask-p) max(40px, calc(var(--ask-p) - var(--sign-h)));
  min-height: calc(100vh - var(--sign-h)); display: flex; flex-direction: column; justify-content: center;
}
/* the sheet stands on the page's axis (2026-09-14: "make this part centred and a bit more full"), as the paper
   page's sheet does: the head, the two sentences, the steps, Send and its note are all centred on it */
.ask__grid { display: grid; gap: 48px; align-items: start; text-align: center; }
/* the sheet's head: the kicker on the axis, breaking one hairline that runs to the wrap's edges, so the last
   screen is held between two rules, this one and the sign-off's (2026-09-14: "it looks a bit empty still"). The
   indent is the tracking after the last letter, so the word's ink, not its box, sits on the axis */
.ask__kick { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; column-gap: 18px; text-indent: .12em; }
.ask__kick::before, .ask__kick::after { content: ""; height: 1px; background: var(--bone-rule); transform-origin: 100% 50%; }
.ask__kick::after { transform-origin: 0 50%; }   /* each half opens from the word outwards */
/* two sentences, two lines: the ask at 720/94, the reply as the turn at 480/100 (weight and width,
   as the hero's bill). Each sentence is its own block so neither can leave a word orphaned; from
   900 up each holds one line across the wrap (the sizes under RESPONSIVE, fitted to the longer turn,
   "We'll show you the one we'd build instead."). */
.ask .h2 { font-size: clamp(26px, 2.95vw, 43px); margin-top: clamp(18px, 3.2vh, 34px); }
.ask__l { display: block; text-wrap: balance; }
.ask__l--turn { font-variation-settings: "wght" 480, "wdth" 100; letter-spacing: -.014em; color: var(--bone); margin-top: .18em; }
.form { display: grid; gap: 22px; }
.form label { position: relative; display: grid; gap: 8px; text-align: start; }   /* a field reads from its left edge, on the centred sheet too */
.form label .mono { color: var(--bone-dim); }
.form input {
  font: inherit; font-size: 18px; min-height: 50px; padding: 10px 0; border-radius: 0;
  background: transparent; border: 0; border-bottom: 1px solid var(--bone-rule); color: var(--paper);
}
.form input::placeholder { color: rgba(244, 244, 244, .32); }
/* focus draws a paper line along the field's rule from the left, as the rules draw in */
.form input:focus { outline: none; }
.form label::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--paper);
  scale: 0 1; transform-origin: 0 50%; transition: scale .45s var(--ease-out);
}
.form label:focus-within::after { scale: 1 1; }
.form__row { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 18px 28px; margin-top: 8px; }
.form__note { flex: 1 1 100%; color: var(--bone-dim); text-wrap: balance; }   /* always under Send, on the axis */
.form__note a { color: var(--paper); text-decoration: none; border-bottom: 1px solid var(--bone-rule); display: inline-block; padding-block: 14px; margin-block: -14px; white-space: nowrap; }
.form__note span { white-space: nowrap; }   /* "or write to" breaks as one: at 320 a lone "to" fell in front of the address */
/* what happens after Send (2026-09-14: asked for the chapter to look "a bit more full"): three steps, each a
   mono number and name and one plain line, centred. On a phone they follow the form as a list, a hairline over
   each; from 600 they stand between the sentences and the form, three across with a hairline tick between
   them, as the paper page's sub line has one (under RESPONSIVE) */
.ask__next { list-style: none; margin: 0; padding: 0; display: grid; }
.ask__next li { --i: 0; position: relative; display: grid; justify-items: center; align-content: start; gap: 8px; padding: 16px 0 18px; border-top: 1px solid transparent; }
.ask__next li:nth-child(2) { --i: 1; }
.ask__next li:nth-child(3) { --i: 2; }
/* the step's rule is drawn, so it can draw in; the transparent border keeps its pixel in the layout */
.ask__next li::before { content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 1px; background: var(--bone-rule); }
/* the indent balances each part's trailing tracking, so the number and name sit on the step's centre */
.ask__next .mono { display: flex; gap: 14px; color: var(--bone-dim); text-indent: .12em; }
.ask__next .mono span { color: var(--paper); }
.ask__next p + p { font-size: 15px; line-height: 1.45; color: #c9c9c9; text-wrap: balance; }   /* balance, not pretty: Firefox has no pretty, and left "rest." alone on a line at 1600 */

/* the entrance (2026-09-14: "nice fading in animations", then, centred, "nice smooth animations for the text to
   come in"), once, each block as it comes into view (data-reveal). Every rule draws in as a line of light and
   settles to its hairline, as the pricing cards' edge does: the head's two halves open from the word outwards,
   the steps' ticks drop from their tops, and the fields' lights run along rules that are already there. Each
   sentence rises into place out of its own line, as if through a slot, the reply a beat after the ask;
   everything else rises 10 px out of nothing. All of it fills backwards only, so at rest the chapter is its
   plain self, and under reduced motion none of it exists. From 600, where all three blocks arrive together,
   the steps wait for the sentences and the form for the steps; on a phone each block arrives alone. */
@media (prefers-reduced-motion: no-preference) {
  .ask { --t-form: .1s; --t-next: .1s; }
  html[data-js] .ask__top:not(.is-in) .ask__kick { color: transparent; }
  html[data-js] .ask__top:not(.is-in) .ask__kick::before,
  html[data-js] .ask__top:not(.is-in) .ask__kick::after,
  html[data-js] .ask__next:not(.is-in) li::before { scale: 0 1; }
  html[data-js] .ask__top:not(.is-in) .ask__l,
  html[data-js] .form:not(.is-in) > *,
  html[data-js] .ask__next:not(.is-in) li > * { opacity: 0; }

  .ask__top.is-in .ask__kick { animation: ask-ink .9s var(--ease) backwards; }
  .ask__top.is-in .ask__kick::before,
  .ask__top.is-in .ask__kick::after { animation: ask-draw 1.7s linear .1s backwards; }
  .ask__top.is-in .ask__l { animation: ask-rise 1.25s var(--ease-out) .2s backwards; }
  .ask__top.is-in .ask__l--turn { animation-delay: .36s; }

  .form > :nth-child(2) { --i: 1; }
  .form > :nth-child(3) { --i: 2.5; }   /* Send follows both fields */
  .form.is-in > * { animation: ask-up .9s var(--ease-out) calc(var(--t-form) + var(--i, 0) * .13s) backwards; }
  /* both fields' lights run on the sign-off hairline's clock: its trigger, its delay and its length (THE SIGN-OFF;
     change one, change the other), so the three lines draw, hold and fade as one, on a phone too, where the lights
     wait for the steps (2026-09-14: staggered .13 s and .3 s shorter, one light was out while the others still faded) */
  html[data-js]:has(.ask__next.is-in):has(.ask .form.is-in) .form label::after { animation: ask-light 1.7s linear 1.2s backwards; }

  .ask__next.is-in li::before { animation: ask-draw 1.6s linear calc(var(--t-next) + var(--i) * .17s) backwards; }
  .ask__next.is-in li > * { animation: ask-up .8s var(--ease-out) calc(var(--t-next) + .22s + var(--i) * .17s) backwards; }
  .ask__next.is-in li > * + * { animation-delay: calc(var(--t-next) + .32s + var(--i) * .17s); }
}
@media (prefers-reduced-motion: no-preference) and (min-width: 600px) {
  .ask { --t-next: .55s; --t-form: .85s; }
  html[data-js] .ask__next:not(.is-in) li::before { scale: 1 0; }
  .ask__next.is-in li::before { animation-name: ask-drop; }
}
/* a rule drawn as light: paper from its origin, easing out to its full length, then settling to the hairline */
@keyframes ask-draw {
  0%   { scale: 0 1; background-color: var(--paper); animation-timing-function: cubic-bezier(.16, 1, .3, 1); }
  55%  { scale: 1 1; background-color: var(--paper); animation-timing-function: cubic-bezier(.22, .61, .28, 1); }
  100% { scale: 1 1; background-color: var(--bone-rule); }
}
/* the same light along a rule that is already drawn: it runs and goes */
@keyframes ask-light {
  0%   { scale: 0 1; opacity: 1; animation-timing-function: cubic-bezier(.16, 1, .3, 1); }
  55%  { scale: 1 1; opacity: 1; animation-timing-function: cubic-bezier(.22, .61, .28, 1); }
  100% { scale: 1 1; opacity: 0; }
}
/* the same light down a tick */
@keyframes ask-drop {
  0%   { scale: 1 0; background-color: var(--paper); animation-timing-function: cubic-bezier(.16, 1, .3, 1); }
  55%  { scale: 1 1; background-color: var(--paper); animation-timing-function: cubic-bezier(.22, .61, .28, 1); }
  100% { scale: 1 1; background-color: var(--bone-rule); }
}
/* a sentence rising out of its own line: it travels its height and .4em more (so no ascender shows at the
   start), while its clip moves the other way by the same amount on the same curve, so the window it shows
   through holds still on the page, .3em over the line and .35em under it for the descenders */
@keyframes ask-rise {
  from { translate: 0 calc(100% + .4em); clip-path: inset(calc(-100% - .7em) -.5em calc(100% + .05em) -.5em); }
  to   { translate: 0 0; clip-path: inset(-.3em -.5em -.35em -.5em); }
}
@keyframes ask-up { from { opacity: 0; translate: 0 10px; } }
@keyframes ask-ink { from { color: transparent; } }

/* ==========================================================================
   THE SIGN-OFF — the paper page's head mirrored at the foot, in ink: one hairline to the wrap's
   edges, the name at one end, the way back up at the other, and on the axis between them the two
   legal buttons (2026-09-14). The band is the masthead's height with its words on the middle line,
   as the masthead's are, so Start's content sits halfway between the two; the buttons are outlined
   mono pills 44 px tall, as "Book a call" is in the masthead's 64. The words are dim and the link and
   buttons paper, as in the form's note above it. Under 1024 the buttons take a row of their own.
   ========================================================================== */
.sign { background: var(--ink-2); color: var(--bone-dim); padding-bottom: env(safe-area-inset-bottom); }
.sign__row {
  position: relative; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; column-gap: 16px;
  min-height: var(--nav-h); border-top: 1px solid transparent;
}
/* the hairline is drawn, so it can open; the transparent border keeps its pixel in the band's height */
.sign__row::before { content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 1px; background: var(--bone-rule); }
/* its entrance follows the last of Start's blocks to arrive, the steps on a phone and the form from 600 (the
   band cannot watch itself: the reveal's −6% foot margin is taller than it at 1080 px): the hairline opens from
   the axis as light, then the words come up. Both fields' lights run on this clock (START): change one, change the other */
@media (prefers-reduced-motion: no-preference) {
  html[data-js]:has(.ask__next:not(.is-in)) .sign__row::before,
  html[data-js]:has(.ask .form:not(.is-in)) .sign__row::before { scale: 0 1; }
  html[data-js]:has(.ask__next:not(.is-in)) .sign__row > *,
  html[data-js]:has(.ask .form:not(.is-in)) .sign__row > * { opacity: 0; }
  html[data-js]:has(.ask__next.is-in):has(.ask .form.is-in) .sign__row::before { animation: ask-draw 1.7s linear 1.2s backwards; }
  html[data-js]:has(.ask__next.is-in):has(.ask .form.is-in) .sign__row > * { animation: ask-up .9s var(--ease-out) 1.55s backwards; }
}
.sign__row > * { white-space: nowrap; }
.sign__name { justify-self: start; }
.sign__legal { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }   /* one width, so the gap between them is on the axis */
/* no flex on the pill: a flex item trims the leading space of " and conditions" ("TERMSAND") */
.sign__btn {
  min-height: 44px; padding: 0 18px; line-height: 42px; border: 1px solid rgba(255, 255, 255, .4); border-radius: 999px;
  background: transparent; color: var(--paper); cursor: pointer;
  transition: background .25s var(--ease), color .25s var(--ease), border-color .25s var(--ease);
}
.sign__btn:hover { background: var(--paper); color: var(--ink); border-color: var(--paper); }   /* the masthead button's hover on ink */
.sign__btn:focus-visible { outline: 2px solid var(--paper); outline-offset: 3px; }
.sign__top { justify-self: end; position: relative; display: inline-flex; align-items: center; gap: .55em; min-height: 44px; color: var(--paper); text-decoration: none; }
.sign__top::after {
  content: ""; position: absolute; left: 0; right: .15em; bottom: 10px; height: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: 0 50%; transition: transform .35s var(--ease);
}
.sign__top:hover::after { transform: scaleX(1); opacity: .5; }   /* the masthead links' underline */
.sign__top span { transition: translate .35s var(--ease); }
.sign__top:hover span { translate: 0 -2px; }

/* ==========================================================================
   REVEAL — only under data-js, so the page is complete without a script
   ========================================================================== */
html[data-js] [data-rise] {
  opacity: 0; translate: 0 26px;
  transition: opacity .9s var(--d, 0s) var(--ease), translate .9s var(--d, 0s) var(--ease);
}
html[data-js] [data-rise].is-in { opacity: 1; translate: none; }

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */
@media (max-width: 899px) {
  .rest { display: none; }
  /* one column: on a phone each Start sentence balances over two lines, set as large as the turn's
     first line ("We'll show you the one") allows from 320 to 480, and the pair parts as two
     statements (from 600 each holds one line, below). The address's underline hugs the words; the
     14 px padding still makes the target 44 px. */
  .ask .h2 { font-size: clamp(26px, calc(9.2vw - 3.5px), 38px); line-height: 1.04; }
  .ask__l--turn { margin-top: .42em; }
  .ask .form__note a {
    border-bottom: 0; text-decoration: underline; text-decoration-color: var(--bone-rule);
    text-decoration-thickness: 1px; text-underline-offset: 5px;
  }
}
@media (min-width: 600px) and (max-width: 899px) {
  /* a portrait tablet: at the phone's size the ask held one line and the turn two, lopsided. Sized
     to the column instead, each sentence holds one line (the turn ~7% short of the column) */
  .ask .h2 { font-size: calc(5.2vw - 3px); }
}
@media (min-width: 600px) {
  /* the centred sheet (2026-09-14: "make this part centred"): the steps come up between the sentences and the
     form, three across, each centred in its third, with a hairline tick between them as on the paper page's sub
     line (none before the first). The two fields stand side by side, meeting on the axis, and Send and its note
     stand under them on the axis, as the paper page's buttons do. On a phone the form keeps its place under the
     sentences, and the steps follow it */
  .ask__next { order: 1; grid-template-columns: repeat(3, 1fr); }
  .ask__grid > .form { order: 2; grid-template-columns: 1fr 1fr; column-gap: 40px; }
  .form__row { grid-column: 1 / -1; }
  .ask__next li { padding: 6px 20px; border-top: 0; }
  .ask__next li::before { top: 0; bottom: 0; left: 0; right: auto; width: 1px; height: auto; transform-origin: 50% 0; }
  .ask__next li:first-child::before { content: none; }
  .ask__next p + p { max-width: 24ch; }
}
@media (min-width: 900px) {
  /* the sheet from 900 (2026-09-14: "a bit more full"): the sentences as large as the turn allows across the
     wrap (--ask-fs; the turn is 18 em, so 4.9vw leaves it inside the wrap at every width), capped by the height
     as well, so the sheet fills the last screen without outgrowing it. The form runs the wrap, so every rule on
     the screen, the head's, the fields' and the sign-off's, ends on the same two edges (at the turn's width the
     fields ended 19 to 30 px inside the others at 900, 1024 and 1880: a near miss), and the gaps follow the height */
  .ask { --ask-fs: min(4.9vw, 7.6vh, 68px); }
  .ask__grid { row-gap: 0; }
  .ask .h2 { font-size: var(--ask-fs); margin-top: clamp(24px, 4.6vh, 52px); }
  .ask__next { margin-top: clamp(28px, 6vh, 68px); }
  .ask__next p + p { font-size: clamp(15px, 1.1vw, 17px); max-width: none; }
  .ask__grid > .form { column-gap: 48px; margin-top: clamp(28px, 6vh, 68px); }
  .ask .form__row .btn { min-width: 184px; }   /* the paper page's button width: a pill on the axis, not a word */
}
@media (min-width: 900px) and (max-height: 740px) {
  /* a short screen (1366×657, 1280×720): the sheet tightens its gaps and its sentences, never the top padding
     the masthead needs, so the last screen still holds it and the sign-off */
  .ask { --ask-fs: min(4.9vw, 6.1vh, 68px); }
  .ask .h2 { margin-top: 16px; }
  .ask__next { margin-top: 22px; }
  .ask__grid > .form { margin-top: 26px; row-gap: 16px; }
}
@media (min-width: 900px) and (max-width: 1023px) and (max-height: 740px) {
  /* and where the sign-off's band is 124 (900×700, 2 px over): the top padding holds at its 90 px floor and the
     form comes 6 px closer to the steps */
  .ask { --ask-p: 90px; }
  .ask__grid > .form { margin-top: 20px; }
}
@media (max-width: 1023px) {
  /* the sign-off's two ends and the buttons do not share one line under 1024 (736 px of them in 706 at
     768): the buttons take a row of their own, 16 px under the hairline, and the band grows by it (59 px,
     and the hairline makes 60), so Start still pads by exactly the band */
  :root { --sign-h: calc(var(--nav-h) + 60px); }
  .sign__row { grid-template-columns: 1fr auto; grid-template-rows: 59px var(--nav-h); }
  .sign__legal { grid-column: 1 / -1; grid-row: 1; align-self: end; justify-self: center; gap: 8px; }
  .sign__btn { padding: 0 14px; }
}
@media (max-width: 479px) {
  .sign__more { display: none; }   /* "Terms" and "Privacy": at 390 the full names came to 353 px in 350, and the second fell under the first */
  .sign__btn { min-width: 112px; }
}
@media (max-width: 300px) {
  /* 280 (a folded phone): the name and the way back up do not fit side by side (261 px in 240; the old
     flex row wrapped the link), so each takes its own line under the buttons */
  .sign__row { grid-template-columns: 1fr; grid-template-rows: 59px 44px 44px; }
  .sign__top { justify-self: start; }
}
@media (min-width: 1024px) {
  /* the stage: one grid cell a screen tall (the fit makes it taller only where the screen cannot hold
     the masthead, 40 px of room, the phone at .7 and 36 px of stone), holding the photograph, the words
     over it, the diary card and, on its axis, the phone. The words part round the phone: the kicker and
     the sign on one line over its top edge, the sentence's first line on that edge, the tag and the
     demonstration line on the foot, where the first screen keeps its edges. They are paper over the
     night */
  .book__grid {
    --stage-h: 100svh; grid-template-rows: var(--stage-h);
    /* the words' size, and the ledgers': a column the words' width (--cw), its foot on the phone's but never
       within 72 px of the stage's foot, where the tag and the rest cue stand (--lb), from .94em under the
       words to that foot, at most eight 60 px hours (--lh), and an hour (--lp) */
    --hfs: clamp(26px, 2.55vw, 40px); --lhd: 34px;
    --cw: min(calc(50% - var(--pad) - var(--pw) / 2 - var(--gut)), calc(10.5 * var(--hfs)));
    --lb: min(calc(var(--py) + var(--ph)), calc(var(--stage-h) - 72px));
    --lh: min(calc(var(--lb) - var(--py) - 2.9 * var(--hfs)), calc(var(--lhd) + 480px));
    --lp: calc((var(--lh) - var(--lhd)) / 8);
  }
  html:not([data-js]) .book__grid { --py: max(6.74vw, 11.98vh); --pw: max(18.96vw, 33.72vh); }   /* no fit: the photographed phone's top and width under object-fit */
  .book__grid > *, .book__grid > .phone { grid-area: 1 / 1; }   /* the phone named too: its own rule above outranks the bare child */
  .book__copy { position: relative; z-index: 1; padding: 0; }
  .book__kick { position: absolute; left: var(--pad); top: calc(var(--py) - 34px); color: var(--paper); }
  .book__h {
    position: absolute; left: var(--pad); right: var(--pad); top: calc(var(--py) - .2em); margin: 0;
    display: flex; justify-content: space-between; align-items: start;
    font-size: var(--hfs); line-height: 1.08;
  }
  .book__l { max-width: min(calc(50% - var(--pw) / 2 - var(--gut)), 10.5em); }   /* two lines each at every width: at 1400×769 the evening held one line beside the morning's two */
  .book__l--turn { text-align: right; }
  .book__demo { position: absolute; right: var(--pad); bottom: 30px; margin: 0; color: var(--paper); }
  .ledger { display: grid; position: absolute; top: calc(var(--lb) - var(--lh)); width: var(--cw); height: var(--lh); grid-template-rows: var(--lhd) repeat(4, 1fr); }
  .ledger--sofa { left: var(--pad); }
  .ledger--diary { right: var(--pad); grid-template-rows: var(--lhd) 1fr; }
  .night__sign { display: inline-flex; top: calc(var(--py) - 34px); }
  .night__tag { position: absolute; left: var(--pad); bottom: 30px; z-index: 3; padding: 0; color: var(--paper); }
  .night__slip {   /* on the diary's entry, at its hour, the column's width, over the hours beside it; never past the ledgers' foot (--slip-h, from lights) */
    display: grid; align-self: start; width: var(--cw);
    margin: min(calc(var(--lb) - var(--lh) + var(--lhd) + var(--at, 2.5) * var(--lp) + 5px), calc(var(--lb) - var(--slip-h, 96px))) var(--pad) 0 0;
  }
}
@media (min-width: 820px) {
  .price__grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 900px) {
  .morph { height: 278vh; }   /* 195vh × SPAN_SHARE, plus a screen */
  .word { height: min(56vh, 520px); margin-top: 2vh; }
  .pane__cap { left: 10px; right: 10px; bottom: 10px; }
}
@media (max-width: 760px) {
  .nav__links { display: none; }
  .nav__mark { grid-column: 1; }
  .nav__cta { grid-column: 3; }
  .hero__bill { bottom: 84px; }
  .hero__kick { max-width: 38ch; }   /* two lines on a phone: the tracking widens every character past 1ch */
  .hero__scroll { display: none; }
  .ask .form__row { gap: 16px; }
  .ask .form__row .btn { flex: 1 1 100%; }   /* Send runs the width of the fields above it */
  .state__h { font-size: clamp(34px, 9.6vw, 60px); }   /* two lines at 390 (no "convert." alone); "We make websites" still one line at 320 */
  /* no room for a rule either side of the buttons: the foot rule drops under them instead, as far
     from the bottom edge as the head rule is from the top */
  .state { padding-bottom: calc(var(--pt) + 32px); }
  .state__row {
    display: flex; flex-wrap: wrap; justify-content: center; gap: 12px 10px;
    margin-top: clamp(32px, 5vh, 52px); padding-bottom: var(--gap); border-bottom: 1px solid var(--rule);
  }
  .state__row::before, .state__row::after { display: none; }
  .state__row .btn { min-width: auto; flex: 0 0 160px; padding-inline: 12px; }
}
/* a short phone: the bill stands half way up the screen, and its kicker on the worktop's lit edge (2.4:1 at 320×568,
   3.2 at 360×640, under any veil that leaves the room). The bill has grown, so the kicker goes (THE FIRST SCREEN) */
@media (max-width: 760px) and (max-height: 650px) {
  .hero__kick { display: none; }
}
/* the deal is a list under 1024 px, not under 760: the fan's strips are too narrow below that. At
   761 its sentences ran over their citations (56 px on the 23% card) and its words lay under the
   card in front (34 px on "0 of 7"); the words only clear from 1000 (2 px) and 1024 (6 px), the
   width the hover's clearance was measured from. Two columns from 600, the claim across both. */
@media (max-width: 1023px) {
  .deck { height: auto; }
  .deck__pin {
    position: static; height: auto; overflow: visible; display: block;
    padding-block: clamp(90px, 14vh, 170px);
  }
  .deck__head { text-align: left; }
  .deck__head .h2 { margin-inline: 0; }
  .deck__space { perspective: none; display: grid; gap: 10px; margin-top: 40px; width: min(var(--wrap), 100% - 2 * var(--pad)); margin-inline: auto; }
  .card { position: relative; left: auto; top: auto; width: auto; aspect-ratio: auto; transform: none; box-shadow: none; grid-template-rows: auto auto auto auto; text-align: left; }
  .card::after, .card__light { display: none; }
  .card--in { border-color: var(--bone-dim); }
  .card__say { max-width: 40ch; }
  .card--r .card__say, .card--in .card__say { justify-self: start; }
  .card__cite { width: auto; }
  .card--r .card__cite { justify-self: stretch; }   /* every rule runs the card's width */
  .card--in .card__cite { border-top-color: var(--bone-rule); }
  .card--in .card__cite::before { display: none; }
  .deck__foot { opacity: 1; text-align: left; margin-top: 34px; padding-top: 22px; border-top: 1px solid var(--bone-rule); }
}
@media (min-width: 600px) and (max-width: 1023px) {
  .deck__space { grid-template-columns: 1fr 1fr; }
  .card--in { grid-column: 1 / -1; }
}
@media (max-width: 380px) {
  .state__kick { font-size: 10.5px; letter-spacing: .08em; }   /* both ends stay on one line at 320 */
  .state__kick span { white-space: nowrap; }
  .sign__row .mono { font-size: 10.5px; letter-spacing: .08em; }   /* and the sign-off, its mirror: one line at 320 */
  /* the same small setting for the masthead (at 320 the mark and the button met with no gap, and
     at 280 the button broke in two) and for the morph's captions (they ran to both edges at 320
     and past them at 280; a second line if they must) */
  .nav__mark { gap: 8px; }
  .nav__mark .mono, .nav__cta { font-size: 10.5px; letter-spacing: .08em; }
  .nav__cta { padding: 0 14px; white-space: nowrap; }
  .morph__caption { font-size: 10.5px; letter-spacing: .08em; white-space: normal; width: calc(100% - 2 * var(--pad)); text-wrap: balance; }
  .morph__foot { font-size: .875rem; }   /* five lines at .95rem reached the last row of rooms at 320×568 */
}
@media (max-width: 300px) {
  .nav__mark .mono { display: none; }   /* 280 (a folded phone): the mark and the button do not fit side by side; at 320 they clear by 32 px */
}

/* ==========================================================================
   REDUCED MOTION — the finished page, still
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .rest { display: none; }
  .hero__scroll i::after { animation: none; transform: none; }
  html[data-js] [data-rise] { transition: none; opacity: 1; translate: none; }
  .offer.is-in .offer__line, .offer.is-in .offer__n, .offer.is-in .offer__list li, .offer.is-in .offer__list li::before { animation: none; }
  .offer.is-in .offer__glint { animation: none; visibility: hidden; }
  .nav, .hero__foot, .hero__veil, .hero__kick, .hero__sub, .hero__row, .hero__l, .btn, .phone__view, .phone__reply, .nav__links a::after, .sign__top::after, .sign__top span, .sign__btn, .form label::after { transition: none; }
  .phone__msg, .phone__replies, .phone__card, .phone__typing i { animation: none; }
  .phone__thread { scroll-behavior: auto; }
  .night__glow { animation: none; }
  .night .night__img, .night__tag, .night__slip, .book__grid[data-lit] .night__slip, html[data-js] .book__grid > .phone[data-rise] { transition: none; }
  .morph { height: auto; }
  .morph__stage { position: static; height: auto; padding-block: 90px; }
  .morph__caption { position: static; translate: none; margin-bottom: 26px; }
  .morph__foot { position: static; translate: none; margin: 34px auto 0; }
  .deck { height: auto; }
  .deck__pin { position: static; }
  .card { transition: none; }
}

/* ==========================================================================
   NO SCRIPT — nothing waits for a script that is not coming
   ========================================================================== */
html:not([data-js]) .nav { position: absolute; color: var(--paper); }   /* it stands on the hero, and nothing dresses it: ink was lost on the photograph */
html:not([data-js]) .morph { height: auto; }
html:not([data-js]) .morph__stage { position: static; height: auto; padding: 0; }
html:not([data-js]) .word,
html:not([data-js]) .morph__caption,
html:not([data-js]) .morph__foot { display: none; }
html:not([data-js]) .deck { height: auto; }
html:not([data-js]) .deck__pin { position: static; }
html:not([data-js]) .phone { display: none; }
