/* Landing page (/landing).
   Hand-written, no framework, no build step. Every class is prefixed `ld-` so this file can
   never collide with the SPA's stylesheets even if both ever load on the same document.

   Loaded after hero-cinematic.css and pet-runner.css on purpose: those two own the video layers
   and the fox; this file owns everything the visitor reads — and may override either.

   Contents
     1. Tokens, fonts, base
     2. Type and shared pieces (eyebrow, headings, buttons, plaque, reveals)
     3. Header
     4. Hero
     5. Beta period + countdown
     6. Community — the Discord send-off (the fox's home)
     7. Team
     8. Footer
     9. Narrow viewports, reduced motion                                                    */


/* ── 1. Tokens, fonts, base ─────────────────────────────────────────────────────────────── */

/* The site's display face, cut down to Latin (28 KB against the 1 MB Korean original) so the
   headline is set in it from the first paint even on a phone. The full file stays as fallback. */
@font-face {
    font-family: "BM EULJIRO";
    src: url("../fonts/bm-euljiro-latin.woff2") format("woff2"),
         url("../../presentation/v1/fonts/BMEULJIRO.otf") format("opentype");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

:root {
    /* Surfaces — the site's own palette (Views/Presentation/v1/shared/_Styles.cshtml). */
    --ld-bg: #0d0a08;
    --ld-bg-deep: #080605;
    --ld-bg-raised: #1a1410;
    --ld-line: #2a2320;
    --ld-line-2: #3d3229;

    /* Ink */
    --ld-text: #f1e9dc;
    --ld-text-2: #d4b896;
    --ld-text-dim: #9a8261;   /* the site's #8b7355, lifted to clear 4.5:1 on the cards */
    --ld-ink: #1a1410;

    /* Gold — the site's light gold, its primary, and the gradient its headings use. */
    --ld-gold: #e7c184;
    --ld-gold-2: #d4943a;
    --ld-gold-text: linear-gradient(180deg, #f3cd8d 0%, #e7c184 46%, #c9a66b 100%);

    /* Type */
    --ld-font-display: "BM EULJIRO", "Segoe UI", system-ui, -apple-system, sans-serif;
    --ld-font-text: "Manrope", "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;

    /* Layout */
    --ld-shell: min(1180px, 100% - 3rem);
    --ld-header-h: 66px;
    --ld-ease: cubic-bezier(0.16, 1, 0.3, 1);
}

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

html {
    scroll-behavior: smooth;
    -webkit-text-size-adjust: 100%;
}

.ld {
    margin: 0;
    background: var(--ld-bg);
    color: var(--ld-text-2);
    font-family: var(--ld-font-text);
    font-size: 16px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    overflow-x: hidden;
}

/* :where() keeps this at zero specificity, so a component's own colour — .ld-btn-primary,
   .ld-nav-icon — always wins over the inherited one. */
:where(.ld) a {
    color: inherit;
    text-decoration: none;
}

/* Zero specificity again (see the anchor rule above): a component's own width/height must
   always beat these. `.ld img { height: auto }` once silently overrode the beta plate's
   `height: 108%`, so the plate was never cover-cropped — just cut off at the section's foot. */
:where(.ld) img,
:where(.ld) svg {
    display: block;
}

:where(.ld) img {
    max-width: 100%;
    height: auto;
}

.ld :focus-visible {
    outline: 2px solid var(--ld-gold);
    outline-offset: 3px;
}

.ld [hidden] {
    display: none !important;
}

.ld-shell {
    width: var(--ld-shell);
    margin-inline: auto;
}

/* Anchored sections land below the fixed header, not under it. */
.ld [id] {
    scroll-margin-top: calc(var(--ld-header-h) + 12px);
}

/* The icon sprite: present in the DOM, never in the layout. */
.ld-sprite {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

/* Keyboard-only escape hatch past the nav. */
.ld-skip {
    position: absolute;
    left: -9999px;
    z-index: 100;
}

.ld-skip:focus {
    left: 1rem;
    top: 1rem;
    padding: 0.6rem 1rem;
    background: var(--ld-gold);
    color: var(--ld-ink);
    font-weight: 700;
    border-radius: 4px;
}


/* ── 2. Type and shared pieces ──────────────────────────────────────────────────────────── */

.ld-eyebrow {
    display: flex;
    align-items: center;
    gap: 0.8rem;
    margin: 0 0 1.1rem;
    font-size: 0.74rem;
    font-weight: 800;
    letter-spacing: 0.32em;
    text-transform: uppercase;
    color: var(--ld-gold-2);
}

.ld-eyebrow::before {
    content: "";
    flex: none;
    width: 30px;
    height: 1px;
    background: linear-gradient(90deg, var(--ld-gold-2), rgba(212, 148, 58, 0.25));
}

.ld-h2 {
    margin: 0 0 1.2rem;
    font-family: var(--ld-font-display);
    font-weight: 400;
    font-size: clamp(2.1rem, 1.2rem + 2.8vw, 3.4rem);
    line-height: 1.04;
    letter-spacing: 0.005em;
    color: var(--ld-text);
    text-wrap: balance;
}

.ld-gold-text {
    font-style: normal;
    background: var(--ld-gold-text);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
}

.ld-lead {
    margin: 0;
    max-width: 56ch;
    font-size: 1.0625rem;
    line-height: 1.75;
    color: var(--ld-text-2);
    text-wrap: pretty;
}

.ld-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.9rem;
    margin-top: 2rem;
}

/* Buttons.
   One pair of corners is cut on the diagonal — the game-UI chamfer — with clip-path, which
   also clips anything a border or a shadow would want to draw outside the shape. So the ghost
   variant is two layers: the element is the border colour, its ::before is the fill, inset by
   one pixel and cut to match. Labels are set in the display face, like the wordmark. */
.ld-btn {
    --ld-cut: 11px;
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    cursor: pointer;
    isolation: isolate;
    font-family: var(--ld-font-display);
    font-size: 1.05rem;
    font-weight: 400;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    line-height: 1;
    white-space: nowrap;
    clip-path: polygon(var(--ld-cut) 0, 100% 0, 100% calc(100% - var(--ld-cut)), calc(100% - var(--ld-cut)) 100%, 0 100%, 0 var(--ld-cut));
    transition: transform 0.18s ease, filter 0.18s ease;
}

.ld-btn > span {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.95rem 1.85rem 0.95rem;
}

.ld-btn-icon {
    width: 1.15em;
    height: 1.15em;
    fill: currentColor;
    flex: none;
}

.ld-btn:hover,
.ld-btn:focus-visible {
    transform: translateY(-2px);
}

.ld-btn:active {
    transform: translateY(0);
    transition-duration: 0.05s;
}

.ld-btn-primary {
    background: linear-gradient(180deg, #f2c67f 0%, #d9993f 48%, #b8792a 100%);
    color: var(--ld-ink);
    text-shadow: 0 1px 0 rgba(255, 236, 200, 0.35);
}

.ld-btn-primary::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.34), rgba(255, 255, 255, 0) 52%, rgba(0, 0, 0, 0.08) 100%);
    pointer-events: none;
}

.ld-btn-primary:hover,
.ld-btn-primary:focus-visible {
    filter: brightness(1.08) saturate(1.06);
}

.ld-btn-ghost {
    background: linear-gradient(180deg, rgba(231, 193, 132, 0.62), rgba(129, 108, 74, 0.5));
    color: var(--ld-text);
}

.ld-btn-ghost::before {
    content: "";
    position: absolute;
    inset: 1px;
    background: rgba(13, 10, 8, 0.72);
    clip-path: polygon(calc(var(--ld-cut) - 1px) 0, 100% 0, 100% calc(100% - var(--ld-cut) + 1px), calc(100% - var(--ld-cut) + 1px) 100%, 0 100%, 0 calc(var(--ld-cut) - 1px));
    transition: background 0.18s ease;
}

.ld-btn-ghost:hover::before,
.ld-btn-ghost:focus-visible::before {
    background: rgba(231, 193, 132, 0.14);
}

.ld-btn-ghost:hover,
.ld-btn-ghost:focus-visible {
    color: #fff6e6;
}

.ld-btn-lg {
    font-size: 1.18rem;
}

.ld-btn-lg > span {
    padding: 1.1rem 2.35rem 1.1rem;
}

.ld-btn-sm {
    --ld-cut: 8px;
    font-size: 0.92rem;
}

.ld-btn-sm > span {
    padding: 0.72rem 1.3rem 0.72rem;
}

/* The plaque: a framed panel with gold corner marks, used for the countdown. The slats
   texture sits on ::after and fades out towards the bottom — mask longhands only, never
   the shorthand (docs/landing-page.md, trap 5). */
.ld-plaque {
    position: relative;
    padding: clamp(1.75rem, 3.2vw, 2.6rem) clamp(1.25rem, 2.6vw, 2.25rem);
    border: 1px solid rgba(231, 193, 132, 0.24);
    background: linear-gradient(180deg, rgba(32, 25, 19, 0.95) 0%, rgba(17, 13, 10, 0.98) 100%);
    box-shadow:
        0 50px 90px -50px rgba(0, 0, 0, 0.95),
        inset 0 1px 0 rgba(231, 193, 132, 0.08);
}

.ld-plaque::after {
    content: "";
    position: absolute;
    inset: 0;
    background: url("../img/pattern-slats.svg") center top / 180px auto repeat;
    opacity: 0.6;
    pointer-events: none;
    -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
            mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
}

.ld-plaque > * {
    position: relative;
    z-index: 1;
}

.ld-plaque-corners {
    position: absolute;
    inset: 5px;
    z-index: 1;
    pointer-events: none;
    --ld-mark: linear-gradient(var(--ld-gold), var(--ld-gold));
    background:
        var(--ld-mark) left top / 16px 2px no-repeat,
        var(--ld-mark) left top / 2px 16px no-repeat,
        var(--ld-mark) right top / 16px 2px no-repeat,
        var(--ld-mark) right top / 2px 16px no-repeat,
        var(--ld-mark) left bottom / 16px 2px no-repeat,
        var(--ld-mark) left bottom / 2px 16px no-repeat,
        var(--ld-mark) right bottom / 16px 2px no-repeat,
        var(--ld-mark) right bottom / 2px 16px no-repeat;
}

/* Scroll-triggered reveals. Only hidden once the inline head script has confirmed JS is
   running, so with scripts off every section is simply there. */
.ld-js .ld-reveal {
    opacity: 0;
    transform: translateY(28px);
    transition: opacity 0.8s ease, transform 1s var(--ld-ease);
    transition-delay: calc(var(--i, 0) * 110ms);
}

.ld-js .ld-reveal.is-in {
    opacity: 1;
    transform: none;
}


/* ── 3. Header ─────────────────────────────────────────────────────────────────────────────
   The bar the rest of the site wears, ported from the app's navbar
   (js_apps/mt2-web/src/styles/NavBarStyle.css): a warm brown plate under the same
   nav-bar-back strip, opening at 0.80 plate / 0.70 texture and firming to 0.95 / 0.95 once the
   page has scrolled or the pointer is on it. The strip is 1920×60, so it is cover-cropped to
   span the bar at any width; narrow viewports anchor it right, where its detail sits.
   The header owns a stacking context (fixed + z-index), which is what lets the texture layer
   sit at z-index -1 and still paint above the plate — and below the brand and the nav. */

.ld-header {
    /* Chrome tones, kept here so the links, the icon and the toggle can't drift apart. */
    --ld-nav-ink: #c4b098;
    --ld-nav-ink-hi: #caa065;
    --ld-nav-edge: rgba(200, 168, 130, 0.3);

    position: fixed;
    inset: 0 0 auto;
    z-index: 50;
    height: var(--ld-header-h);
    background: rgba(54, 42, 32, 0.8);
    border-bottom: 1px solid rgba(100, 87, 73, 0.5);
    box-shadow: 0 12px 28px -20px rgba(0, 0, 0, 0.95);
    transition: background 0.35s ease, border-color 0.35s ease;
}

.ld-header::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background: url("../img/nav-bar-back.png") center / cover no-repeat;
    opacity: 0.7;
    transition: opacity 0.35s ease;
}

/* The one liberty taken over the app's flat border: a gold hairline along the foot, which is
   what keeps the bar from reading as a slab over the hero's bright first act. */
.ld-header::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 1px;
    pointer-events: none;
    background: linear-gradient(90deg, transparent, rgba(231, 193, 132, 0.34) 20%, rgba(231, 193, 132, 0.34) 80%, transparent);
}

/* The bar never changes size — only its weight. Both triggers, one look: the app firms its
   navbar on hover as well as on scroll. */
.ld-header:hover,
.ld-header.is-scrolled {
    background: rgba(54, 42, 32, 0.95);
    border-bottom-color: rgb(100, 87, 73);
    -webkit-backdrop-filter: blur(14px) saturate(1.2);
            backdrop-filter: blur(14px) saturate(1.2);
}

.ld-header:hover::before,
.ld-header.is-scrolled::before {
    opacity: 0.95;
}

.ld-header-inner {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    height: 100%;
}

.ld-brand {
    flex: none;
}

.ld-brand img {
    height: 40px;
    width: auto;
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.55));
}

.ld-nav {
    display: flex;
    align-items: center;
    gap: 2rem;
    margin-left: auto;
}

.ld-nav-links {
    display: flex;
    align-items: center;
    gap: 1.7rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ld-nav-links a {
    position: relative;
    display: inline-block;
    padding: 0.45rem 0;
    font-size: 0.84rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ld-nav-ink);
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
    transition: color 0.18s ease;
}

.ld-nav-links a::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    background: var(--ld-nav-ink-hi);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.28s var(--ld-ease);
}

.ld-nav-links a:hover,
.ld-nav-links a:focus-visible {
    color: var(--ld-nav-ink-hi);
}

.ld-nav-links a:hover::after,
.ld-nav-links a:focus-visible::after {
    transform: scaleX(1);
}

.ld-nav-actions {
    display: flex;
    align-items: center;
    gap: 0.9rem;
}

.ld-nav-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    border: 1px solid var(--ld-nav-edge);
    border-radius: 50%;
    background: rgba(30, 25, 20, 0.5);
    color: var(--ld-nav-ink);
    transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease, transform 0.18s ease;
}

.ld-nav-icon svg {
    width: 21px;
    height: 21px;
    fill: currentColor;
}

.ld-nav-icon:hover,
.ld-nav-icon:focus-visible {
    background: rgba(200, 168, 130, 0.16);
    border-color: var(--ld-nav-ink-hi);
    color: var(--ld-nav-ink-hi);
    transform: translateY(-1px);
}

/* ── 3.1 The language switcher ──────────────────────────────────────────────────────────
   Nine links in a chamfered plate, in the header's own chrome tones — the flags carry the
   recognition, the native names carry the meaning, and the two-letter code is what the closed
   trigger shows. The items are ordinary <a href="/landing?lang=xx"> links, so the switcher
   works with JavaScript off; the plate hangs below the bar, and the header's stacking context
   contains it, so the z-index below only has to beat its siblings.

   IT OPENS ON A CLICK, NOT ON HOVER, and the two paths are deliberately kept apart:

   * with JS (`html.ld-js`), `.is-open` is the single source of truth and landing.js §2b owns
     it — click, Escape, outside-click, tab-away. Hover does NOT open the plate: the trigger
     sits immediately left of "Play now", so every trip to that button would sweep the pointer
     across it and drop nine rows over the page;
   * without JS (`html.ld-no-js`), there is nothing to set the class, so :hover and
     :focus-within open it instead. That is the fallback, and the ONLY place they appear.

   Using both at once is what breaks it: :focus-within would hold the plate open against the
   very click that closed it, because clicking a <button> focuses it. */

.ld-lang {
    position: relative;
    flex: none;
}

.ld-lang-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.42rem;
    height: 42px;
    padding: 0 0.62rem;
    border: 1px solid var(--ld-nav-edge);
    border-radius: 6px;
    background: rgba(30, 25, 20, 0.5);
    color: var(--ld-nav-ink);
    font-family: var(--ld-font-text);
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    line-height: 1;
    cursor: pointer;
    transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease;
}

.ld-lang-flag {
    display: block;
    width: 20px;
    height: 15px;
    object-fit: cover;
    border-radius: 2px;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45);
}

/* The tracking above puts a full letter-space after the last letter; pulled back so the caret
   sits the same distance from "EN" as the flag does, rather than 0.1em further out. */
.ld-lang-code {
    margin-right: -0.1em;
}

.ld-lang-caret {
    width: 13px;
    height: 13px;
    fill: none;
    stroke: currentColor;
    opacity: 0.7;
    transition: transform 0.22s var(--ld-ease);
}

/* The trigger lights on hover whether or not the plate opens — that is the affordance saying
   it is a control, and it is the same one .ld-nav-icon next to it gives. */
.ld-lang-toggle:hover,
.ld-lang-toggle:focus-visible,
.ld-lang.is-open .ld-lang-toggle,
.ld-no-js .ld-lang:focus-within .ld-lang-toggle {
    background: rgba(200, 168, 130, 0.16);
    border-color: var(--ld-nav-ink-hi);
    color: var(--ld-nav-ink-hi);
}

/* The caret, though, points at the plate's state — so it turns only when the plate is open. */
.ld-lang.is-open .ld-lang-caret,
.ld-no-js .ld-lang:hover .ld-lang-caret,
.ld-no-js .ld-lang:focus-within .ld-lang-caret {
    transform: rotate(180deg);
}

.ld-lang-menu {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    z-index: 5;
    min-width: 208px;
    margin: 0;
    padding: 0.35rem;
    list-style: none;
    background: rgba(40, 32, 25, 0.97);
    -webkit-backdrop-filter: blur(16px) saturate(1.3);
            backdrop-filter: blur(16px) saturate(1.3);
    border: 1px solid rgba(200, 168, 130, 0.24);
    border-radius: 8px;
    box-shadow: 0 26px 44px -18px rgba(0, 0, 0, 0.92);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition: opacity 0.2s ease, transform 0.26s var(--ld-ease), visibility 0s linear 0.26s;
}

.ld-lang.is-open .ld-lang-menu,
.ld-no-js .ld-lang:hover .ld-lang-menu,
.ld-no-js .ld-lang:focus-within .ld-lang-menu {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s, 0s, 0s;
}

/* On the no-JS hover path the gap between the trigger and the plate would close the menu
   mid-reach; this bridges it. Inert when the plate is closed — it is hidden with it. */
.ld-lang-menu::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: -12px;
    height: 12px;
}

.ld-lang-item {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.5rem 0.6rem;
    border-radius: 5px;
    color: var(--ld-nav-ink);
    font-size: 0.9rem;
    font-weight: 600;
    line-height: 1.2;
    transition: background 0.16s ease, color 0.16s ease;
}

.ld-lang-item img {
    flex: none;
    width: 20px;
    height: 15px;
    object-fit: cover;
    border-radius: 2px;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45);
}

.ld-lang-name {
    flex: 1 1 auto;
}

.ld-lang-abbr {
    flex: none;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    opacity: 0.55;
}

.ld-lang-item:hover,
.ld-lang-item:focus-visible {
    background: rgba(200, 168, 130, 0.14);
    color: var(--ld-nav-ink-hi);
}

.ld-lang-item.is-current {
    background: rgba(200, 168, 130, 0.1);
    color: var(--ld-gold);
}

.ld-lang-item.is-current .ld-lang-abbr {
    opacity: 0.8;
}

/* Hidden until the viewport is narrow enough to need it — see section 9. */
.ld-nav-toggle {
    display: none;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 44px;
    height: 44px;
    padding: 0 11px;
    margin-left: auto;
    border: 1px solid var(--ld-nav-edge);
    border-radius: 6px;
    background: rgba(30, 25, 20, 0.5);
    cursor: pointer;
}

.ld-nav-toggle-bar {
    display: block;
    width: 20px;
    height: 2px;
    background: var(--ld-nav-ink);
    transition: transform 0.25s var(--ld-ease), opacity 0.2s ease;
}

.ld-nav-toggle.is-open .ld-nav-toggle-bar:nth-child(1) {
    transform: translateY(7px) rotate(45deg);
}

.ld-nav-toggle.is-open .ld-nav-toggle-bar:nth-child(2) {
    opacity: 0;
}

.ld-nav-toggle.is-open .ld-nav-toggle-bar:nth-child(3) {
    transform: translateY(-7px) rotate(-45deg);
}


/* ── 4. Hero ────────────────────────────────────────────────────────────────────────────── */

.ld-hero {
    /* How tall the fade at the hero's foot is; the scroll cue and Skip sit just above it.
       Longer than the torn edge it replaced — a gradient needs room to be a gradient. */
    --ld-edge-h: clamp(58px, 7vw, 115px);
    position: relative;
    min-height: 100vh;
    min-height: 100svh;
    display: grid;
    place-items: center;
    padding: calc(var(--ld-header-h) + 1.5rem) 1.5rem calc(var(--ld-edge-h) + 6rem);
    overflow: hidden;
    isolation: isolate;
    background: var(--ld-bg-deep);
}

.ld-hero-copy {
    position: relative;
    z-index: 2;
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    max-width: 960px;
    text-align: center;
}

/* The reveal. Every direct child rides in on the stage's data-beat, staggered, inside the
   1.2 s the module spends ramping its scrim. Hanging it off the attribute rather than a class
   of our own is what keeps it in step with the film on every delivery path. */
.ld-hero-copy > * {
    opacity: 0;
    transform: translateY(26px);
    transition: opacity 1s ease, transform 1.3s var(--ld-ease);
}

.ld-hero-stage[data-beat] ~ .ld-hero-copy > * {
    opacity: 1;
    transform: none;
}

.ld-hero-stage[data-beat] ~ .ld-hero-copy > :nth-child(1) { transition-delay: 0.15s; }
.ld-hero-stage[data-beat] ~ .ld-hero-copy > :nth-child(2) { transition-delay: 0.28s; }
.ld-hero-stage[data-beat] ~ .ld-hero-copy > :nth-child(3) { transition-delay: 0.5s; }
.ld-hero-stage[data-beat] ~ .ld-hero-copy > :nth-child(4) { transition-delay: 0.66s; }

/* Scripts off: no module, no beat — show the copy over the loop's poster. */
.ld-no-js .ld-hero-copy > * {
    opacity: 1;
    transform: none;
}

.ld-no-js .ld-hero-stage {
    background: url("../video/poster-ambient.jpg") center / cover no-repeat;
    filter: saturate(0.74) brightness(0.62) contrast(1.04);
}

/* The beta banner. Not a web pill: a chamfered plate (the ghost button's two-layer trick —
   clip-path clips borders, so the element is the gold edge and ::before the glass fill) hung
   between two hairline wings tipped with diamonds. The date is set in the heading gradient,
   and a faint specular pass crosses the plate every few seconds, cousin to the wordmark's. */
.ld-hero-pill {
    display: inline-flex;
    align-items: center;
    margin: 0 0 1.8rem;
    font-size: 0.78rem;
    font-weight: 800;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: rgba(241, 233, 220, 0.85);
    filter: drop-shadow(0 10px 22px rgba(0, 0, 0, 0.55));
}

.ld-pill-wing {
    position: relative;
    width: clamp(36px, 6vw, 62px);
    height: 1px;
    background: linear-gradient(90deg, rgba(231, 193, 132, 0.05), rgba(231, 193, 132, 0.62));
}

.ld-pill-wing::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    width: 5px;
    height: 5px;
    transform: translate(-2px, -50%) rotate(45deg);
    background: rgba(231, 193, 132, 0.7);
}

.ld-pill-wing-r {
    transform: scaleX(-1);
}

.ld-pill-core {
    --ld-cut: 9px;
    position: relative;
    isolation: isolate;
    display: inline-flex;
    align-items: center;
    gap: 0.65rem;
    padding: 0.72rem 1.3rem;
    background: linear-gradient(180deg, rgba(231, 193, 132, 0.5), rgba(129, 108, 74, 0.38));
    clip-path: polygon(var(--ld-cut) 0, 100% 0, 100% calc(100% - var(--ld-cut)), calc(100% - var(--ld-cut)) 100%, 0 100%, 0 var(--ld-cut));
}

.ld-pill-core::before {
    content: "";
    position: absolute;
    inset: 1px;
    z-index: -1;
    background: linear-gradient(180deg, rgba(24, 18, 13, 0.78), rgba(10, 8, 6, 0.84));
    clip-path: polygon(calc(var(--ld-cut) - 1px) 0, 100% 0, 100% calc(100% - var(--ld-cut) + 1px), calc(100% - var(--ld-cut) + 1px) 100%, 0 100%, 0 calc(var(--ld-cut) - 1px));
}

.ld-hero-pill time {
    background: var(--ld-gold-text);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
}

/* The pass: gold at a fraction of opacity, like the wordmark's — never a white bar. */
.ld-pill-sheen {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(112deg,
        rgba(255, 222, 166, 0) 40%,
        rgba(255, 222, 166, 0.1) 47%,
        rgba(255, 240, 205, 0.28) 50%,
        rgba(255, 222, 166, 0.1) 53%,
        rgba(255, 222, 166, 0) 60%);
    background-repeat: no-repeat;
    background-size: 260% 100%;
    background-position: 100% 0;
    animation: ld-sheen 9s cubic-bezier(0.4, 0.05, 0.25, 1) 2.4s infinite;
}

.ld-pill-dot {
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--ld-gold-2);
    box-shadow: 0 0 0 0 rgba(212, 148, 58, 0.6);
    animation: ld-pulse 2.2s ease-out infinite;
}

.ld[data-beta-state="live"] .ld-pill-dot {
    background: #6fcf6a;
    animation-name: ld-pulse-live;
}

@keyframes ld-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(212, 148, 58, 0.55); }
    100% { box-shadow: 0 0 0 12px rgba(212, 148, 58, 0); }
}

@keyframes ld-pulse-live {
    0%   { box-shadow: 0 0 0 0 rgba(111, 207, 106, 0.55); }
    100% { box-shadow: 0 0 0 12px rgba(111, 207, 106, 0); }
}

.ld-hero-title {
    margin: 0 0 1.5rem;
    line-height: 0;
}

/* filter on the outer wrapper, mask on the inner sheen — in that order, or the shadow is cast
   by the rectangle instead of the letters (docs/landing-page.md, trap 4). */
.ld-wordmark {
    position: relative;
    display: inline-block;
    width: min(640px, 82vw, 80vh);
    filter: drop-shadow(0 22px 36px rgba(0, 0, 0, 0.6));
}

.ld-wordmark img {
    width: 100%;
    height: auto;
}

/* The sweep is light on metal, not a white bar across a card. Two things make it read that way.

   `overlay` rather than `screen`: overlay only brightens what is already bright, so the gold
   catches the pass while the torn plaque behind the letters keeps its dark and its texture.
   `screen` lifts every pixel equally, which milks the plaque grey and flattens the gradient
   inside the letters — the whole mark goes pale for a moment, and that is what looks cheap.

   And gold, never white: the band is the mark's own colour at a fraction of its opacity, a
   wide soft shoulder around one narrow bright crest, so the letters stay orange under it.
   Raked to 114° so it crosses the tops of the letters before their feet, the way light falls
   across relief rather than a sheet passing in front of it. */
.ld-wordmark-sheen {
    position: absolute;
    inset: 0;
    mix-blend-mode: overlay;
    pointer-events: none;
    -webkit-mask-image: url("../img/wordmark.webp");
            mask-image: url("../img/wordmark.webp");
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    background: linear-gradient(114deg,
        rgba(255, 202, 132, 0)    39%,     /* ~360 px of soft warm shoulder … */
        rgba(255, 206, 140, 0.13) 44.6%,
        rgba(255, 222, 166, 0.34) 48.3%,
        rgba(255, 236, 196, 0.58) 49.5%,
        rgba(255, 248, 224, 0.88) 50%,     /* … around a ~17 px specular crest */
        rgba(255, 236, 196, 0.58) 50.5%,
        rgba(255, 222, 166, 0.34) 51.7%,
        rgba(255, 206, 140, 0.13) 55.4%,
        rgba(255, 202, 132, 0)    61%);
    background-repeat: no-repeat;
    background-size: 260% 100%;
    background-position: 100% 0;
    animation: ld-sheen 7.5s cubic-bezier(0.4, 0.05, 0.25, 1) 1.6s infinite;
}

/* Crosses in a fifth of the cycle and then stays out of the way — a glint, not a shimmer. */
@keyframes ld-sheen {
    0%   { background-position: 100% 0; }
    26%  { background-position: 0% 0; }
    100% { background-position: 0% 0; }
}

.ld-hero-lead {
    margin: 0 0 2.3rem;
    font-size: clamp(1.1rem, 0.8rem + 1vw, 1.4rem);
    font-weight: 500;
    letter-spacing: 0.01em;
    line-height: 1.5;
    color: var(--ld-text);
    text-shadow: 0 2px 18px rgba(0, 0, 0, 0.65);
    text-wrap: balance;
}

.ld-hero-lead > span {
    white-space: nowrap;
}

.ld-hero-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 1rem;
}

/* "Skip intro" — only while the overture is actually playing: cinematic path, before the beat. */
.ld-hero-skip {
    position: absolute;
    right: clamp(1rem, 3vw, 2.5rem);
    bottom: calc(var(--ld-edge-h) + 1.1rem);
    z-index: 4;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.62rem 0.9rem 0.62rem 1.05rem;
    border: 1px solid rgba(241, 233, 220, 0.32);
    border-radius: 999px;
    background: rgba(8, 6, 5, 0.42);
    -webkit-backdrop-filter: blur(6px);
            backdrop-filter: blur(6px);
    color: rgba(241, 233, 220, 0.9);
    font-family: var(--ld-font-text);
    font-size: 0.74rem;
    font-weight: 700;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.3s ease, visibility 0s linear 0.3s, border-color 0.18s ease, color 0.18s ease;
}

.ld-hero-skip-icon {
    width: 14px;
    height: 14px;
}

.ld-hero-stage[data-mode="cinematic"]:not([data-beat]) ~ .ld-hero-skip {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition-delay: 1.4s, 1.4s, 0s, 0s;
}

.ld-hero-skip:hover,
.ld-hero-skip:focus-visible {
    border-color: var(--ld-gold);
    color: var(--ld-gold);
}

/* The scroll cue: a hairline with a gold drop running down it. */
.ld-hero-scroll {
    position: absolute;
    left: 50%;
    bottom: calc(var(--ld-edge-h) + 1.1rem);
    z-index: 4;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.7rem;
    transform: translateX(-50%);
    color: rgba(241, 233, 220, 0.58);
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.32em;
    text-transform: uppercase;
    opacity: 0;
    visibility: hidden;
    transition: opacity 1s ease 1.5s, visibility 0s linear 1.5s, color 0.18s ease;
}

.ld-hero-stage[data-beat] ~ .ld-hero-scroll,
.ld-no-js .ld-hero-scroll {
    opacity: 1;
    visibility: visible;
}

.ld-hero-scroll:hover,
.ld-hero-scroll:focus-visible {
    color: var(--ld-gold);
    transition-delay: 0s;
}

.ld-hero-scroll-track {
    position: relative;
    width: 1px;
    height: 46px;
    background: rgba(241, 233, 220, 0.18);
    overflow: hidden;
}

.ld-hero-scroll-track::after {
    content: "";
    position: absolute;
    left: 0;
    top: -18px;
    width: 1px;
    height: 18px;
    background: var(--ld-gold);
    animation: ld-scroll-drop 2.1s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}

@keyframes ld-scroll-drop {
    0%   { transform: translateY(0); }
    100% { transform: translateY(64px); }
}

/* The seam, over the footage. A straight line and a short fade to the page colour — no sheet,
   no silhouette. It ends on exactly --ld-bg, which is also where the beta section's own shade
   starts, so the two sides meet at the same value and the boundary itself is invisible. */
.ld-hero-fade {
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    z-index: 3;
    height: var(--ld-edge-h);
    pointer-events: none;
    background: linear-gradient(to bottom,
        rgba(13, 10, 8, 0) 0%,
        rgba(13, 10, 8, 0.28) 30%,
        rgba(13, 10, 8, 0.62) 56%,
        rgba(13, 10, 8, 0.88) 78%,
        rgba(13, 10, 8, 1) 100%);
}


/* ── 5. Beta period — the scene ─────────────────────────────────────────────────────────
   Three layers, back to front: the village plate, the embers, the Shaman. landing.js moves the
   plate and the figure against the scroll (data-depth / data-zoom on the <img>s) and nudges
   them with the pointer on desktop; everything else here is static. She stands bottom-left,
   the temple — the best of the plate — shows in the open beside her, and the copy column sits
   over the right, where the shade is darkest. */

.ld-section {
    position: relative;
    padding: clamp(4.5rem, 9vw, 7.5rem) 0;
}

.ld-section-head {
    max-width: 680px;
    margin-bottom: clamp(2.5rem, 5vw, 4rem);
}

.ld-beta {
    overflow: hidden;
    isolation: isolate;
    /* Kept short on purpose: the plate has to cover the section, and at about 750 px it turns
       width-limited, which is the point where the whole painting is on screen. */
    min-height: clamp(560px, 78vh, 760px);
    padding: clamp(3.5rem, 6vw, 4.5rem) 0 clamp(2.25rem, 4vw, 3rem);
    background: var(--ld-bg);
}

.ld-scene {
    position: absolute;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    pointer-events: none;
}

/* Oversized by 8 % — enough that the drift never shows an edge once the zoom has fully
   opened, little enough that the painting then sits at its native scale. */
.ld-scene-plate {
    position: absolute;
    left: 0;
    top: -4%;
    width: 100%;
    height: 108%;
    object-fit: cover;
    /* The temple sits at 37 % across the painting: on a tall narrow section (phones) that is
       the slice that survives the crop. The zoom pivots on the centre, so the tight framing at
       the page top is the middle of the picture and the opening reveals top and bottom alike. */
    object-position: 37% 50%;
    transform-origin: 50% 50%;
    will-change: transform;
}

/* The shade: the page colour at the very top, ramping off as the picture comes up — the down
   half of the seam's fade, and it has to start at a solid --ld-bg or the straight line shows.
   Deepest down the right behind the copy, and back to the page colour at the foot. */
.ld-scene-shade {
    position: absolute;
    inset: 0;
    background:
        linear-gradient(to bottom,
            rgba(13, 10, 8, 1) 0%, rgba(13, 10, 8, 0.88) 3%, rgba(13, 10, 8, 0.6) 8%,
            rgba(13, 10, 8, 0.32) 13%, rgba(13, 10, 8, 0.1) 18%, rgba(13, 10, 8, 0) 23%,
            rgba(13, 10, 8, 0) 68%, rgba(13, 10, 8, 1) 100%),
        /* The low sun, top-left. Under the ramp above, not over it: on top it was a lamp
           switching on at the section's first pixel — the one place that has to match what is
           above it exactly. Under it, the ramp fades the sun in for us. */
        radial-gradient(46% 58% at 9% 2%, rgba(255, 198, 120, 0.3), rgba(255, 198, 120, 0) 70%),
        linear-gradient(90deg,
            rgba(13, 10, 8, 0.32) 0%, rgba(13, 10, 8, 0.06) 20%, rgba(13, 10, 8, 0.12) 40%,
            rgba(13, 10, 8, 0.68) 50%, rgba(13, 10, 8, 0.9) 60%, rgba(13, 10, 8, 0.95) 100%);
}

/* Embers. Each <i> carries its own position, size, timing and drift as custom properties set
   in the view; the animation is transform + opacity only, so they live on the compositor. */
.ld-scene-embers {
    position: absolute;
    inset: 0;
}

.ld-scene-embers i {
    position: absolute;
    left: var(--x);
    bottom: var(--y);
    width: var(--s);
    height: var(--s);
    border-radius: 50%;
    background: #ffe6ad;
    box-shadow:
        0 0 7px 2px rgba(255, 205, 120, 0.95),
        0 0 20px 6px rgba(255, 150, 50, 0.38);
    opacity: 0;
    will-change: transform, opacity;
    animation: ld-ember var(--d) linear var(--t) infinite;
}

/* The front swarm sits above the figure (the section is its own stacking context). */
.ld-scene-embers-front {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
}

.ld-scene-embers-front i {
    box-shadow:
        0 0 9px 3px rgba(255, 205, 120, 0.95),
        0 0 26px 8px rgba(255, 150, 50, 0.4);
}

@keyframes ld-ember {
    0%   { transform: translate3d(0, 0, 0) scale(0.5); opacity: 0; }
    8%   { opacity: 0.95; }
    28%  { transform: translate3d(calc(var(--a) * 0.6), calc(var(--r) * 0.3), 0) scale(1); }
    45%  { opacity: 0.55; }
    58%  { transform: translate3d(calc(var(--a) * -0.45), calc(var(--r) * 0.62), 0) scale(0.9); opacity: 0.9; }
    82%  { opacity: 0.35; }
    100% { transform: translate3d(calc(var(--a) * 0.25), var(--r), 0) scale(0.4); opacity: 0; }
}

/* The Shaman. Cut at the thighs in the source, so she stands on the section's bottom edge
   and her last few percent dissolve into the shade — mask longhands only (trap 5). She is not
   mirrored: the glyph on her armband and the scroll at her waist would read backwards. */
.ld-scene-figure {
    position: absolute;
    left: clamp(0px, 1vw, 16px);
    bottom: 0;
    z-index: 1;
    width: clamp(440px, 51vw, 760px);
    pointer-events: none;
}

/* A pool of warm light behind her, so she sits in the scene instead of on it. */
.ld-scene-figure::before {
    content: "";
    position: absolute;
    left: 10%;
    right: 10%;
    bottom: -10%;
    height: 70%;
    background: radial-gradient(50% 60% at 50% 80%, rgba(255, 170, 70, 0.22), rgba(255, 170, 70, 0) 70%);
}

.ld-scene-shaman {
    position: relative;
    display: block;
    width: 100%;
    height: auto;
    will-change: transform;
    -webkit-mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 11%);
            mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 11%);
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
}

/* The seam to the team section. The shade below can only close the plate: the Shaman, the
   warm pool behind her and the front embers all sit above it, and each was still lit at the
   section's last row — a hard step against the page colour. This closes the foot over all of
   them and lands on exactly --ld-bg. It sits under the copy (z-index 3), so the countdown and
   the button stay crisp and only their backdrop deepens. */
.ld-beta::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 2;
    height: clamp(90px, 13vh, 150px);
    pointer-events: none;
    background: linear-gradient(to bottom,
        rgba(13, 10, 8, 0) 0%, rgba(13, 10, 8, 0.4) 46%,
        rgba(13, 10, 8, 0.84) 80%, rgba(13, 10, 8, 1) 100%);
}

.ld-beta-inner {
    position: relative;
    z-index: 3;
}

.ld-beta-copy {
    max-width: 540px;
    margin-left: auto;
}

.ld-beta-copy .ld-h2 {
    margin-bottom: 0.9rem;
}

.ld-beta-copy .ld-lead {
    margin-bottom: 1.5rem;
    text-shadow: 0 1px 12px rgba(0, 0, 0, 0.6);
}

.ld-beta .ld-actions {
    margin-top: 0;
    justify-content: center;
}

.ld-countdown-plaque {
    margin: 0 0 1.5rem;
    padding: 1.5rem 1.5rem 1.7rem;
    text-align: center;
}

.ld-plaque-label {
    margin: 0 0 1rem;
    font-size: 0.74rem;
    font-weight: 800;
    letter-spacing: 0.32em;
    text-transform: uppercase;
    color: var(--ld-gold-2);
}

.ld-countdown {
    display: flex;
    justify-content: center;
    align-items: flex-start;
    gap: clamp(0.35rem, 1.2vw, 0.8rem);
}

.ld-count {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 4ch;
}

.ld-count-num {
    font-family: var(--ld-font-display);
    font-size: clamp(2.5rem, 1.4rem + 3vw, 4rem);
    line-height: 1;
    font-variant-numeric: tabular-nums;
    background: var(--ld-gold-text);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.5));
}

.ld-count-unit {
    margin-top: 0.55rem;
    font-size: 0.66rem;
    font-weight: 800;
    letter-spacing: 0.28em;
    text-transform: uppercase;
    color: var(--ld-text-dim);
}

.ld-count-sep {
    padding-top: 0.1em;
    font-family: var(--ld-font-display);
    font-size: clamp(2rem, 1.2rem + 2.4vw, 3.2rem);
    line-height: 1;
    color: rgba(231, 193, 132, 0.4);
}

.ld-plaque-date {
    margin: 1.1rem 0 0;
    font-size: 0.94rem;
    color: var(--ld-text-2);
}

.ld-plaque-date time {
    color: var(--ld-text);
    font-weight: 600;
}

.ld[data-beta-state="ended"] .ld-countdown {
    display: none;
}


/* ── 6. Community — the Discord send-off (the fox's home) ───────────────────────────────
   The last section before the footer, and where the fox leads. Two layers of the owner's key
   art: the ember sky behind (landing.js "Scenes" opens it from 1.16× to 1× as the section
   arrives — the zoom-out — then drifts it against the scroll and leans it away from the
   pointer), and Yang with the nine-tail presenting the Discord mark, standing on the
   section's bottom edge at the left. The copy takes the right, over the darkest stretch of
   sky; the patrolling fox keeps the right ground ([0.70, 0.93], set in fox-guide.js
   and landing.js — the art owns the left). */

.ld-community {
    position: relative;
}

/* The fox's ground line. It is the footer's own top rule, warmed over the stretch he patrols
   (the art keeps the left) — painted one pixel below the section so the two share a row. A
   separate hairline 60 px up read as a second, accidental line. It cannot live inside
   .ld-pet-band: that is overflow:hidden, as the runner requires. */
.ld-community::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 1px;
    z-index: 3;
    pointer-events: none;
    background: linear-gradient(90deg, rgba(231, 193, 132, 0) 56%, rgba(231, 193, 132, 0.55) 70%, rgba(231, 193, 132, 0.55) 94%, rgba(231, 193, 132, 0) 100%);
}

/* The module's container: relative and clipped, as pet-runner requires. */
.ld-pet-band {
    position: relative;
    overflow: hidden;
    isolation: isolate;
    min-height: clamp(480px, 58vh, 600px);
    display: flex;
    align-items: center;
    background: var(--ld-bg);
}

.ld-disc-scene {
    position: absolute;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    pointer-events: none;
}

/* Oversized so neither the drift nor the settled zoom ever shows an edge. */
.ld-disc-bg {
    position: absolute;
    left: 0;
    top: -5%;
    width: 100%;
    height: 110%;
    object-fit: cover;
    object-position: 50% 40%;
    transform-origin: 50% 50%;
    will-change: transform;
}

/* Page colour at the very top (the seam rule: the team's foot above ends on the same value),
   a gentle floor at the bottom, and a right-hand ramp the copy sits over. */
.ld-disc-shade {
    position: absolute;
    inset: 0;
    background:
        linear-gradient(to bottom,
            rgba(13, 10, 8, 1) 0%, rgba(13, 10, 8, 0.85) 4%, rgba(13, 10, 8, 0.52) 9%,
            rgba(13, 10, 8, 0.24) 15%, rgba(13, 10, 8, 0) 22%,
            rgba(13, 10, 8, 0) 76%, rgba(13, 10, 8, 0.5) 100%),
        linear-gradient(90deg,
            rgba(13, 10, 8, 0) 44%, rgba(13, 10, 8, 0.3) 64%, rgba(13, 10, 8, 0.52) 100%);
}

/* The sparks: the shared ember system, one size down — a tighter, cooler glow so thirty of
   them read as drifting fire dust rather than thirty lanterns. */
.ld-disc-embers {
    z-index: 1;
}

.ld-disc-embers i {
    box-shadow:
        0 0 6px 1px rgba(255, 205, 120, 0.9),
        0 0 14px 4px rgba(255, 150, 50, 0.32);
}

/* Yang and the nine-tail. Cut at the canvas foot in the source, so they stand on the
   section's bottom edge and the last stretch dissolves into the floor shade — mask longhands
   only (trap 5). The container takes the reveal; the script's transform rides on the <img>. */
.ld-disc-figures {
    position: absolute;
    left: clamp(0px, 2vw, 44px);
    bottom: 0;
    z-index: 1;
    width: clamp(390px, 40vw, 600px);
    pointer-events: none;
}

.ld-disc-heroes {
    position: relative;
    width: 100%;
    height: auto;
    will-change: transform;
    -webkit-mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 8%);
            mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 8%);
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
}

/* Their entrance: rises and settles like the page's reveals, but from lower and slower —
   the scale is on the container so it composes with the script's transform on the <img>. */
.ld-js .ld-reveal-figures {
    transform: translateY(48px) scale(0.97);
    transform-origin: 50% 100%;
    transition: opacity 0.9s ease, transform 1.6s var(--ld-ease);
}

.ld-js .ld-reveal-figures.is-in {
    transform: none;
}

/* The fox's floor: a soft dark grounding along the bottom, and a faint lantern warmth above
   the line he stands on. The line itself is not here — see .ld-community::after. */
.ld-band-ground {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
    height: 60px;
    pointer-events: none;
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.45));
}

.ld-band-ground::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 110px;
    background: linear-gradient(to top, rgba(212, 148, 58, 0.08), rgba(212, 148, 58, 0));
    -webkit-mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 42%, rgba(0, 0, 0, 0.9) 62%, rgba(0, 0, 0, 0.9) 92%, rgba(0, 0, 0, 0) 100%);
            mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 42%, rgba(0, 0, 0, 0.9) 62%, rgba(0, 0, 0, 0.9) 92%, rgba(0, 0, 0, 0) 100%);
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
}

.ld-band-inner {
    position: relative;
    z-index: 2;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    padding: clamp(2.5rem, 4.5vw, 3.5rem) 0 clamp(12.5rem, 22vh, 14rem);
}

.ld-band-copy {
    grid-column: 2;
    padding-left: clamp(0px, 4vw, 3.5rem);
}

.ld-band-copy .ld-lead {
    text-shadow: 0 1px 12px rgba(0, 0, 0, 0.6);
}

/* A soft contact shadow under the fox. The runner root is the element that moves; the fox's
   feet sit at footY of the frame, and its centre is 0.42 or 0.58 across depending on which way
   it faces, so one wide ellipse centred on the frame covers both. */
#ld-pet-band .ec-runner::before {
    content: "";
    position: absolute;
    left: 50%;
    top: calc(var(--ec-frame) * 0.648);
    width: calc(var(--ec-frame) * 0.44);
    height: calc(var(--ec-frame) * 0.07);
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.55), rgba(0, 0, 0, 0) 70%);
    pointer-events: none;
}

#ld-pet-band .ec-runner-bubble {
    font-family: var(--ld-font-text);
}

#ld-pet-band .ec-runner-cta {
    font-family: var(--ld-font-display);
    font-weight: 400;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}


/* ── 7. Team ────────────────────────────────────────────────────────────────────────────── */
/* The room behind them is the owner's render of the four at their desks, dimmed under a shade
   that starts and ends on the page colour so both seams stay invisible, with the dotted
   texture over it — previewed behind a temporary toggle, kept, and now simply on. */

.ld-team {
    position: relative;
    overflow: hidden;
    isolation: isolate;
}

.ld-team-scene {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
}

.ld-team-bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 30%;
}

.ld-team-shade {
    position: absolute;
    inset: 0;
    background:
        radial-gradient(85% 70% at 50% 42%, rgba(13, 10, 8, 0) 30%, rgba(13, 10, 8, 0.5) 100%),
        linear-gradient(to bottom,
            rgba(13, 10, 8, 1) 0%, rgba(13, 10, 8, 0.82) 6%, rgba(13, 10, 8, 0.5) 12%,
            rgba(13, 10, 8, 0.3) 20%, rgba(13, 10, 8, 0.26) 55%,
            rgba(13, 10, 8, 0.62) 82%, rgba(13, 10, 8, 1) 100%);
}

/* The dotted texture, above the render. It used to be gated behind a preview toggle; the
   owner judged it by eye and kept it, so it is simply on — no class gate, no transition,
   nothing to switch it. */
.ld-team-dots {
    position: absolute;
    inset: 0;
    background: url("../img/pattern-dots.svg") 0 0 / 20px 20px repeat;
    opacity: 0.5;
}

.ld-team-inner {
    position: relative;
    z-index: 1;
}

.ld-team-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: clamp(1rem, 2vw, 1.4rem);
    margin: 0;
    padding: 0;
    list-style: none;
}

.ld-member {
    display: flex;
    flex-direction: column;
}

/* The figure: the character stands in front of a glass panel whose top edge the head breaks —
   the panel starts a sixth of the way down, the image does not. All four share a ground line
   in the source frames, so bottom-aligned they stand as a set. */
.ld-member-figure {
    position: relative;
    margin: 0;
}

.ld-member-figure::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 17%;
    bottom: 0;
    border: 1px solid rgba(231, 193, 132, 0.16);
    border-bottom: 0;
    background: linear-gradient(180deg, rgba(26, 20, 16, 0.62) 0%, rgba(13, 10, 8, 0.82) 100%);
    transition: border-color 0.3s ease;
}

.ld-member-figure::after {
    content: "";
    position: absolute;
    left: 8%;
    right: 8%;
    bottom: -1px;
    height: 1px;
    background: linear-gradient(90deg, rgba(231, 193, 132, 0), rgba(231, 193, 132, 0.45), rgba(231, 193, 132, 0));
}

.ld-member-glow {
    position: absolute;
    left: 50%;
    bottom: 0;
    width: 86%;
    height: 48%;
    transform: translateX(-50%);
    background: radial-gradient(50% 100% at 50% 100%, rgba(212, 148, 58, 0.3), rgba(212, 148, 58, 0.06) 58%, rgba(212, 148, 58, 0) 100%);
    opacity: 0.75;
    transition: opacity 0.35s ease;
    pointer-events: none;
}

.ld-member-img {
    position: relative;
    z-index: 1;
    width: 100%;
    height: auto;
    filter: drop-shadow(0 16px 18px rgba(0, 0, 0, 0.55));
    transition: transform 0.45s var(--ld-ease);
}

.ld-member-meta {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 1rem 0.85rem 1.3rem;
    border: 1px solid rgba(231, 193, 132, 0.14);
    border-top: 0;
    background: linear-gradient(180deg, rgba(26, 20, 16, 0.94), rgba(13, 10, 8, 0.96));
    text-align: center;
    transition: border-color 0.3s ease;
}

.ld-member-name {
    margin: 0;
    font-family: var(--ld-font-display);
    font-weight: 400;
    font-size: 1.45rem;
    line-height: 1.1;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--ld-text);
    transition: color 0.3s ease;
}

.ld-member-role {
    margin: 0.45rem 0 0;
    font-size: 0.67rem;
    font-weight: 800;
    letter-spacing: 0.15em;
    line-height: 1.7;
    text-transform: uppercase;
    color: var(--ld-gold-2);
    text-wrap: balance;
}

/* A hairline with a diamond at its waist — the plaque language, one size down. */
.ld-member-rule {
    position: relative;
    width: 74px;
    height: 1px;
    margin: 0.9rem 0;
    background: linear-gradient(90deg, rgba(231, 193, 132, 0), rgba(231, 193, 132, 0.5), rgba(231, 193, 132, 0));
}

.ld-member-rule::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 4px;
    height: 4px;
    transform: translate(-50%, -50%) rotate(45deg);
    background: rgba(231, 193, 132, 0.65);
}

.ld-member-duties {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0.79rem;
    line-height: 1.45;
    color: var(--ld-text-dim);
    text-wrap: balance;
}

.ld-member:hover .ld-member-img {
    transform: translateY(-8px);
}

.ld-member:hover .ld-member-glow {
    opacity: 1;
}

.ld-member:hover .ld-member-name {
    color: var(--ld-gold);
}

.ld-member:hover .ld-member-figure::before,
.ld-member:hover .ld-member-meta {
    border-color: rgba(231, 193, 132, 0.32);
}


/* ── 8. Footer ─────────────────────────────────────────────────────────────────────────────
   The plate the page lands on, and the bookend to the header: the same warm brown material,
   seen as a foundation rather than as a bar over the world. Four layers, and the order is the
   whole thing:

     background   the plate — a vertical brown gradient, with the top rail (gold hairline,
                  brass rail, bevel) drawn over it as three no-repeat strips
     ::before     the relief, overlay-blended into the plate
     ::after      the light — a warm pool at the top centre, a vignette down each side, and a
                  fall to the page's own black along the foot, which is also what takes the
                  relief out towards the bottom
     the content, above both

   Two things are load-bearing and easy to lose:

   `isolation: isolate` — without it the ::before's `mix-blend-mode` reaches past the footer
   and composites against the page behind it, so the relief washes over the section above.

   The relief is displayed at **440px**, which is roughly the scale it sits at in the game's own
   panels. It is the whole difference between tooled leather and wallpaper: at 200px the cloud
   scroll turns into busy noise, and at 800px it reads as smudges. If the tile is ever re-cut,
   keep the display size, not the file size. */

.ld-footer {
    /* Plate tones, kept together so the rail and the fall can't drift apart from the face. */
    --ld-foot-face: #2e231a;
    --ld-foot-mid: #1f1710;
    --ld-foot-foot: #100b07;
    --ld-foot-rail: rgba(86, 66, 50, 0.92);

    position: relative;
    isolation: isolate;
    padding: 3.25rem 0 2.25rem;
    background:
        /* gold hairline, brass rail, bevel — each fading out before the viewport edge, so the
           rail reads as a fitting on a panel and not as a border on a box */
        linear-gradient(90deg, transparent, rgba(231, 193, 132, 0.38) 18%, rgba(231, 193, 132, 0.38) 82%, transparent) 0 0 / 100% 1px no-repeat,
        linear-gradient(90deg, transparent, var(--ld-foot-rail) 14%, var(--ld-foot-rail) 86%, transparent) 0 1px / 100% 2px no-repeat,
        linear-gradient(90deg, transparent, rgba(231, 193, 132, 0.10) 16%, rgba(231, 193, 132, 0.10) 84%, transparent) 0 3px / 100% 1px no-repeat,
        linear-gradient(180deg, var(--ld-foot-face) 0%, var(--ld-foot-mid) 52%, var(--ld-foot-foot) 100%);
}

/* The relief: a seamless cut of the tooling on the game's own panel texture
   (`/presentation/v1/content_bg.png`), high-passed to a grey emboss map so the plate's colour
   stays the CSS's to set. `overlay` is what carves it in — a plain translucent layer would
   grey-wash the brown, because the map's flat ground is mid-grey, not transparent.
   Starts below the rail so the gold hairline isn't blended over. */
.ld-footer::before {
    content: "";
    position: absolute;
    inset: 4px 0 0;
    z-index: 0;
    pointer-events: none;
    background: url("../img/pattern-clouds.png") 50% 0 / 440px auto repeat;
    mix-blend-mode: overlay;
    opacity: 0.95;
}

/* The light. Painted over the relief, so the fall to black at the foot fades the tooling out
   with it — one gradient doing two jobs, and the reason the relief needs no mask of its own
   (which would have collided with the blend above). */
.ld-footer::after {
    content: "";
    position: absolute;
    inset: 4px 0 0;
    z-index: 0;
    pointer-events: none;
    background:
        radial-gradient(125% 100% at 50% 0%, rgba(212, 148, 58, 0.15), rgba(212, 148, 58, 0) 62%),
        linear-gradient(90deg, rgba(8, 6, 5, 0.42), rgba(8, 6, 5, 0) 22%, rgba(8, 6, 5, 0) 78%, rgba(8, 6, 5, 0.42)),
        linear-gradient(180deg, rgba(16, 11, 7, 0) 18%, rgba(16, 11, 7, 0.55) 62%, rgba(13, 10, 8, 0.92) 100%);
}

.ld-footer > .ld-shell {
    position: relative;
    z-index: 1;
}

/* The mark, seated on the rail like a boss on a panel. The shadow is what stops it floating. */
.ld-footer-crest {
    position: absolute;
    top: 0;
    left: 50%;
    z-index: 2;
    width: 34px;
    height: 34px;
    transform: translate(-50%, -50%);
    background: url("../img/mark.png") center / contain no-repeat;
    filter: drop-shadow(0 3px 8px rgba(0, 0, 0, 0.85));
    pointer-events: none;
}

.ld-footer-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 1.5rem 2.5rem;
}

.ld-footer-brand {
    display: block;
}

.ld-footer-brand img {
    height: 38px;
    width: auto;
    opacity: 0.88;
    filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.6));
    transition: opacity 0.2s ease, filter 0.2s ease;
}

.ld-footer-brand:hover img {
    opacity: 1;
    filter: drop-shadow(0 2px 14px rgba(231, 193, 132, 0.35));
}

.ld-footer-tag {
    margin: 0.65rem 0 0;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--ld-text-dim);
}

.ld-footer-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem 1.6rem;
    margin-left: auto;
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ld-text-dim);
}

.ld-footer-nav a {
    position: relative;
    padding-bottom: 0.4rem;
    transition: color 0.18s ease;
}

/* The hover mark is the rail's own figure again — a hairline that fades out at both ends —
   rather than a full-width underline, which at this size reads as a text-decoration slip. */
.ld-footer-nav a::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--ld-gold), transparent);
    transform: scaleX(0);
    transition: transform 0.28s var(--ld-ease);
}

.ld-footer-nav a:hover,
.ld-footer-nav a:focus-visible {
    color: var(--ld-gold);
}

.ld-footer-nav a:hover::after,
.ld-footer-nav a:focus-visible::after {
    transform: none;
}

/* Same fading rule as the rail above, for the same reason: a flat 1px border straight across
   cuts the plate in two. */
.ld-footer-base {
    margin-top: 2.25rem;
    padding-top: 1.25rem;
    background: linear-gradient(90deg, transparent, rgba(231, 193, 132, 0.2), transparent) 0 0 / 100% 1px no-repeat;
}

.ld-footer-legal {
    margin: 0;
    font-size: 0.78rem;
    color: var(--ld-text-dim);
}


/* ── 9. Narrow viewports, reduced motion ────────────────────────────────────────────────── */

@media (max-width: 1180px) {
    .ld-scene-figure {
        width: clamp(380px, 47vw, 640px);
        left: 0;
    }

    .ld-beta-copy {
        max-width: 500px;
    }
}

/* Below this the column and the figure cannot share the width: she gets a stage of her own at
   the foot of the section, and the copy ends above it. */
@media (max-width: 1023px) {
    .ld-beta {
        min-height: 0;
        padding-bottom: 460px;
    }

    .ld-scene-figure {
        width: min(70vw, 480px);
        left: clamp(0px, 3vw, 1.5rem);
    }

    .ld-scene-shade {
        background:
            linear-gradient(to bottom,
                rgba(13, 10, 8, 1) 0%, rgba(13, 10, 8, 0.72) 10%, rgba(13, 10, 8, 0.4) 30%,
                rgba(13, 10, 8, 0.3) 68%, rgba(13, 10, 8, 0.2) 84%, rgba(13, 10, 8, 0.88) 100%),
            linear-gradient(90deg, rgba(13, 10, 8, 0.3) 0%, rgba(13, 10, 8, 0.45) 50%, rgba(13, 10, 8, 0.6) 100%);
    }

    .ld-beta-copy {
        max-width: 640px;
        margin-left: 0;
    }
}

@media (max-width: 900px) {
    /* The strip's detail lives at its right end; centred, a phone-width crop shows only the
       empty middle of it. Same call the app's navbar makes below 960px. */
    .ld-header::before {
        background-position: right center;
    }

    .ld-nav-toggle {
        display: flex;
    }

    /* Collapsed by default; landing.js flips `is-open` on the toggle's click. With JS off
       the nav stays hidden, which is why every link in it also exists elsewhere on the page. */
    .ld-nav {
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        margin: 0;
        padding: 0.5rem 1.5rem 1.5rem;
        background: rgba(40, 32, 25, 0.92);
        -webkit-backdrop-filter: blur(16px) saturate(1.3);
                backdrop-filter: blur(16px) saturate(1.3);
        border-bottom: 1px solid rgba(200, 168, 130, 0.2);
        box-shadow: 0 30px 50px -20px rgba(0, 0, 0, 0.9);
        opacity: 0;
        visibility: hidden;
        transform: translateY(-10px);
        transition: opacity 0.25s ease, transform 0.3s var(--ld-ease), visibility 0s linear 0.3s;
    }

    .ld-nav.is-open {
        opacity: 1;
        visibility: visible;
        transform: none;
        transition-delay: 0s, 0s, 0s;
    }

    .ld-nav-links {
        flex-direction: column;
        align-items: stretch;
        gap: 0;
    }

    .ld-nav-links a {
        display: block;
        padding: 0.9rem 0;
        border-bottom: 1px solid rgba(200, 168, 130, 0.16);
        font-size: 0.95rem;
        text-shadow: none;
    }

    .ld-nav-links a::after {
        display: none;
    }

    .ld-nav-actions {
        justify-content: space-between;
        padding-top: 1.1rem;
    }

    /* The actions row is the last thing in the collapsed panel, so the plate falls clear of it
       onto the page rather than over the links. Hover is not a gesture a phone has — the click
       toggle in landing.js is what opens it here — and the row is only as wide as three items,
       so the plate gets a little more room to spell the names in. */
    .ld-lang-menu {
        min-width: 232px;
    }

    .ld-team-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        max-width: 620px;
    }
}

@media (max-width: 719px) {
    /* The runner does not mount this narrow; the key art stands in for the fox and the band
       stacks: copy first, the art standing on the section's foot below it. */
    .ld-pet-band {
        min-height: 0;
        display: flex;
        flex-direction: column;
        align-items: stretch;
    }

    .ld-disc-figures {
        position: relative;
        order: 2;
        left: auto;
        bottom: auto;
        width: min(88vw, 460px);
        margin: 0 auto;
    }

    .ld-band-inner {
        order: 1;
        grid-template-columns: 1fr;
        padding: 3.5rem 0 1.5rem;
    }

    .ld-band-copy {
        grid-column: 1;
        padding-left: 0;
        text-align: center;
    }

    .ld-band-copy .ld-eyebrow,
    .ld-band-copy .ld-actions {
        justify-content: center;
    }

    .ld-band-copy .ld-lead {
        margin-inline: auto;
    }

    .ld-community::after {
        display: none;
    }
}

@media (max-width: 560px) {
    :root {
        --ld-shell: min(1180px, 100% - 2rem);
    }

    .ld-hero {
        padding-bottom: 6rem;
    }

    .ld-hero-pill {
        font-size: 0.68rem;
        letter-spacing: 0.16em;
    }

    .ld-pill-wing {
        width: 22px;
    }

    .ld-hero-actions {
        flex-direction: column;
        align-items: stretch;
        width: min(100%, 360px);
    }

    .ld-hero-actions .ld-btn > span {
        width: 100%;
        justify-content: center;
    }

    .ld-hero-skip {
        right: 1rem;
    }

    .ld-actions .ld-btn {
        width: 100%;
    }

    .ld-actions .ld-btn > span {
        width: 100%;
        justify-content: center;
    }

    .ld-beta {
        padding-bottom: 320px;
    }

    .ld-scene-figure {
        width: min(86vw, 350px);
    }

    .ld-count {
        min-width: 3.2ch;
    }

    .ld-count-unit {
        letter-spacing: 0.18em;
        font-size: 0.6rem;
    }

    .ld-team-grid {
        gap: 0.75rem;
    }

    .ld-member-name {
        font-size: 1.3rem;
    }

    .ld-member-role {
        font-size: 0.62rem;
        letter-spacing: 0.1em;
    }

    .ld-member-duties {
        font-size: 0.74rem;
    }

    /* The brand column and the nav stack, so the nav loses its push to the right. The relief
       comes down with the viewport — held at 440px it would show barely two cloud heads across
       a phone, which reads as a stain rather than as tooling. */
    .ld-footer-inner {
        align-items: flex-start;
    }

    .ld-footer-nav {
        margin-left: 0;
    }

    .ld-footer::before {
        background-size: 300px auto;
    }

    .ld-footer-crest {
        width: 28px;
        height: 28px;
    }
}

/* Short viewports (a 768-row laptop with the browser chrome up, a phone on its side): make
   room for the copy by letting the wordmark give way first, then the scroll cue. */
@media (max-height: 760px) {
    .ld-wordmark {
        width: min(520px, 82vw, 64vh);
    }

    .ld-hero {
        padding-bottom: calc(var(--ld-edge-h) + 5rem);
    }

    .ld-hero-pill {
        margin-bottom: 1.2rem;
    }

    .ld-hero-title {
        margin-bottom: 1rem;
    }

    .ld-hero-lead {
        margin-bottom: 1.6rem;
    }
}

@media (max-height: 620px) {
    .ld-hero-scroll {
        display: none;
    }
}

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

    .ld-js .ld-reveal {
        opacity: 1;
        transform: none;
        transition: none;
    }

    .ld-hero-copy > *,
    .ld-hero-scroll,
    .ld-hero-skip,
    .ld-header,
    .ld-btn,
    .ld-member-img,
    .ld-nav,
    .ld-nav-links a::after {
        transition: none;
    }

    .ld-wordmark-sheen,
    .ld-pill-dot,
    .ld-pill-sheen,
    .ld-hero-scroll-track::after {
        animation: none;
    }

    .ld-scene-embers i {
        animation: none;
        opacity: 0.4;
    }

    .ld-btn:hover,
    .ld-member:hover .ld-member-img {
        transform: none;
    }
}


/* ── 10. The fox as a guide (js/fox-guide.js) ──────────────────────────────────────────────
   Fixed to the viewport and moved only by transform. The root is inert; the fox's own box
   (.fg-hit) and the bubble take the pointer. Same bubble as the band's, so he sounds like
   himself wherever he is. */

.fg {
    position: fixed;
    left: 0;
    top: 0;
    z-index: 45;
    width: var(--fg-frame);
    height: var(--fg-frame);
    pointer-events: none;
    will-change: transform;
    outline: none;
}

.fg-rot {
    width: 100%;
    height: 100%;
    transform-origin: 50% 50%;
}

.fg-sprite {
    display: block;
    width: 100%;
    height: 100%;
    transform-origin: 50% 50%;
    pointer-events: none;
    transition: filter 0.2s ease;
}

.fg-hit {
    position: absolute;
    pointer-events: auto;
    cursor: pointer;
    border-radius: 40%;
}

.fg.is-open .fg-sprite,
.fg:focus-visible .fg-sprite {
    filter: drop-shadow(0 0 10px rgba(255, 214, 130, 0.85)) drop-shadow(0 0 22px rgba(255, 170, 60, 0.35));
}

/* Keyboard focus shows as the same glow the pointer gets — no box around his frame. */

/* Belt and braces for the hand-over between the guide and the band. An <img> with no src
   attribute still lays out at its width/height attributes, and Chrome paints a 1px #C0C0C0
   placeholder border around that empty box — 456 px square, clipped by the band's overflow to
   a 357 px rectangle sitting exactly where the fox should be. Both foxes null their src between
   states, so neither is allowed to paint while it has none. */
.fg-sprite:not([src]),
.ec-runner-sprite:not([src]) {
    visibility: hidden;
}

.fg-bubble {
    position: absolute;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.55rem;
    padding: 0.75rem 1rem 0.85rem;
    min-width: max-content;
    max-width: 17rem;
    transform: translate(-50%, 6px);
    background: rgba(21, 17, 13, 0.94);
    border: 1px solid rgba(231, 193, 132, 0.34);
    border-radius: 12px;
    box-shadow: 0 18px 40px -18px rgba(0, 0, 0, 0.9);
    color: var(--ld-text);
    font-family: var(--ld-font-text);
    font-size: 0.9375rem;
    line-height: 1.45;
    text-align: center;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 260ms ease, transform 260ms cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear 260ms;
}

.fg.is-talking .fg-bubble {
    opacity: 1;
    visibility: visible;
    transform: translate(-50%, 0);
    pointer-events: auto;
    transition-delay: 0s, 0s, 0s;
}

/* An invisible apron below the bubble, so the pointer can cross from the fox to the buttons
   without ever being "outside". */
.fg-bubble::before {
    content: "";
    position: absolute;
    left: -12px;
    right: -12px;
    top: 100%;
    height: 56px;
}

.fg-bubble.is-side::before {
    left: -30px;
    right: auto;
    top: -12px;
    bottom: -12px;
    width: 30px;
    height: auto;
}

/* The tail: a rotated square, so it carries the same 1px edge as the body. */
.fg-bubble::after {
    content: "";
    position: absolute;
    left: var(--fg-tail, 50%);
    bottom: -6px;
    width: 11px;
    height: 11px;
    margin-left: -5.5px;
    background: rgba(21, 17, 13, 0.94);
    border-right: 1px solid rgba(231, 193, 132, 0.34);
    border-bottom: 1px solid rgba(231, 193, 132, 0.34);
    transform: rotate(45deg);
}

/* On the wall the bubble sits beside him, tail to the left. */
.fg-bubble.is-side {
    transform: translate(6px, -50%);
}

.fg.is-talking .fg-bubble.is-side {
    transform: translate(0, -50%);
}

.fg-bubble.is-side::after {
    left: -6px;
    bottom: auto;
    top: 50%;
    margin-left: 0;
    margin-top: -5.5px;
    border-right: 0;
    border-bottom: 1px solid rgba(231, 193, 132, 0.34);
    border-left: 1px solid rgba(231, 193, 132, 0.34);
}

.fg-line {
    display: block;
    opacity: 0.9;
}

.fg-actions {
    display: none;
    gap: 0.45rem;
    flex-wrap: wrap;
    justify-content: center;
}

.fg.is-open .fg-actions {
    display: flex;
}

.fg-btn {
    padding: 0.45rem 0.95rem;
    border: 1px solid transparent;
    border-radius: 999px;
    background: var(--ld-gold);
    color: var(--ld-ink);
    font-family: var(--ld-font-display);
    font-size: 0.875rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    transition: filter 160ms ease, transform 160ms ease, background 160ms ease;
}

.fg-btn:hover,
.fg-btn:focus-visible {
    filter: brightness(1.08);
    transform: translateY(-1px);
}

.fg-btn:focus-visible {
    outline: 2px solid var(--ld-gold);
    outline-offset: 3px;
}

.fg-btn-quiet {
    background: transparent;
    border-color: rgba(231, 193, 132, 0.4);
    color: var(--ld-text-2);
}

.fg-btn-quiet:hover,
.fg-btn-quiet:focus-visible {
    background: rgba(231, 193, 132, 0.12);
    color: var(--ld-text);
}
