/* ---------------------------------------------------------------------------
   Reset, document defaults, typography, and the shared interactive primitives.
   --------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Anchored sections must clear the sticky header. */
  scroll-padding-top: 5.5rem;
  /* Nothing on this page may scroll the page sideways. Wide objects scroll
     inside their own container instead, and the layout is verified with no
     page-level overflow at 320, 375, 768, 1024, and 1440 CSS pixels.

     This is `clip` on the root rather than `hidden` on the body. `hidden` on
     the body turns it into a scroll container, which breaks `position: sticky`
     in Safari, and Safari is this product's audience. `clip` establishes no
     scroll container, so the sticky header keeps working. */
  overflow-x: clip;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  background-color: var(--canvas);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1,
h2,
h3,
h4 {
  margin: 0;
  font-family: var(--font-display);
  line-height: var(--lh-heading);
  font-weight: 600;
  letter-spacing: -0.011em;
  /* No tracked all-caps anywhere. Direction A section 2. */
  text-transform: none;
}

h1 {
  font-size: var(--fs-hero);
  line-height: var(--lh-tight);
  letter-spacing: -0.021em;
  font-weight: 700;
  text-wrap: balance;
}

h2 {
  font-size: var(--fs-section);
}

h3 {
  font-size: var(--fs-subsection);
}

h4 {
  font-size: var(--fs-card-title);
}

p {
  margin: 0;
}

/* The user-agent gives figure a 40px side margin, which quietly narrowed every
   stage that is marked up as a figure. */
figure,
figcaption,
blockquote {
  margin: 0;
}

ul,
ol {
  margin: 0;
  padding: 0;
}

/* A tightened Latin line box CLIPS stacked Thai tone marks (เข้าใจได้). Thai
   needs its natural box back, so line height is scoped up per language rather
   than lowered globally. */
:lang(th) h1,
:lang(th) h2,
:lang(th) h3,
:lang(th) h4 {
  line-height: 1.45;
  letter-spacing: 0;
}

:lang(th) body,
:lang(th) p,
:lang(th) li,
:lang(th) dd,
:lang(th) td {
  line-height: 1.85;
}

/* Thai has no inter-word spaces, so a hard break mid-word is the normal
   rendering. Let the browser do it rather than forcing overflow. */
:lang(th) {
  word-break: normal;
  overflow-wrap: break-word;
  line-break: normal;
}

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

a {
  color: var(--link);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  transition: color var(--dur-quick) var(--ease-standard);
}

a:hover {
  color: var(--link-hover);
}

/* Focus is never removed. 2px accent outline, 2px offset, on everything. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-chip);
}

/* Visible only when focused. The first thing a keyboard user reaches. */
.skip-link {
  position: absolute;
  left: var(--gutter);
  top: 0;
  z-index: 100;
  transform: translateY(-150%);
  display: inline-block;
  padding: var(--space-label) var(--space-group);
  background-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
  text-decoration: none;
  border-radius: 0 0 var(--radius-card) var(--radius-card);
  transition: transform var(--dur-quick) var(--ease-standard);
}

.skip-link:focus {
  transform: translateY(0);
  color: var(--on-accent);
}

/* --- text primitives ------------------------------------------------------ */

.eyebrow {
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  color: var(--text-secondary);
  /* sentence case, never tracked caps */
  letter-spacing: 0;
}

.lede {
  font-size: var(--fs-body-lg);
  color: var(--text-secondary);
  max-width: var(--measure);
}

.prose {
  max-width: var(--measure);
}

.caption {
  font-size: var(--fs-caption);
  color: var(--text-secondary);
}

.mono {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-variant-numeric: tabular-nums;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- actions -------------------------------------------------------------- */

/* A rounded square, never a pill. components/v2/Button.tsx: a pill "reads
   consumer and fights the instrument feel". radius.card (10) at ctaH (52). */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-related);
  min-height: var(--cta-h);
  min-width: var(--tap-min);
  padding: 0 var(--space-section);
  border: var(--stroke-hairline) solid transparent;
  border-radius: var(--radius-card);
  font-family: var(--font-ui);
  font-size: var(--fs-card-title);
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--dur-quick) var(--ease-standard),
    border-color var(--dur-quick) var(--ease-standard),
    color var(--dur-quick) var(--ease-standard),
    transform var(--dur-quick) var(--ease-standard);
}

.button--primary {
  background-color: var(--accent);
  color: var(--on-accent);
}

.button--primary:hover {
  background-color: var(--accent-pressed);
  color: var(--on-accent);
}

/* Press is scale plus tint. Never a hover lift. */
.button:active {
  transform: scale(var(--press-scale));
}

.button--secondary {
  background-color: transparent;
  border-color: var(--hairline);
  color: var(--ink);
}

.button--secondary:hover {
  border-color: var(--link);
  color: var(--link);
}

/* `.button` sets display, which outranks the user-agent rule for [hidden].
   Without this a control the script has not enabled yet is still visible. */
.button[hidden] {
  display: none;
}

.button--compact {
  min-height: var(--tap-min);
  padding: 0 var(--space-group);
  font-size: var(--fs-sub);
}

/* Apple's own "Download on the App Store" artwork, used unmodified and
   self-hosted. Their marketing guidelines forbid recreating, recolouring or
   restyling it, so nothing here touches the image: the rule only sizes the
   tap target and reserves the clear space Apple requires, which is 10 percent
   of the badge's height on every side. Apple's minimum height for the badge on
   screen is 40px, which is what `--compact` renders and what the artwork's own
   viewBox is drawn at. */
.store-badge {
  --store-badge-h: 52px;
  display: inline-flex;
  padding: calc(var(--store-badge-h) * 0.1);
  border-radius: var(--radius-card);
  transition: transform var(--dur-quick) var(--ease-standard);
}

.store-badge img {
  display: block;
  width: auto;
  height: var(--store-badge-h);
}

.store-badge:active {
  transform: scale(var(--press-scale));
}

.store-badge--compact {
  --store-badge-h: 40px;
}

/* Google's own "Get it on Google Play" artwork, used unmodified and self-hosted,
   the same way Apple's is. Two differences from `.store-badge` matter:

   1. Google's web badge ships its clear space INSIDE the PNG, so no padding is
      added here. Adding any would double it.
   2. The two locales are scaled separately. Measured from the files: the English
      asset is 646x250 with the artwork inset 41px on every side (67.2 percent of
      the height), while the Thai asset's artwork runs the full width and is 76.8
      percent of the height. One shared box would render them at visibly
      different sizes, so each height below is the one that puts 52px of actual
      badge on the page, matching Apple's 52.

   It is wrapped in a <span>, never an <a>: there is no Play listing to link to
   yet. The badge is never dimmed, cropped or recoloured to say so; the sentence
   under the row does that. */
.play-badge {
  position: relative;
  display: inline-flex;
  align-items: center;
}

/* Says what the badge cannot say for itself, directly under it, in the ink so it
   is read with the badge and not after it. Google bakes about 13px of clear
   space into the bottom of its PNG; the label sits in that space, so the row's
   height and centring are unchanged. The badge alt carries the same words. */
.play-badge__soon {
  position: absolute;
  top: calc(100% - 7px);
  right: 0;
  left: 0;
  color: var(--ink);
  font-size: var(--fs-sub);
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  white-space: nowrap;
}

.play-badge img {
  display: block;
  width: auto;
  height: 77px;
}

.play-badge--th img {
  height: 68px;
}

.text-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-tight);
  min-height: var(--tap-min);
  font-weight: 500;
  color: var(--link);
}

.text-link .arrow {
  transition: transform var(--dur-quick) var(--ease-standard);
}

.text-link:hover .arrow {
  transform: translateX(2px);
}
