/* WorldCards — world.cards marketing site.
 *
 * DESIGN CONTRACT (settled 2026-08-18, design-shotgun):
 * The PAGE is modern; the CARDS are vintage. Nothing in the page chrome may
 * read as 1930s — no aged paper, no letterpress, no wide-tracked display caps
 * in headings or buttons, no cartographic marginalia. The travel-poster
 * aesthetic lives strictly inside the card rectangles.
 *
 * That is a deliberate, documented departure from DESIGN.md § Typography,
 * which sets marketing hero headings in tracked display caps. Tracked caps on
 * web chrome read as a vintage poster shop rather than a 2026 app site; the
 * app itself is unaffected. Colour tokens still mirror DESIGN.md exactly.
 */

:root {
  /* Surfaces — DESIGN.md § Color */
  --bg-base: #faf6ee;
  --bg-surface: #ffffff;
  --bg-sunken: #f0ebdf;
  --bg-atlas: #1a2030;

  /* Text.
     `--text-muted` is DARKER than DESIGN.md's #7A776E: that value measures
     4.15:1 on cream and 3.76:1 on the sunken band, below the 4.5:1 WCAG AA
     floor for normal text, and it carries real body copy here (the lede, the
     step and claim paragraphs). #6B685F is the smallest darkening that clears
     AA on BOTH surfaces (5.17 / 4.68) while holding the warm hue. The app is
     unaffected — it uses the token at larger sizes and on different grounds.
     `--text-subtle` is decorative only (colophon, table headers); never put
     body copy on it. */
  --text-primary: #1a2030;
  --text-muted: #6b685f;
  --text-subtle: #a09a8e;

  /* Accent.
     `--accent-primary` is DESIGN.md's accent.primary, used at full brightness
     as the button FILL. Cream on it is only 2.85:1, so the label is navy
     (`--text-primary`) instead — 5.29:1, clears AA, and keeps the brand blue
     exactly as specified rather than muting the page's one chromatic note.
     `--accent-ink` is the same hue darkened for TEXT on cream (5.25:1 on
     bg.base, 4.76:1 on bg.sunken): links and the ghost button's label, where
     the bright token would fail. */
  --accent-primary: #3d9dc7;
  --accent-ink: #2a6e8c;
  --accent-press: #6fbad9;

  /* Spacing — 4px base */
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s6: 24px;
  --s8: 32px;
  --s12: 48px;
  --s16: 64px;
  --s24: 96px;

  /* Radius — modern web, not the app's card geometry */
  --radius-sm: 6px;
  --radius: 8px;
  --radius-card: 10px;

  /* Type. Body/UI is a modern humanist sans (Karla). There is deliberately NO
     display face here: the card titles people see are baked into the exported
     card PNGs by the app's own renderer, so the site never sets Barlow. Do not
     re-add a --font-card token; nothing on this page draws card type. */
  --font-ui: "Karla", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "DM Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --shadow-sm: 0 1px 2px rgba(20, 25, 40, 0.06);
  --shadow-card: 0 6px 24px rgba(20, 25, 40, 0.1);
  --shadow-lift: 0 18px 48px rgba(20, 25, 40, 0.16);

  --measure: 62ch;
  --nav-h: 72px;

  /* Motion. One entrance curve for the whole site — a decisive ease-out that
     arrives fast and settles, matching DESIGN.md § Motion `ease.enter`.
     Reveals are deliberately slower (700ms) than UI feedback (80-200ms):
     an entrance is atmosphere, a state change is a response. */
  --ease-enter: cubic-bezier(0.16, 1, 0.3, 1);
  --reveal-dur: 700ms;
}

* {
  box-sizing: border-box;
}
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  /* Push the footer to the bottom of the viewport on a short page. This site
     is one screen tall on a desktop, so without it the footer floats in the
     middle with dead cream below — reading as a page that failed to load the
     rest of itself. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg-base);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* ---- Modern type scale ---------------------------------------------------
   Sentence case, tight leading, NORMAL tracking. The negative letter-spacing
   on large sizes is what makes a big geometric sans read contemporary rather
   than postery — do not remove it. */

h1,
h2,
h3 {
  margin: 0;
  font-weight: 700;
  color: var(--text-primary);
  text-wrap: balance;
}

h1 {
  /* Capped at 3.4rem, not 4.25: the two-beat headline only works if each
     sentence holds ONE line, and "Reach the landmark." needs ~535px of column
     to do that. Raising this re-breaks the rhythm — widen the column instead. */
  font-size: clamp(2.25rem, 4.4vw, 3.4rem);
  line-height: 1.05;
  letter-spacing: -0.025em;
}

h2 {
  font-size: clamp(1.75rem, 3.2vw, 2.75rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
}

h3 {
  font-size: 1.125rem;
  line-height: 1.3;
  letter-spacing: -0.01em;
}

p {
  margin: 0 0 var(--s4);
  max-width: var(--measure);
}

/* A two-beat headline: each sentence stays whole, and the break falls BETWEEN
   them. `display: inline-block` makes each span an atomic unit the browser
   will not split, so the phrase either fits on one line or moves entirely to
   the next — never mid-sentence. */
.two-beat span {
  display: inline-block;
}

a {
  color: var(--accent-ink);
  text-underline-offset: 2px;
}
a:hover {
  color: var(--text-primary);
}

:focus-visible {
  outline: 2px solid var(--accent-primary);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

.wrap {
  width: min(100% - var(--s8), 1140px);
  margin-inline: auto;
}
.narrow {
  width: min(100% - var(--s8), 720px);
  margin-inline: auto;
}

/* ---- Nav ----------------------------------------------------------------- */

.site-header {
  height: var(--nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
}

.wordmark {
  font-family: var(--font-ui);
  font-weight: 800;
  font-size: 1.3rem;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  text-decoration: none;
}

/* ---- Hero: type left, device right -------------------------------------- */

.hero {
  padding-block: var(--s12) var(--s16);
}

.hero-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s12);
  /* Center on mobile (single column, nothing to align against); on desktop the
     copy is pinned toward the top instead — see the 940px block. Centring the
     copy against a phone twice its height pushes the headline a third of the
     way down the hero, so the eye reaches the device first. */
  align-items: center;
}

@media (min-width: 940px) {
  /* The copy column gets the larger share: the phone is a fixed-width object
     (--phone-w) and does not benefit from extra space, while the headline
     needs enough width to keep each sentence on one line. */
  .hero-grid {
    grid-template-columns: 1.15fr 0.85fr;
    gap: var(--s12);
    align-items: start;
  }
  /* Nudge the copy down a little so it optically centres against the phone
     without starting a third of the way down the section. */
  .hero-grid > :first-child {
    padding-top: var(--s12);
  }
  .hero {
    padding-block: var(--s12) var(--s16);
  }
}

/* The line that reconciles the name with the catalogue: the product is called
   WorldCards and ships one country at a time, so the hero says which one.
   Small, tracked, above the headline — a kicker, not a heading. */
.eyebrow {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-ink);
  margin: 0 0 var(--s3);
}

.lede {
  font-size: 19px;
  line-height: 1.55;
  color: var(--text-muted);
  margin-top: var(--s6);
  max-width: 42ch;
}

/* ---- Store badges --------------------------------------------------------
   Licensed artwork from Apple and Google, used unmodified: no recolour, no
   rounded-corner override, no shadow, no hover restyle. Both stores' brand
   guidelines forbid it, and a badge that looks bespoke also stops reading as
   a badge.

   The two are DIFFERENT widths by design (120 vs 135) and only share a 40px
   height. So the row aligns on height and each <img> keeps its own width —
   never set a common width, which squashes one of them. */

.store-badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s3);
  margin-top: var(--s8);
}

.store-badges a {
  display: inline-flex;
  /* The badge art is 40px tall; Apple's guidelines set a 44px minimum, and
     44px is also the tap-target floor. Scaling the art up to meet it would
     blur nothing (it is SVG) but would break the height both stores specify,
     so the LINK grows around the badge instead. */
  align-items: center;
  min-height: 44px;
}

.store-badges img {
  height: 40px;
  width: auto;
  display: block;
}

/* ---- Card primitive ------------------------------------------------------
   Cards are exported from the app's own Skia renderer complete with title,
   tier border and caption strip (2026-08-18 decision), so the site draws NO
   frame of its own — just geometry and shadow. Never re-add a CSS border
   here; that would be a second implementation of the card face. */

/* The card's true aspect is the ART's native ratio, 864/1184 — NOT 2:3.
   `CARD_ASPECT` in mobile/components/card/constants.ts is the source of truth,
   and exported cards are 1024x1403. Setting 2/3 here squashes every card. */
.card {
  aspect-ratio: 1024 / 1403;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--bg-sunken);
  display: block;
  /* No `width: 100%`: these are <img> elements carrying intrinsic 1024px
     width attributes, and the callers (.hero-card, .deck-card) set their own
     widths. A blanket 100% here beats those in the cascade and every card
     renders at full size. */
  height: auto;
  object-fit: cover;
}

/* The hero's single card. Fixed max-width rather than a fluid share of the
   column: the card carries its own frame and caption strip, and stretching it
   past ~340px makes the baked-in type outsize the page's own. */
.hero-art {
  display: flex;
  justify-content: center;
}
.hero-art .card {
  width: min(100%, 340px);
  box-shadow: var(--shadow-card);
}

/* ---- Prose (legal pages) ------------------------------------------------- */

.prose {
  padding-block: var(--s12) var(--s16);
}
.prose h1 {
  font-size: clamp(2rem, 5vw, 2.75rem);
  margin-bottom: var(--s3);
}

.prose h2 {
  font-size: 1.3rem;
  margin-top: var(--s12);
  margin-bottom: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--bg-sunken);
}

.prose h3 {
  font-size: 1rem;
  margin-top: var(--s6);
  margin-bottom: var(--s2);
}
.prose ul {
  padding-left: var(--s6);
  max-width: var(--measure);
}
.prose li {
  margin-bottom: var(--s2);
}

.updated {
  font-family: var(--font-mono);
  font-size: 13px;
  /* --text-subtle (2.59:1) is decorative-only; this line is real text a
     reader needs (the policy's effective date), so it takes the AA-safe
     muted token. */
  color: var(--text-muted);
  margin-bottom: var(--s8);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  margin: var(--s6) 0;
  font-size: 15px;
  display: block;
  overflow-x: auto;
  white-space: nowrap;
}

@media (min-width: 760px) {
  .data-table {
    display: table;
    white-space: normal;
  }
}

.data-table th,
.data-table td {
  text-align: left;
  padding: var(--s3) var(--s4) var(--s3) 0;
  border-bottom: 1px solid var(--bg-sunken);
  vertical-align: top;
}

.data-table th {
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
}

.data-table td:first-child {
  font-weight: 700;
}

.callout {
  background: var(--bg-surface);
  border-left: 3px solid var(--accent-primary);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: var(--s4) var(--s6);
  margin: var(--s6) 0;
  box-shadow: var(--shadow-sm);
}
.callout p:last-child {
  margin-bottom: 0;
}

code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: var(--bg-sunken);
  padding: 1px 5px;
  border-radius: var(--radius-sm);
}

ol.steps-numbered {
  counter-reset: step;
  list-style: none;
  padding: 0;
  max-width: var(--measure);
}

ol.steps-numbered li {
  counter-increment: step;
  position: relative;
  padding-left: var(--s12);
  margin-bottom: var(--s6);
}

ol.steps-numbered li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: -2px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent-primary);
  color: var(--bg-base);
  font-weight: 700;
  font-size: 15px;
  display: grid;
  place-items: center;
}

/* ---- Footer --------------------------------------------------------------
   No dark band: the footer sits on the page's own cream, separated by a
   hairline rather than a colour change. On a one-screen site an inverted
   band is a second visual section competing with the hero for weight. */

.site-footer {
  border-top: 1px solid var(--bg-sunken);
  padding-block: var(--s6);
  margin-top: auto; /* the flex spacer that does the pushing */
  padding-top: var(--s6);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3) var(--s8);
}

/* Footer links measured 24px tall. `inline-flex` + min-height gives them a
   44px tap box; the negative margin keeps the visual row height unchanged so
   the footer does not grow. */
.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s6);
  align-items: center;
}

.footer-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-block: -10px;
  color: var(--text-muted);
  text-decoration: none;
  font-size: 15px;
}
.footer-links a:hover {
  color: var(--text-primary);
}

.colophon {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-subtle);
  margin: 0;
  /* Two lines, one sentence each — the break is a <br> in the markup, not a
     wrap. The longer sentence ("Map data © …") is 65 characters, so the box
     has to be wide enough to hold it on one line or the <br> buys nothing. */
  max-width: 68ch;
  text-align: right;
}

/* Below the footer's stacking point the box is narrower than 65ch, so the
   long sentence wraps anyway and the <br> only adds a ragged short line on
   top of it. Dropping the break there lets the paragraph fill the width. */
/* One breakpoint governs the whole footer, so the row's stacking and the
   colophon's alignment can never disagree: above it the two halves sit on one
   line with the credits right-aligned in two sentences, below it everything
   stacks left. `flex-wrap` alone would pick its own moment to break. */
@media (max-width: 720px) {
  .site-footer {
    flex-direction: column;
    align-items: flex-start;
  }
  .colophon {
    text-align: left;
    max-width: none;
  }
  .colophon br {
    display: none;
  }
}
