/* ============================================================
   base.css — reset, typography, primitives, motion utilities
   Consumes ONLY --z-* tokens. Logical properties only.
   ============================================================ */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Anchor offset so sticky header never covers a jumped-to heading */
  scroll-padding-block-start: 6rem;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  background: var(--z-bg);
  color: var(--z-fg);
  font-family: var(--z-font-body);
  font-size: var(--z-text-body);
  font-weight: var(--z-weight-body);
  line-height: var(--z-leading-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

input, button, textarea, select { font: inherit; color: inherit; }
/* appearance:none matters on Safari specifically. With the default
   `appearance: auto`, macOS and iOS apply native push-button theming to
   <button>, and that theming imposes its own internal text layout — it
   overrides the flex centring, so labels sit off-centre. Chromium and
   headless WebKit do not theme controls, which is why this only shows up
   in real Safari. background/border alone do not disable it. */
button, input[type="submit"], input[type="button"] {
  appearance: none;
  -webkit-appearance: none;
}
button { background: none; border: none; cursor: pointer; }

a { color: inherit; text-decoration: none; }

/* [hidden] must beat any component `display`. Without this, a class that
   sets display:flex/inline-flex silently defeats the attribute and the
   element stays on screen — which is how the wizard's Submit button
   appeared on step 1, stacked under Next. */
[hidden] { display: none !important; }

h1, h2, h3, h4 {
  font-family: var(--z-font-display);
  font-weight: var(--z-weight-black);
  line-height: var(--z-leading-tight);
  letter-spacing: var(--z-tracking-h1);
  text-wrap: balance;
}
p { text-wrap: pretty; }

:where(:focus-visible) {
  outline: 2px solid var(--z-focus-ring);
  outline-offset: 3px;
  border-radius: var(--z-radius-sm);
}

/* ── focus on a CHAMFERED control ─────────────────────────
   clip-path clips `outline`: the ring is painted outside the shape and then
   thrown away, so a keyboard user gets NO indicator at all. That is the one
   real cost of the cut, and this is the fix — an INSET box-shadow renders
   inside the shape, which is the one focus treatment a chamfer cannot eat.
   The outline is removed explicitly rather than left to be silently eaten,
   and 3px clears the control's own 2px border.

   ANY new chamfered control must be added here. A cut corner with a clipped
   outline is an accessibility regression, not a style. */
.btn:focus-visible,
.site-nav__tab:focus-visible,
.lang-toggle:focus-visible,
.nav-toggle:focus-visible,
.chip:focus-visible,
.game-card__badge:focus-visible,
.games-pointer__link:focus-visible,
.mslot__play:focus-visible,
.field textarea:focus-visible,
.check:focus-within {
  outline: none;
  box-shadow: inset 0 0 0 3px var(--z-focus-ring);
}

::selection { background: var(--z-gold); color: var(--z-jam); }

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

.skip-link {
  position: absolute;
  inset-block-start: var(--z-space-2);
  inset-inline-start: var(--z-space-2);
  z-index: var(--z-layer-toast);
  padding: var(--z-space-3) var(--z-space-4);
  background: var(--z-cta-bg);
  color: var(--z-cta-fg);
  border-radius: var(--z-radius-md);
  font-weight: var(--z-weight-bold);
  transform: translateY(-200%);
}
.skip-link:focus { transform: translateY(0); }

/* ---- layout primitives ------------------------------------ */
.container {
  inline-size: 100%;
  max-inline-size: var(--z-container-max);
  margin-inline: auto;
  padding-inline: var(--z-container-pad-inline);
}
.container--narrow { max-inline-size: var(--z-container-narrow); }

.section {
  padding-block: var(--z-section-pad-block);
  position: relative;
}
/* NOTE: this class deliberately does NOT set `content-visibility: auto`.
 *
 * It did, with `contain-intrinsic-size: auto 640px`. Measured on the real
 * page, the placeholder height was roughly half the true section height —
 * the #contact anchor resolved to 8492px while its actual position was
 * 15318px. Two consequences, both disqualifying on a funnel page where
 * every nav item and the header CTA is an in-page anchor:
 *   1. anchor links landed in the wrong place
 *   2. scroll position shifted as each section rendered (large CLS)
 *
 * The class is kept as a hook. If content-visibility is revisited, every
 * section needs a measured contain-intrinsic-size, not a shared guess.
 */
.section--deferred { /* intentionally empty — see note above */ }

/* Horizontal pin-scrub primitive (.process / .formats — pin-scroll.js).
   The whole rule set: position:relative with no offsets has zero visible
   effect on its own — everything else that makes the pin work lives behind
   [data-pin-state], written only once JS has successfully measured. This
   line alone is what a no-JS or reduced-motion page renders. */
[data-pin] { position: relative; }

.stack > * + * { margin-block-start: var(--z-space-4); }
.stack--lg > * + * { margin-block-start: var(--z-space-8); }

/* LABEL style from the system: DM Mono, 11px, .2em tracking, uppercase.
   Jam ink — never Melt Gold, which fails contrast on a yellow field. */
.eyebrow {
  font-family: var(--z-font-label);
  /* Fluid, not fixed. A label locked at 12px is proportionally huge on a
     phone and proportionally tiny on a 16" screen; interpolating between
     390 and 1600 keeps it reading at the same optical weight on both.
     Tracking eases from .2em to .18em because wide tracking at a larger
     size starts pulling the word apart. */
  font-size: clamp(0.75rem, 0.71rem + 0.14vw, 0.875rem);
  font-weight: var(--z-weight-medium);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--z-fg-muted);
}
/* Arabic joins; tracking prises the joins apart, and at label size that is
   the difference between a word and a row of letters. uppercase is a no-op
   for Arabic too, so it goes with it. The Latin eyebrows are untouched. */
html[lang="ar"] .eyebrow {
  letter-spacing: 0;
  text-transform: none;
}

.lede {
  /* Also fluid. This is the line that carries every section's argument, and
     at a fixed 17px it read as body copy rather than as a deck. Growing to
     22px on a wide screen is what makes the page breathe without touching
     any headline. Measure widened 56ch -> 66ch to match. */
  font-size: clamp(1.0625rem, 1rem + 0.35vw, 1.375rem);
  line-height: 1.65;
  color: var(--z-fg-muted);
  max-inline-size: 66ch;
}
html[lang="ar"] .lede { line-height: 1.85; }   /* Arabic needs the extra room */

/* ---- buttons ----------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--z-space-2);
  padding-block: var(--z-space-3);
  padding-inline: var(--z-space-8);
  /* THE CUT. clip-path genuinely clips `outline`, so focus is drawn with an
     INSET box-shadow instead — inset shadows render inside the shape, which
     is the one focus treatment a chamfer cannot eat. See :focus-visible. */
  border-radius: 2px;
  clip-path: var(--z-chamfer);
  font-weight: var(--z-weight-bold);
  font-size: var(--z-text-small);
  min-block-size: var(--z-hit-min);   /* "hit target never under 44px" */
  transition: transform var(--z-dur-fast) var(--z-ease-out),
              background-color var(--z-dur-fast) var(--z-ease-out),
              border-color var(--z-dur-fast) var(--z-ease-out);
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }

/* One Jam button per view. Melt Gold is the second action. Ghost tertiary. */
.btn--primary { background: var(--z-cta-bg); color: var(--z-cta-fg); }
.btn--primary:hover { background: var(--z-jam); filter: brightness(1.25); }

.btn--gold { background: var(--z-cta2-bg); color: var(--z-cta2-fg); }
.btn--gold:hover { background: var(--z-gold); filter: brightness(1.06); }

.btn--ghost {
  border: 2px solid var(--z-hairline-strong);
  color: var(--z-fg);
}
.btn--ghost:hover { border-color: var(--z-jam); }

/* ---- cards -------------------------------------------------- */
.card {
  background: var(--z-card-bg);
  border: 1px solid var(--z-card-border);
  /* The cut, same depth as the buttons — that fixed depth is the point:
     a 44px toggle and a 400px card read as one decision, not two angles.
     The 1px border is clipped along the two diagonals, which is the same
     trade every chamfered control on the site already makes. */
  border-radius: 2px;
  clip-path: var(--z-chamfer);
  padding: var(--z-space-6);
  transition: background-color var(--z-dur-base) var(--z-ease-out),
              border-color var(--z-dur-base) var(--z-ease-out);
}

/* ============================================================
   Motion utilities

   CONTRACT: base state is ALWAYS VISIBLE. JS adds `.js-reveal`
   to the <html> element only when motion is permitted, which is
   what arms the hidden-then-reveal state. So no-JS and
   reduced-motion users both see fully rendered content, and a
   JS failure can never blank the page.
   ============================================================ */
html.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(var(--z-space-6));
  transition: opacity var(--z-dur-reveal) var(--z-ease-out),
              transform var(--z-dur-reveal) var(--z-ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}
html.js-reveal [data-reveal="inline"] {
  transform: translateX(var(--z-slide-x));
}
/* Cards in a staggered grid settle in as well as rise — the scale is small
   on purpose, because a card that grows too far reads as a popup rather
   than as content arriving. */
html.js-reveal [data-stagger] [data-reveal] {
  /* Deliberately louder than the page-level reveal. A card in a grid is one
     of eight competing for the same glance, and at 24px/0.97 the movement
     was below the threshold at which a scrolling eye registers it at all. */
  transform: translateY(var(--z-space-12)) scale(.92);
}
html.js-reveal [data-stagger] [data-reveal].is-visible {
  transform: none;
}
html.js-reveal [data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  html.js-reveal [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
