/* ==========================================================================
   Stage — the fixed blueprint backdrop between header and footer, with the
   slides changing at its centre.

   Without JavaScript the slides simply stack and the page scrolls, so the
   content stays reachable and crawlable. The deck behaviour is layered on top.
   ========================================================================== */

.shell {
    display: flex;
    flex-direction: column;
    min-height: 100svh;
}

.js .shell {
    height: 100svh;
}

/* The deck owns the wheel, so the page itself must never scroll or rubber-band
   underneath it. Set by the script, so this never traps a visitor without JS. */
.deck-on body {
    overflow: hidden;
    overscroll-behavior: none;
}

.stage {
    position: relative;
    flex: 1;
    display: grid;
    /* minmax(0, …) rather than a bare 1fr: a grid track is sized to its content
       by default and would grow past the stage, taking the slide's scroll
       container with it and clipping the overflow instead of scrolling it. */
    grid-template-rows: minmax(0, 1fr);
    grid-template-columns: minmax(0, 1fr);
    place-items: center;
    /* No vertical padding: the slide's scroll area has to reach the header and
       footer edges, otherwise content is cut at an invisible line inside the
       stage and simply looks broken. The breathing room moves into the slide
       itself, where it scrolls with the content. */
    padding-inline: var(--container-gutter);
    overflow: hidden;
}

/* Content leaving the scroll area fades into the page instead of being
   chopped, so the boundary reads as intentional at both ends. */
.js .stage::before,
.js .stage::after {
    content: "";
    position: absolute;
    z-index: 2;
    left: 0;
    right: 0;
    height: var(--space-6);
    pointer-events: none;
}

.js .stage::before {
    top: 0;
    background: linear-gradient(
        180deg,
        var(--color-surface) 12%,
        rgb(255 255 255 / 0) 100%
    );
}

.js .stage::after {
    bottom: 0;
    background: linear-gradient(
        0deg,
        var(--color-surface) 12%,
        rgb(255 255 255 / 0) 100%
    );
}

/* The technical drawing of the mascot's parts, spanning the full width. Its
   middle is empty by design, which is exactly where the slides sit. */
.stage__backdrop {
    position: absolute;
    inset: 0;
    /* Fitted whole, never cropped: the stage is wider than 16:9 on a large
       screen, so stretching to the full width pushed the drawn parts past the
       top and bottom edges. Contain keeps every part on screen and still fills
       nearly the entire width at ordinary laptop proportions. */
    background: url(../assets/image/main.webp) center / contain no-repeat;
    opacity: 0.9;
    pointer-events: none;
}

.stage__slides {
    position: relative;
    z-index: 1;
    width: min(100%, 44rem);
}

/* A soft wash of the page colour behind the text, so it never competes with
   the fine linework. Anchored to the slide block rather than to the drawing,
   so it follows the text at every viewport instead of being guessed at. */
.stage__slides::before {
    content: "";
    position: absolute;
    inset: -18% -14%;
    z-index: -1;
    pointer-events: none;
    background: radial-gradient(
        ellipse at center,
        rgb(255 255 255 / 0.96) 0%,
        rgb(255 255 255 / 0.9) 50%,
        rgb(255 255 255 / 0) 100%
    );
}

/* ---------- slides ---------- */

.slide {
    display: grid;
    /* Without the explicit track the column takes the widest child's max-content
       width and pushes the text out past the slide, where it gets clipped. */
    grid-template-columns: minmax(0, 1fr);
    /* Packed from the top rather than stretched: grid's default would spread the
       rows to fill the slide, which put every slide's artwork at a different
       height and pushed the tallest ones out of reach above the scroll area. */
    align-content: start;
    gap: var(--space-4);
    justify-items: center;
    text-align: center;
}

.js .slide {
    grid-area: 1 / 1;
    /* Grid items refuse to shrink below their content unless told to, which
       would stop the slide from ever scrolling. */
    min-height: 0;
    /* Every slide fills the stage, so its content always starts at the same
       height. Left to stretch on its own a slide sizes to its content and each
       one begins somewhere different. */
    height: 100%;
    opacity: 0;
    visibility: hidden;
    /* Each slide arrives its own way — see the per-slide poses below. Only
       opacity and transform are animated, so every one of them stays on the
       compositor and costs nothing to run. */
    transform: var(--enter, translateY(1.25rem));
    transition:
        opacity var(--duration-base) var(--ease-out),
        transform var(--duration-slow) var(--ease-out),
        visibility 0s linear var(--duration-slow);

    /* A slide taller than the stage scrolls inside itself rather than being
       clipped; the deck only moves on once that scroll reaches an end. The bar
       is hidden — the deck blocks in the header already show where you are.
       overscroll-behavior stops the gesture leaking to the document. */
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: none;
    /* Clears the fades at both ends, so the first and last lines are never
       sitting under one of them when the slide is at rest. */
    padding-block: var(--space-6);
}

.js .slide::-webkit-scrollbar {
    display: none;
}

.js .stage__slides {
    display: grid;
    /* Both halves matter: max-height caps the wrapper at the stage, and the
       minmax track stops the slide inside it from stretching the row to its own
       content height. Without the track the slide simply grows and spills out
       of the stage instead of scrolling within it. */
    /* A definite height rather than a cap: with max-height alone the wrapper
       still sizes to its tallest slide and gets centred in the stage, which
       offset every overflowing slide downwards by a different amount. */
    height: 100%;
    grid-template-rows: minmax(0, 1fr);
    grid-template-columns: minmax(0, 1fr);
}

.js .slide.is-active {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition:
        opacity var(--duration-base) var(--ease-out),
        transform var(--duration-slow) var(--ease-out);
}

/* A different entrance per slide, so moving through the deck never feels like
   the same card being redrawn. */
.js .slide:nth-child(1) { --enter: translateY(1.5rem); }
.js .slide:nth-child(2) { --enter: translateX(2.5rem); }
.js .slide:nth-child(3) { --enter: scale(0.94); }
.js .slide:nth-child(4) { --enter: translateY(-1.75rem) rotate(-1.2deg); }
.js .slide:nth-child(5) { --enter: translateX(-2.5rem); }
.js .slide:nth-child(6) { --enter: scale(1.05); }

/* The one line on each slide that names the thing plainly — the headings are
   written to sell, this is written to be searched for. */
.slide__kicker {
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-accent);
}

/* The shared pattern for every slide: artwork on top, then the words. Sized
   against the viewport height so a tall image never pushes the call to action
   off a short screen. */
.slide__art {
    width: min(100%, 34rem);
    height: auto;
    max-height: 32svh;
    object-fit: contain;
}

/* The artwork trails the scroll by a fraction, so the slide has a foreground
   and a background instead of one flat sheet. --drift is written by the deck;
   with no JS it is simply absent and the image sits still. Promoted to its own
   layer so the drift is a compositor transform and never a repaint. */
.js .slide__art {
    transform: translate3d(0, var(--drift, 0px), 0);
}

.js .slide.is-active .slide__art {
    will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
    .js .slide__art {
        transform: none;
    }
}

.slide__title {
    font-size: clamp(1.6rem, 1.1rem + 2.2vw, 2.6rem);
}

.slide__text {
    max-width: 34rem;
    font-size: var(--text-lg);
    color: var(--color-text-muted);
}

.slide__points {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-2) var(--space-5);
}

.slide__point {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    color: var(--color-text);
}

.slide__point::before {
    content: "";
    width: 0.4rem;
    height: 0.4rem;
    border-radius: 50%;
    background: var(--color-accent);
}

.slide__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--space-3) var(--space-4);
    margin-top: var(--space-2);
}

.slide__note {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
}

/* A slim decorative loop that closes the slide. Deliberately narrower than the
   artwork above it so it reads as a footnote rather than a second hero; the
   aspect-ratio is declared so it reserves its box before the poster decodes
   and nothing below it shifts.

   The clip's own backdrop is #fbfbfb against a #ffffff page — four levels
   apart, invisible as a colour but plainly visible as a rectangle. Two things
   remove it: the brightness lift clips that near-white to true white, and the
   mask feathers all four edges so there is no boundary left to see. */
.slide__loop {
    --feather: 9%;

    width: min(100%, 26rem);
    height: auto;
    aspect-ratio: 1440 / 560;
    margin-top: var(--space-2);
    object-fit: cover;
    filter: brightness(1.025);
    -webkit-mask-image:
        linear-gradient(to right, transparent, #000 var(--feather), #000 calc(100% - var(--feather)), transparent),
        linear-gradient(to bottom, transparent, #000 var(--feather), #000 calc(100% - var(--feather)), transparent);
    mask-image:
        linear-gradient(to right, transparent, #000 var(--feather), #000 calc(100% - var(--feather)), transparent),
        linear-gradient(to bottom, transparent, #000 var(--feather), #000 calc(100% - var(--feather)), transparent);
    -webkit-mask-composite: source-in;
    mask-composite: intersect;
}

/* ---------- rooms list ---------- */

.rooms {
    display: grid;
    gap: var(--space-3);
    width: 100%;
    text-align: left;
}

@media (min-width: 48rem) {
    .rooms {
        grid-template-columns: repeat(2, 1fr);
        gap: var(--space-3) var(--space-5);
    }
}

.rooms__item {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
}

.rooms__icon {
    flex: none;
    font-size: 1.15rem;
    line-height: 1.4;
}

.rooms__body {
    display: grid;
    gap: 2px;
    min-width: 0;
}

.rooms__name {
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: 700;
}

.rooms__text {
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
    color: var(--color-text-muted);
}

.rooms--tight {
    gap: var(--space-2) var(--space-5);
}

/* ---------- settings demo ---------- */

.settings {
    display: grid;
    gap: var(--space-4);
    width: 100%;
    text-align: left;
}

@media (min-width: 48rem) {
    .settings {
        grid-template-columns: repeat(2, 1fr);
        gap: var(--space-5);
    }
}

.settings__group {
    display: grid;
    gap: var(--space-2);
    align-content: start;
    padding: var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    box-shadow: var(--shadow-sm);
}

.settings__title {
    font-family: var(--font-display);
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.settings__hint {
    font-size: var(--text-xs);
    color: var(--color-accent);
}

.switches {
    display: grid;
    gap: var(--space-1);
}

.switch__control {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    padding: var(--space-2) 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: var(--text-sm);
    color: var(--color-text);
    text-align: left;
    cursor: pointer;
}

.switch__track {
    position: relative;
    flex: none;
    width: 2.4rem;
    height: 1.35rem;
    border-radius: var(--radius-pill);
    background: var(--ink-200);
    box-shadow: 0 1px 2px rgb(4 4 7 / 0.14) inset;
    transition: background-color var(--duration-base) var(--ease-out);
}

.switch__track::after {
    content: "";
    position: absolute;
    top: 0.175rem;
    left: 0.175rem;
    width: 1rem;
    height: 1rem;
    border-radius: 50%;
    background: var(--color-surface);
    box-shadow: 0 1px 3px rgb(4 4 7 / 0.3);
    transition: transform var(--duration-base) var(--ease-out);
}

.switch__control[aria-checked="true"] .switch__track {
    background: var(--color-accent);
}

.switch__control[aria-checked="true"] .switch__track::after {
    transform: translateX(1.05rem);
}

.switch__control[aria-checked="false"] .switch__label {
    color: var(--color-text-muted);
}

/* ---------- subscription carousel ---------- */

.plans {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
}

/* Scroll-snap does the paging, so the track works by touch on its own and the
   arrows are an addition rather than the only way through. */
.plans__track {
    display: flex;
    flex: 1;
    min-width: 0;
    gap: var(--space-3);
    padding: var(--space-1);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    overscroll-behavior-x: contain;
}

.plans__track::-webkit-scrollbar {
    display: none;
}

.plans__nav {
    flex: none;
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    border: 1px solid var(--color-border);
    border-radius: 50%;
    background: var(--color-surface);
    color: var(--color-text-muted);
    cursor: pointer;
    transition:
        color var(--duration-fast) var(--ease-out),
        border-color var(--duration-fast) var(--ease-out);
}

.plans__nav svg {
    width: 1rem;
    height: 1rem;
}

.plans__nav:hover:not(:disabled) {
    color: var(--color-accent);
    border-color: var(--color-accent);
}

.plans__nav:disabled {
    opacity: 0.35;
    cursor: default;
}

.plan {
    position: relative;
    flex: 0 0 13.5rem;
    scroll-snap-align: start;
    /* Column flex rather than grid: it lets the buy link claim the leftover
       space with margin-top:auto, so the buttons line up across cards whose
       feature lists are different lengths. */
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    box-shadow: var(--shadow-sm);
    text-align: left;
}

.plan--featured {
    border-color: var(--brand-300);
    box-shadow: 0 8px 24px -14px rgb(252 58 114 / 0.7);
}

.plan__badge {
    font-size: 0.7rem;
    font-weight: var(--weight-semibold);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-accent);
}

.plan__badge--muted {
    color: var(--color-text-muted);
}

.plan__name {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: 800;
}

.plan__price {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

.plan__stars {
    font-family: var(--font-display);
    font-size: 1.35rem;
    font-weight: 800;
    color: var(--color-text);
}

.plan__term {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
}

.plan__list {
    display: grid;
    gap: 2px;
    margin-top: var(--space-2);
    font-size: var(--text-xs);
    line-height: var(--leading-snug);
    color: var(--color-text-muted);
}

.plan__list li {
    padding-left: var(--space-4);
    position: relative;
}

.plan__list li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.5em;
    width: 0.3rem;
    height: 0.3rem;
    border-radius: 50%;
    background: var(--brand-300);
}

.plan__buy {
    margin-top: auto;
    padding-top: var(--space-3);
    padding: var(--space-2);
    border-radius: var(--radius-pill);
    background: var(--color-surface-sunken);
    color: var(--color-text);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    text-align: center;
    text-decoration: none;
    transition:
        background-color var(--duration-fast) var(--ease-out),
        color var(--duration-fast) var(--ease-out);
}

.plan__buy:hover,
.plan--featured .plan__buy {
    background: var(--color-accent);
    color: var(--color-text-inverse);
}

/* ---------- join ---------- */

.join {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-3);
    width: 100%;
}

.join__action {
    flex: 1 1 13rem;
    max-width: 16rem;
    padding-block: var(--space-4);
    font-size: var(--text-base);
}

.join__referral {
    max-width: 34rem;
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
    color: var(--color-text-muted);
}

/* ---------- faq ----------
   Collapsed by default: the answers are there for anyone who wants them and
   for a crawler reading the markup, without turning the closing slide into a
   wall of text. */

.faq {
    display: grid;
    gap: var(--space-2);
    width: 100%;
    max-width: 34rem;
    text-align: left;
}

.faq__item {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
}

.faq__item[open] {
    border-color: var(--color-border-strong);
}

/* Flex rather than a floated marker: on a question that wraps to two lines a
   float drops the sign to the last line instead of keeping it with the first. */
.faq__question {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    line-height: var(--leading-snug);
    cursor: pointer;
    list-style: none;
}

/* Safari still paints its own disclosure triangle without this. */
.faq__question::-webkit-details-marker {
    display: none;
}

.faq__question::after {
    content: "+";
    flex: none;
    color: var(--color-accent);
    font-weight: var(--weight-bold);
}

.faq__item[open] .faq__question::after {
    content: "−";
}

.faq__answer {
    padding: 0 var(--space-4) var(--space-4);
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
    color: var(--color-text-muted);
}

/* ---------- status roulette ---------- */

.roulette {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    gap: var(--space-2);
    width: 100%;
    max-width: 28rem;
}

/* Window and lever on one row: the control belongs to the machine, and the
   slide stays short enough to fit without scrolling on most screens. */
.roulette__machine {
    display: flex;
    align-items: stretch;
    gap: var(--space-2);
    width: 100%;
    /* The reel holds long, non-wrapping status names; without this the row is
       sized to them and pushes past the slide. */
    min-width: 0;
    padding: var(--space-2);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    box-shadow: var(--shadow-sm);
}

/* One cell tall, so the strip beneath reads as a single spinning window. */
.roulette__window {
    position: relative;
    flex: 1;
    min-width: 0;
    height: 3rem;
    overflow: hidden;
    border-radius: var(--radius-md);
    background: var(--color-surface-sunken);
    box-shadow: 0 2px 6px rgb(4 4 7 / 0.12) inset;
}

.roulette__spin {
    flex: none;
    padding-inline: var(--space-4);
    border: 0;
    border-radius: var(--radius-md);
    background: var(--color-accent);
    color: var(--color-text-inverse);
    font: inherit;
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    white-space: nowrap;
    cursor: pointer;
    box-shadow:
        0 1px 0 rgb(255 255 255 / 0.35) inset,
        0 6px 16px -8px rgb(252 58 114 / 0.95);
    transition: background-color var(--duration-fast) var(--ease-out);
}

.roulette__spin:hover:not(:disabled) {
    background: var(--color-accent-hover);
}

.roulette__spin:disabled {
    opacity: 0.55;
    cursor: default;
}

.roulette__reel {
    will-change: transform;
}

.roulette__cell {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    height: 3rem;
    padding-inline: var(--space-3);
}

.roulette__name {
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: 700;
    white-space: nowrap;
}

.roulette__tier {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    white-space: nowrap;
}

.roulette__cell--rare .roulette__tier { color: #2f7fd0; }
.roulette__cell--legendary .roulette__tier { color: #b8860b; }
.roulette__cell--mythic .roulette__tier { color: var(--color-accent); }

/* Marks the row that counts, so the result is not just asserted in text. */
.roulette__pointer {
    position: absolute;
    inset-block: 0;
    left: 0;
    width: 3px;
    border-radius: var(--radius-pill);
    background: linear-gradient(180deg, var(--brand-300), var(--brand-600));
}

.roulette__result {
    min-height: 1.4em;
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

.roulette__result.is-won {
    font-weight: var(--weight-semibold);
    color: var(--color-text);
}

.roulette__result--mythic {
    color: var(--color-accent);
}

/* A phone has to fit the whole slide between header and footer without
   scrolling, so everything tightens rather than the call to action falling off
   the bottom. */
@media (max-width: 40rem) {
    .slide {
        gap: var(--space-3);
    }

    .slide__art {
        max-height: 19svh;
    }

    .slide__text {
        font-size: var(--text-base);
        line-height: var(--leading-snug);
    }

    .slide__points {
        gap: var(--space-1) var(--space-4);
    }

    .slide__point {
        font-size: var(--text-xs);
    }

    .slide__actions {
        margin-top: 0;
    }
}

/* ---------- no-JS stacking ---------- */

/* Anchored to html: a bare :not(.js) matches any ancestor that lacks the class
   — including the wrapper — so this margin was landing on every slide but the
   first even with scripting on, and pushed them all out of alignment. */
html:not(.js) .slide + .slide {
    margin-top: var(--space-8);
}

@media (prefers-reduced-motion: reduce) {
    .js .slide {
        transition: none;
    }
}
