/* ============================================================
   Weighted — site stylesheet

   One document per page, responsive between 320 and 1280+. The design
   comes from the Claude Design canvas in `Website/canvas/`, where every
   page exists twice: a 1280px desktop artboard and a 390px phone one.
   Those two are the endpoints this file interpolates between. Where a
   value differs across the pair, the phone value is the base and the
   desktop value lives in the one breakpoint at 880px.

   Type sizes that changed between the two artboards use clamp() so the
   range in between is not a straight jump. The clamp middle term is in
   vw, tuned so the maximum is reached at roughly 1280px.
   ============================================================ */

/* ---------- tokens ---------- */

:root {
  /* The ground is dark. The app itself is a black-ground app with a chalk-gold
     accent, and the site used to be its photographic negative: a pale grey page
     holding black screenshots. Two products in one scroll. Now the page is the
     same night the app is, one shade off pure black so a phone screen still
     reads as a screen and not as a hole cut in the page. */
  --ground:      #0e1113;   /* the page */
  --ground-2:    #161a1c;   /* the lifted band */
  --night:       #07090a;   /* the deepest band, below the page itself */
  --ink:         #eef1ed;
  --ink-2:       #9aa3a1;   /* secondary text. 7.0:1 on --ground. */
  --ink-night:   #eef1ed;
  --ink-night-2: rgba(238, 241, 237, .78);
  --gold:        #e3b23c;   /* chalk gold. Owns whole fields, not text. */
  --gold-ink:    #edc35c;   /* gold lifted far enough to carry text on the dark ground */
  /* Ink for anything standing ON gold. Gold is a light surface, so every token
     inside a gold plate flips back; see the `--ink` overrides further down. */
  --on-gold:     #14181a;
  /* The white rule. It is the only pure-ish light the page owns besides text,
     and it exists to break up a run of gold headings. */
  --stripe:      #eef1ed;

  --rule:        rgba(238, 241, 237, .20);
  --rule-gold:   rgba(20, 24, 26, .35);
  --rule-night:  rgba(238, 241, 237, .18);

  --display: 'Big Shoulders Display', 'Barlow Condensed', system-ui, sans-serif;
  --cond:    'Barlow Condensed', system-ui, sans-serif;
  --body:    'Barlow', system-ui, sans-serif;

  --gutter: 20px;
  /* The gold row in a table overhangs its container by this much on both
     sides, and pads itself back by the same, so its text still lines up
     with the rows above. On phone the gutter equals it, so it runs to
     the screen edge; on desktop it reads as a plate laid over the table. */
  --bleed: 20px;

  --band-y: 28px;
  --wrap: 1280px;
}

@media (min-width: 880px) {
  :root {
    --gutter: 56px;
    --band-y: 56px;
  }
}

/* ---------- base ---------- */

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

html { background: var(--ground); }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.55;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, ul, ol { margin: 0; }
img, svg { display: block; max-width: 100%; }

/* A link in running text is CHALK with a gold rule under it, not gold text.
   Gold-on-gold was the problem: the page already spends the colour on headings and
   on whole filled fields, so every third sentence with a link in it grew a second
   gold thing at body size, and a colour that is everywhere stops pointing at
   anything. Underline-only keeps the link obvious (it is the one underlined thing
   on the page) and gives the colour back to the fields it owns. Hover swaps them
   round, so the affordance still lights up. */
a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--gold);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
a:hover { color: var(--gold-ink); text-decoration-color: currentColor; }
/* Chrome, not prose. These are buttons, badges and navigation: they already say they
   are clickable by being what they are, and an underline through a store badge or a
   masthead is noise. Every selector here is a class, so it outranks the bare `a` rule
   above without needing anything louder. */
.skip, .brand, .btn-store, .badge, .cta, .plate,
.nav a, .foot__list a, .colophon a { text-decoration: none; }
/* The 404 links are the exception that proves the list: they are not chrome, they are the
   only thing on the page you can do, and they sit in the middle of it with no masthead
   position or button shape to say so. They keep the underline. */
a:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}
/* --ink on --night is 1.06:1, so the ring was invisible on exactly the band
   that closes every page and holds the App Store badge. Gold on night is
   9.44:1. */
.band--night a:focus-visible { outline-color: var(--gold); }

/* Keyboard users get to skip the masthead; everyone else never sees this. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--gold);
  color: var(--ink);
  font-family: var(--cond);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: .12em;
  text-transform: uppercase;
  padding: 12px 18px;
  z-index: 10;
}
.skip:focus { left: 0; }

.wrap {
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ---------- type ---------- */

.eyebrow {
  font-family: var(--cond);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--gold-ink);
}
/* Grey. An eyebrow that labels an example inside a section is not the section's
   own pointer; two of them side by side over the worked sums were a second pair
   of gold caps under a gold heading. Same call as .card__n further down. */
.eyebrow--plain { color: var(--ink-2); letter-spacing: .14em; }
.eyebrow--onGold { color: inherit; font-weight: 700; }

/* THE BIG HEADINGS ARE CHALK, AND GOLD OWNS FIELDS. This was the other way
   round for two days and it did not survive contact with a subpage. On the
   homepage the gold h1 has a gold sum and a lit device to answer it, so it is
   one voice among several. A subpage has none of that: it is an eyebrow, an h1,
   two or three h2s and running text, and painting every one of those gold made
   four gold blocks in a column with nothing between them but grey paragraphs.
   Gold at every heading is gold at no heading.

   So the rule goes back to the one the design started with: the colour owns
   whole fields (the formula plate, the gold band, the address plate, the
   buttons) and small pointers (the eyebrow, the active step number). Headings
   are chalk. --ink is #eef1ed on both the page and the night band, and flips to
   near-black inside anything gold-filled, so one declaration covers all three
   grounds. The hero keeps its gold line; see .hero__h1. */
h1, .h1, h2, .h2 { color: var(--ink); }

h1, .h1 {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(46px, 5.4vw, 68px);
  line-height: .94;
  letter-spacing: -.02em;
  text-wrap: balance;
}

h2, .h2 {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(34px, 3.2vw, 40px);
  line-height: 1;
  letter-spacing: -.02em;
}
/* The heading that opens a band rather than labelling a table. */
.h2--lead {
  font-size: clamp(34px, 3.6vw, 46px);
  line-height: .98;
}
.h2--close {
  font-size: clamp(38px, 4.2vw, 52px);
  line-height: .96;
}

/* Explicitly pale, because .h3 is worn by h2 elements in the card decks and
   would otherwise inherit the gold from the rule above. Gold at every heading
   level is gold at no heading level. */
.h3 {
  color: var(--ink);
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(26px, 2.3vw, 30px);
  line-height: 1;
}

.lede { font-size: clamp(18px, 1.5vw, 19px); line-height: 1.5; }
.note { font-size: 16px; line-height: 1.55; color: var(--ink-2); }

/* ---------- the white stripe ---------- */

/* A hairline that runs the full width of the window, with a short gold tick
   sitting on its left end. It marks the seam between two bands that share a
   ground, which on the homepage is every seam: hero, display and the two dark
   bands below it are all one colour, so without it the page is one scroll with
   no joints in it. It is a real element between bands rather than a border on
   one of them, which keeps it independent of which band happens to come next. */
.stripe {
  position: relative;
  height: 1px;
  background: var(--stripe);
  opacity: .82;
}
.stripe::before {
  content: '';
  position: absolute;
  /* Flush to the window edge, not to the text column. Aligning it with the
     gutter needs the viewport width, and 100vw includes the scrollbar, which is
     exactly the sum that puts a page 15px sideways. */
  left: 0;
  top: -2px;
  width: clamp(44px, 8vw, 132px);
  height: 5px;
  background: var(--gold);
}

/* ---------- bands ---------- */

.band { padding-block: var(--band-y) calc(var(--band-y) + 4px); }
.band--light { background: var(--ground-2); }
.band--gold  { background: var(--gold); }
.band--night { background: var(--night); color: var(--ink-night); }

/* GOLD IS A LIGHT SURFACE. Rather than hunting down every `color: var(--ink)`
   that happens to sit on gold, the tokens flip back inside anything gold-filled.
   One rule, and a child added later cannot get it wrong. */
.band--gold,
.skip,
.btn-store,
.cta,
.plate,
.formula,
.rate__row--best,
.arith__row--total {
  color: var(--on-gold);
  --ink:   #14181a;
  --ink-2: rgba(20, 24, 26, .72);
  --gold-ink: #523806;
  --rule:  rgba(20, 24, 26, .28);
}

.band--night a { color: var(--ink-night); }
.band--night a:hover { color: var(--gold); }
/* The buttons in this band ARE gold, so the rule above would paint their text
   the same colour as their fill. Ink, always, on any gold surface. */
.band--night .cta,
.band--night .cta:hover,
.band--night .plate,
.band--night .plate:hover { color: var(--on-gold); }
.band--night .note { color: var(--ink-night-2); }

/* ---------- masthead ---------- */

.mast {
  border-bottom: 1px solid var(--rule);
}
.mast__inner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px 28px;
  padding-block: 16px 12px;
}
.brand {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-right: auto;
  color: var(--ink);
}
.brand__name {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(22px, 2vw, 26px);
  letter-spacing: -.01em;
  line-height: 1;
}
/* Below the wordmark there is no room for the descriptor on a phone. */
.brand__rest {
  display: none;
  font-family: var(--cond);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.nav {
  order: 3;
  width: 100%;
  display: flex;
  /* Five items, and at 320px they do not fit on one line. They wrap instead of pushing
     the row past the viewport: the fifth is the one that leaves for another host, so the
     line it lands on is the honest place for it. */
  flex-wrap: wrap;
  gap: 10px 16px;
  padding-bottom: 12px;
  font-family: var(--cond);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-2);
}
/* The nav and colophon links are 13px small caps, so their text box is about
   20px tall. This grows the hit area to comfortably over 44px without moving
   a single pixel of the layout. */
.nav a, .colophon a { position: relative; }
.nav a::after, .colophon a::after {
  content: '';
  position: absolute;
  inset: -13px -6px;
}
.nav a { color: inherit; }
.nav a:hover { color: var(--ink); }
.nav a[aria-current='page'] {
  color: var(--ink);
  border-bottom: 2px solid var(--gold);
  padding-bottom: 3px;
}

/* Not a fifth page. The four links to its left go somewhere on this site; this one
   leaves for the signed-in app on its own subdomain, which is a different kind of
   destination and gets a rule to say so rather than a colour. Chalk, not gold: gold
   owns filled fields and the eyebrow-sized pointers, and a fifth gold word in the
   masthead is exactly the creep that was walked back on 26 August. */
.nav__login {
  color: var(--ink);
  padding-left: 16px;
  border-left: 1px solid var(--rule);
}

.btn-store {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding-inline: 20px;
  background: var(--gold);
  color: var(--ink);
  font-family: var(--cond);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: .12em;
  text-transform: uppercase;
  line-height: 1;
}
.btn-store:hover { color: var(--ink); filter: brightness(1.06); }

@media (min-width: 880px) {
  .mast__inner { padding-block: 20px; }
  .brand__rest { display: block; }
  .nav {
    order: 0;
    width: auto;
    /* 24px between four items, 18px between five: at exactly 880 the row is brand +
       five links + the Download button, and the wider gap pushed the button onto a
       second line for the sixty pixels between here and 940. Measured, not guessed. */
    gap: 18px;
    padding-bottom: 0;
    font-size: 15px;
  }
  .nav__login { padding-left: 18px; }
}

@media (min-width: 1000px) {
  .nav { gap: 24px; }
  .nav__login { padding-left: 24px; }
}

/* ---------- page head ---------- */

.phead { padding-block: 26px 24px; }
.phead__title { margin-top: 12px; }
.phead__lede { margin-top: 14px; }

@media (min-width: 880px) {
  .phead {
    padding-block: 60px 56px;
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: clamp(24px, 3.8vw, 48px);
    align-items: end;
  }
  .phead__head { grid-column: span 7; }
  .phead__lede {
    grid-column: span 5;
    margin-top: 0;
    padding-bottom: 6px;
  }
  .phead__title { margin-top: 18px; }
}

/* ---------- section head (heading + rail note + rule) ---------- */

.sechead {
  padding-bottom: 12px;
  border-bottom: 2px solid var(--ink);
}
.sechead__note {
  display: block;
  margin-top: 8px;
  font-family: var(--cond);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.band--gold .sechead { border-bottom-color: var(--ink); }
.band--gold .sechead__note { color: inherit; }

@media (min-width: 880px) {
  .sechead {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: clamp(16px, 2.4vw, 32px);
    padding-bottom: 14px;
  }
  .sechead__note { margin-top: 0; text-align: right; }
}

/* ---------- cards (the numbered three-up) ---------- */

.cards {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 24px;
  margin-top: 22px;
}
.card { display: flex; flex-direction: column; gap: 8px; }
/* Grey, not gold. Six of these on a page, each one a gold line above a chalk heading,
   was the single biggest source of yellow on the sub-pages: it put the colour on the
   label rather than on the thing being labelled. The number still separates itself
   from the heading by being condensed, small, tracked out and uppercase, which is four
   signals before colour is even asked to help. */
.card__n {
  font-family: var(--cond);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.band--gold .card__n { color: inherit; }

@media (min-width: 880px) {
  .cards { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 44px; margin-top: 34px; }
  .cards--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 56px; }
  .card { gap: 10px; }
}

/* ---------- definition rows (label / value) ---------- */

.rows { display: flex; flex-direction: column; }
.row {
  padding-block: 12px;
  border-bottom: 1px solid var(--rule);
}
.row:last-child { border-bottom: 0; }
.row__k {
  font-family: var(--cond);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.row__v {
  margin-top: 3px;
  /* A mail address is one unbreakable token. In the 880px grid the value track is
     minmax(0, 1fr), so the track shrinks but the word does not, and support@weightedapp.com
     hung 3px past the viewport on /contact. `anywhere` also feeds min-content sizing, which
     `break-word` does not, so it is the one that actually lets the track resolve. */
  overflow-wrap: anywhere;
}

.band--night .row { border-bottom-color: var(--rule-night); }
.band--night .row__k { color: rgba(238, 240, 236, .7); }

@media (min-width: 880px) {
  .row {
    display: grid;
    grid-template-columns: minmax(0, 260px) minmax(0, 1fr);
    gap: clamp(16px, 2.4vw, 40px);
    padding-block: 14px;
  }
  .row__k { font-size: 15px; }
  .row__v { margin-top: 0; }
}

/* ---------- the rate table on pricing ---------- */

.rate { display: flex; flex-direction: column; border-top: 2px solid var(--ink); }
.rate__row {
  padding-block: 16px;
  border-bottom: 1px solid var(--rule);
}
.rate__row--best {
  background: var(--gold);
  border-bottom: 0;
  margin-inline: calc(var(--bleed) * -1);
  padding: 18px var(--bleed);
}
.rate__head { display: flex; align-items: baseline; gap: 10px; }
.rate__n {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(44px, 4.4vw, 52px);
  line-height: .9;
  letter-spacing: -.02em;
}
.rate__per {
  font-family: var(--cond);
  font-weight: 600;
  font-size: clamp(13px, 1.2vw, 14px);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.rate__row--best .rate__per { color: inherit; font-weight: 700; }
.rate__b { margin-top: 8px; max-width: 62ch; }

@media (min-width: 880px) {
  .rate__row {
    display: grid;
    grid-template-columns: minmax(0, 340px) minmax(0, 1fr);
    gap: clamp(16px, 2.4vw, 40px);
    align-items: baseline;
    padding-block: 20px;
  }
  .rate__row--best { padding: 22px var(--bleed); }
  .rate__b { margin-top: 0; }
}

/* ---------- the currency picker under the rate table ----------

   Wat een bezoeker betaalt hangt aan het land van zijn Apple ID, en dat kan deze pagina
   niet zien: de prijs erboven is een gis op basis van de tijdzone. Deze regel is de uitweg
   voor de mens voor wie die gis fout uitpakt, en daarom staat hij dicht bij de bedragen en
   niet ergens in de voet. Hij wordt door js/price.js gebouwd, dus zonder JavaScript staat
   hier niets: een knop die niets doet is erger dan geen knop. */

/* WAAROM DE SECTIE ZELF EEN STAPELCONTEXT KRIJGT. Het open paneel hangt over de sectiegrens
   heen, en het werd overschilderd door de band eronder. Een z-index op het paneel repareert
   dat niet, en op de `<details>` eromheen ook niet: met 50 op `.cur` gaf `elementFromPoint`
   op elke hoogte onder de grens nog steeds het element uit de vólgende sectie terug. Pas een
   stapelcontext op de sectie zelf wint het. Gemeten op vijf hoogtes, niet beredeneerd, en de
   verklaring die ik eerst opschreef (`::details-content`) klopte niet: die staat op
   contain:none. Drie en niet meer, want de plakkende kop staat op tien en hoort erover. */
.has-cur { position: relative; z-index: 3; }

.cur { position: relative; display: inline-block; margin-top: 14px; }

.cur__now {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;              /* zelfde vloer als elke andere losse knop op deze site */
  font-family: var(--cond);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-2);
  cursor: pointer;
  list-style: none;
}
.cur__now::-webkit-details-marker { display: none; }
.cur__now::after {
  content: '';
  width: 7px; height: 7px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform .16s ease;
}
.cur[open] .cur__now::after { transform: translateY(1px) rotate(225deg); }
.cur__now:hover { color: var(--ink); }
.cur__now:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

.cur__code {
  color: var(--gold-ink);
  border-bottom: 1px solid currentColor;
  font-weight: 700;
}
.cur__now:hover .cur__code { color: var(--gold); }

.cur__menu {
  position: absolute;
  z-index: 20;
  top: calc(100% + 4px);
  left: 0;
  width: 380px;
  max-width: calc(100vw - var(--gutter) * 2);
  /* Niet `--ground-2`: dat is exact de kleur van de band waar dit paneel overheen valt, dus
     dan is de rand het enige verschil tussen menu en pagina. `--night` staat onder allebei. */
  background: var(--night);
  border: 1px solid var(--rule);
  box-shadow: 0 18px 44px rgba(0, 0, 0, .6);
}
.cur__list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: min(52vh, 380px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.cur__opt {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 8px 14px;
  border: 0;
  border-left: 2px solid transparent;
  background: none;
  color: var(--ink-2);
  text-align: left;
  cursor: pointer;
  font: inherit;
}
.cur__opt:hover { background: rgba(238, 241, 237, .06); color: var(--ink); }
.cur__opt:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }
.cur__opt[aria-current='true'] { border-left-color: var(--gold); color: var(--ink); }

.cur__opt-code {
  font-family: var(--cond);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: .1em;
  color: var(--ink);
}
.cur__opt[aria-current='true'] .cur__opt-code { color: var(--gold-ink); }
.cur__opt-name {
  font-size: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cur__opt-price {
  font-family: var(--cond);
  font-weight: 600;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}

/* ---------- question and answer ---------- */

.qa { display: flex; flex-direction: column; }
.qa__i {
  padding-block: 16px;
  border-bottom: 1px solid var(--rule);
}
.qa__i:last-child { border-bottom: 0; }
.qa__q {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(24px, 2.1vw, 26px);
  line-height: 1.05;
}
.qa__a { margin-top: 8px; }
.band--gold .qa__i { border-bottom-color: var(--rule-gold); }

@media (min-width: 880px) {
  .qa__i { padding-block: 18px; }
}

/* A band split into a heading rail and a body column. */
.rail { display: block; }
.rail__note { margin-top: 14px; }
.rail__body { margin-top: 18px; }

@media (min-width: 880px) {
  .rail {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: clamp(24px, 3.8vw, 48px);
    align-items: start;
  }
  /* The heading follows its own answers down. On support the body column runs five
     questions long and the head is four lines, which left six to eight hundred pixels
     of blank column beside them and scrolled the heading out of view long before the
     last answer arrived. Sticky costs nothing where the two columns are of a size,
     because the head never gets the chance to travel; it only shows up where the gap
     was the problem. Needs the `align-items: start` above: a stretched grid item fills
     its row and can never stick. */
  .rail__head {
    grid-column: span 4;
    position: sticky;
    top: clamp(24px, 5vh, 48px);
  }
  .rail__body { grid-column: span 8; margin-top: 0; }
  .rail--5 .rail__head { grid-column: span 5; }
  .rail--5 .rail__body { grid-column: span 7; }
  .rail--6 .rail__head { grid-column: span 6; }
  .rail--6 .rail__body { grid-column: span 6; }
}

/* ---------- the formula plate and its worked cases ---------- */

.formula {
  background: var(--gold);
  color: var(--ink);
  padding: 16px 18px 18px;
  margin-top: 22px;
}
.formula__eq {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(30px, 3.6vw, 46px);
  line-height: 1.04;
  letter-spacing: -.01em;
  margin-top: 6px;
}
.formula__note {
  font-size: 15px;
  line-height: 1.45;
  margin-top: 10px;
  max-width: 46ch;
}

.worked { display: grid; grid-template-columns: minmax(0, 1fr); }
.worked__i {
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--rule-night);
}
.worked__sum {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(30px, 2.8vw, 34px);
  line-height: 1.1;
  margin-top: 6px;
}
.worked__b { margin-top: 8px; color: rgba(238, 240, 236, .78); }

@media (min-width: 880px) {
  .formula { padding: 20px 26px 24px; margin-top: 34px; }
  .worked {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin-top: 34px;
    border-top: 1px solid var(--rule-night);
  }
  .worked__i { margin-top: 0; padding: 22px clamp(20px, 3vw, 40px) 0 0; border-top: 0; }
  .worked__i + .worked__i { padding: 22px 0 0 clamp(20px, 3vw, 40px); border-left: 1px solid var(--rule-night); }
}

/* ---------- numbered checklist ---------- */

.numlist { display: flex; flex-direction: column; }
.numlist__i {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr);
  gap: 16px;
  align-items: baseline;
  padding-block: 14px;
  border-bottom: 1px solid var(--rule);
}
.numlist__i:last-child { border-bottom: 0; }
/* Same call as .card__n, and it matters more here because these are set at 30px: four
   gold display numerals down the left of a list out-shout the instructions beside them. */
.numlist__n {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(26px, 2.4vw, 30px);
  line-height: 1;
  color: var(--ink-2);
}

@media (min-width: 880px) {
  .numlist__i { grid-template-columns: 56px minmax(0, 1fr); gap: 24px; padding-block: 16px; }
}

/* A plain list of statements with rules between them. */
.claims { display: flex; flex-direction: column; border-top: 2px solid var(--ink); margin-top: 18px; }
.claims > li {
  list-style: none;
  padding-block: 13px;
  border-bottom: 1px solid var(--rule-gold);
  font-size: clamp(17px, 1.4vw, 18px);
  line-height: 1.5;
}
.claims > li:last-child { border-bottom: 0; }

/* ---------- the big gold address plate ---------- */

.plate {
  display: block;
  background: var(--gold);
  color: var(--ink);
  padding: 18px 18px 20px;
  margin-top: 24px;
}
.plate:hover { color: var(--ink); filter: brightness(1.04); }
.plate__label {
  display: block;
  font-family: var(--cond);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: .16em;
  text-transform: uppercase;
}
.plate__value {
  display: block;
  /* The address only fits because Big Shoulders is condensed. While the webfont
     is still arriving the fallback is wider, so let it break instead of pushing
     a scrollbar onto the page. */
  overflow-wrap: anywhere;
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(34px, 5vw, 64px);
  line-height: 1;
  letter-spacing: -.02em;
  margin-top: 5px;
}

@media (min-width: 880px) {
  .plate { padding: 24px 26px 26px; margin-top: 40px; }
}

/* ---------- closing band ---------- */

.close { display: flex; flex-direction: column; gap: 18px; }
.close__text { display: flex; flex-direction: column; gap: 14px; }
.close__lede { color: var(--ink-night-2); max-width: 52ch; }

/* The site's own button, for destinations that are not the App Store
   (the full policy, the EULA, the support address). Anything pointing at
   the store uses Apple's own badge: black on light ground, white on the
   closing band. Neither is redrawn. */
.cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 56px;
  padding: 10px 24px;
  background: var(--gold);
  color: var(--ink);
  line-height: 1.05;
  text-align: center;
}
.cta:hover { color: var(--ink); filter: brightness(1.06); }
.cta__small {
  font-family: var(--cond);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: .18em;
  text-transform: uppercase;
}
.cta__big {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(21px, 2.4vw, 34px);
  letter-spacing: -.01em;
}

@media (min-width: 880px) {
  .close {
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
    gap: 48px;
  }
  .close__text { gap: 16px; }
  .close__text .h2--close { max-width: 20ch; }
  /* `.badge` carries `align-self: flex-start` for the hero and the footer, where it sits in
     a left-aligned column. In this row it silently won against the container's `flex-end`
     and stuck the badge to the top, which left a 200px hole under it on every page that
     ends in this band. It lines up with the last line of the text instead. */
  .close .badge { align-self: flex-end; }
  .cta {
    flex: none;
    align-items: flex-start;
    text-align: left;
    min-height: 0;
    padding: 18px 30px;
  }
  .badge--close { flex: none; align-self: flex-end; }
}

/* ---------- colophon ---------- */

.colophon {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 16px 24px;
  margin-top: 26px;
  padding-top: 14px;
  border-top: 1px solid rgba(238, 240, 236, .2);
  font-family: var(--cond);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(238, 240, 236, .75);
}
.colophon__links { display: flex; gap: 22px; }
.colophon a { color: rgba(238, 240, 236, .65); }
.colophon a:hover { color: var(--ink-night); }
.colophon a[aria-current='page'] { color: var(--gold); }

@media (min-width: 880px) {
  .colophon { margin-top: 48px; padding-top: 18px; }
}

/* ---------- the hero, which only the homepage has ---------- */

/* Type, and then the product. The old hero put a gold field, a screenshot, a
   sum and a badge above the fold at once and had to shrink all four to do it.
   The page is dark now, the app is dark, and the device below runs at a size
   worth looking at, so the hero can do one job: say what this is. */
.hero { padding-block: 30px 36px; }
/* The one gold heading left on the site. It is gold because the hero answers it
   with a gold sum and a lit device in the same view, so the colour is one voice
   among several rather than the only one; a subpage has no such company, which
   is why the default is chalk. */
.hero__h1 {
  color: var(--gold-ink);
  margin-top: 14px;
  font-size: clamp(48px, 9.4vw, 104px);
  line-height: .92;
}
/* The second line is pale, not gold. The sentence is one sentence; the colour
   change is where the claim stops and the qualifier starts, and it is the same
   two-colour pairing the stripe makes between bands. */
.hero__h1 .pale { color: var(--ink); }
.hero__lede { margin-top: 20px; color: var(--ink-2); max-width: 54ch; }
.hero__act {
  margin-top: 26px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}

@media (min-width: 880px) {
  .hero { padding-block: 68px 60px; }
  .hero__inner {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: clamp(24px, 3.8vw, 48px);
    align-items: end;
  }
  .hero__eyebrow { grid-column: 1 / -1; }
  .hero__h1 { grid-column: 1 / 8; margin-top: 26px; }
  .hero__side { grid-column: 8 / 13; padding-bottom: 10px; }
  .hero__lede { margin-top: 0; }
  .hero__act { margin-top: 22px; }
}

/* ---------- the display: one device, five screens ---------- */

/* The section is taller than the window and the device is stuck to the middle
   of it, so scrolling the page turns the app over instead of moving it past.
   Height is one viewport to lock, then a dwell plus a handover per screen.

   `is-live` is added by the script. Without it the section is one screen tall
   and shows the first shot as a still, so a reader with no JavaScript gets a
   finished picture rather than four viewports of empty scroll. */
.stage {
  position: relative;
  background: var(--ground);
}
.stage.is-live {
  height: calc(100vh + 4 * 62vh);
  height: calc(100svh + 4 * 62svh);
}
.stage__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  display: flex;
  align-items: center;
  overflow: hidden;
}
.stage__grid {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
}
/* Both children need a width. In a centred flex column they shrink to their own
   content, which turned the sentence column into a 200px ribbon of two-word
   lines that then overflowed the height reserved for it and pushed the device
   off the bottom of the panel. */
.stage__copy, .stage__device { width: 100%; }
/* The device leads on a phone. The list of five is dropped there anyway, so
   putting the label above it would mean reading a caption before there is
   anything to caption. */
.stage__device { order: -1; }
.stage__copy > .eyebrow { display: none; }

/* On a phone the five titles do not fit beside a device that is already most of
   the screen, so the list is dropped and the active line carries its own number
   and title. On desktop the list is the spine and the line drops them again. */
.steps { display: none; }
.says {
  position: relative;
  width: 100%;
  /* Room for the longest of the five at the narrowest width, so the panel does
     not change height when the line changes. */
  min-height: 8em;
  text-align: center;
}
/* Out first, then in, rather than a crossfade. Two paragraphs at half opacity
   on top of each other is unreadable in a way two half-opacity pictures are
   not: the letters interleave. The incoming line waits for the outgoing one to
   be gone. */
.say {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity .14s ease-out;
}
.say.is-on { opacity: 1; transition: opacity .2s ease-out .15s; }
.say__n {
  font-family: var(--cond);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: .16em;
  color: var(--gold-ink);
  margin-right: 10px;
}
.say__t {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(24px, 5.4vw, 30px);
  line-height: 1;
  color: var(--ink);
}
.say__b {
  display: block;
  margin-top: 8px;
  font-size: 16px;
  line-height: 1.45;
  color: var(--ink-2);
  max-width: 42ch;
  margin-inline: auto;
}

@media (min-width: 880px) {
  .stage__grid {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: clamp(24px, 3.8vw, 48px);
    align-items: center;
  }
  .stage__copy { grid-column: 1 / 6; }
  .stage__device { grid-column: 7 / 13; order: 0; }
  .stage__copy > .eyebrow { display: block; }

  .steps {
    display: flex;
    flex-direction: column;
    list-style: none;
    margin: 20px 0 0;
    padding: 0;
    border-top: 1px solid var(--rule);
  }
  .step {
    display: flex;
    align-items: baseline;
    gap: 14px;
    padding-block: 11px;
    border-bottom: 1px solid var(--rule);
    color: var(--ink-2);
    transition: color .28s ease-out;
  }
  .step__n {
    font-family: var(--cond);
    font-weight: 700;
    font-size: 13px;
    letter-spacing: .14em;
    flex: none;
    width: 2.4em;
  }
  .step__t {
    font-family: var(--display);
    font-weight: 700;
    font-size: 24px;
    line-height: 1;
  }
  /* The active line lights up. Nothing moves: a list that reflows while you
     scroll past it is a list you cannot read. */
  .step.is-on { color: var(--ink); }
  .step.is-on .step__n { color: var(--gold-ink); }

  .says { min-height: 4.8em; text-align: left; margin-top: 18px; }
  .say__n, .say__t { display: none; }
  .say__b { margin-top: 0; margin-inline: 0; font-size: 17px; }
}

/* ---------- the screens inside the device ---------- */

/* Five real screenshots, stacked, one on top. The handover is a dissolve with a
   little scale and a little blur on both halves at once, so the outgoing screen
   softens and lifts while the incoming one settles: the chrome they share
   (black ground, tab bar) never moves and only the content between them melts.
   Every value is written by the script, one write per screen per frame. */
.screenstack { position: absolute; inset: 0; }
.screenstack > picture {
  position: absolute;
  inset: 0;
  display: block;
  opacity: 0;
  will-change: opacity, transform, filter;
}
/* No script, no scroll: the first screen is simply the screen. */
.screenstack > picture:first-child { opacity: 1; }
.screenstack img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---------- the display frame ---------- */

/* --dw lives here rather than on .dev-scene so the glass layer can be sized off
   the same number and land on the device instead of near it. */
.display {
  position: relative;
  --dw: min(248px, 56vw, 34svh);
}
.display .dev-scene { --w: var(--dw); padding-right: 0; position: relative; z-index: 1; }

/* ---------- the lamp above the device ---------- */

/* The rest of the site has no glows in it and that rule still stands for its own surfaces:
   bands, cards and rules are flat. This is not a surface, it is a light, and it exists to
   answer a question the flat version could not. A device standing on a near-black page has
   nothing to stand ON: no floor, no contact, no scale. One lamp gives it all three, because
   a pool of light IS a floor, and the shadow it interrupts IS the contact.

   Everything is behind the device (z-index 0 against the scene's 1), so no part of it ever
   crosses the screenshot. A beam over the glass would be lens flare, which is decoration;
   a beam behind it is a room. */
.beam {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/* The cone, as three stacked ellipses rather than a trapezoid.

   The trapezoid was the obvious build and it was wrong: `clip-path` gives two dead straight
   diagonals, and no amount of `blur()` turns a cut edge into a falloff — it turns it into a
   soft cut edge, which still reads as a grey wedge lying on the page. Light does not have a
   boundary. Three overlapping radial gradients, narrow and bright at the top, wide and faint
   lower down, have no edge anywhere and cost one paint instead of a filter pass.

   Warm, not white: at these opacities a neutral highlight over #0e1113 comes out grey, and
   grey light on a gold page is fog. The top stop is gold lifted toward white, not white. */
/* WIDTH IS THE WHOLE SIZE OF THE SOURCE. Every x-radius below is a fraction of this
   box, so growing the box alone scales the hotspot, the mid ellipse and the base by
   the same factor and the cone keeps its shape while the lamp behind it gets bigger.
   Reaching for the three percentages instead would have changed the shape as well,
   which is a different lamp rather than a larger one. Height is deliberately left
   alone: a wider source at the same distance throws a wider cone, not a longer one. */
.beam__cone {
  position: absolute;
  left: 50%;
  top: -14%;
  width: 160%;
  height: 124%;
  transform: translateX(-50%);
  background:
    radial-gradient(ellipse 15% 29% at 50% 0%, rgba(248, 229, 178, .30), rgba(248, 229, 178, 0) 70%),
    radial-gradient(ellipse 27% 45% at 50% 17%, rgba(234, 192, 100, .11), rgba(234, 192, 100, 0) 72%),
    radial-gradient(ellipse 45% 56% at 50% 50%, rgba(227, 178, 60, .05), rgba(227, 178, 60, 0) 74%);
  animation: beam-breathe 11s ease-in-out infinite alternate;
}

/* The floor. Its centre sits BELOW the bottom of the device, not behind it: a pool that
   overlaps the phone is a halo, and a halo is the thing this was built to avoid. Pushed down
   past the box the device stands in, it becomes the ground the device is standing on. */
.beam__pool {
  position: absolute;
  left: 50%;
  bottom: -12%;
  width: 152%;
  height: 20%;
  transform: translateX(-50%);
  background: radial-gradient(ellipse 50% 50% at 50% 50%, rgba(230, 186, 84, .20), rgba(227, 178, 60, .055) 52%, rgba(227, 178, 60, 0) 80%);
  animation: beam-breathe 11s ease-in-out infinite alternate;
}

/* The shadow, painted over the pool it is a hole in, on the same centre line. Nudged left
   and narrower than the device: the lamp is above and slightly in front, and the device
   leans away from you, so it stands on a short shadow rather than lying on a long one. */
.beam__shadow {
  position: absolute;
  left: 50%;
  bottom: -6%;
  width: 66%;
  height: 8%;
  transform: translateX(-54%);
  /* Softened along with the lamp. A wide source sees further around the object than a
     point does, so the edge of what it cannot reach is broad: the core gives up a
     little density and the falloff runs out later. A hard-edged shadow under a big
     lamp is the one combination that reads as wrong. */
  background: radial-gradient(ellipse 50% 50% at 50% 50%, rgba(3, 5, 6, .93), rgba(3, 5, 6, .58) 40%, rgba(3, 5, 6, 0) 84%);
}

/* Dust. A beam you cannot see anything in is a gradient; a beam with something drifting in
   it is a beam. Fourteen specks, each with its own x, size, speed and phase, all of them
   negative delays so the set is already mid-drift on the first frame instead of starting
   together. */
/* Wider and taller than the device so the specks have somewhere lit to be: the flanks where
   the cone spills past the phone, and the strip above it. The percentages on each speck are
   read against THIS box, not against the device. */
/* Widened with the cone. The specks are only worth animating where there is light to
   catch them, so this box tracks `.beam__cone`: at 180% the device covers the middle
   22% to 78% of it and the cone still reaches past both ends, which leaves two lit
   flanks half again as wide as before to scatter dust into. */
.motes {
  position: absolute;
  left: -40%;
  width: 180%;
  top: -14%;
  height: 116%;
}
/* --dim is how much of the beam reaches this particular speck. The cone is bright in
   the middle and gives out at its edges, but a speck's opacity is its own property and
   knows nothing about the gradient behind it, so without this the ones furthest out
   burn at full strength over near-black and read as stars rather than as dust. Set per
   speck from how far it sits off centre; the default keeps anything unmarked at full. */
.motes i {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--s);
  height: var(--s);
  border-radius: 50%;
  background: rgba(245, 238, 218, .78);
  opacity: 0;
  animation: mote-drift var(--d) linear infinite;
  animation-delay: var(--delay);
}

@keyframes beam-breathe {
  from { opacity: .78; }
  to   { opacity: 1; }
}

/* Up and slightly across, the way dust moves in still air near a warm lamp. It fades in and
   out inside its own travel, so nothing ever pops into existence at a fixed point. */
@keyframes mote-drift {
  0%   { transform: translate3d(0, 12px, 0); opacity: 0; }
  18%  { opacity: calc(var(--dim, 1) * .55); }
  62%  { opacity: calc(var(--dim, 1) * .8); }
  100% { transform: translate3d(9px, -46px, 0); opacity: 0; }
}

/* Someone who asked for less motion keeps the light and loses the movement. The dust stays
   visible, because a still speck of dust is still dust. */
@media (prefers-reduced-motion: reduce) {
  .beam__cone, .beam__pool { animation: none; }
  .motes i { animation: none; opacity: calc(var(--dim, 1) * .5); }
}
/* Slightly turned, screen mostly toward you. The script drives it from -17 to -8
   degrees across the section; -13 is where it rests if nothing drives it. */
.display .dev { transform: rotateX(4deg) rotateY(-13deg); }

@media (min-width: 880px) {
  .display { --dw: min(330px, 30vw, 42svh); }
  .glass { --reach: 1; }
}

/* ---------- the controls that come off the screen ---------- */

/* Four real affordances from the logging screen, lifted off the glass and
   brought forward as their own objects while the first screen is up. They are
   the app's own controls carrying the app's own numbers, so the effect is the
   product gaining depth rather than decoration arriving on top of it.

   This is the one place on the site with a blur in it, and it is a
   backdrop-filter: the chip has no colour of its own, it takes the device
   underneath it. A drop shadow would have been the thing to avoid; borrowing
   the screen is the opposite move. */
.glass {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: var(--dw);
  /* Above the device, which is above the lamp. Without this the chips inherit the source
     order and the beam's stacking context can take them, which puts a control that is
     supposed to be coming toward you behind the thing it came off. */
  z-index: 2;
  transform: translateX(-50%);
  perspective: 900px;
  pointer-events: none;
  --g: 0;
  /* The device's own yaw, unitless so it can be added to. Written every frame by
     display.js from the same expression that turns the device, and left at the
     resting angle of `.display .dev` for anyone the script never runs for. The
     chips read it so that they lie in the screen's plane instead of facing the
     reader: a control that came off the glass and is still square to you is a
     sticker on top of a photo, not an object that left a surface. */
  --yaw: -13;
  /* How far the controls travel away from the device. On a phone the device is
     already most of the width, so the same absolute reach would push a chip off
     the side; .stage__sticky clips it, which means the chip would simply be cut
     in half rather than the page breaking. Shortened instead. */
  --reach: .58;
}
.chip {
  position: absolute;
  left: var(--x);
  top: var(--y);
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 13px;
  border-radius: 999px;
  border: 1px solid rgba(238, 241, 237, .30);
  background: rgba(238, 241, 237, .10);
  -webkit-backdrop-filter: blur(13px) saturate(1.5);
  backdrop-filter: blur(13px) saturate(1.5);
  color: var(--ink);
  font-family: var(--cond);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: .12em;
  text-transform: uppercase;
  white-space: nowrap;
  opacity: var(--g);
  /* Same two angles as the device, in the same order (tilt in the page's plane
     first, then the turn around the object's own axis), so the chip and the
     screen it came off are parallel at every scroll position. There used to be a
     14-degree entry rotation here that unwound to zero; it has gone, because
     zero is exactly the angle that disagrees with the screen. What is left to
     announce the arrival is the travel, the scale and the fade, which is what
     coming off a surface actually looks like. */
  transform:
    translate(-50%, -50%)
    translate3d(calc(var(--tx) * var(--g) * var(--reach)), calc(var(--ty) * var(--g) * var(--reach)), calc(var(--tz) * var(--g) * var(--reach)))
    rotateX(4deg)
    rotateY(calc(var(--yaw) * 1deg))
    scale(calc(.84 + .16 * var(--g)));
  will-change: transform, opacity;
}
/* Finish is a gold pill in the app. It stays one here, or the chip stops being
   the button it came off. */
.chip--gold {
  background: rgba(227, 178, 60, .88);
  border-color: rgba(227, 178, 60, .95);
  color: var(--on-gold);
}
.chip__v {
  font-family: var(--display);
  font-weight: 800;
  font-size: 18px;
  line-height: 1;
  letter-spacing: -.01em;
  text-transform: none;
}

/* ---------- the sum, and the store badge ---------- */

.sum {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  border-top: 2px solid var(--ink);
  border-bottom: 1px solid var(--rule);
  padding-block: 16px;
}
.sum__n {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(62px, 6.6vw, 84px);
  line-height: .8;
  letter-spacing: -.03em;
  color: var(--gold);
}
.sum__unit { font-size: .31em; font-weight: 600; margin-left: 3px; color: var(--ink); }
.sum__side { display: flex; flex-direction: column; gap: 5px; padding-top: 4px; }

.badge { display: inline-block; align-self: flex-start; line-height: 0; }
.badge img { width: 180px; height: auto; }
.badge--close img { width: clamp(180px, 17vw, 216px); }

@media (min-width: 880px) {
  .sum { gap: 20px; padding-block: 18px; }
  .sum__side { gap: 6px; padding-top: 6px; }
}

/* ---------- the arithmetic block on the homepage ---------- */

/* This block used to carry a second device beside the table. It does not any
   more: the display above is now a device at full size turning over five
   screens, and a smaller copy of the same object 800 pixels below it made the
   page look like it had one asset and was rationing it. What is left is the
   proof itself, which never needed a picture. */
@media (min-width: 880px) {
  .rail--arith { align-items: start; }
  .rail--arith .rail__head { grid-column: span 5; }
  .rail--arith .rail__body { grid-column: span 7; }
}

/* ---------- the device, built as a solid ---------- */

/* An iPhone 15 Pro in black titanium, turned a little away from you, and it is a real
   solid rather than a picture of one: 44 copies of the same silhouette stacked backwards
   along Z, so the flank you see is the object's own edge and cannot disagree with the
   corner radius. Measurements come from the actual device (70.6 x 146.6 x 8.25 mm, screen
   65.1 x 141.1 mm), and every one of them is a fraction of the width, so it stays honest
   at any size. Ported from `Weighted/tools/store-screenshots/design/device.css`, where the
   same stack renders the App Store screenshots.

   ONE THING FROM THAT FILE IS STILL LEFT OUT: the glare. Gloss on glass is decoration and
   the screen here is the content. What survives is material: the titanium band and the
   chamfer running down the flank are gradients because a metal edge does not have one flat
   colour, and they live inside the object.

   The shadow and the tilt used to be left out too, on the grounds that a device floating on
   a soft blur is the category signature. That held while the device stood on a flat page.
   It stopped holding when the section got a light source (see `.beam` below): a lit object
   that casts nothing is not restrained, it is wrong, and the eye reads it as a sticker. So
   there is a shadow now, and it belongs to the lamp rather than to the device.

   ORIENTATION. rotateY stays NEGATIVE the whole way through the turn. A negative angle
   pushes the LEFT side back and brings the right flank forward, which on an iPhone is the
   side wearing exactly one key, the side button. That is why every slice carries one button
   and not three: the action button and the two volume keys are on the left flank, which is
   now the side you cannot see. Cross zero and you would show the left flank with the right
   flank's single key on it, i.e. an iPhone that does not exist. */

.dev-scene {
  /* A real length, never a percentage. Every other measurement is `calc(var(--w) * k)`,
     including the height, and a percentage inside a height calc resolves against a height
     that is itself auto — the whole device collapsed to nothing and the column rendered
     empty. */
  --w: min(280px, 62vw);
  --aspect: 2.0765;
  --pad: calc(var(--w) * 0.0389);
  --thick: calc(var(--w) * 0.1169);
  --radius: calc(var(--w) * 0.1680);
  --screen-radius: calc(var(--w) * 0.1291);
  perspective: calc(var(--w) * 4.25);
  perspective-origin: 58% 38%;
  display: flex;
  justify-content: center;
  /* The flank sticks out to the right of the front face while it is turned, and on a phone
     the figure column is already the full width. Room for it, measured from the thickness
     rather than guessed, so nothing can push the page sideways. */
  padding-right: var(--thick);
}

.dev {
  position: relative;
  width: var(--w);
  height: calc(var(--w) * var(--aspect));
  transform-style: preserve-3d;
  /* The resting angle. Anything that cannot scroll-drive it (an older browser, someone who
     asked for less motion) sees the device at 18 degrees, which is a finished picture and
     not a half-played animation.
     rotateX comes first so the tilt happens in the page's own plane and the turn happens
     after it, around the device's own axis. The other order swings the whole thing off
     centre, which is a pose rather than a lean. Four degrees is the whole tilt: enough that
     the top edge catches the lamp, not enough to read as a stock render. */
  transform: rotateX(4deg) rotateY(-18deg);
}

.dev__body { position: absolute; inset: 0; transform-style: preserve-3d; }
.dev__slice { position: absolute; inset: 0; border-radius: var(--radius); }
/* The key stands 0.5 mm proud of the flank (0.7% of the width) and only exists on the
   middle half of the thickness, exactly as on the device. One per slice: the right flank
   carries the side button and nothing else. */
.dev__btn {
  position: absolute;
  right: calc(var(--w) * -0.007);
  width: calc(var(--w) * 0.010);
  border-radius: calc(var(--w) * 0.004);
}

.dev__front {
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  /* Mirrored with the device (142deg was the same ramp for the left flank). The bright
     stop has to land on the edge nearest the lamp, or the band lights the side that is
     turned away and the metal stops reading as metal. */
  background: linear-gradient(218deg, #9b9d9c 0%, #63666a 7%, #303234 22%, #25272a 46%, #3c3f42 61%, #71747a 77%, #2c2e31 92%, #17181a 100%);
}
/* The black bezel between band and glass. Concentric, or the corner is wrong. */
.dev__bezel {
  position: absolute;
  inset: calc(var(--w) * 0.0127);
  border-radius: calc(var(--radius) - var(--w) * 0.0127);
  background: #050506;
}
.dev__screen {
  position: absolute;
  inset: var(--pad);
  border-radius: var(--screen-radius);
  overflow: hidden;
  background: #08090b;
}
.dev__screen img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Dynamic Island: 125 x 36.7pt, 11pt below the screen edge, converted to device width. */
.dev__island {
  position: absolute;
  top: calc(var(--w) * 0.0647);
  left: 50%;
  transform: translateX(-50%);
  width: calc(var(--w) * 0.2934);
  height: calc(var(--w) * 0.0861);
  border-radius: 999px;
  background: #000;
}

/* The turn. Driven by the scroll position of the section itself, so there is no script on
   this page and nothing to run on a thread: `view()` is the element's own progress through
   the viewport, and the browser owns the interpolation. It ends at 7 degrees rather than 0
   because a device that lands dead flat reads as a screenshot again, and the point of
   building a solid was that it is one. */
.arith__table { display: flex; flex-direction: column; border-top: 2px solid var(--ink); }
.arith__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding-block: 14px;
  border-bottom: 1px solid var(--rule);
}
.arith__row--total {
  background: var(--gold);
  border-bottom: 0;
  margin-inline: calc(var(--bleed) * -1);
  padding: 16px var(--bleed);
}
.arith__k {
  font-family: var(--cond);
  font-weight: 600;
  font-size: clamp(13px, 1.2vw, 15px);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.arith__row--total .arith__k { color: inherit; font-weight: 700; }
.arith__v { font-family: var(--display); font-weight: 700; font-size: clamp(26px, 2.6vw, 32px); line-height: 1; }
.arith__row--total .arith__v { font-weight: 800; font-size: clamp(30px, 3vw, 36px); }

/* ---------- the footer, as a site map ---------- */

/* Every page links to every other page from the bottom, in columns. Two reasons,
   and the second is the one that pays: a reader who got to the end without
   tapping the badge is looking for somewhere else to go, and a crawler reading
   this page gets the whole structure from any entry point, which is what puts
   the sub-page list under a search result instead of a bare title. */
.foot {
  background: var(--night);
  color: var(--ink-night);
  padding-block: 34px 20px;
}
.foot__inner { display: flex; flex-direction: column; gap: 32px; }
.foot__brand { display: flex; flex-direction: column; align-items: flex-start; gap: 14px; }
.foot__line { color: var(--ink-night-2); max-width: 34ch; font-size: 16px; line-height: 1.45; }
.foot__cols {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 26px 24px;
}
.foot__h {
  font-family: var(--cond);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--gold);
  line-height: 1;
}
.foot__list {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.foot__list a {
  position: relative;
  display: inline-block;
  color: rgba(238, 241, 237, .74);
  font-size: 16px;
  line-height: 1.2;
}
/* Same trick as the masthead: grow the tap target without moving a pixel. */
.foot__list a::after { content: ''; position: absolute; inset: -13px -8px; }
.foot__list a:hover { color: var(--ink-night); }
.foot__list a[aria-current='page'] { color: var(--gold); }

@media (min-width: 880px) {
  .foot { padding-block: 56px 24px; }
  .foot__inner { flex-direction: row; justify-content: space-between; gap: 56px; }
  .foot__brand { flex: 0 1 32ch; }
  .foot__cols { grid-template-columns: repeat(2, minmax(0, 190px)); gap: 56px; }
}

/* ---------- 404 ---------- */

/* The 404 is the one page with nothing under it: no bands, no closing block, no
   colophon. Left at its natural height it filled the top third of the window and
   stopped, which on an error page reads as a second thing having gone wrong. So it
   claims what the masthead leaves and centres itself in it.

   It used to claim it with two measured numbers, one per breakpoint: the masthead is
   117.14px on a phone and 86px above 880. That held right up until the masthead grew a
   fifth link and wrapped to three rows at 320, and then the page was 30px taller than
   the window — a scrollbar on a page with nothing to scroll, which is exactly what
   those numbers existed to prevent. A measurement that has to be re-taken whenever the
   masthead changes is a measurement that will be wrong. So this page is a flex column
   now and the main simply takes what the header leaves, whatever that turns out to be.

   Padding stays as the floor: on a window too short to centre in, the content wins. */
.page-oops {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}
.oops {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: 80px 100px;
  text-align: center;
}
.oops .h1 { text-wrap: balance; }
.oops__links { display: flex; flex-wrap: wrap; gap: 18px; justify-content: center; margin-top: 28px; }

/* ---------- motion ---------- */

/* The page eases in instead of snapping in. Deliberately pure CSS with no trigger
   of any kind: a keyframe with `both` runs the moment the stylesheet applies and
   always lands on the visible state. If the stylesheet fails, nothing is hidden in
   the first place. The Next.js version of this site did the same thing with a
   scroll observer and shipped a blank page when the observer never fired for
   above-the-fold content, which is the one failure mode worth designing out.

   Each navigation is a fresh document, so this doubles as the transition between
   pages: the old page leaves, the new one eases in. */

@keyframes enter-fade {
  from { opacity: 0; }
}
@keyframes enter-rise {
  from { opacity: 0; transform: translateY(12px); }
}

.mast {
  animation: enter-fade .5s ease-out both;
}

/* The first screen, staggered. `.hero` on the home page, `.phead` on the six
   subpages, `.oops` on the 404.

   The home page runs a longer cascade than the rest, because the hero is the
   only first screen with five separate things in it. It used to arrive in two
   beats: the headline, and then the entire right-hand column as one block, so
   the lede, the 135.5 and the badge all landed on the same frame and the
   stagger was invisible. Now each of them is its own beat.

   The whole cascade is over in under a second (last delay .42, last frame
   ~.92s) and each step is 80ms or less apart, which is short enough to read as
   the page assembling itself rather than as a sequence you have to sit out. */
.hero__eyebrow,
.phead__head,
.oops {
  animation: enter-rise .6s cubic-bezier(.22, .61, .36, 1) both .06s;
}
.hero__h1 {
  animation: enter-rise .55s cubic-bezier(.22, .61, .36, 1) both .14s;
}
.phead__lede {
  animation: enter-rise .6s cubic-bezier(.22, .61, .36, 1) both .18s;
}
.hero__lede {
  animation: enter-rise .55s cubic-bezier(.22, .61, .36, 1) both .22s;
}
/* Scoped to the hero on purpose: `.sum` and `.badge` also appear further down
   the page, inside bands that already fade in as one plane. */
.hero__act > .sum {
  animation: enter-rise .55s cubic-bezier(.22, .61, .36, 1) both .30s;
}
.hero__act > .badge {
  animation: enter-rise .5s cubic-bezier(.22, .61, .36, 1) both .36s;
}
.hero__act > .eyebrow {
  animation: enter-fade .5s ease-out both .42s;
}

/* Everything below the first screen arrives as one plane, not band by band.
   By the time you have scrolled to it the animation is long finished.
   The display is excluded: it is four viewports tall and holds a sticky child,
   and there is no reason to hand the compositor a layer that size for a fade
   nobody is in a position to see. */
main > .band,
main > section:not(.hero):not(.phead):not(.stage) {
  animation: enter-fade .7s ease-out both .26s;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
    /* Zonder deze regel blijft de gestapelde vertraging staan en kijkt iemand die
       juist geen beweging wil eerst een kwart seconde tegen een lege band aan. */
    animation-delay: 0s !important;
    transition-delay: 0s !important;
  }
}
