/* Almari's site, in the app's brand (CLAUDE.md's design law, 2026-10-10 on):
   Almari Pink on a Milk ground, Ink words, Cherry the deep note. League Spartan
   Bold for titles (the app's title face), Open Sauce Sans for everything else,
   Caveat for the one handwritten line. Every font is SIL OFL and self-hosted,
   its licence beside it in fonts/. */

@font-face { font-family: "Open Sauce Sans"; src: url("/fonts/OpenSauceSans-Regular.ttf") format("truetype"); font-weight: 400; font-display: swap; }
@font-face { font-family: "Open Sauce Sans"; src: url("/fonts/OpenSauceSans-Medium.ttf") format("truetype"); font-weight: 500; font-display: swap; }
@font-face { font-family: "Open Sauce Sans"; src: url("/fonts/OpenSauceSans-Bold.ttf") format("truetype"); font-weight: 700; font-display: swap; }
@font-face { font-family: "League Spartan"; src: url("/fonts/LeagueSpartan-Bold.ttf") format("truetype"); font-weight: 700; font-display: swap; }
/* While Open Sauce Sans loads, the words are set in the reader's own Arial
   (Android's Roboto), scaled to Open Sauce's width, so the lede keeps its lines
   and nothing under it moves as the face swaps in (the phone's own face, San
   Francisco, is 15% narrower, and swapping it out dropped what stood under
   the lede a line). Each scale is measured on this page's own words: the
   Regular's so the lede breaks its lines where Open Sauce does in every column
   from 240 to 560px (Arial at 110% differs in one width of 643, Roboto at
   109.5% in seven), the Medium's on the top bar's links (Arial's Regular
   stands in), the Bold's on the store pill's "iPhone and Android" (Arial's;
   Roboto's on the two store names the pill once carried). A local face is
   never fetched. */
@font-face { font-family: "Open Sauce Fallback"; src: local("Arial"), local("ArialMT"); font-weight: 400; size-adjust: 110%; }
@font-face { font-family: "Open Sauce Fallback"; src: local("Arial"), local("ArialMT"); font-weight: 500; size-adjust: 110.5%; }
@font-face { font-family: "Open Sauce Fallback"; src: local("Arial Bold"), local("Arial-BoldMT"); font-weight: 700; size-adjust: 102%; }
@font-face { font-family: "Open Sauce Fallback Roboto"; src: local("Roboto"), local("Roboto Regular"), local("Roboto-Regular"); font-weight: 400; size-adjust: 109.5%; }
@font-face { font-family: "Open Sauce Fallback Roboto"; src: local("Roboto Medium"), local("Roboto-Medium"); font-weight: 500; size-adjust: 111%; }
@font-face { font-family: "Open Sauce Fallback Roboto"; src: local("Roboto Bold"), local("Roboto-Bold"); font-weight: 700; size-adjust: 113%; }
/* While League Spartan loads, or if it fails, the titles are set in the
   phone's or the computer's own Arial Bold (Android's Roboto Bold), scaled to
   League Spartan's width: the hero's headline is 13.38 em in League Spartan
   and 14.56 em in Arial Bold, so at 91.9% it is one line in either, and
   nothing under it moves as the face swaps in. A local face is never fetched. */
@font-face {
  font-family: "League Spartan Fallback";
  src: local("Arial Bold"), local("Arial-BoldMT"), local("Roboto Bold"), local("Roboto-Bold");
  font-weight: 700;
  size-adjust: 91.9%;
}
/* Caveat, cut to the characters of the one line it sets, its alternates and
   kerning kept (15 KB of its 257; README), so it is preloaded with the page,
   and main.js shows the line only once it is here. */
@font-face { font-family: "Caveat"; src: url("/fonts/Caveat-Caption.ttf") format("truetype"); font-weight: 400; font-display: swap; }

:root {
  /* The palette (mobile/src/styles/tokens.ts). Almari Pink is 2.96:1 on white,
     so it is never words: it draws shapes (the top bar's rule), and the same hue
     deepened, the accent, is every link and button. */
  --milk: #FFF9F5;       /* the ground */
  --white: #FFFFFF;      /* cards and fields on it */
  --ink: #191717;        /* words, 17.1:1 on Milk */
  --grey-dark: #605E5E;  /* secondary words, 6.17:1 on Milk */
  --pink: #C98292;       /* Almari Pink: shapes only */
  --accent: #A25E6F;     /* words and buttons: 4.61:1 on Milk, white on it 4.81:1 */
  --accent-pressed: #8E4C5D;
  --cherry: #8B1E2D;     /* the logo, the handwritten line, the step circles: white on it 9.05:1 */
  --wash: #F8EAE8;
  --tint: #EED7DD;
  --line: #E6E6E6;
  --silver: #C0C0C0;     /* the polaroids' edge; the store pill's small line, 9.81:1 on Ink */
  --field-edge: #8F8D8D; /* a field's edge, 3.3:1 on white (WCAG's 3:1 for a control) */
  --sans: "Open Sauce Sans", "Open Sauce Fallback", "Open Sauce Fallback Roboto", system-ui, -apple-system, "Segoe UI", sans-serif;
  --title: "League Spartan", "League Spartan Fallback", "Open Sauce Sans", system-ui, sans-serif;
  --hand: "Caveat", "Bradley Hand", cursive;
  --gutter: 20px;
  --max: 1080px;
  /* The hero runs wider than the sections under it, so its two columns have room. */
  --hero-max: 1240px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; background: var(--milk); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
body {
  margin: 0;
  background: var(--milk);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
::selection { background: var(--tint); color: var(--ink); }
img { max-width: 100%; height: auto; display: block; }
/* The top bar is fixed: a section reached by its link starts below it. */
section { scroll-margin-top: 72px; }
a { color: inherit; }

/* ── The logo ────────────────────────────────────────────── */
/* The app's script logo with the key in its A, drawn from its black template
   (mobile/assets/images/welcome/almari-script@3x.png, 1062 x 372, the ink cut
   tight) as a mask, so it takes whatever colour its place gives it. */
.logo {
  display: block;
  aspect-ratio: 1062 / 372;
  background-color: currentColor;
  -webkit-mask: url("/assets/almari-logo.png") center / contain no-repeat;
  mask: url("/assets/almari-logo.png") center / contain no-repeat;
}
@media (forced-colors: active) {
  .logo { background-color: CanvasText; forced-color-adjust: none; }
}

/* ── Top bar ─────────────────────────────────────────────── */
/* Clear at the top of the page, nothing but its links on the Milk, only while
   all of the hero (its logo, its words, every card's turned corners) stands
   below it; the Milk ground with Home's 2pt Almari Pink rule from the moment
   anything of it would pass under it, its Cherry logo coming in WITH the rule
   (Hamza, 2026-10-19: the rule came in and the logo only a scroll later, held
   back until the hero's own had gone under the bar; so both logos show for a
   moment now, the bar's small one over the hero's large one). main.js toggles
   .clear from the page's geometry. It turns solid with its logo at once, and
   fades back to clear with it only when nothing is under it to show through.
   Its Milk, its blur and its rule are one layer under its links (::before), so
   they come and go as one opacity, which the browser can change in the same
   frame as the scroll (below), and its logo with them. */
.top {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px var(--gutter);
  /* The rule's room; ::before draws it. */
  border-bottom: 2px solid transparent;
}
.top::before {
  content: "";
  position: absolute;
  inset: 0 0 -2px;
  z-index: -1;
  background: rgba(255, 249, 245, 0.94);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 2px solid var(--pink);
}
.mark { display: block; padding: 6px 0; color: var(--cherry); }
.mark .logo { width: 96px; }
.nav { display: flex; gap: 16px; }
/* Each link a 44px touch target at every width, Apple's least (it was 42 on
   phones, where the 14px words set a shorter line). */
.nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-weight: 500;
  font-size: 14px;
  white-space: nowrap;
  text-decoration: none;
  color: var(--grey-dark);
  transition: color 0.25s ease;
}
.nav a:hover { color: var(--accent); }
.top.clear::before { opacity: 0; transition: opacity 0.25s ease; animation: bar-solid 0s 3s forwards; }
.top.clear .mark { opacity: 0; visibility: hidden; transition: opacity 0.25s ease, visibility 0s 0.25s; animation: mark-shown 0s 3s forwards; }
/* NEVER CLEAR LINKS OVER THE PAGE: the bar starts clear and without its logo,
   so if main.js never takes it over (a script that failed or was blocked), it
   turns solid with its logo by itself 3s in, as the cards come in by
   themselves (below). And until main.js has it, where the browser can tie an
   animation to the scroll, the bar is solid whenever the page is not at its
   very top: a reload put back mid-page, or a reader who scrolls before a slow
   main.js arrives, never sees clear links over Our Story. main.js cancels both
   as it takes over (.watched); without JavaScript, noscript.css shows the bar
   solid from the start. */
@supports (animation-timeline: scroll()) {
  .top.clear::before {
    animation: bar-solid auto linear both, bar-solid 0s 3s forwards;
    animation-timeline: scroll(root), auto;
    animation-range: 0px 1px, normal;
  }
  .top.clear .mark {
    animation: mark-shown auto linear both, mark-shown 0s 3s forwards;
    animation-timeline: scroll(root), auto;
    animation-range: 0px 1px, normal;
  }
}
.top.watched::before,
.top.watched .mark { animation: none; }
/* A FLICK NEVER OUTRUNS IT: the browser scrolls a frame before main.js hears
   of it. So where it can tie an animation to the scroll, the bar is also tied
   to the scroll offsets at which it must turn solid, which main.js works out
   from the same geometry and writes on the bar: solid by --bar-to, where
   anything of the hero comes within 8px of it, easing in from --bar-from,
   16px of scroll before (never before the page's top), which stands in for
   the fade above. The browser runs it off the main thread, in the same frame
   as it moves the page (measured: with the main thread held busy mid-scroll,
   the bar still turns solid on time), but only with no transition on the
   same opacity, which kept it on the main thread once the bar had been solid
   and come back. It runs only while main.js has the bar clear, so main.js can
   always turn it solid sooner, and where the browser cannot tie an animation
   to the scroll, main.js's classes alone decide, with the fade. The bar's
   logo is tied to the same offsets, so it comes in in the same frame as the
   rule (its own opacity is drawn on the main thread, measured the same way,
   but no longer a frame behind main.js's class). */
@supports (animation-timeline: scroll()) {
  .top.watched.clear::before,
  .top.watched.clear .mark {
    transition: none;
    animation: bar-solid auto linear both;
    animation-timeline: scroll(root);
    animation-range: var(--bar-from) var(--bar-to);
  }
  .top.watched.clear .mark { animation-name: mark-shown; }
}
@keyframes bar-solid { to { opacity: 1; } }
@keyframes mark-shown { to { opacity: 1; visibility: visible; } }

/* ── Hero: the app's opening ─────────────────────────────── */
/* The app's welcome screen on the Milk ground: the script logo, three
   polaroids of real weddings that slide in from the sides of the window
   (main.js), the handwritten line under them, then the words and the store
   pill. A phone gets the app's stack; from 900px, and on a phone held
   sideways (below), the words stand on the left and the polaroids, larger, on
   the right. */
.hero {
  isolation: isolate;
  /* Each card starts wholly off its edge of the window, and nothing of it may
     widen the page, mid-slide included: `clip` where it is known, so the hero
     is not a scroll container, `hidden` before it. */
  overflow: hidden;
  overflow: clip;
  padding: 84px 0 44px;
}
.hero-inner {
  /* The headline is sized to its own column, measured from here (`cqi`). */
  container-type: inline-size;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "logo" "photos" "headline" "lede" "stores";
  justify-items: center;
  max-width: var(--hero-max);
  margin: 0 auto;
  padding: 0 var(--gutter);
  text-align: center;
}
/* The logo is Cherry everywhere, with the key in its A (Michael and the team,
   2026-10-23), as on the app's opening, which this page is: the hero's, the
   top bar's and the footer's alike (the hero's was Almari Pink until then). */
.wordmark { grid-area: logo; margin: 0; color: var(--cherry); }
.wordmark .logo { width: clamp(196px, 62vw, 300px); }
/* ONE line at every width (Michael, 2026-10-16): the line is 13.38 em wide in
   League Spartan Bold (measured in the browser), so it sets at its column's
   width over 13.6 em, up to 4rem: ~25px on a 375px phone, ~21px on a 320px
   one, ~45px in the left column of a 1280-1599px window (the hero stops at
   1240px), ~43px from 1600px, where it is 1440px wide. It is SIZED to one line, never held to one (no nowrap):
   the fallback face is scaled to League Spartan's width (above), and a face
   wider still wraps to two balanced lines rather than being cut (Codex on
   #578). A browser without container units takes the window's width
   for the column's. Past the default text size it grows with the reader's
   text (`2.5rem - 24px` is 16px at the default, under any column's fit, and
   56px at twice it), and wraps where it must, so it never ends up smaller
   than the words under it. */
.headline {
  grid-area: headline;
  text-wrap: balance;
  font-family: var(--title);
  font-weight: 700;
  font-size: max(min(4rem, calc((100vw - 2 * var(--gutter)) / 13.6)), calc(2.5rem - 24px));
  font-size: max(min(4rem, calc(100cqi / 13.6)), calc(2.5rem - 24px));
  line-height: 1.1;
  margin: 22px 0 12px;
  color: var(--ink);
}
/* TWO balanced lines on a laptop (Michael, 2026-10-18: three was "ugly"): the
   line is 68.2 em on one line in Open Sauce Sans (measured in the browser), so
   36.5 em holds half of it with room for where the words break, and balance
   evens the two. In the split, from 840px, the words take the wider column and
   the paragraph 17px (below), so it is two lines there too from a 1280px
   window. Phones wrap it further, as they must. */
.lede {
  grid-area: lede;
  max-width: 36.5em;
  text-wrap: balance;
  margin: 0 0 24px;
  font-size: 1.125rem;
  color: var(--grey-dark);
}
/* One plain-words pill until the apps are in the stores (index.html's
   TODO(launch): never Apple's or Google's logos in a badge of our own). On
   the Milk ground it is Ink, as their own badges are black: its words white
   on it (17.85:1), the line above them Silver (9.81:1). */
.stores { grid-area: stores; display: flex; justify-content: center; }
.store {
  display: inline-flex;
  align-items: center;
  min-height: 3.5rem;
  padding: 8px 24px;
  border-radius: 999px;
  background: var(--ink);
  color: var(--white);
  cursor: default;
}
.store-words { display: flex; flex-direction: column; line-height: 1.15; text-align: left; }
.store-small { font-size: 0.6875rem; color: var(--silver); }
.store-name { font-weight: 700; font-size: 1.0625rem; }

/* The cards are drawn over the words. On the split (below), 01 and 02 come in
   from the window's left edge and cross the words on their way, landing on
   top as photos tossed onto a table do; at rest none of them overlaps a
   word. */
.wordmark,
.headline,
.lede,
.stores { position: relative; z-index: 1; }

/* The polaroids and their line. On a phone the cards span the window less
   13px a side, the app's edge for three, up to 560px (Phones, below, keeps
   them under a phone's fold). */
.hero-photos {
  grid-area: photos;
  justify-self: stretch;
  position: relative;
  z-index: 2;
  margin: 18px calc(13px - var(--gutter)) 0;
}
.polaroids {
  position: relative;
  /* Decor: a card passing over the words never takes a click meant for them. */
  pointer-events: none;
  width: min(100%, 560px);
  margin: 0 auto;
  /* The three cards' turned extent in card widths (welcomeLayout.ts's THREE,
     2.2876 across and 1.5629 down), so their room is kept before the photos
     arrive and every corner stays inside it; a card is 43.71% of it. */
  aspect-ratio: 2.2876 / 1.5629;
  --w: 43.71%;
}
/* The app's card (WelcomePolaroids): white, a 1px Silver edge drawn inside it,
   a 1px corner, no shadow; a square photo 0.90 of its width, 0.05 of its width
   in from its top and sides; 1.22 times as tall as it is wide. Each turns
   about its own centre. Where each stands, its tilt, and the edge and the
   order it comes in from are set here for each arrangement, and main.js reads
   them. */
.polaroid {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w);
  aspect-ratio: 100 / 122;
  background: var(--white);
  border-radius: 1px;
  outline: 1px solid var(--silver);
  outline-offset: -1px;
  transform: rotate(var(--tilt));
}
/* The photo's square is the pink wash until its photo is drawn over it, and
   stays the wash if the photo fails: main.js hides a failed image (.failed),
   so no broken-image glyph shows in its place. While main.js holds the cards
   (.held), a photo shows only once it has loaded whole (.ready), so a card is
   its wash or its photo, never a photo half painted down over the wash, as a
   slow connection draws one. It shows at once while the cards are hidden, so
   none slides in with its photo still fading up over the wash; only a photo
   that arrives once they are shown (.in: they faded in place without it)
   fades in, over its wash. A `top` percentage is of the card's height, 1.22
   of its width. */
.polaroid::before {
  content: "";
  position: absolute;
  left: 5%;
  top: calc(5% / 1.22);
  width: 90%;
  aspect-ratio: 1;
  background: var(--wash);
}
.polaroid img {
  position: relative;
  width: 90%;
  /* A margin's percentage is of the card's width, the top's included. */
  margin: 5% 5% 0;
  aspect-ratio: 1;
  object-fit: cover;
}
.held .polaroid img:not(.ready) { opacity: 0; }
.in .polaroid img { transition: opacity 0.2s ease-out; }
.polaroid img.failed { visibility: hidden; }
/* Three layered: 01 back left, 03 back right, 02 in front, low in the middle;
   01 from the left, 03 from the right, then 02 from the left after them. */
.p1 { --x: 4.30%; --y: 4.96%; --tilt: -10deg; --from: left; --order: 0; }
.p3 { --x: 52.38%; --y: 7.52%; --tilt: 9deg; --from: right; --order: 1; }
.p2 { --x: 28.34%; --y: 20.32%; --tilt: -3deg; --from: left; --order: 2; }
/* NEVER A BLANK CARD SLIDING IN: the cards, their line and its heart wait,
   hidden, for main.js, which plays the entrance once the photos are ready.
   If main.js never runs, they come in place by themselves 3s in, the heart
   whole; main.js cancels that as it takes over (.held), shows the cards at
   rest (.in) as its entrance begins, and the line (.line-in) once its face is
   here. Without JavaScript, noscript.css shows them at once. The cards fade
   as ONE layer, the cluster, so where they overlap a card never shows through
   the one in front of it halfway through a fade. */
.polaroids,
.caption { opacity: 0; animation: failsafe-in 0.3s ease-out 3s forwards; }
.pen-heart path { animation: failsafe-heart 0s 3s forwards; }
.held .polaroids,
.held .caption,
.held .pen-heart path { animation: none; }
.in .polaroids,
.line-in .caption { opacity: 1; }
.line-in .pen-heart path { stroke-dashoffset: 0; }
@keyframes failsafe-in { to { opacity: 1; } }
@keyframes failsafe-heart { to { stroke-dashoffset: 0; } }

/* ── The handwritten line ───────────────────────────────── */
/* The opening's caption under the cards, Cherry, with the pen heart after it,
   drawn on as the last card lands (main.js). The app's 16pt at the least
   between the cards and it. ONE line, as the app never wraps it: the words are
   9.65 em in Caveat (welcomeLayout.ts's CAPTION_EM; 9.64 measured here) and
   the heart and its gap 1.15 em, so wherever its size would not fit the
   room, it sets at the room over 10.9 em, at every text size. Where the line
   is wider than that in its em, main.js sets it smaller (`--line-fit`) by as
   much as it needs to keep that room, inside the line's own height, so
   nothing around it moves: in a fallback hand, if Caveat fails or is late
   (Bradley Hand on an iPhone is 12.5 em), or under a reader's own wider
   letter and word spacing. The line is a box of its own so main.js hears it
   change width. */
.caption {
  margin: 16px 0 0;
  text-align: center;
  white-space: nowrap;
  font-family: var(--hand);
  font-size: clamp(1.5rem, 7vw, 2.25rem);
  font-size: min(clamp(1.5rem, 7vw, 2.25rem), 100cqi / 10.9);
  line-height: 1.2;
  color: var(--cherry);
}
.caption-line { display: inline-block; font-size: calc(1em * var(--line-fit, 1)); }
.pen-heart {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  margin-left: 0.25em;
  vertical-align: -0.12em;
}
/* The heart's one stroke, the designer's, traced on by main.js from nothing
   (a dash offset past 1, so not even a round cap's dot shows) to whole. */
.pen-heart path {
  fill: none;
  stroke: currentColor;
  stroke-width: 70;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 1 2;
  stroke-dashoffset: 1.05;
}

/* ── Our story ───────────────────────────────────────────── */
.story {
  max-width: var(--max);
  margin: 0 auto;
  padding: 56px var(--gutter) 88px;
  display: grid;
  gap: 40px;
  grid-template-columns: 1fr;
  align-items: center;
}
.founders { margin: 0; }
/* The co-founders, square, on the wash while it loads. */
.founders img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 24px;
  background: var(--wash);
}
.story-words h2,
.how-intro h2,
.contact h2 {
  font-family: var(--title);
  font-weight: 700;
  font-size: clamp(34px, 5.2vw, 46px);
  line-height: 1.08;
  margin: 0 0 20px;
  color: var(--ink);
}
.story-words p { margin: 0 0 18px; color: var(--ink); text-wrap: pretty; }
.story-words p:last-child { margin-bottom: 0; }

/* ── How it works ───────────────────────────────────────── */
.how {
  max-width: var(--max);
  margin: 0 auto;
  padding: 72px var(--gutter) 88px;
  border-top: 1px solid var(--line);
}
.how-intro { max-width: 640px; margin: 0 0 40px; }
.how-intro h2 { margin-bottom: 16px; }
.how-intro p { margin: 0; color: var(--grey-dark); font-size: 18px; text-wrap: pretty; }
.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 16px;
  grid-template-columns: 1fr;
}
/* White cards on the Milk ground, as the app's are; a soft edge carries them. */
.step {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 28px 26px 30px;
}
.step-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  background: var(--cherry);
  color: var(--white);
  font-weight: 700;
  font-size: 15px;
  margin-bottom: 16px;
}
.step h3 {
  font-family: var(--title);
  font-weight: 700;
  font-size: 23px;
  line-height: 1.15;
  margin: 0 0 8px;
  color: var(--ink);
}
.step p { margin: 0; color: var(--grey-dark); font-size: 16px; }

/* ── Contact ─────────────────────────────────────────────── */
.contact {
  max-width: 620px;
  margin: 0 auto;
  padding: 72px var(--gutter) 96px;
  border-top: 1px solid var(--line);
}
.contact-lede { color: var(--grey-dark); margin: 0 0 28px; text-wrap: pretty; }
.form { display: grid; gap: 18px; }
.field { display: grid; gap: 6px; }
.field span { font-weight: 500; font-size: 15px; }
.field input,
.field textarea {
  font: inherit;
  color: var(--ink);
  background: var(--white);
  border: 1px solid var(--field-edge);
  border-radius: 14px;
  padding: 13px 14px;
  min-height: 48px;
  width: 100%;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.field textarea { resize: vertical; min-height: 140px; }
.field input:focus,
.field textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--tint); }
.trap { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }
.send {
  justify-self: start;
  font: inherit;
  font-weight: 700;
  color: var(--white);
  background: var(--accent);
  border: 0;
  border-radius: 999px;
  min-height: 52px;
  padding: 0 32px;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.1s ease;
}
.send:hover { background: var(--accent-pressed); }
.send:active { transform: scale(0.98); }
.send:disabled { opacity: 0.55; cursor: default; }
.send:focus-visible { outline: 3px solid var(--tint); outline-offset: 2px; }
.status { margin: 0; min-height: 1.6em; color: var(--grey-dark); }
.status.error { color: var(--cherry); }
.privacy { margin: -8px 0 4px; font-size: 14px; color: var(--grey-dark); text-wrap: balance; }

/* ── Footer ──────────────────────────────────────────────── */
.foot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 28px var(--gutter) 40px;
  color: var(--grey-dark);
  font-size: 14px;
  border-top: 1px solid var(--line);
}
.foot .logo { width: 72px; color: var(--cherry); }
.foot p { margin: 0; }
.foot { flex-wrap: wrap; }
.foot-links a { display: inline-block; min-height: 44px; line-height: 44px; color: var(--accent); font-weight: 700; text-decoration: none; }
.foot-links a:hover, .foot-links a:focus-visible { text-decoration: underline; }

/* ── Wider screens ───────────────────────────────────────── */
@media (min-width: 760px) {
  :root { --gutter: 32px; }
  .steps { grid-template-columns: repeat(3, 1fr); gap: 20px; }
  .story { grid-template-columns: 5fr 6fr; gap: 64px; }
  .nav { gap: 22px; }
  .nav a { font-size: 15px; }
}

/* ── The split: words left, polaroids right ─────────────── */
/* From 900px the hero fills the window: the words on the left, left-aligned,
   centred against the polaroids, larger, on the right, with their line under
   them as on the app's screen. A phone held sideways (from 640 wide and up to
   540 tall) gets it too, where the stack would leave only the logo and the
   tops of the cards on its first screen. */
@media (min-width: 900px), (min-width: 640px) and (max-height: 540px) {
  .hero {
    display: flex;
    align-items: center;
    /* As tall as the window, but never much taller than it is wide: a tablet
       stood upright would leave a third of a screen of empty Milk above the
       words. Every laptop's window is wider than 4:3, so it fills it. */
    min-height: min(100vh, 1200px, 75vw);
    min-height: min(100svh, 1200px, 75vw);
    /* Clear of the bar, and a little above the middle of the window under
       it, where the eye takes a centre to be. */
    padding: 96px 0 80px;
  }
  .hero-inner {
    flex: 1;
    grid-template-columns: minmax(0, 9fr) minmax(0, 11fr);
    grid-template-rows: 1fr auto auto auto auto 1fr;
    grid-template-areas:
      ".        photos"
      "logo     photos"
      "headline photos"
      "lede     photos"
      "stores   photos"
      ".        photos";
    column-gap: 64px;
    justify-items: start;
    text-align: left;
  }
  .hero-photos { align-self: center; margin: 0; }
  /* Cards up to ~300px, and never so tall that they and their line leave the
     window of a short laptop (its padding, the line and some air: 290px), and
     never under the app's floor of 320px for the three, which a phone held
     sideways would take them under, unless the column itself is narrower:
     295px on a 640px phone held sideways. */
  .polaroids {
    width: min(100%, 690px, max(320px, (100vh - 290px) * 1.4637));
    width: min(100%, 690px, max(320px, (100svh - 290px) * 1.4637));
  }
  .wordmark { justify-self: stretch; }
  .wordmark .logo { width: min(100%, 380px); }
  /* The left column is 9/20 of the row, less the gap (11/20 from 840px, below). */
  .headline {
    font-size: max(min(4rem, calc((min(100vw, var(--hero-max)) - 2 * var(--gutter) - 64px) * 0.45 / 13.6)), calc(2.5rem - 24px));
    font-size: max(min(4rem, calc((100cqi - 64px) * 0.45 / 13.6)), calc(2.5rem - 24px));
    margin: 28px 0 14px;
  }
  .lede { max-width: 34rem; margin-bottom: 30px; text-wrap: pretty; }
  .stores { justify-content: flex-start; }
  /* One line in the right column, 11/20 of the row less the gap (9/20 from 840px, below); never wider
     than the cards over it, where a short window has made them small; and
     never larger to the eye than the headline, the page's main line, which
     stays at its column's fit as the reader's text grows: Caveat's small
     letters are 0.357 em tall and League Spartan's 0.41, so the line may be
     1.15 times the headline's size (the app matches them so, Home's Caveat
     greeting at 24 to League Spartan's 21). */
  .caption {
    font-size: clamp(2rem, 2.5vw, 2.5rem);
    font-size: min(
      clamp(2rem, 2.5vw, 2.5rem),
      (100cqi - 64px) * 0.55 / 10.9,
      max(320px, (100svh - 290px) * 1.4637) / 10.9,
      1.15 * max(min(4rem, (100cqi - 64px) * 0.45 / 13.6), 2.5rem - 24px)
    );
  }
}

/* The words take the wider column, 11/20, and the cards 9/20 (Michael,
   2026-10-19, "option 1" of three rendered side by side): in 9/20 the
   paragraph needed three lines on a laptop and four from 900 to 1279px, where
   here it is two from 1280. Only where the cards' column still holds the three
   cards' 320px floor: (the window - 2 x 32px gutters - the 64px gap) x 9/20 is
   320px from an 840px window, so a phone held sideways under that keeps the
   split above (Codex on #588: from 640px it took each card to ~105px). The
   headline and the handwritten line follow their columns; the paragraph is
   17px and balanced here (a phone held sideways keeps its own, below). */
@media (min-width: 900px), (min-width: 840px) and (max-height: 540px) {
  .hero-inner { grid-template-columns: minmax(0, 11fr) minmax(0, 9fr); }
  .headline {
    font-size: max(min(4rem, calc((min(100vw, var(--hero-max)) - 2 * var(--gutter) - 64px) * 0.55 / 13.6)), calc(2.5rem - 24px));
    font-size: max(min(4rem, calc((100cqi - 64px) * 0.55 / 13.6)), calc(2.5rem - 24px));
  }
  .lede { max-width: 38em; font-size: 1.0625rem; text-wrap: balance; }
  .caption {
    font-size: clamp(2rem, 2.5vw, 2.5rem);
    font-size: min(
      clamp(2rem, 2.5vw, 2.5rem),
      (100cqi - 64px) * 0.45 / 10.9,
      max(320px, (100svh - 290px) * 1.4637) / 10.9,
      1.15 * max(min(4rem, (100cqi - 64px) * 0.55 / 13.6), 2.5rem - 24px)
    );
  }
}

/* A big screen gives the two columns more room, and the cards with it. */
@media (min-width: 1600px) {
  :root { --hero-max: 1440px; }
  .wordmark .logo { width: min(100%, 440px); }
  .polaroids {
    width: min(100%, 780px, max(320px, (100vh - 290px) * 1.4637));
    width: min(100%, 780px, max(320px, (100svh - 290px) * 1.4637));
  }
}

/* ── Phones held sideways ────────────────────────────────── */
/* The split on a short screen: it starts as close under the bar as a phone's
   stack does, the logo gives up height to the words (a quarter of the window
   at the most: its width over 2.855 is its height), and the lede is a phone's. The cards stand level with the logo, not centred
   against the words: on a phone, where the words run taller than the window,
   that would put the cards' feet and their line below the fold. */
@media (min-width: 640px) and (max-height: 540px) {
  .hero { padding: 84px 0 44px; }
  .hero-photos { align-self: start; }
  .wordmark .logo { width: min(100%, 380px, 24svh * 2.855); }
  .lede { font-size: 1rem; }
}

/* ── Phones ──────────────────────────────────────────────── */
/* The one-line headline sets small here (~21-29px), so the lede under it
   steps down to keep under it. The cards give up height before the page's
   first screen does: never so tall that the words and the store pill fall
   below a phone's fold. `--rest` is everything else the stack holds and 12px
   of air under the pill: the padding above the logo and the gaps
   (188px), the logo (its width over 2.855), the handwritten line (1.2 of its
   size), the headline (1.1 of its size) and the lede's four lines with the
   pill (10rem), each as styles.css sizes it, so it grows with the window's
   width as they do and with the reader's text, and the cards give way to the
   words, down to the app's floor of 140px a card, 320px for the three. It is
   checked against the browser's own window on the phones it is likeliest to
   be read on (an iPhone's Safari, 390 x 664 or 393 x 659; Android's Chrome,
   360 x 660), not only the screens' sizes. */
@media (max-width: 479px) {
  .lede { font-size: 1rem; }
  .polaroids {
    --rest: calc(188px + clamp(196px, 62vw, 300px) / 2.855 + 1.2 * clamp(1.5rem, 7vw, 2.25rem) + 1.1 * (100vw - 2 * var(--gutter)) / 13.6 + 10rem);
    width: min(100%, 560px, max(320px, (100vh - var(--rest)) * 1.4637));
    width: min(100%, 560px, max(320px, (100svh - var(--rest)) * 1.4637));
  }
}

/* ── Small phones: the app's two cards ───────────────────── */
/* Three only where the app shows three: from 360 wide, and from 700 tall on a
   phone (its rule, so the SE and anything as short has two); never on the
   split. Two, as the SE's redline: 01 on the left, 02 on the right and over
   it, coming in from the right; 03 sits out. They span the window less 43px a
   side (2.1493 card widths across and 1.4594 down, turned), down to the app's
   floor of 100px a card, and their line keeps the wider room under them. */
@media (max-width: 359px), (max-width: 479px) and (max-height: 699px) {
  .polaroids {
    width: min(100% - 60px, 344px, max(215px, (100vh - var(--rest)) * 1.4727));
    width: min(100% - 60px, 344px, max(215px, (100svh - var(--rest)) * 1.4727));
    aspect-ratio: 2.1493 / 1.4594;
    --w: 46.53%;
  }
  .p1 { --x: 4.15%; --y: 4.85%; --tilt: -9deg; }
  .p2 { --x: 49.75%; --y: 12.04%; --tilt: 8deg; --from: right; --order: 1; }
  .p3 { display: none; }
}

/* ── The narrowest phones ────────────────────────────────── */
/* The bar's logo and its three links need 381px at their full size
   (measured: the links 213px, the logo 96, their gaps and the gutters), so
   under 400px the logo is 80px and the gaps 10, which holds them on one line,
   7px apart, from 360px; under that, How It Works gives way (the page scrolls
   to it anyway). */
@media (max-width: 399px) {
  .nav { gap: 10px; }
  .mark .logo { width: 80px; }
}
@media (max-width: 359px) {
  .nav a[href="#how"] { display: none; }
}
