/* ============================================================
 * landing-page-rework / TASK_02 — landing surface stylesheet
 * ------------------------------------------------------------
 * Public marketing page mounted at ``/`` (anonymous visitors).
 * The play shell at ``/play/`` keeps loading ``game.css`` only —
 * this file is loaded ONLY by ``frontend/templates/game/landing.html``.
 *
 * Section order mirrors the template DOM order so a CSS read flows
 * top-to-bottom alongside the markup:
 *   tokens → reset → typography → buttons → layout shell →
 *   hero → pillars → mechanics → races → faq → footer →
 *   responsive (≤960 → ≤640 → ≤380) → reduced-motion respect.
 *
 * Tokens reused from ``game.css :root`` — surface / ink / accent /
 * radius / spacing / typography ladders are inherited verbatim. The
 * landing-only tokens below extend (do NOT shadow) the play surface
 * palette — the only new vocabulary is the parchment-vellum band and
 * the marketing-sized type scale (h1 56 / h2 32 / h3 22) the play
 * shell does not need.
 * ============================================================ */

:root {
    /* ── Marketing-only tokens. Extends ``game.css`` :root. ── */

    /* Display type scale — the play UI tops out at h1 24 px which is
       too small for a hero tagline. The landing surface introduces a
       four-tier display ladder; ``--lfs-display`` is the hero band
       (~clamp 36 → 56 px on viewport width). */
    --lfs-display:    clamp(2.25rem, 4.5vw, 3.5rem);   /* hero h1 */
    --lfs-h2:         clamp(1.625rem, 2.6vw, 2.25rem); /* section heading */
    --lfs-h3:         clamp(1.125rem, 1.5vw, 1.375rem);/* card title */
    --lfs-lead:       clamp(1rem, 1.3vw, 1.125rem);    /* subheading + lead p */

    /* Parchment-vellum band — used as the hero overlay + section
       backdrop tint so the marketing surface feels warmer than the
       cool atelier UI without inventing new colours from scratch. */
    --landing-vellum:        rgba(235, 227, 210, 0.04);
    --landing-vellum-strong: rgba(235, 227, 210, 0.07);

    /* Hero scrim — a vertical gradient laid over the panorama so the
       tagline + CTAs stay legible on bright screenshots. Tones ground
       on the existing surface variants, not new hex literals. */
    --landing-scrim: linear-gradient(
        180deg,
        rgba(21, 18, 15, 0.50) 0%,
        rgba(21, 18, 15, 0.72) 60%,
        rgba(21, 18, 15, 0.90) 100%
    );

    /* Primary CTA gradient — accent-strong → accent so the "Зареєструватися"
       button reads as the page's main affordance without inventing a new
       brand colour. */
    --landing-cta-primary: linear-gradient(
        180deg, var(--accent-strong) 0%, var(--accent) 100%
    );

    /* Per-page max content width. Wide enough for a 4-card pillar row
       at desktop; clamped to 1200 px so very wide monitors don't
       stretch the layout into squint-mode. */
    --landing-shell-max: 1200px;
}

/* ─── Reset (lightweight; the play surface's full reset is too aggressive
       for a content page). Just the universal box model + overflow rules
       so flex / grid alignment is predictable. */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
    margin: 0;
    background: var(--surface);
    color: var(--ink);
    font-family: var(--font-body);
    font-size: var(--fs-body);
    line-height: var(--lh-body);
    -webkit-text-size-adjust: 100%;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* The landing body is scrollable — the play shell hides overflow on
   <body>. Override that here so the marketing surface scrolls. */
html, body { overflow-x: hidden; overflow-y: auto; height: auto; min-height: 100%; }

img { max-width: 100%; height: auto; display: block; }
a { color: var(--accent-strong); text-decoration: none; }
a:hover, a:focus { color: var(--accent); }

/* ─── Typography (display ladder) ─────────────────────────── */
.landing-h1, .landing-h2, .landing-h3 {
    font-family: var(--font-display);
    font-weight: 600;
    line-height: var(--lh-tight);
    letter-spacing: 0.01em;
    color: var(--ink);
    margin: 0;
}
.landing-h1 { font-size: var(--lfs-display); }
.landing-h2 { font-size: var(--lfs-h2); }
.landing-h3 { font-size: var(--lfs-h3); }
.landing-lead {
    font-size: var(--lfs-lead);
    line-height: var(--lh-loose);
    color: var(--ink-mute);
    margin: 0;
}

/* ─── Buttons (CTA + secondary). Reuses ``--tap`` minimums from
       game.css for touch targets. ──────────────────────────── */
.landing-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: var(--tap);
    padding: 0.875rem 1.75rem;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-family: var(--font-body);
    font-size: 1rem;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    transition: transform 120ms, background 120ms, border-color 120ms;
    text-align: center;
    white-space: nowrap;
}
.landing-btn-primary {
    background: var(--landing-cta-primary);
    color: #1B1814;     /* dark ink on warm gradient — atlas contrast */
    border-color: var(--accent-strong);
}
.landing-btn-primary:hover, .landing-btn-primary:focus {
    transform: translateY(-1px);
    color: #1B1814;
}
.landing-btn-secondary {
    background: transparent;
    color: var(--ink);
    border-color: var(--border-firm);
}
.landing-btn-secondary:hover, .landing-btn-secondary:focus {
    border-color: var(--accent-strong);
    color: var(--accent-strong);
}
.landing-btn-large {
    padding: 1.125rem 2.25rem;
    font-size: 1.125rem;
}

/* ─── Layout shell ─────────────────────────────────────────── */
.landing-shell {
    width: 100%;
    overflow: hidden;
}
.landing-section {
    padding: clamp(3rem, 6vw, 5rem) 1.25rem;
}
.landing-container {
    max-width: var(--landing-shell-max);
    margin: 0 auto;
    width: 100%;
}
.landing-section + .landing-section {
    border-top: 1px solid var(--rule);
}
.landing-section-heading {
    text-align: center;
    margin-bottom: clamp(1.5rem, 3vw, 2.5rem);
}
.landing-section-heading .landing-lead {
    margin-top: 0.75rem;
    max-width: 720px;
    margin-left: auto;
    margin-right: auto;
}

/* Anchor offset — when the secondary "Подивитись як це працює" CTA
   scrolls to the mechanics section, give it breathing room from the
   top edge so the section heading is not flush against the viewport. */
[id^="landing-"] { scroll-margin-top: 2rem; }

/* ─── Hero (AC04) ──────────────────────────────────────────── */
.landing-hero {
    position: relative;
    min-height: clamp(520px, 78vh, 720px);
    display: flex;
    align-items: center;
    padding: clamp(4rem, 8vw, 6rem) 1.25rem;
    overflow: hidden;
    isolation: isolate;
    background: var(--surface-sunken);
}
.landing-hero-bg {
    position: absolute;
    inset: 0;
    z-index: -2;
    background: radial-gradient(
        circle at 50% 35%,
        var(--surface-elev) 0%,
        var(--surface) 60%,
        var(--surface-sunken) 100%
    );
}
.landing-hero-bg picture, .landing-hero-bg img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center 30%;
}
/* TASK_03 — parallax bg lifts -24 px on full hero scroll-out via
   ``frontend/static/js/landing.js``. ``will-change: transform``
   pins the layer onto the compositor so the per-frame GPU paint
   stays cheap. The transform itself is written by the JS rAF
   loop; defaulting it to identity keeps the layer aligned when JS
   is disabled (AC03 — static skeleton remains readable). */
.landing-hero-bg[data-hero-panorama] {
    transform: translate3d(0, 0, 0);
    transition: transform 80ms linear;
    will-change: transform;
}
/* When the <img> ``onerror`` handler removes the src (panorama
   asset missing in dev / pre-imagery-gen-pass), the empty <img>
   would otherwise paint a broken-image glyph. Hide it so the
   ``--landing-scrim`` + radial-gradient backdrop carry the hero
   alone. */
.landing-hero-bg img[src=""], .landing-hero-bg img:not([src]) {
    display: none;
}
.landing-hero::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: var(--landing-scrim);
    pointer-events: none;
}
.landing-hero-content {
    max-width: 760px;
    margin: 0 auto;
    text-align: center;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    align-items: center;
}
.landing-hero-tagline {
    text-shadow: 0 2px 24px rgba(0, 0, 0, 0.55);
}
/* TASK_03 — small all-caps eyebrow above the tagline. Reads as a
   marketing surface from the first glance without competing with
   the H1 hierarchy. Tracks `--accent-strong` so the hue ladders
   into the primary CTA. */
.landing-hero-eyebrow {
    font-family: var(--font-display);
    font-size: var(--fs-label);
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--accent-strong);
    margin: 0;
    text-shadow: 0 1px 12px rgba(0, 0, 0, 0.55);
}
/* TASK_03 — reassurance meta line under the CTA row ("Безкоштовно ·
   Без завантажень …"). Quiet ink-faint colour so it sits visually
   below the CTAs, not above. */
.landing-hero-meta {
    font-size: var(--fs-label);
    color: var(--ink-faint);
    margin: 0.5rem 0 0;
    letter-spacing: 0.02em;
}
.landing-hero-cta-row {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    justify-content: center;
    margin-top: 1rem;
}

/* ─── Pillars (AC05) ───────────────────────────────────────── */
.landing-pillars {
    background: var(--landing-vellum);
}
.landing-pillars-grid {
    display: grid;
    gap: 1.25rem;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.landing-pillar-card {
    background: var(--surface-elev);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg);
    padding: 1.5rem 1.25rem 1.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    transition: border-color 200ms, transform 200ms;
}
.landing-pillar-card:hover {
    border-color: var(--accent-strong);
    transform: translateY(-2px);
}
.landing-pillar-illustration {
    aspect-ratio: 16 / 10;
    background: var(--surface-sunken);
    border-radius: var(--radius-md);
    overflow: hidden;
    border: 1px solid var(--rule);
    position: relative;
}
.landing-pillar-illustration picture {
    display: block;
    width: 100%;
    height: 100%;
}
.landing-pillar-illustration img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* TASK_04 — subtle hover micro-animation: the illustration
       lifts forward (1.04 scale) when the card is hovered or
       focused. ``transform-origin: center`` keeps the focal point
       centred so the crop doesn't drift. ``will-change: transform``
       pins the layer onto the GPU compositor so the per-frame
       paint stays cheap. The transition matches the card's
       border-color easing so the two effects feel coupled. */
    transform-origin: center;
    transition: transform 320ms ease-out;
    will-change: transform;
}
.landing-pillar-card:hover .landing-pillar-illustration img,
.landing-pillar-card:focus-within .landing-pillar-illustration img {
    transform: scale(1.04);
}
/* When the <img> ``onerror`` handler removes the src (pillar asset
   missing in dev / pre-imagery-gen-pass + tile fallback also
   missing), the empty <img> would otherwise paint a broken-image
   glyph. Hide it so the ``--surface-sunken`` backdrop carries the
   illustration slot alone. Mirrors the ``.landing-hero-bg img:not([src])``
   gate from TASK_03 hero polish. */
.landing-pillar-illustration img[src=""],
.landing-pillar-illustration img:not([src]) {
    display: none;
}
.landing-pillar-title {
    margin: 0.25rem 0 0;
}
.landing-pillar-description {
    color: var(--ink-mute);
    line-height: var(--lh-body);
    margin: 0;
}

/* ─── Mechanics walkthrough (AC06) ─────────────────────────── */
.landing-mechanics {
    background: var(--surface);
}
.landing-mechanics-list {
    display: flex;
    flex-direction: column;
    gap: clamp(2rem, 5vw, 4rem);
    counter-reset: mechanic;
}
.landing-mechanic {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: clamp(1.5rem, 4vw, 3rem);
    align-items: center;
    scroll-margin-top: 2rem;
}
.landing-mechanic:nth-child(even) {
    direction: rtl;          /* swap visual order */
}
.landing-mechanic > * {
    direction: ltr;          /* re-set children so text reads LTR */
}
.landing-mechanic-illustration {
    aspect-ratio: 16 / 10;
    background: var(--surface-elev);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg);
    overflow: hidden;
}
.landing-mechanic-illustration picture, .landing-mechanic-illustration img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.landing-mechanic-body {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}
.landing-mechanic-step {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: var(--radius-pill);
    background: var(--accent);
    color: #1B1814;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 1.125rem;
    flex-shrink: 0;
}
.landing-mechanic-title {
    margin: 0;
}
.landing-mechanic-description {
    color: var(--ink-mute);
    line-height: var(--lh-body);
    margin: 0;
    max-width: 56ch;
}

/* TASK_05 — scroll-reveal fade-up. The JS binder in landing.js
   adds ``.is-visible`` once the panel crosses the 25% intersection
   threshold; the CSS handles the actual animation. The default
   resting state pre-reveal is ``opacity: 0`` + a 24 px upward
   translate so the entrance animation reads as the panel sliding
   into view rather than just popping. ``will-change: opacity,
   transform`` pins the GPU layer so the per-panel transform never
   triggers a full repaint. The reduced-motion clause at the
   bottom of this file pins both the opacity + transform back to
   the visible state so visitors who opt out of motion see the
   panels at full opacity from the first paint (defence-in-depth
   alongside the JS-side gate that skips the observer entirely). */
.landing-mechanic[data-mechanic-reveal] {
    opacity: 0;
    transform: translate3d(0, 24px, 0);
    transition: opacity 480ms ease-out, transform 480ms ease-out;
    will-change: opacity, transform;
}
.landing-mechanic[data-mechanic-reveal].is-visible {
    opacity: 1;
    transform: translate3d(0, 0, 0);
}

/* TASK_05 — graceful no-JS / no-IntersectionObserver fallback.
   Without ``.is-visible``, the panels would stay invisible
   forever for legacy browsers that ship without
   ``IntersectionObserver`` (the JS binder skips the observation
   entirely in that case). The ``@supports not`` query is the
   modern CSS hook; we cannot feature-detect ``IntersectionObserver``
   from CSS directly, so we use the closest companion API
   (``contain``) as a proxy: every browser with
   ``IntersectionObserver`` also ships ``contain``. The fallback
   prints the panels at full opacity so a JS-disabled visitor still
   sees the walkthrough. */
@supports not (contain: layout) {
    .landing-mechanic[data-mechanic-reveal] {
        opacity: 1;
        transform: none;
    }
}

/* TASK_05 — illustration entrance accent. When the panel reveals,
   nudge the illustration in from the side opposite the body block
   (alternating per ``:nth-child(even)`` because the parent grid is
   already RTL-flipped on even rows). Keeps the reveal feeling
   choreographed without inventing a new animation primitive. The
   reduced-motion clause pins this back to identity, same as the
   parent panel. */
.landing-mechanic[data-mechanic-reveal] .landing-mechanic-illustration {
    transform: translate3d(-16px, 0, 0);
    transition: transform 560ms ease-out 80ms;
    will-change: transform;
}
.landing-mechanic[data-mechanic-reveal]:nth-child(even) .landing-mechanic-illustration {
    transform: translate3d(16px, 0, 0);
}
.landing-mechanic[data-mechanic-reveal].is-visible .landing-mechanic-illustration {
    transform: translate3d(0, 0, 0);
}

/* ─── Race showcase (AC07) ─────────────────────────────────── */
/*
 * TASK_06 — the showcase is now a real grid of <details> cards: the
 * <summary> carries the portrait + UA name (collapsed shape), and the
 * body underneath holds the lore + lineage / terrain chips. Click on
 * the summary toggles open without JS (AC03 contract). When a card is
 * open it spans two columns on desktop so the lore line has room to
 * breathe; the grid auto-flow keeps neighbour cards intact.
 */
.landing-races {
    background: var(--landing-vellum);
}
.landing-races-grid {
    display: grid;
    gap: 0.75rem;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    /* ``dense`` lets neighbour cells reflow into the gap left by an
       open card, so the grid never paints a hole when a row is mid-
       expansion. ``align-items: start`` stops every card from being
       stretched to the row's tallest sibling — closed cards stay
       compact even when one open card pushes the row taller. */
    grid-auto-flow: dense;
    align-items: start;
}
.landing-race-card {
    position: relative;
    background: var(--surface-elev);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-md);
    overflow: hidden;
    transition: border-color 160ms, transform 160ms;
    padding: 0;
    /* ``<details>`` ships a default disclosure-triangle marker on the
       summary — we hide it and lean on the whole portrait card as the
       affordance instead. Browser support: ``::-webkit-details-marker``
       for Safari + ``list-style: none`` for Firefox / Chromium. */
    list-style: none;
}
.landing-race-card[open] {
    border-color: var(--accent-strong);
    /* Open cards span two columns at desktop so the lore + chips have
       a comfortable reading width; the auto-fill grid + dense flow
       handles neighbour reflow. Phones (≤640) get a 1-column layout
       and the card keeps its single-column footprint. */
    grid-column: span 2;
}
.landing-race-card:hover:not([open]),
.landing-race-card:focus-within:not([open]) {
    border-color: var(--accent-strong);
    transform: translateY(-2px);
}
.landing-race-summary {
    /* The summary is the visual surface for the collapsed shape — a
       3/4 aspect-ratio cell with the portrait flexing to fill and the
       name banded along the bottom. ``aspect-ratio`` keeps every
       collapsed card the same height. */
    display: flex;
    flex-direction: column;
    aspect-ratio: 3 / 4;
    cursor: pointer;
    /* AC03 — no outline-suppression for keyboard users. We re-paint
       the focus ring via ``focus-visible`` further down. */
    outline: none;
}
.landing-race-summary::-webkit-details-marker { display: none; }
.landing-race-summary::marker { content: ''; }
.landing-race-summary:focus-visible {
    box-shadow: inset 0 0 0 2px var(--accent-strong);
}
.landing-race-portrait {
    flex: 1;
    width: 100%;
    height: auto;
    object-fit: cover;
    object-position: top center;
    background: var(--surface-sunken);
}
/* Defence-in-depth — when the inline ``onerror`` strips ``src`` the
   broken-image glyph is gone but the empty <img> would otherwise paint
   a 0×0 box. ``min-height`` keeps the slot proportional so the name
   banner sits where the visitor expects. */
.landing-race-portrait:not([src]),
.landing-race-portrait[src=''] {
    min-height: 100px;
    background: var(--surface-sunken);
}
.landing-race-name {
    padding: 0.375rem 0.5rem;
    font-size: var(--fs-label);
    font-weight: 600;
    color: var(--ink);
    text-align: center;
    background: var(--surface-sunken);
    border-top: 1px solid var(--rule);
    line-height: 1.2;
    /* Two-line clamp so long names ("Lightfoot Halfling") don't push
       the card height around. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.landing-race-body {
    padding: 0.875rem 1rem 1rem;
    background: var(--landing-vellum-strong);
    border-top: 1px solid var(--rule);
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
}
.landing-race-lore {
    margin: 0;
    color: var(--ink);
    font-size: var(--fs-body);
    line-height: 1.45;
}
.landing-race-chips {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.landing-race-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 0.375rem;
    padding: 0.25rem 0.625rem;
    background: var(--surface-sunken);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm);
    font-size: var(--fs-label);
}
.landing-race-chip-label {
    color: var(--ink-mute);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: 0.75em;
    font-weight: 600;
}
.landing-race-chip-value {
    color: var(--ink);
    font-weight: 500;
}

/* ─── FAQ (AC08) ───────────────────────────────────────────── */
.landing-faq {
    background: var(--surface);
}
/* TASK_07 — section intro line under the heading. Mirrors the
   ``.landing-races .landing-lead`` shape; capped width keeps the
   line legible (~60 chars) on desktop without crowding the wider
   .landing-section-heading layout. */
.landing-faq-intro {
    max-width: 640px;
    margin: 0.5rem auto 0;
}
.landing-faq-list {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    max-width: 800px;
    margin: 0 auto;
}
.landing-faq-item {
    background: var(--surface-elev);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-md);
    overflow: hidden;
}
.landing-faq-item[open] {
    border-color: var(--border-firm);
}
.landing-faq-question {
    padding: 1rem 1.25rem;
    font-family: var(--font-display);
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--ink);
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    user-select: none;
}
.landing-faq-question::-webkit-details-marker { display: none; }
.landing-faq-question::after {
    content: "+";
    font-size: 1.5rem;
    line-height: 1;
    color: var(--accent-strong);
    transition: transform 200ms;
    flex-shrink: 0;
}
.landing-faq-item[open] .landing-faq-question::after {
    content: "−";
    transform: none;
}
.landing-faq-answer {
    padding: 0 1.25rem 1.125rem;
    color: var(--ink-mute);
    line-height: var(--lh-body);
    margin: 0;
}

/* ─── Footer CTA + links (AC09 + AC10) ─────────────────────── */
.landing-footer {
    background: var(--surface-sunken);
    border-top: 1px solid var(--border-soft);
    padding: clamp(3.5rem, 6vw, 5rem) 1.25rem 2.5rem;
}
.landing-footer-cta {
    text-align: center;
    max-width: 720px;
    margin: 0 auto 2.5rem;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    align-items: center;
}
/* TASK_07 — small all-caps eyebrow above the closing-pitch H2.
   Mirrors ``.landing-hero-eyebrow`` (TASK_03) so the footer reads
   as intentionally framed rather than a stray heading. Ships with
   ``letter-spacing: 0.16em`` + ``text-transform: uppercase`` for
   the marketing-marker look without a custom font weight. */
.landing-footer-eyebrow {
    margin: 0;
    color: var(--accent-strong);
    font-family: var(--font-display);
    font-size: var(--fs-label);
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}
/* TASK_07 — reassurance copy under the CTA button. Reads as a
   tertiary line ("Реєстрація — 30 секунд · …") so the close pitch
   stays visually anchored to the primary CTA without competing with
   it for attention. Stays muted (``--ink-faint``) so the CTA remains
   the dominant glance target. */
.landing-footer-cta-meta {
    margin: 0.25rem 0 0;
    color: var(--ink-faint);
    font-size: var(--fs-label);
}
/* TASK_07 — wrapping block for the links row + its heading. The
   block sits below the CTA + meta line and above the copyright
   meta line. Border-top separator marks the visual transition from
   "closing pitch" to "footer chrome". */
.landing-footer-links-block {
    max-width: var(--landing-shell-max);
    margin: 0 auto;
    padding-top: 1.5rem;
    border-top: 1px solid var(--rule);
    text-align: center;
}
.landing-footer-links-heading {
    margin: 0 0 0.75rem;
    font-family: var(--font-display);
    font-size: var(--fs-label);
    font-weight: 600;
    color: var(--ink-mute);
    letter-spacing: 0.04em;
}
.landing-footer-links {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    justify-content: center;
    align-items: center;
    list-style: none;
    margin: 0;
    padding: 0;
    color: var(--ink-faint);
    font-size: var(--fs-label);
}
.landing-footer-link {
    color: var(--ink-mute);
    text-decoration: none;
    padding: 0.25rem 0.5rem;
    border-radius: var(--radius-sm);
    transition: color 120ms;
}
.landing-footer-link:hover, .landing-footer-link:focus {
    color: var(--accent-strong);
}
/* TASK_07 — placeholder rows (Discord + GitHub until the channels
   are stood up). Carries ``data-link-status="placeholder"`` +
   ``aria-disabled="true"`` so the affordance reads as "coming
   soon" both visually and at the a11y layer. The cursor flips to
   ``not-allowed``, the colour stays muted, and hover keeps the
   muted state (instead of flipping to ``--accent-strong`` like a
   live link). */
.landing-footer-link[data-link-status="placeholder"] {
    color: var(--ink-faint);
    cursor: not-allowed;
    opacity: 0.7;
}
.landing-footer-link[data-link-status="placeholder"]:hover,
.landing-footer-link[data-link-status="placeholder"]:focus {
    color: var(--ink-faint);
    text-decoration: line-through;
    text-decoration-color: var(--ink-faint);
    text-decoration-thickness: 1px;
}
/* world-rotation-meta / TASK_10 — public leaderboard footer link
   (AC-F4). Sits in a dedicated row above the contact list so the
   ``LandingFooterLinksTest`` AC10 sentinel's ``data-footer-link=``
   count == 3 contract stays intact. */
.landing-footer-leaderboard {
    text-align: center;
    margin: 0 0 1.5rem;
    font-size: var(--fs-body);
}
.landing-footer-leaderboard-link {
    color: var(--accent-strong);
    text-decoration: none;
    padding: 0.25rem 0.5rem;
    border-radius: var(--radius-sm);
    transition: color 120ms;
}
.landing-footer-leaderboard-link:hover,
.landing-footer-leaderboard-link:focus {
    color: var(--accent);
    text-decoration: underline;
}
.landing-footer-meta {
    text-align: center;
    color: var(--ink-faint);
    font-size: var(--fs-label);
    margin-top: 1.25rem;
}

/* ─── Responsive ─── ≤960 ──────────────────────────────────── */
@media (max-width: 960px) {
    .landing-mechanic {
        grid-template-columns: 1fr;
        gap: 1.25rem;
    }
    .landing-mechanic:nth-child(even) {
        direction: ltr;
    }
}

/* ─── Responsive ─── ≤640 (phones) ─────────────────────────── */
@media (max-width: 640px) {
    .landing-section {
        padding: clamp(2.5rem, 8vw, 3.5rem) 1rem;
    }
    .landing-hero {
        min-height: clamp(440px, 90vh, 600px);
        padding: clamp(3rem, 8vw, 4rem) 1rem;
    }
    .landing-hero-cta-row {
        flex-direction: column;
        width: 100%;
        max-width: 320px;
    }
    .landing-hero-cta-row .landing-btn {
        width: 100%;
    }
    .landing-pillars-grid {
        grid-template-columns: 1fr;
    }
    .landing-races-grid {
        grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
        gap: 0.5rem;
    }
    /* TASK_06 — phones get a tighter race grid (3 / 4 columns
       depending on viewport width) and an open card spans the full
       row so the lore + chips have room without squeezing siblings
       into a thin band. */
    .landing-race-card[open] {
        grid-column: 1 / -1;
    }
    .landing-footer-links {
        flex-direction: column;
        gap: 0.5rem;
    }
}

/* ─── Responsive ─── ≤380 (ultra-narrow) ───────────────────── */
@media (max-width: 380px) {
    .landing-races-grid {
        grid-template-columns: repeat(3, 1fr);
    }
    .landing-race-card[open] {
        grid-column: 1 / -1;
    }
    .landing-h1 { font-size: 1.875rem; }
    .landing-h2 { font-size: 1.375rem; }
}

/* ─── Reduced-motion respect (mirrors game.css convention). ── */
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    .landing-pillar-card,
    .landing-pillar-illustration img,
    .landing-race-card,
    .landing-btn,
    .landing-faq-question::after,
    .landing-hero-bg[data-hero-panorama] {
        transition: none;
    }
    .landing-pillar-card:hover,
    .landing-race-card:hover,
    .landing-race-card:hover:not([open]),
    .landing-race-card:focus-within:not([open]),
    .landing-btn:hover {
        transform: none;
    }
    /* TASK_04 — pin the pillar illustration zoom to identity scale
       so the hover micro-animation never fires for visitors who have
       opted out of motion. Mirrors the parallax pin below. */
    .landing-pillar-card:hover .landing-pillar-illustration img,
    .landing-pillar-card:focus-within .landing-pillar-illustration img {
        transform: none !important;
    }
    /* TASK_03 — pin the parallax bg layer at the identity transform
       so a JS-bound rAF write that ignored the OS preference still
       lands as a no-op on the compositor. landing.js double-checks
       the ``prefers-reduced-motion`` matcher before binding, but the
       CSS-side override is the safety net. */
    .landing-hero-bg[data-hero-panorama] {
        transform: translate3d(0, 0, 0) !important;
    }
    /* TASK_05 — pin the mechanics scroll-reveal at the visible
       resting state so visitors who opted out of motion still see
       the walkthrough at full opacity from the first paint. The JS
       binder skips the observer entirely in this case, so the
       ``.is-visible`` class is never added — without this override
       the panels would stay at ``opacity: 0`` indefinitely. The
       illustration nudge selectors share the same fallback so the
       choreography never fires. */
    .landing-mechanic[data-mechanic-reveal] {
        opacity: 1 !important;
        transform: none !important;
        transition: none;
    }
    .landing-mechanic[data-mechanic-reveal] .landing-mechanic-illustration {
        transform: none !important;
        transition: none;
    }
}
