/* Read95 marketing site.
   Built from `design/Read95 Landing Editorial.dc.html`.

   The artboard is a fixed 1240px desktop canvas with inline styles, so two
   things happen on the way to a real page. Its literal hex values are traded
   for the brand tokens they already match exactly (#2A2B2D is --r95-graphite,
   #F7F3EA is --r95-paper, and so on down the file), which keeps the site on the
   same palette as the app instead of a copy of it that drifts. And it gains the
   breakpoints an artboard has no way to express: everything below 1024px is
   authored here, not designed there. */

@import url("tokens/colors.css");

/* The typefaces are served from this site (assets/fonts/fonts.css, linked from
   each page ahead of this file), never from a font service: a page asks no
   third party for anything. tools/vendor-fonts.mjs made the copies and is
   where the families and weights are chosen. */

:root {
  --ui: "Albert Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --reading: "Newsreader", Georgia, "Times New Roman", serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  /* Arabic. Declared HERE and not in tokens/typography.css, which this
     stylesheet does not import: only tokens/colors.css is pulled in, so a
     variable added to the typography file is documentation rather than code.
     Readex Pro chosen 2026-09-02 over Tajawal, IBM Plex Sans Arabic, Almarai
     and Cairo, for carrying Latin and Arabic in one voice.

     Tajawal is the fallback rather than a system face, because it is already
     the brand's Arabic: the HUB's cover renderer was moved to it in August, so
     if Readex Pro fails to load or lacks a glyph the page drops to a face this
     product already speaks in, not to whatever the device happens to have.

     It is not free, and the first version of this note wrongly said it was.
     Measured: the Arabic page fetches 53 KB of font across two files, and the
     second is Tajawal. `display=swap` renders the next face in the stack while
     the first is still arriving, so the fallback gets used for a moment on a
     cold load and is therefore downloaded. 22 KB to keep a failed or
     incomplete Readex Pro inside the brand is worth paying; it is a cost, not
     an absence of one. Verified both ways: normal loads render Readex Pro, and
     blocking it at the network drops the page to Tajawal rather than to a
     system face. */
  --arabic: "Readex Pro", "Tajawal", "Noto Sans Arabic", sans-serif;

  --shell: 1240px;
  --gutter: 48px;

  /* The claim strip's flat ground. Not a brand token: the design system's
     darkest colour is graphite (#2A2B2D), and this is a site-only value taken
     from the Claude Design comp's #1E1F20, one step deeper at the owner's
     request so the strip reads clearly as its own section against the dark
     bottom edge of the hero photograph. */
  --site-claim-ground: #18191A;
}

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

html { scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background: var(--r95-stone-100);
  color: var(--r95-graphite);
  font-family: var(--ui);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--r95-slate); text-decoration: none; }

/* Every interactive element gets a visible focus ring. The artboard has no
   focus states at all, which is normal for a canvas and not shippable. */
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--r95-slate);
  outline-offset: 3px;
  border-radius: 4px;
}
.on-dark a:focus-visible,
.on-dark button:focus-visible { outline-color: var(--r95-stone-100); }

img, svg { display: block; max-width: 100%; }

/* Visible only when tabbed to. The masthead sits ahead of the headline on every
   load, so a keyboard reader otherwise walks the whole nav to reach the page. */
.skip {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translate(-50%, -140%);
  z-index: 100;
  background: var(--r95-stone-100);
  color: var(--r95-graphite);
  font-size: 14px;
  font-weight: 500;
  padding: 12px 20px;
  border-radius: 0 0 12px 12px;
  transition: transform 140ms ease;
}
.skip:focus { transform: translate(-50%, 0); color: var(--r95-graphite); }

.shell {
  max-width: var(--shell);
  margin: 0 auto;
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}

/* The eyebrow: mono, tracked out, quiet. Used for every label on the page. */
.eyebrow {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  /* grey-600, not the artboard's grey-500: at 11px the lighter one is 3.44:1
     on stone, and these labels are the page's only wayfinding. */
  color: var(--r95-grey-600);
  margin: 0;
  font-weight: 400;
}
.eyebrow--paper { color: var(--r95-paper-ink-soft); }
/* Full stone-100, not 0.9: the hero eyebrow is 11px mono over a photograph, the
   one label on the page with no flat ground under it, and the tenth of opacity
   was worth about 0.4 of contrast on the lit shelves. */
.eyebrow--inverse { color: var(--r95-stone-100); }

/* Newsreader italic carries emphasis everywhere in this design: it is the one
   place the reading face appears inside a UI-face heading, and it is what makes
   the headline feel like a book rather than a product page. */
.em {
  font-family: var(--reading);
  font-style: italic;
  font-weight: 400;
}

/* ── Coming soon ───────────────────────────────────────────────────── */

/* Where the store badges stood until the listings are live: one button to the
   early-tester sign-up and a two-line note beside it. Kept to the badges' 48px
   so the hero copy above it stays where its contrast was measured. The badges
   (assets/stores/) return here at launch; see the README. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 24px;
  border: 0;
  border-radius: 999px;
  font-family: var(--ui);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.005em;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 140ms ease;
}
.btn--light { background: var(--r95-stone-100); color: var(--r95-graphite); }
.btn--light:hover { background: var(--r95-stone-000); color: var(--r95-graphite); }
.btn--dark { background: var(--r95-graphite); color: var(--r95-stone-100); }
.btn--dark:hover { background: #3A3B3D; color: var(--r95-stone-100); }
.btn:disabled { opacity: 0.6; cursor: progress; }

.soon { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 20px; }
.soon__note {
  margin: 0;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.45;
  color: var(--r95-stone-100);
}
.soon__note span { display: block; }

/* ── Photographs ───────────────────────────────────────────────────────
   The artboard carried <image-slot> elements holding an art-direction note and
   no picture. These are those six, generated to that direction and compressed
   to WebP: 6.8 MB of PNG became 204 KB, which matters on the one page whose
   whole job is loading fast for someone who has not decided to trust us yet. */

.shot {
  position: relative;
  overflow: hidden;
  background: var(--r95-stone-300);
}
.shot > img { width: 100%; height: 100%; object-fit: cover; }

/* The two that sit behind copy, filling their section rather than a card. */
.shot--full { height: 100%; }

/* ── iPhone frame ──────────────────────────────────────────────────────
   A plain-CSS transcription of IOSDevice from `ios-frame.jsx`: 402x874 at a
   48px corner, a 126x37 dynamic island and a 139x5 home indicator. The React
   component is not reused because it would pull a runtime onto a static page
   to draw one rounded rectangle. Geometry is kept exact so the mock keeps
   reading as a device rather than as a phone-shaped box. */

.device {
  --device-w: 402px;
  --device-h: 874px;
  position: relative;
  width: var(--device-w);
  height: var(--device-h);
  flex: none;
  border-radius: 48px;
  overflow: hidden;
  background: #f2f2f7;
  box-shadow: 0 40px 80px rgba(0, 0, 0, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.12);
}
.device__island {
  position: absolute;
  top: 11px;
  left: 50%;
  transform: translateX(-50%);
  width: 126px;
  height: 37px;
  border-radius: 24px;
  background: #000;
  z-index: 50;
}
.device__home {
  position: absolute;
  bottom: 8px;
  left: 50%;
  transform: translateX(-50%);
  width: 139px;
  height: 5px;
  border-radius: 100px;
  background: rgba(0, 0, 0, 0.25);
  z-index: 60;
}
.device__screen { height: 100%; overflow: hidden; }

/* A real app capture sits below the island rather than under it: the web build
   has no notch and lays its title out from y=0, so the frame reserves the strip
   the hardware would.
   The colour of that strip goes on the screen, not on the image. An image's own
   background is painted underneath it and is invisible the moment it loads, so
   putting it there left the reserved strip showing the frame's iOS-default
   #f2f2f7: cool grey against the app's warm stone, and a visible seam. */
.device--shot .device__screen { background: var(--r95-stone-100); }
.device__shot {
  display: block;
  width: 100%;
  height: auto;
  margin-top: 54px;
}

/* ── Hero ──────────────────────────────────────────────────────────── */

.hero {
  position: relative;
  z-index: 2;
  min-height: 860px;
  background: var(--r95-graphite);
  color: var(--r95-stone-100);
}
.hero__bg { position: absolute; inset: 0; }
.masthead {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding-top: 26px;
  padding-bottom: 26px;
}
.wordmark {
  display: flex;
  align-items: center;
  gap: 11px;
  color: var(--r95-stone-100);
  font-size: 17px;
  font-weight: 500;
  letter-spacing: -0.02em;
}
.wordmark img { width: 26px; height: 26px; }
.wordmark:hover { color: var(--r95-stone-100); }

.masthead__nav { display: flex; align-items: center; gap: 30px; }
/* Children only. `.masthead__nav a:not(.btn)` also reached the links inside the
   language switcher's dropdown and outranked `.langswitch__list a` (0,2,1
   against 0,1,1): it painted every language in near-white on the dropdown's
   near-white ground, and the phone rule below hid them outright. */
.masthead__nav > a { font-size: 14px; color: var(--r95-stone-100); }
.masthead__nav > a:hover { color: var(--r95-stone-100); }

.hero__body {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
}
.hero__grid {
  display: grid;
  grid-template-columns: 1fr 420px;
  gap: 48px;
  align-items: end;
  padding-bottom: 76px;
}
.hero__title {
  margin: 0;
  font-size: clamp(44px, 5.4vw, 74px);
  line-height: 1.02;
  letter-spacing: -0.03em;
  font-weight: 500;
  color: var(--r95-stone-100);
  max-width: 17ch;
  text-wrap: pretty;
}
.hero__standfirst {
  margin: 14px 0 0;
  font-family: var(--reading);
  font-size: 21px;
  line-height: 1.56;
  color: var(--r95-stone-100);
  max-width: 40ch;
  text-wrap: balance;
}
/* Names the passage in the phone. The hero shows a real page of a real book,
   and a reader who wonders what it is should not have to guess. */
.hero__caption {
  margin: 14px 0 0;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  line-height: 1.5;
  color: var(--r95-stone-100);
  max-width: 72ch;
}
.hero__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 18px 28px; margin-top: 36px; }

/* The phone breaks the section edge and overlaps the claim strip below it by
   262px, ending mid-strip as in the Claude Design comp. The strip keeps the
   phone's column empty, so nothing runs underneath it. */
.hero__device {
  display: flex;
  justify-content: flex-end;
  margin-bottom: -262px;
  filter: drop-shadow(0 40px 80px rgba(16, 17, 18, 0.55));
}

/* ── Claim strip ───────────────────────────────────────────────────── */

/* A flat section of its own, distinct from the photograph's dark bottom edge.
   On the old light strip the overhanging phone's lookup sheet read as a stray
   card floating in white space; on one continuous dark ground it read as a
   gradient. The third column lines up with the hero's 420px device rail
   (140 + 48 + 488 + 48 = 724, where the rail starts) and is left empty: it is
   the phone's. */
.claim {
  position: relative;
  z-index: 1;
  background: var(--site-claim-ground);
  color: var(--r95-stone-100);
}
.claim__grid {
  display: grid;
  grid-template-columns: 140px minmax(0, 1fr) 420px;
  gap: 48px;
  padding-top: 84px;
  padding-bottom: 92px;
}
.claim .eyebrow { padding-top: 12px; }
/* The learning-language chips open the strip, above THE IDEA, rather than
   crowding the hero photograph. On desktop the phone overhangs this strip on
   its end side, so the row keeps clear of the phone's 420px column. */
.claim__learn { padding-top: 56px; }
.claim__learn .learnpicker { margin-top: 0; }
.claim__learn + .claim__grid { padding-top: 36px; }
@media (min-width: 1081px) {
  .claim__learn .learnpicker { max-width: calc(100% - 468px); }
}
.claim__text {
  margin: 0;
  font-family: var(--reading);
  font-size: clamp(24px, 2.4vw, 32px);
  line-height: 1.4;
  color: rgba(246, 245, 242, 0.92);
  max-width: 30ch;
  text-wrap: pretty;
}

/* ── What an evening looks like ────────────────────────────────────── */

.evening { padding-top: 96px; padding-bottom: 96px; }
.evening h2 { margin: 0 0 44px; }
.evening__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 56px 40px; }

.feature { display: flex; flex-direction: column; }
/* 16:10 rather than 4:3. Four photographs at 4:3 made this the longest section
   on the page for the least text; the frames are soft interiors that crop
   without losing their subject. */
.feature .shot { aspect-ratio: 16 / 10; border-radius: 14px; }
.feature h3 {
  margin: 22px 0 0;
  font-size: 26px;
  letter-spacing: -0.02em;
  font-weight: 500;
  text-wrap: balance;
}
.feature p {
  margin: 10px 0 0;
  font-size: 16px;
  line-height: 1.62;
  color: var(--r95-grey-600);
  max-width: 44ch;
}

/* ── The mechanic ──────────────────────────────────────────────────── */

/* overflow: hidden on both phone sections. Each phone ends flush with its
   section, and its drop-shadow (30px down, 60px blur) spilled across the
   hairline into the next section as a grey smudge on the pricing rule. Clipped,
   the phone reads as standing on the line. */
.mechanic {
  background: var(--r95-paper);
  border-top: 1px solid var(--r95-paper-border);
  border-bottom: 1px solid var(--r95-paper-border);
  overflow: hidden;
}
.mechanic__grid {
  display: grid;
  grid-template-columns: 1fr 402px;
  gap: 80px;
  align-items: end;
  padding-top: 96px;
}
.mechanic__copy { padding-bottom: 96px; }
.mechanic h2 {
  margin: 20px 0 0;
  font-size: clamp(32px, 3.6vw, 44px);
  line-height: 1.1;
  letter-spacing: -0.025em;
  font-weight: 500;
  color: var(--r95-paper-ink);
  max-width: 20ch;
}
.mechanic p {
  margin: 22px 0 0;
  font-size: 17px;
  line-height: 1.65;
  color: var(--r95-paper-ink-soft);
  max-width: 46ch;
  text-wrap: pretty;
}


.mechanic__device { display: flex; justify-content: center; filter: drop-shadow(0 30px 60px rgba(42, 43, 45, 0.22)); }
.mechanic__device .device { --device-h: 760px; }

/* ── Why reading ───────────────────────────────────────────────────────
   The argument under the claim strip's assertion, without a device: it is the
   one section that is only reasoning.

   Marketing prose is set in the UI face from here down. Three sections of long
   serif paragraphs read as an essay, not a page, and the brand keeps Newsreader
   for reading text: the claim strip's sentence and the italic emphasis. */
.method { padding-top: 104px; padding-bottom: 104px; border-top: 1px solid var(--r95-stone-400); }
.method__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
  align-items: start;
}
.method h2 {
  margin: 20px 0 0;
  font-size: clamp(32px, 3.6vw, 44px);
  line-height: 1.1;
  letter-spacing: -0.025em;
  font-weight: 500;
  color: var(--r95-graphite);
  max-width: 19ch;
  text-wrap: balance;
}
.method__body { display: flex; flex-direction: column; gap: 18px; padding-top: 34px; }
.method__body p {
  margin: 0;
  font-size: 17px;
  line-height: 1.65;
  color: var(--r95-grey-600);
  max-width: 46ch;
  text-wrap: pretty;
}

/* The 95/5 bar is the logo stretched: a graphite mass and a mist sliver, soft
   outer corners, tight inner ones, and a narrow cut between (logo-portion.svg:
   18 and 6 on the mass, 8 and 4 on the sliver, a 5-unit cut, on an 84-unit
   height; scaled to this 64px bar as 14 and 5, 6 and 3, and 4px). It is the
   only 95/5 bar on the page. Logical properties, so it mirrors on rtl. */
.band { margin-top: 72px; }
.band__bar { display: flex; gap: 4px; height: 64px; }
.band__known {
  flex: 95;
  background: var(--r95-graphite);
  border-radius: 5px;
  border-start-start-radius: 14px;
  border-end-start-radius: 14px;
}
.band__new {
  flex: 5;
  background: var(--r95-mist);
  border-radius: 3px;
  border-start-end-radius: 6px;
  border-end-end-radius: 6px;
}
.band__legend {
  display: flex;
  justify-content: space-between;
  margin-top: 12px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  color: var(--r95-grey-600);
}
.footnotes {
  margin-top: 44px;
  padding-top: 18px;
  border-top: 1px solid var(--r95-stone-400);
  max-width: 78ch;
}
.footnotes p {
  margin: 0 0 6px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--r95-grey-600);
}
.footnotes i { font-family: var(--reading); font-style: italic; }

.band__zones { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 40px; margin-top: 40px; }
.band__zone p { margin: 10px 0 0; font-size: 15.5px; line-height: 1.6; color: var(--r95-grey-600); max-width: 34ch; }
.band__zone .band__key {
  margin: 0;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  color: var(--r95-graphite);
}

/* ── What becomes readable ─────────────────────────────────────────────
   Mirrored against the mechanic block above it, device to the left, so two
   copy-plus-phone sections in a row do not read as one template used twice. */
.reach {
  background: var(--r95-stone-100);
  border-top: 1px solid var(--r95-stone-400);
  overflow: hidden;
}
.reach__grid {
  display: grid;
  grid-template-columns: 402px 1fr;
  gap: 80px;
  /* start, like the mechanic block: with end, the copy column's height pushed
     the phone 300px down and left a void above it on the left. */
  align-items: start;
  padding-top: 96px;
}
.reach__copy { padding-bottom: 96px; }
.reach h2 {
  margin: 20px 0 0;
  font-size: clamp(32px, 3.6vw, 44px);
  line-height: 1.1;
  letter-spacing: -0.025em;
  font-weight: 500;
  color: var(--r95-graphite);
  max-width: 20ch;
  text-wrap: balance;
}
.reach__copy p {
  margin: 22px 0 0;
  font-size: 17px;
  line-height: 1.65;
  color: var(--r95-grey-600);
  max-width: 46ch;
  text-wrap: pretty;
}
.reach__device {
  display: flex;
  justify-content: center;
  filter: drop-shadow(0 30px 60px rgba(42, 43, 45, 0.22));
}
.reach__device .device { --device-h: 760px; }

/* Desktop only: both phones bleed past the bottom edge of their section, so the
   frame's lower curve and the app's tab bar are cut off by the section below
   rather than drawn. Each section already clips (overflow: hidden), which is
   what keeps the shadow off the next section's hairline. Below 1080px the
   phone is a centred product shot with room under it, and it stays whole. */
@media (min-width: 1081px) {
  .mechanic__device,
  .reach__device { margin-bottom: -120px; }
}


/* ── Arabic ────────────────────────────────────────────────────────────
   Two separate faults, and the second was the uglier one.

   The three brand faces carry no Arabic at all, so every Arabic character fell
   through to a browser default. Readex Pro replaces all three on an Arabic
   page: it has both scripts, so the page keeps one voice rather than pairing a
   borrowed Arabic fallback with a Latin wordmark.

   And LETTER-SPACING BREAKS ARABIC. The script is cursive; tracking pulls the
   joins apart and renders words as loose disconnected letters. Every eyebrow,
   legend and tally label on this site is tracked at 0.1-0.12em, which on the
   Arabic page was doing exactly that. Tracking is zeroed for Arabic, tight
   display tracking included: negative tracking is meaningless on a script that
   joins.

   Scoped by lang, not by page, because Arabic also appears inside other pages:
   the switcher and the footer both list العربية by its own name. */
html[lang="ar"] {
  /* Both naming schemes, because this stylesheet carries two: --ui/--reading/
     --mono are declared at the top of this file and are what the components
     actually use, while --font-ui/--font-reading/--font-mono come from
     tokens/typography.css. That file is NOT imported, so its variables were
     never in scope: overriding them changed nothing, and then resolved to an
     undefined value that took the whole declaration down to Times New Roman.
     Only the three below are real. */
  --ui: var(--arabic);
  --reading: var(--arabic);
  --mono: var(--arabic);
}
html[lang="ar"] body,
[lang="ar"] { font-family: var(--arabic); }

html[lang="ar"] .eyebrow,
html[lang="ar"] .band__key,
html[lang="ar"] .band__legend,
html[lang="ar"] .foot__label,
html[lang="ar"] .colophon,
html[lang="ar"] .hero__title,
html[lang="ar"] .claim__text,
html[lang="ar"] h1,
html[lang="ar"] h2,
html[lang="ar"] h3,
html[lang="ar"] h4,
[lang="ar"] { letter-spacing: 0; }

/* Arabic sets taller: the script carries more above and below the line, and the
   measure that suits Newsreader crowds it. */
html[lang="ar"] .hero__title { line-height: 1.3; }
html[lang="ar"] .hero__standfirst,
html[lang="ar"] .claim__text,
html[lang="ar"] .method__body p,
html[lang="ar"] .reach__copy p,
html[lang="ar"] .mechanic p,
html[lang="ar"] .band__zone p,
html[lang="ar"] .feature p { line-height: 1.85; }

/* ── Learning-language picker ──────────────────────────────────────
   The language the page is SELLING, as opposed to the one it is written in.
   Chips rather than a select: there are eight, they are short, and seeing the
   list is the point. */
.learnpicker {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-top: 26px;
}
.learnpicker button {
  padding: 6px 13px;
  border: 1px solid rgba(246, 245, 242, 0.28);
  border-radius: 999px;
  background: transparent;
  color: rgba(246, 245, 242, 0.82);
  font-family: var(--ui);
  font-size: 13px;
  cursor: pointer;
}
.learnpicker button:hover { border-color: rgba(246, 245, 242, 0.55); }
.learnpicker button[aria-pressed="true"] {
  background: var(--r95-stone-100);
  border-color: var(--r95-stone-100);
  color: var(--r95-graphite);
  font-weight: 500;
}

/* ── Language switcher ─────────────────────────────────────────────
   The page language, not the language being learned. Quiet and mono: it is
   chrome for the minority who need it, and the majority arrive on the right
   page already because the server negotiated it. */
.langswitch { position: relative; }
.langswitch__button {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 34px;
  padding: 0 12px;
  border: 1px solid rgba(246, 245, 242, 0.28);
  border-radius: 999px;
  background: transparent;
  color: rgba(246, 245, 242, 0.9);
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.1em;
  cursor: pointer;
}
.langswitch__button:hover { border-color: rgba(246, 245, 242, 0.55); }
.langswitch__list {
  position: absolute;
  top: 42px;
  inset-inline-end: 0;
  z-index: 60;
  min-width: 168px;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: var(--r95-stone-050);
  border: 1px solid var(--r95-stone-400);
  border-radius: 10px;
  box-shadow: 0 18px 40px rgba(20, 20, 22, 0.34);
}
.langswitch__list a {
  display: block;
  padding: 8px 10px;
  border-radius: 7px;
  font-family: var(--ui);
  font-size: 13.5px;
  color: var(--r95-grey-600);
  text-decoration: none;
}
.langswitch__list a:hover { background: var(--r95-stone-200); }
.langswitch__list a[aria-current="true"] { color: var(--r95-graphite); font-weight: 500; }

/* Arabic right-aligns its text. The whole page mirrors with dir="rtl",
   masthead included: that is the 2026-09-02 decision, and these rules only
   align the prose that grid placement alone does not. */
[dir="rtl"] .hero__title,
[dir="rtl"] .hero__standfirst,
[dir="rtl"] .hero__caption,
[dir="rtl"] .claim__text,
[dir="rtl"] .method__body p,
[dir="rtl"] .method h2,
[dir="rtl"] .reach__copy p,
[dir="rtl"] .reach h2,
[dir="rtl"] .mechanic p,
[dir="rtl"] .mechanic h2,
[dir="rtl"] .feature p,
[dir="rtl"] .feature h3 { text-align: right; }

/* The hero photograph is composed for the copy on the left and the phone on
   the right: chair in the middle gap, bare wall under the mock, nothing bright
   under the headline. Mirrored, the phone sat over the lamp and shelves and
   the headline over the empty wall. The frame has no lettering, so it flips.
   The closing photograph is two hands and a phone and reads either way. */
[dir="rtl"] .hero__bg img { transform: scaleX(-1); }

/* ── Pricing ───────────────────────────────────────────────────────── */

.pricing { padding-top: 96px; padding-bottom: 104px; border-top: 1px solid var(--r95-stone-400); }
.pricing__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 80px; align-items: start; }
.pricing h2 {
  margin: 20px 0 0;
  font-size: clamp(32px, 3.6vw, 44px);
  line-height: 1.1;
  letter-spacing: -0.025em;
  font-weight: 500;
  max-width: 16ch;
  text-wrap: balance;
}
.pricing__lede { margin: 22px 0 0; font-size: 16px; line-height: 1.62; color: var(--r95-grey-600); max-width: 44ch; }

/* Region, not billing period. The artboard offered monthly and annual; the
   product sells one monthly plan at three regional prices, so the control
   switches the thing that actually varies. */
.regions { display: flex; gap: 6px; margin-top: 28px; }
.regions button {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--r95-grey-600);
  background: transparent;
  border: 1px solid var(--r95-stone-400);
  border-radius: 999px;
  padding: 8px 14px;
  cursor: pointer;
  transition: border-color 140ms ease, color 140ms ease, background-color 140ms ease;
}
.regions button:hover { border-color: var(--r95-stone-600); color: var(--r95-graphite); }
.regions button[aria-pressed="true"] {
  background: var(--r95-graphite);
  border-color: var(--r95-graphite);
  color: var(--r95-stone-100);
}

.plan {
  background: var(--r95-stone-050);
  border: 1.5px solid var(--r95-graphite);
  border-radius: 18px;
  padding: 32px 34px;
  box-shadow: 0 4px 14px rgba(42, 43, 45, 0.07);
}
.plan__head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; }
.plan__price { display: flex; align-items: baseline; gap: 9px; }
.plan__amount { font-size: 34px; letter-spacing: -0.02em; font-weight: 500; }
.plan__period { font-size: 15px; color: var(--r95-grey-600); }
.plan__mark { font-family: var(--mono); font-size: 11px; letter-spacing: 0.12em; color: var(--r95-graphite); }
.plan__list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-size: 15px;
  color: var(--r95-grey-600);
  border-top: 1px solid var(--r95-stone-400);
  padding: 20px 0 0;
  margin: 22px 0 0;
  list-style: none;
}


/* ── Closing ───────────────────────────────────────────────────────── */

.closing { position: relative; overflow: hidden; background: var(--r95-graphite); }
.closing__scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(24, 25, 26, 0.72) 0%, rgba(24, 25, 26, 0.86) 100%);
}
.closing__inner { position: relative; z-index: 2; padding-top: 112px; padding-bottom: 44px; }
.closing__grid { display: grid; grid-template-columns: 1fr auto; gap: 60px; align-items: end; }
.closing h2 {
  margin: 24px 0 0;
  font-size: clamp(32px, 3.6vw, 44px);
  line-height: 1.1;
  letter-spacing: -0.03em;
  font-weight: 500;
  max-width: 18ch;
  color: var(--r95-stone-100);
}
/* The footer was a wordmark and nothing else. It now carries the eight
   languages, the page's own sections, and the page in its eight languages:
   every entry is real, and nothing points at a page that does not exist. */
.foot {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1.2fr);
  gap: 48px;
  margin-top: 96px;
  padding-top: 32px;
  border-top: 1px solid rgba(246, 245, 242, 0.16);
}
.foot__label {
  margin: 0;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  color: rgba(246, 245, 242, 0.7);
}
.foot__langs { margin: 14px 0 0; font-size: 14.5px; line-height: 1.7; color: rgba(246, 245, 242, 0.84); max-width: 36ch; }
.foot__links { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 9px; font-size: 14.5px; }
.foot__links--pair { display: grid; grid-template-columns: repeat(2, max-content); gap: 9px 32px; }
.foot__links a { color: rgba(246, 245, 242, 0.84); }
.foot__links a:hover,
.foot__links a[aria-current="page"] { color: var(--r95-stone-100); }
.colophon {
  margin: 56px 0 0;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: rgba(246, 245, 242, 0.7);
}

/* ── Consent ───────────────────────────────────────────────────────────
   A panel in the corner, not a wall over the page: the page is readable
   before anyone answers, and both answers are the same button. The END
   corner, over the hero's phone picture: in the start corner it covered the
   "Become an early tester" button at a 900px-tall laptop screen. */
.consent {
  position: fixed;
  z-index: 90;
  bottom: 16px;
  inset-inline-end: 16px;
  max-width: 460px;
  padding: 20px 22px;
  background: var(--r95-stone-050);
  color: var(--r95-graphite);
  border: 1px solid var(--r95-stone-400);
  border-radius: 14px;
  box-shadow: 0 18px 44px rgba(20, 20, 22, 0.28);
}
.consent[hidden] { display: none; }
/* On the sign-up page the end side is the form, and the end corner covered
   its submit button; the start side is the short introduction. */
body:has(.signup) .consent { inset-inline-end: auto; inset-inline-start: 16px; }
.consent__text { margin: 0; font-size: 14px; line-height: 1.55; color: var(--r95-grey-600); }
.consent__actions { display: flex; gap: 10px; margin-top: 16px; }
.consent__actions .btn { flex: 1; }
.btn--small { min-height: 40px; padding: 0 18px; font-size: 14px; }
[dir="rtl"] .consent__text { text-align: right; }

.foot__consent { margin: 40px 0 0; }
.foot__consent .linkbutton--inverse { color: rgba(246, 245, 242, 0.84); font-size: 14.5px; }
.foot__consent .linkbutton--inverse:hover { color: var(--r95-stone-100); }
.foot__consent .linkbutton--inverse[hidden] { display: none; }

/* ── Early access ──────────────────────────────────────────────────────
   The sign-up page (template-early-access.html). Not from the artboard: a
   graphite band carries the same masthead as the home page, so the switcher
   and the wordmark keep their dark-ground styles, then the form on stone and
   a short dark footer. */

.subhead { background: var(--r95-graphite); color: var(--r95-stone-100); }

.signup { padding-top: 88px; padding-bottom: 112px; }
.signup__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: 80px;
  align-items: start;
}
.signup h1 {
  margin: 20px 0 0;
  font-size: clamp(34px, 4vw, 52px);
  line-height: 1.06;
  letter-spacing: -0.03em;
  font-weight: 500;
  max-width: 14ch;
  text-wrap: balance;
}
.signup__intro p:not(.eyebrow) {
  margin: 22px 0 0;
  font-size: 17px;
  line-height: 1.62;
  color: var(--r95-grey-600);
  max-width: 44ch;
}
.signup__intro .signup__small { font-size: 14px; margin-top: 28px; }

.signup__panel {
  background: var(--r95-stone-050);
  border: 1px solid var(--r95-stone-400);
  border-radius: 18px;
  padding: 36px 36px 32px;
  box-shadow: 0 4px 14px rgba(42, 43, 45, 0.06);
}
.signup__form { display: flex; flex-direction: column; gap: 26px; }
/* `hidden` loses to a display rule, so the form stayed on screen above its
   own thank-you until this said otherwise. */
.signup__form[hidden], .signup__done[hidden] { display: none; }

.field { display: flex; flex-direction: column; gap: 9px; margin: 0; padding: 0; border: 0; min-width: 0; }
.field__label {
  padding: 0;
  font-size: 14.5px;
  font-weight: 500;
  color: var(--r95-graphite);
}
.field__label--small { font-size: 12.5px; font-weight: 400; color: var(--r95-grey-600); }
/* A legend sits outside its fieldset's flex flow, so the gap above does not
   reach it: without a margin it touched the controls it names. */
.field > legend { margin-bottom: 9px; }
fieldset.field > legend + .learnrows { margin-top: 5px; }
.field__hint { margin: 2px 0 0; font-size: 13.5px; line-height: 1.5; color: var(--r95-grey-600); }

.field input[type="email"],
.field select {
  width: 100%;
  min-height: 46px;
  padding: 0 14px;
  font: inherit;
  font-size: 16px; /* 16px or iOS Safari zooms the page on focus */
  color: var(--r95-graphite);
  background: var(--r95-stone-000);
  border: 1px solid var(--r95-stone-600);
  border-radius: 10px;
}
.field select {
  appearance: none;
  padding-inline-end: 38px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%235B5A55' stroke-width='1.6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
}
[dir="rtl"] .field select { background-position: left 14px center; }
.field input:focus-visible,
.field select:focus-visible {
  outline: 2px solid var(--r95-slate);
  outline-offset: 1px;
  border-color: var(--r95-slate);
}

/* Two pills rather than bare radio dots: the choice is a phone, and a large
   target is kinder on the device people will be holding. */
.choice { display: flex; gap: 10px; }
.choice label { flex: 1; position: relative; cursor: pointer; }
.choice input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.choice span {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 46px;
  border: 1px solid var(--r95-stone-600);
  border-radius: 10px;
  background: var(--r95-stone-000);
  font-size: 15px;
  color: var(--r95-graphite);
}
.choice input:checked + span { background: var(--r95-graphite); border-color: var(--r95-graphite); color: var(--r95-stone-100); }
.choice input:focus-visible + span { outline: 2px solid var(--r95-slate); outline-offset: 2px; }

.learnrows { display: flex; flex-direction: column; gap: 14px; }
.learnrow { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px; align-items: end; }
/* The Remove column exists only when there is something to remove; an empty
   auto column still took its gap and left the row short of the field above. */
.learnrow:has(> .learnrow__remove:not([hidden])) { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; }
.learnrow__remove,
.linkbutton {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 14px;
  color: var(--r95-slate);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.learnrow__remove { min-height: 46px; padding: 0 2px; }
.linkbutton { align-self: flex-start; margin-top: 4px; }
.learnrow__remove[hidden], .linkbutton[hidden] { display: none; }

/* The honeypot: present for a form filler, absent for everyone else. Not
   display:none, which some fillers know to skip. */
.hp { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

.signup__form .btn { align-self: flex-start; margin-top: 4px; }
/* No red: the palette has no error colour, and the product states problems
   plainly rather than in alarm. A stone panel with a graphite rule. */
.signup__error {
  margin: 0;
  padding: 12px 14px;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--r95-graphite);
  background: var(--r95-stone-200);
  border-inline-start: 3px solid var(--r95-graphite);
  border-radius: 6px;
}
.signup__error[hidden] { display: none; }

.signup__done h2 {
  margin: 18px 0 0;
  font-size: clamp(28px, 3vw, 36px);
  line-height: 1.12;
  letter-spacing: -0.025em;
  font-weight: 500;
}
.signup__done p:not(.eyebrow) { margin: 14px 0 0; font-size: 16px; line-height: 1.6; color: var(--r95-grey-600); }
.signup__done a { display: inline-block; margin-top: 24px; font-size: 15px; text-decoration: underline; text-underline-offset: 3px; }
.signup__done:focus { outline: none; }

.sitefoot { background: var(--r95-graphite); padding-bottom: 44px; }
.foot--short { grid-template-columns: 1fr; margin-top: 0; border-top: 0; padding-top: 48px; }

[dir="rtl"] .signup h1,
[dir="rtl"] .signup__intro p,
[dir="rtl"] .signup__done { text-align: right; }

/* ── Below the artboard ────────────────────────────────────────────────
   Everything from here down is authored, not designed. The canvas stops at one
   desktop width, and these are the decisions that width never had to make.

   The through-line: the reading measure is the layout. Prose keeps its ch-based
   caps at every size, columns collapse rather than compress, and the phone mock
   scales as one piece so its 402px internals never need a second set of rules. */

/* Tablet and small laptop: the two-column pairings still hold, the hero's
   fixed 420px device rail does not. */
@media (max-width: 1080px) {
  :root { --gutter: 36px; }

  .hero { min-height: 0; }
  /* Relative, not static: z-index only applies to a positioned element, so
     going static here would drop the hero copy behind the background and the
     scrim that are absolutely positioned over it. It renders, in the right
     place, in the right colour, and is completely invisible. */
  .hero__body { position: relative; }
  /* In one column the phone mock sits directly under the buttons and names
     itself, and the caption was the lowest-contrast text on the page: 3.9:1
     at 1024px, over the window. */
  .hero__caption { display: none; }
  .hero__grid {
    grid-template-columns: 1fr;
    gap: 56px;
    padding-top: 40px;
    padding-bottom: 96px;
  }
  /* Freed from the corner, the phone is a product shot rather than a bleed,
     so it sits centred and stops overlapping the strip below. */
  .hero__device { justify-content: center; margin-bottom: 0; }
  /* The phone no longer overhangs here, so its reserved column goes too. */
  /* The hero's bottom padding already separates the phone from this sentence;
     on one dark ground the two paddings added up to an empty 180px band. */
  .claim__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 2.4fr); padding-top: 40px; }

  .mechanic__grid { grid-template-columns: 1fr; gap: 0; }
  .mechanic__copy { padding-bottom: 56px; }
  .mechanic__device { justify-content: flex-start; padding-bottom: 96px; }

  .method__grid { grid-template-columns: 1fr; gap: 28px; }
  .method h2 { max-width: 24ch; }
  .method__body { padding-top: 0; }

  .reach__grid { grid-template-columns: 1fr; gap: 0; }
  .reach__copy { padding-bottom: 56px; order: 1; }
  .reach__device { justify-content: flex-start; padding-bottom: 96px; order: 2; }

  .pricing__grid { gap: 48px; }

  .signup__grid { grid-template-columns: 1fr; gap: 48px; }
  .signup h1 { max-width: 20ch; }
}

/* One column. The claim strip's label sits above its sentence instead of
   beside it, and the pricing card follows its own heading. */
@media (max-width: 860px) {
  .claim__grid { grid-template-columns: 1fr; gap: 20px; }
  /* The feature pairs stay two-up well past here. A card going full width on a
     tablet is not a bigger card, it is four photographs the size of the screen
     and a page four screens longer. They collapse at phone width. */
  .evening__grid { gap: 40px 28px; }
  .band__zones { grid-template-columns: 1fr; gap: 26px; }
  .pricing__grid { grid-template-columns: 1fr; }
  .closing__grid { grid-template-columns: 1fr; gap: 36px; align-items: start; }
  .foot { grid-template-columns: 1fr 1fr; gap: 36px 32px; }
  .foot > :first-child { grid-column: 1 / -1; }
}

/* Phone. The mock is scaled whole rather than re-lain out: it is a picture of
   an app, and a picture that reflows is no longer a picture of anything.
   transform keeps the 402px internals untouched, and the wrapper's height is
   corrected to match so the scaled frame does not leave a gap under itself. */
@media (max-width: 620px) {
  :root { --gutter: 22px; }

  .masthead { padding-top: 20px; padding-bottom: 20px; flex-wrap: wrap; gap: 14px; }
  /* Section links are orientation for a page you can see at a glance; on a
     phone the only thing in the masthead that earns its space is the switcher. */
  .masthead__nav > a { display: none; }

  .hero__grid { padding-bottom: 64px; gap: 44px; }
  .hero__standfirst { font-size: 19px; }
  /* A 16:9 frame cropped to a phone is a tall slice of its middle, which is
     the window, and the standfirst sat on the panes below 4.5:1. The left edge
     is shelves and the lamp. Measured the README's way, on each line's own
     band: eyebrow 13.1:1, headline 12.0:1, standfirst 10.3:1 (Arabic 13.0,
     12.6, 8.8). Under rtl the image is mirrored after this crop, so Arabic
     gets the same shelves. */
  .hero__bg img { object-position: 0% 50%; }
  /* Eight chips stacked to three rows in German and Finnish and grew the hero
     with every language. One row that scrolls keeps its height. */
  /* The row lives in a grid cell, and a grid item's min-width is its content
     unless told otherwise: without this the cell grew to the unwrapped row's
     full width and the page scrolled sideways by 260px. */
  .hero__grid > * { min-width: 0; }
  .learnpicker {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    margin-left: calc(-1 * var(--gutter));
    margin-right: calc(-1 * var(--gutter));
    padding-left: var(--gutter);
    padding-right: var(--gutter);
  }
  .learnpicker::-webkit-scrollbar { display: none; }
  .learnpicker button { flex: none; }
  .hero__actions { gap: 20px; }

  /* A scaled element still reserves its unscaled box, so the frame has to be
     given the size it now appears to be. Both axes: without the width the page
     scrolls sideways by the 402px the frame no longer occupies, and without the
     height it leaves a gap under itself. Origin is top left so the numbers stay
     the plain multiplication they look like. */
  .hero__device,
  .mechanic__device,
  .reach__device {
    display: block;
    width: calc(402px * 0.78);
    margin-left: auto;
    margin-right: auto;
  }
  .device { transform: scale(0.78); transform-origin: top left; }
  /* In rtl a block wider than its box overflows to the LEFT, so the unscaled
     402px frame started at x = -78 and the scale kept it there: the Arabic
     page scrolled sideways on every phone. Scaling from the right corner puts
     the shrunk frame back inside the box the wrapper reserves. */
  [dir="rtl"] .device { transform-origin: top right; }
  .hero__device { height: calc(874px * 0.78); }
  .mechanic__device,
  .reach__device { height: calc(760px * 0.78); margin-bottom: 64px; }

  .claim__grid { padding-top: 32px; padding-bottom: 64px; }
  .evening { padding-top: 64px; padding-bottom: 64px; }
  .evening h2 { margin-bottom: 32px; }
  .evening__grid { grid-template-columns: 1fr; gap: 48px; }
  .band { margin-top: 48px; }
  .feature h3 { font-size: 24px; }

  .mechanic__grid { padding-top: 64px; }
  .method { padding-top: 64px; padding-bottom: 64px; }
  .reach__grid { padding-top: 64px; }

  .pricing { padding-top: 64px; padding-bottom: 72px; }
  .plan { padding: 26px 24px; }

  .closing__inner { padding-top: 76px; }

  .consent,
  body:has(.signup) .consent { inset-inline: 12px; bottom: 12px; max-width: none; padding: 16px 18px; }

  .signup { padding-top: 48px; padding-bottom: 72px; }
  .signup__panel { padding: 26px 20px 24px; margin-left: calc(-1 * 8px); margin-right: calc(-1 * 8px); }
  /* Language and level stack, each full width, with Remove under them. */
  .learnrow,
  .learnrow:has(> .learnrow__remove:not([hidden])) { grid-template-columns: 1fr; gap: 8px; }
  .learnrow + .learnrow { padding-top: 14px; border-top: 1px solid var(--r95-stone-400); }
  .learnrow__remove { min-height: 0; justify-self: start; }
  .signup__form .btn { align-self: stretch; }
  .foot { grid-template-columns: 1fr; margin-top: 64px; }
  .colophon { margin-top: 44px; }
}

/* Narrow phones: below this the frame is wider than the viewport even scaled. */
@media (max-width: 420px) {
  .device { transform: scale(0.64); }
  .hero__device,
  .mechanic__device,
  .reach__device { width: calc(402px * 0.64); }
  .hero__device { height: calc(874px * 0.64); }
  /* reach__device was missing from both lines, so below 420px its frame was
     scaled to 0.64 inside a box still sized for 0.78. */
  .mechanic__device,
  .reach__device { height: calc(760px * 0.64); }
}
