@charset "utf-8";

/* ============================================================================
   Carnet — workshop register, on the web.

   The palette is not chosen here. It is transcribed from the light half of
   ios/Carnet/DesignSystem/Colors.swift, whose fifteen light/dark pairs are
   gated in the app by tools/check_contrast.py at WCAG AA in both schemes. The
   site uses the light values only; see the note below the tokens.
   If a value changes there it changes here, or the site stops being the app.
   ========================================================================= */

/* --- the app's own faces, self-hosted ------------------------------------
   Not Google Fonts. This site's whole argument is that nothing leaves your
   device; a stylesheet request that hands a visitor's IP to a third party to
   fetch a typeface would be arguing against the product in the network tab. */

@font-face { font-family: 'Bitter'; src: url('fonts/Bitter-SemiBold.woff2') format('woff2');
             font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: 'Bitter'; src: url('fonts/Bitter-Bold.woff2') format('woff2');
             font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: 'Plex'; src: url('fonts/IBMPlexSans-Regular.woff2') format('woff2');
             font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'Plex'; src: url('fonts/IBMPlexSans-Medium.woff2') format('woff2');
             font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: 'Plex'; src: url('fonts/IBMPlexSans-SemiBold.woff2') format('woff2');
             font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: 'PlexMono'; src: url('fonts/IBMPlexMono-Regular.woff2') format('woff2');
             font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'PlexMono'; src: url('fonts/IBMPlexMono-Medium.woff2') format('woff2');
             font-weight: 500; font-style: normal; font-display: swap; }

/* --- the fifteen pairs ---------------------------------------------------- */

:root {
  --ground:   #F7F4EE;
  --panel:    #FFFDF9;
  --ruled:    #E8E1D3;
  --ink:      #1C1A17;
  --muted:    #696258;
  --rule:     #DFD8C9;
  --edge:     #C9C1B1;
  --patina:   #5A7566;
  --patina-2: #475E51;
  --oxide:    #9A4A34;
  --brass:    #8A6A2F;
  --photo-a:  #CFC6B4;
  --photo-b:  #B6AC9A;

  --margin-col: clamp(0px, 7vw, 108px);
  --gutter:     clamp(20px, 4vw, 44px);
  --sheet:      1320px;
  --ruling:     28px;

  --serif: 'Bitter', Georgia, 'Times New Roman', serif;
  --sans:  'Plex', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono:  'PlexMono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* Exponential ease-out. Motion arrives fast and settles; nothing bounces,
     because nothing in a ledger bounces. */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --slow: 820ms;
  --mid:  460ms;
  --fast: 200ms;

  color-scheme: light;
}

/* One rendition, deliberately.

   The app ships a dark scheme — "reading lamp", tobacco ground and aged-paper
   text — and this site used to mirror it. It was taken out on 15 Sep 2026, and
   the reason is the plates: all 28 artboards are the app's own design files
   with their colours welded into inline styles, so every one of them is light
   and cannot follow. A dark ground behind nineteen fixed-light screens is not a
   dark site, it is a light site with a dark border, and it demonstrated nothing
   about the app's dark mode because no dark artboard exists to show.

   Warm paper is the identity. A page read for four minutes at a desk is not the
   scene the app's dark palette was tuned for — a phone held in a garage at
   night is. If dark ever comes back here it starts in design/, not in CSS.
*/

/* --- ground rules --------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* `clip`, not `hidden`. Both stop a stray pixel of sideways scroll, but
     `hidden` makes this a scroll container and kills the sticky masthead;
     `clip` does not, and engines that do not know it simply ignore it. */
  overflow-x: clip;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1.0625rem;
  line-height: 1.62;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
  /* Never overflow-x: hidden here. It makes body a clipping context, which
     breaks position: sticky on the masthead in some engines and clips the
     fixed plate index. Nothing on this page overflows horizontally; the
     plate rows scroll inside their own overflow container instead. */
}

/* The browser surfaces nobody draws. They ship with defaults belonging to no
   design system, and leaving them is the cheapest tell that a page was
   assembled rather than built. */
::selection { background: var(--brass); color: var(--ground); }
:root { accent-color: var(--patina); caret-color: var(--patina); }
* { scrollbar-color: var(--edge) transparent; scrollbar-width: thin; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--edge); border: 3px solid var(--ground); }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }

:where(a, button, [tabindex], summary, input, label):focus-visible {
  outline: 2px solid var(--patina);
  outline-offset: 3px;
}

h1, h2, h3, h4 {
  font-family: var(--serif);
  font-weight: 600;
  letter-spacing: -0.021em;
  text-wrap: balance;
  margin: 0;
  overflow-wrap: break-word;
}
/* German builds words English never has to fit: "Dokumentation" at display size
   is 290px, on a 320px phone with a 280px column. Hyphenation is the German
   typographic answer, and it is scoped to German so English headlines never
   grow a hyphen they do not need. */
:lang(de) h1, :lang(de) h2, :lang(de) h3 { hyphens: auto; -webkit-hyphens: auto; }

p { margin: 0; max-width: 68ch; }

a { color: var(--patina); text-decoration-thickness: 1px; text-underline-offset: 0.22em; }
a:hover { color: var(--patina-2); }

.num { font-variant-numeric: tabular-nums lining-nums; }

/* Clipped rather than parked at left:-9999px. An off-canvas element still
   counts toward scrollable overflow in some engines, which buys a page a
   phantom horizontal scrollbar nobody can trace. */
.skip {
  position: absolute; left: 8px; top: 8px; z-index: 100;
  width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
  white-space: nowrap;
  background: var(--ink); color: var(--ground); padding: 12px 18px;
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.08em;
}
.skip:hover, .skip:focus { color: var(--ground); }
.skip:focus {
  width: auto; height: auto; overflow: visible; clip-path: none;
  display: flex; align-items: center; min-height: 44px;
}

/* --- the sheet ------------------------------------------------------------
   A ledger page: a margin column ruled off down the left, plate numbers in it,
   everything else in the body column. No card, no shadow, no radius — planes
   are separated by a hairline, the way a ruled book separates them. */

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

.leaf {
  display: grid;
  grid-template-columns: var(--margin-col) minmax(0, 1fr);
  border-top: 1px solid var(--rule);
}

.leaf > .margin {
  border-right: 1px solid var(--rule);
  padding: 38px 16px 38px 0;
  text-align: right;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  color: var(--muted);
}
.leaf > .margin .n { color: var(--brass); display: block; }
.leaf > .body { padding: 38px 0 clamp(56px, 7vw, 96px) clamp(0px, 3vw, 38px); min-width: 0; }

@media (max-width: 860px) {
  .leaf { grid-template-columns: 1fr; }
  /* The margin column would stack above the heading here, and a plate number
     sitting above a heading is a kicker — the one thing the floor bans
     outright. Every plate already carries its number on its own label, so the
     margin is dropped rather than relocated. */
  .leaf > .margin { display: none; }
  .leaf > .body { padding: 30px 0 56px 0; }
}

/* The ruling, 28px, behind the regions that hold plates — as in the app, where
   ruled ground sits behind list regions and solid panels sit on it like slips
   pasted in. A plate on a bed is that same gesture at page scale. */
.ruled, .bed {
  background-image: repeating-linear-gradient(
    to bottom, transparent 0, transparent calc(var(--ruling) - 1px),
    var(--ruled) calc(var(--ruling) - 1px), var(--ruled) var(--ruling));
}
.bed {
  padding: clamp(14px, 1.8vw, 24px);
  width: fit-content;
  max-width: 100%;
}
.bed.wide { width: auto; }

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

.display {
  font-size: clamp(2.35rem, 5.6vw, 4.3rem);
  line-height: 0.98;
  letter-spacing: -0.035em;
  font-weight: 600;
}
.h2 {
  font-size: clamp(1.85rem, 3.5vw, 2.8rem);
  line-height: 1.06;
  letter-spacing: -0.028em;
}
.h3 { font-size: clamp(1.15rem, 1.7vw, 1.38rem); line-height: 1.22; }

.lede {
  font-size: clamp(1.08rem, 1.5vw, 1.3rem);
  line-height: 1.5;
  max-width: 36ch;
}

.prose { color: var(--muted); max-width: 66ch; }
.prose + .prose { margin-top: 0.9em; }
.prose strong { color: var(--ink); font-weight: 500; }

.mono {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.085em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.caption {
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.55;
  letter-spacing: 0.012em;
  color: var(--muted);
  max-width: 46ch;
}
.caption b { color: var(--ink); font-weight: 500; }

/* --- the plate -----------------------------------------------------------
   A screen of the app, mounted. One hairline, a mono label beneath it. No
   bezel and no perspective render: the thesis refuses the floating phone, and
   a plate in a catalogue is shown flat, at its own size, numbered. */

.plate {
  --mat: clamp(11px, 1.3vw, 18px);
  display: grid;
  grid-template-columns: minmax(0, max-content);
  position: relative;
  /* ruled ground inside the frame, with the screen pasted onto it — the app's
     own composition, where panels sit on ruling like slips pasted into a book */
  background-color: var(--ground);
  background-image: repeating-linear-gradient(
    to bottom, transparent 0, transparent calc(var(--ruling) - 1px),
    var(--ruled) calc(var(--ruling) - 1px), var(--ruled) var(--ruling));
  border: 1px solid var(--ink);
  padding: var(--mat);
  width: fit-content;
  max-width: 100%;
}

.plate-screen {
  position: relative;
  overflow: hidden;
  background: #F7F4EE;
  /* The artboard is laid in at its own size and scaled as a block. --s is
     refined by script to fit the column exactly; these defaults are what a
     reader with no JavaScript gets, and they fit a 320px phone. */
  --s: 0.64;
  width: calc(402px * var(--s));
  height: calc((874px - var(--crop, 0) * 1px) * var(--s));
  max-width: 100%;
}
@media (min-width: 760px)  { .plate-screen { --s: 0.78; } }
@media (min-width: 1200px) { .plate-screen { --s: 0.9; } }
.plate-screen > * {
  transform: translateY(calc(var(--crop, 0) * -1px * var(--s, 1))) scale(var(--s, 1));
  transform-origin: top left;
}
.plate-a4 .plate-screen {
  --s: 0.2;
  width: calc(794px * var(--s));
  height: calc((1123px - var(--crop, 0) * 1px) * var(--s));
}
@media (min-width: 760px)  { .plate-a4 .plate-screen { --s: 0.24; } }
@media (min-width: 1200px) { .plate-a4 .plate-screen { --s: 0.3; } }

.plate-label {
  display: flex;
  gap: 10px;
  align-items: baseline;
  /* The caption must not size the plate. Zero width with a 100% floor removes
     it from the grid's max-content measurement, so the frame is exactly as
     wide as the screen it holds and the caption wraps inside that. */
  width: 0;
  min-width: 100%;
  /* full-bleed to the frame, so the mat surrounds the screen and stops at the
     label rather than boxing it in too */
  margin: var(--mat) calc(var(--mat) * -1) calc(var(--mat) * -1);
  border-top: 1px solid var(--rule);
  padding: 9px 12px 10px;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.07em;
  color: var(--muted);
  background: var(--panel);
}
.plate-label .n { color: var(--brass); flex: none; }

/* --- controls ------------------------------------------------------------- */

.action {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 0 22px;
  background: var(--patina);
  color: var(--ground);
  border: 1px solid var(--patina);
  font-family: var(--sans);
  font-size: 0.975rem;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.action:hover { background: var(--patina-2); border-color: var(--patina-2); color: var(--ground); }
.action:active { transform: translateY(1px); }
/* Not yet released, and the page says so rather than dressing a dead link as a
   button. The dashed edge is the app's own vocabulary — it is how Carnet draws
   the empty attachment tile: a place where something will go. */
.action[aria-disabled='true'] {
  background: transparent;
  color: var(--muted);
  border: 1px dashed var(--edge);
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  cursor: default;
  /* quieter than the live action beside it: this is a stamp saying "not yet",
     not a second button competing for the first look */
  min-height: 40px;
  padding: 0 16px;
  align-self: center;
}
.action[aria-disabled='true']:hover { background: transparent; color: var(--muted); border-color: var(--edge); }
.action[aria-disabled='true']:active { transform: none; }

.action-quiet {
  display: inline-flex; align-items: center; gap: 9px;
  min-height: 48px; padding: 0 20px;
  background: transparent; color: var(--ink);
  border: 1px solid var(--edge);
  font-size: 0.975rem; font-weight: 500; text-decoration: none; cursor: pointer;
  transition: border-color var(--fast) var(--ease);
}
.action-quiet:hover { border-color: var(--ink); color: var(--ink); }

/* a tab in the register: a mono word with a 2px edge under the live one */
.tab {
  appearance: none;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  padding: 12px 2px 8px;
  min-height: 44px;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  color: var(--muted);
  cursor: pointer;
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.tab:hover { color: var(--ink); }
.tab[aria-selected='true'] { color: var(--ink); border-bottom-color: var(--ink); }

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

.masthead {
  position: sticky; top: 0; z-index: 60;
  /* Opaque. The frosted blur was glass as decoration in a world made of paper,
     and at two rows on a phone it read as a smudge of the page underneath
     rather than as depth. The hairline that appears once stuck does the job. */
  background: var(--ground);
  border-bottom: 1px solid transparent;
  transition: border-color var(--mid) var(--ease);
}
.masthead[data-stuck='true'] { border-bottom-color: var(--rule); }
.masthead .sheet {
  display: flex; align-items: center; gap: 18px;
  min-height: 62px;
}
.wordmark {
  display: flex; align-items: center; gap: 11px;
  min-height: 44px;
  text-decoration: none; color: var(--ink); margin-right: auto;
}
.wordmark img { width: 28px; height: 28px; display: block; }
.wordmark b { font-family: var(--serif); font-weight: 600; font-size: 1.06rem; letter-spacing: -0.02em; white-space: nowrap; }
.wordmark span { font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.14em; color: var(--muted); white-space: nowrap; }
@media (max-width: 1120px) {
  /* "RESTORATION LOGBOOK" costs about 160px. Below this the row cannot carry it
     and the nav at the same time, and the nav is the part that does work. */
  .wordmark span { display: none; }
}

.masthead nav { display: flex; gap: 22px; }
.masthead nav a {
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.1em;
  color: var(--muted); text-decoration: none; padding: 8px 0;
  /* A two-word label that breaks reads as "THE / RECORD" stacked in a 62px bar.
     Squeezing the row must push items out of it, never fold them in half. */
  white-space: nowrap;
}
.masthead nav a:hover { color: var(--ink); }
/* Privacy and Support are destinations, not sections of this page. A hairline
   separates them from the anchors so the nav reads as two kinds of link, and
   the two pages Apple requires are reachable from the header rather than only
   from the foot of a 14,000px document. */
.masthead nav a.page:first-of-type {
  margin-left: 4px; padding-left: 22px;
  border-left: 1px solid var(--rule);
}
.masthead nav a.page { color: var(--muted); }
.masthead nav a.page:hover { color: var(--patina); }
@media (max-width: 880px) {
  /* Not display:none. The index is hidden at this width too, so dropping the
     nav leaves a very long document with nothing to jump with. */
  .masthead .sheet { flex-wrap: wrap; padding-bottom: 0; min-height: 56px; }
  .masthead nav {
    order: 3; width: 100%;
    gap: 20px; overflow-x: auto; overscroll-behavior-x: contain;
    border-top: 1px solid var(--rule);
    scrollbar-width: none;
  }
  .masthead nav::-webkit-scrollbar { display: none; }
  /* 44px, like every other target in this product. The reader is 35-70 and
     often in poor light, and a 39px link in a second row is exactly the sort of
     thing that passes on a desk and fails in a garage. */
  .masthead nav a {
    display: inline-flex; align-items: center;
    min-height: 44px; padding: 0;
    white-space: nowrap;
  }
  .masthead nav a.page:first-of-type { margin-left: 0; padding-left: 0; border-left: 0; }
}

@media (max-width: 700px) {
  /* Six labels do not fit a phone. The strip scrolled, which meant it was cut
     mid-word at the screen edge and the two links that matter most — the pages
     Apple checks and Settings opens — sat 170px off-screen where nobody would
     find them. The section anchors go; the price already has the button beside
     the wordmark, and the plate captions carry the rest of the wayfinding. */
  .masthead nav a:not(.page) { display: none; }
  .masthead nav { gap: 26px; overflow-x: visible; }
}

/* 44px, like every other target here. This is the primary control in the
   masthead and it was the smallest thing in it — 38px, six under the floor the
   rest of the product holds, on the one surface that is always on screen. */
.masthead .action, .masthead .action-quiet { min-height: 44px; padding: 0 16px; font-size: 0.875rem; }

@media (max-width: 380px) {
  /* At 320 the price block came out wider than the wordmark, so the header read
     as a price with a logo attached. The qualifier goes and the brand wins the
     row back; "once" is still the first thing the price line says on arrival. */
  .masthead .once { display: none; }
  .masthead .action, .masthead .action-quiet { padding: 0 13px; }
}

/* A jump target under a sticky bar is a jump to the wrong place: the heading
   you asked for ends up behind the masthead. The offset is the bar plus a
   little air, and it follows the bar's two heights. */
main section[id] { scroll-margin-top: 78px; }
@media (max-width: 880px) { main section[id] { scroll-margin-top: 123px; } }

/* --- hero ----------------------------------------------------------------- */

.hero {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: clamp(28px, 4.5vw, 72px);
  align-items: start;
  padding-block: clamp(32px, 4.6vw, 62px) clamp(46px, 6vw, 88px);
}
@media (max-width: 1080px) {
  .hero { grid-template-columns: 1fr; gap: 40px; }
  .hero > .hero-copy { order: -1; }
}

.hero-copy { display: flex; flex-direction: column; gap: 26px; padding-top: 4px; }

.hero-screens { display: flex; flex-direction: column; gap: 0; width: fit-content; max-width: 100%; }
.hero-tabs { display: flex; gap: 20px; border-bottom: 1px solid var(--rule); margin-bottom: 18px; }

.price-line {
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  padding: 15px 0;
}
.price-line .fig {
  font-family: var(--serif); font-weight: 600; font-size: 1.62rem;
  font-variant-numeric: tabular-nums; letter-spacing: -0.02em;
}
.price-line .note { font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.07em; color: var(--muted); }

.hero-actions { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }

/* The index of plates, down the far edge — numbers only, the way a printed
   catalogue indexes its plates in the margin. Names ride along for screen
   readers and surface on hover, so the index never needs a lane wide enough to
   collide with the text it sits beside. */
.plate-index {
  position: fixed; right: 22px; top: 50%; transform: translateY(-50%);
  z-index: 50;
  display: none;
  flex-direction: column; gap: 0;
  padding-left: 14px;
  border-left: 1px solid var(--rule);
  opacity: 0; pointer-events: none;
  transition: opacity var(--mid) var(--ease);
}
.plate-index { visibility: hidden; transition: opacity var(--mid) var(--ease), visibility 0s linear var(--mid); }
/* visibility, not just opacity: an index faded to nothing was still five stops in
   the keyboard's tab order at the top of the page, landing focus on links nobody
   could see. Hidden elements leave the tab order; transparent ones do not. */
.plate-index[data-shown='true'] {
  opacity: 1; pointer-events: auto; visibility: visible;
  transition: opacity var(--mid) var(--ease), visibility 0s;
}
.plate-index a {
  position: relative;
  display: block;
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  color: var(--edge); text-decoration: none; padding: 5px 0;
  transition: color var(--fast) var(--ease);
}
.plate-index a:hover, .plate-index a:focus-visible { color: var(--ink); }
.plate-index a[data-live='true'] { color: var(--brass); }
/* the name, kept out of the lane until it is wanted */
.plate-index a .t {
  position: absolute; right: calc(100% + 12px); top: 50%;
  transform: translateY(-50%);
  white-space: nowrap;
  background: var(--panel);
  border: 1px solid var(--rule);
  padding: 3px 9px;
  color: var(--ink);
  opacity: 0; pointer-events: none;
  transition: opacity var(--fast) var(--ease);
}
.plate-index a:hover .t, .plate-index a:focus-visible .t { opacity: 1; }
@media (min-width: 1360px) {
  /* The index gets a lane by narrowing the sheet, not by waiting for a screen
     wide enough to spare one. Numbers only, so the lane is ~60px. */
  :root { --sheet: 1240px; }
  .plate-index { display: flex; }
}
@media (prefers-reduced-motion: reduce) { .plate-index { transition: none; } }

/* --- two-up and row layouts ----------------------------------------------- */

.pair {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: clamp(26px, 4vw, 60px);
  align-items: start;
}
.pair.flip { grid-template-columns: minmax(0, 1fr) minmax(0, auto); }
.pair.flip > .pair-copy { order: -1; }
@media (max-width: 920px) {
  .pair, .pair.flip { grid-template-columns: 1fr; gap: 28px; }
  /* Heading first, always. In source order half the pairs put a full-height
     screen before the sentence that says what it is, so on a phone you
     scrolled through a picture of the app with no idea why it was there. */
  .pair > .pair-copy, .pair.flip > .pair-copy { order: -1; }
}

@media (min-width: 921px) and (min-height: 820px) {
  /* The copy travels with its plate. A plate runs to about 800px and its copy to
     about 300, and left static that was half a screen of bare paper beside the
     lower half of every screen. */
  .pair:not(.wide) > .pair-copy { position: sticky; top: 96px; }
  /* Only on a window tall enough to hold the whole column: the longest copy
     runs to about 750px, and pinned on a short laptop its last lines would
     wait below the fold until the plate beside it had scrolled away. */
}
.pair-copy { display: flex; flex-direction: column; gap: 18px; padding-top: 2px; }

/* Two phones side by side want about 700px before their copy can sit next to
   them, and the sheet never has that: at 1024 the grid crushed the copy column
   to 97px and cut "Nineteen / languages" off the right edge, and even at 1440 it
   left a 238px column. So this pair always stacks — heading first, the two
   proofs together underneath it at full size. */
.pair.wide { grid-template-columns: 1fr; gap: 28px; }
.pair.wide > .pair-copy { order: -1; }

.plate-row {
  display: flex; gap: clamp(16px, 2.4vw, 32px); align-items: flex-start;
  overflow-x: auto; overscroll-behavior-x: contain;
  padding-bottom: 12px; scroll-snap-type: x proximity;
}
.plate-row > * { scroll-snap-align: start; flex: none; }

.cols {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 0 clamp(24px, 3vw, 46px);
}
.cols > div { border-top: 1px solid var(--rule); padding: 18px 0 24px; }
.cols h3 { margin-bottom: 8px; }

/* --- the register: a fact list, ruled, tabular ----------------------------- */

.register { border-top: 1px solid var(--ink); max-width: 640px; }
.register > div {
  display: grid;
  /* The key's floor is its longest word, not zero. With zero, a long value like
     "First one free, then unlimited" squeezed "Vehicles" into the value's edge,
     and in German "Abbildungsnummern" was clipped outright. */
  grid-template-columns: minmax(min-content, 1fr) auto;
  gap: 18px;
  align-items: baseline;
  padding: 13px 0;
  border-bottom: 1px solid var(--rule);
}
.register .k { color: var(--muted); font-size: 0.95rem; }
.register .v {
  margin: 0; font-family: var(--serif); font-weight: 600;
  font-variant-numeric: tabular-nums; text-align: right;
}
.register .v.small { font-family: var(--sans); font-weight: 500; font-size: 0.95rem; }
.register .v.quiet { color: var(--muted); }

/* --- the dossier, assembling ---------------------------------------------- */

.dossier-stage {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
  gap: clamp(26px, 4vw, 56px);
  align-items: start;
}
@media (max-width: 1040px) { .dossier-stage { grid-template-columns: 1fr; } }


.dossier-pages {
  display: flex; flex-wrap: wrap; gap: clamp(12px, 1.6vw, 22px);
}

.dossier-panel { position: sticky; top: 96px; display: flex; flex-direction: column; gap: 20px; }
@media (max-width: 920px) {
  /* Stacked, the five A4 pages add about 2,000px to a phone scroll and you
     never see two of them together. Swiped, they read the way the document
     does — one page, then the next — and the switch stays on screen while you
     move through them. */
  .dossier-pages {
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    padding-bottom: 12px;
    scrollbar-width: thin;
  }
  .dossier-pages > .plate { flex: none; scroll-snap-align: center; }
  .dossier-panel { position: static; }
}

.switch {
  display: flex; align-items: center; gap: 14px;
  border-top: 1px solid var(--ink); border-bottom: 1px solid var(--rule);
  padding: 14px 0; cursor: pointer; min-height: 56px;
  user-select: none;
}
.switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.switch .track {
  flex: none; width: 46px; height: 26px;
  border: 1px solid var(--edge); background: var(--ground);
  position: relative;
  transition: background var(--mid) var(--ease), border-color var(--mid) var(--ease);
}
.switch .track::after {
  content: ''; position: absolute; top: 3px; left: 3px;
  width: 18px; height: 18px; background: var(--muted);
  transition: transform var(--mid) var(--ease), background var(--mid) var(--ease);
}
.switch input:checked + .track { border-color: var(--oxide); background: var(--ground); }
.switch input:checked + .track::after { transform: translateX(20px); background: var(--oxide); }
.switch input:focus-visible + .track { outline: 2px solid var(--patina); outline-offset: 3px; }
.switch .label b { display: block; font-family: var(--sans); font-weight: 500; font-size: 0.975rem; }
.switch .label span {
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.06em; color: var(--muted);
}

/* The wipe. Every amount leaves the document and its space stays, so the
   reader watches the money go rather than the page reflow around it — which is
   also what the printed dossier does: the column is there, the figure is not. */
[data-cost] { transition: opacity var(--mid) var(--ease); }
.costs-out [data-cost] { opacity: 0; }
[data-cost-block] { transition: opacity var(--mid) var(--ease); }
.costs-out [data-cost-block] { opacity: 0.12; }

/* --- disclosure ----------------------------------------------------------- */

.qa { border-top: 1px solid var(--ink); max-width: 860px; }
.qa details { border-bottom: 1px solid var(--rule); }
.qa summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: baseline; gap: 16px;
  padding: 17px 0; min-height: 44px;
  font-family: var(--serif); font-weight: 600; font-size: 1.06rem; letter-spacing: -0.015em;
}
.qa summary::-webkit-details-marker { display: none; }
.qa summary::after {
  content: ''; flex: none; margin-left: auto; align-self: center;
  width: 10px; height: 10px;
  border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--mid) var(--ease);
}
.qa details[open] summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.qa .answer { padding: 0 0 20px; color: var(--muted); max-width: 70ch; }
.qa .answer p + p { margin-top: 0.8em; }

/* --- footer --------------------------------------------------------------- */

.foot { border-top: 1px solid var(--ink); }
.foot .sheet {
  display: flex; flex-wrap: wrap; gap: 30px 56px;
  padding-block: 42px 60px; align-items: flex-start;
}
.foot nav { display: flex; flex-direction: column; gap: 7px; }
.foot nav b {
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.14em;
  color: var(--muted); font-weight: 400; margin-bottom: 4px;
}
.foot nav a { color: var(--ink); text-decoration: none; font-size: 0.95rem; }
.foot nav a:hover { color: var(--patina); text-decoration: underline; }
.foot .colophon { margin-left: auto; max-width: 40ch; }

@media (max-width: 880px) {
  /* 25px apart is a desk measurement. On a phone every footer link gets the
     same 44px the rest of the product holds, and the colophon stops being
     pushed off the left edge by the margin that right-aligns it on desktop. */
  .foot nav { gap: 0; }
  .foot nav a { display: flex; align-items: center; min-height: 44px; }
  .foot .colophon { margin-left: 0; }
}

/* --- reading pages (privacy, support) ------------------------------------- */

/* A document is set to a measure, not to a screen. Narrowing the whole sheet
   pulls the masthead in with the text, so the page reads as one column rather
   than a header stranded across an empty page. */
body.doc { --sheet: 980px; }


.reading { max-width: 74ch; padding-block: clamp(28px, 4.5vw, 56px) 80px; }
.reading h2 {
  font-size: clamp(1.4rem, 2.4vw, 1.8rem); margin-top: 2.1em; margin-bottom: 0.5em;
  padding-top: 0.75em; border-top: 1px solid var(--rule);
}
.reading h3 { font-size: 1.06rem; margin-top: 1.7em; margin-bottom: 0.35em; line-height: 1.3; }
/* These headings carried body leading (1.62). On a desk they fit one line and it
   never showed; on a phone they wrap, and a two-line heading at paragraph
   leading reads as two loose lines rather than one heading. */
.reading h2 { line-height: 1.15; }
.reading p, .reading li { color: var(--muted); }
.reading p { margin-block: 0.85em; max-width: none; }
.reading strong { color: var(--ink); font-weight: 500; }
.reading ul { padding-left: 1.15em; margin-block: 0.85em; }
.reading li { margin-block: 0.4em; }
.reading li::marker { color: var(--edge); }
.reading .stamp {
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.08em;
  color: var(--muted); border-top: 1px solid var(--ink); padding-top: 12px;
  display: flex; flex-wrap: wrap; gap: 6px 20px;
}

/* --- motion ---------------------------------------------------------------
   Everything is fully visible with no JavaScript and with reduced motion on.
   The pre-state is added by script, so the page never hides its own content
   waiting for a frame that may not come. */

.js [data-settle] {
  opacity: 0;
  transform: translateY(15px);
  transition: opacity var(--slow) var(--ease), transform var(--slow) var(--ease);
}
.js [data-settle][data-shown='true'] { opacity: 1; transform: none; }

/* a hairline that draws itself in, the way a rule is ruled */
.js [data-rule] {
  transform: scaleX(0); transform-origin: left center;
  transition: transform var(--slow) var(--ease);
}
.js [data-rule][data-shown='true'] { transform: scaleX(1); }

@media (prefers-reduced-motion: reduce) {
  .js [data-settle], .js [data-rule] {
    opacity: 1 !important; transform: none !important; transition: none !important;
  }
  .action:active { transform: none; }
  .masthead { backdrop-filter: none; background: var(--ground); }
}
