/* The Screening Room — the public homepage.

   Tokens mirror DESIGN.md (After Dark). The page is a dark room: the only
   lit object is the screen, and a single gold beam from top-right is the only
   light source. Scroll dims the beam; nothing else on the page is lit. */

@font-face {
    font-family: 'Bebas Neue';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/landing/fonts/BebasNeue-400.woff2') format('woff2');
}

@font-face {
    font-family: 'Archivo';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('/landing/fonts/Archivo-var.woff2') format('woff2');
}

:root {
    --ground: #0E0C10;
    --raised: #16131B;
    --panel: #1C1822;
    --panel-2: #221D2A;
    --line: rgba(242, 238, 233, 0.08);
    --text: #F2EEE9;
    --text-soft: #D8CFC6;
    --text-muted: #A79FA6;
    --text-faint: #8B8490;
    --gold: #E3A94E;
    --gold-deep: #B87F2E;
    --gold-hover: #F2C77E;
    --gold-grad: #C98F35;
    --red: #D4574D;
    --on-gold: #14100A;
    --display: 'Bebas Neue', 'Oswald', 'Arial Narrow', sans-serif;
    --body: 'Archivo', 'Helvetica Neue', system-ui, sans-serif;
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --beam: 1;               /* 1 = house lights half up; 0 = fully dark. Set by scroll. */
    --screen-w: min(1180px, calc(100vw - 64px));
}

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

html { scroll-behavior: smooth; }

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

body {
    margin: 0;
    min-height: 100vh;
    background: var(--ground);
    color: var(--text);
    font-family: var(--body);
    font-size: 16px;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
}

::selection { background: rgba(227, 169, 78, 0.35); color: var(--text); }

a { color: var(--gold); text-decoration: none; }
a:hover { color: var(--gold-hover); }

/* The gold ring is the one focus indicator on the page, so it has to win the
   cascade everywhere. At (0,1,0) it lost to .sheet-card's own 1px outline,
   which measures about 1.05:1 against the panel — twenty tab stops with no
   visible focus at all. :where() keeps the specificity at zero for the
   selector while !important settles it against later same-weight rules. */
:focus-visible {
    outline: 2px solid var(--gold) !important;
    outline-offset: 3px;
    border-radius: 4px;
}

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

/* Sits off-screen until tabbed to, then lands on the beam-lit ground like any
   other control in the room. */
.skip {
    position: fixed;
    top: 12px;
    left: 12px;
    z-index: 20;
    padding: 12px 18px;
    border-radius: 9px;
    background: var(--gold);
    color: var(--on-gold);
    font-size: 14px;
    font-weight: 600;
    transform: translateY(-160%);
    transition: transform 0.2s var(--ease-out);
}

/* :focus, not :focus-visible — a skip link has to appear for any way of
   reaching it, and :focus-visible is not guaranteed for programmatic focus. */
.skip:focus {
    transform: translateY(0);
    color: var(--on-gold);
}

button {
    font: inherit;
    color: inherit;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
}

img { display: block; max-width: 100%; }

.display {
    font-family: var(--display);
    font-weight: 400;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    line-height: 0.92;
    margin: 0;
    text-wrap: balance;
}

/* ---------- Light ---------- */

/* The beam: a cone from the top-right corner, brightest near its source,
   with the slightest warm spill across the floor. Opacity rides --beam. */
.beam {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 0;
    /* A wedge from the top-right corner aimed at the screen. Conic for the
       edges, radial for the fall-off with distance; the blur softens the
       edges the way air does. */
    background:
        conic-gradient(from 196deg at 103% -6%,
            transparent 0deg,
            rgba(227, 169, 78, 0.06) 9deg,
            rgba(227, 169, 78, 0.30) 16deg,
            rgba(227, 169, 78, 0.42) 24deg,
            rgba(227, 169, 78, 0.30) 32deg,
            rgba(227, 169, 78, 0.06) 39deg,
            transparent 48deg);
    -webkit-mask-image: radial-gradient(ellipse 95% 130% at 103% -6%, #000 0%, rgba(0, 0, 0, 0.7) 40%, rgba(0, 0, 0, 0.18) 72%, transparent 92%);
    mask-image: radial-gradient(ellipse 95% 130% at 103% -6%, #000 0%, rgba(0, 0, 0, 0.7) 40%, rgba(0, 0, 0, 0.18) 72%, transparent 92%);
    filter: blur(22px);
    opacity: var(--beam);
    transition: opacity 0.45s var(--ease-out);
    mix-blend-mode: screen;
}

/* The same wedge, sharper and fainter, so the cone reads as light with an
   edge rather than only as a blurred wash. */
.beam::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
        conic-gradient(from 196deg at 103% -6%,
            transparent 12deg,
            rgba(242, 238, 233, 0.07) 17deg,
            rgba(242, 238, 233, 0.10) 24deg,
            rgba(242, 238, 233, 0.07) 31deg,
            transparent 36deg);
    -webkit-mask-image: radial-gradient(ellipse 90% 120% at 103% -6%, #000 0%, rgba(0, 0, 0, 0.5) 45%, transparent 85%);
    mask-image: radial-gradient(ellipse 90% 120% at 103% -6%, #000 0%, rgba(0, 0, 0, 0.5) 45%, transparent 85%);
}

/* Projector grain: an animated noise field over everything, faint enough to
   be felt rather than seen. */
.grain {
    position: fixed;
    inset: -50%;
    width: 200%;
    height: 200%;
    pointer-events: none;
    z-index: 1;
    opacity: calc(0.05 + 0.14 * var(--beam));
    /* Concentrated inside the beam: the mask is the same wedge, in page
       space, which this oversized layer covers twice over. */
    -webkit-mask-image: conic-gradient(from 196deg at 76.5% 22%, transparent 6deg, #000 16deg, #000 32deg, transparent 44deg);
    mask-image: conic-gradient(from 196deg at 76.5% 22%, transparent 6deg, #000 16deg, #000 32deg, transparent 44deg);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 0.92 0 0 0 0 0.8 0 0 0 0.9 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    animation: grain 0.9s steps(6) infinite;
    mix-blend-mode: screen;
}

@keyframes grain {
    0% { transform: translate(0, 0); }
    20% { transform: translate(-3%, 2%); }
    40% { transform: translate(2%, -4%); }
    60% { transform: translate(-4%, -1%); }
    80% { transform: translate(3%, 3%); }
    100% { transform: translate(-1%, -2%); }
}

@media (prefers-reduced-motion: reduce) {
    .grain { animation: none; }
}

.dust {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 1;
    opacity: var(--beam);
    overflow: hidden;
}

.dust i {
    position: absolute;
    top: -2%;
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: rgba(242, 230, 205, 0.55);
    box-shadow: 0 0 6px rgba(242, 230, 205, 0.5);
    animation: dust var(--dur, 14s) linear var(--delay, 0s) infinite;
    opacity: 0;
}

@keyframes dust {
    0% { transform: translate(0, 0) scale(0.6); opacity: 0; }
    10% { opacity: 0.7; }
    90% { opacity: 0.35; }
    100% { transform: translate(var(--dx, -30vw), 70vh) scale(1); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .dust { display: none; }
}

/* ---------- Header ---------- */

.top {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 22px 32px;
}

.mark {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    font-family: var(--display);
    font-size: 24px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--gold);
}

.top-action {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    font-size: 13.5px;
    font-weight: 500;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    padding: 0 14px;
    border-radius: 9px;
    transition: color 0.15s ease, background 0.15s ease;
}

.top-action:hover { color: var(--text); background: rgba(242, 238, 233, 0.05); }

/* ---------- The room ---------- */

.room {
    position: relative;
    z-index: 2;
    /* Tall enough that the screen has somewhere to stay while the scroll
       changes the act; short enough that the room is not a slog. */
    height: 140vh;
}

.stage {
    position: sticky;
    top: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 28px;
    padding: 80px 32px 32px;
}

/* The screen: 2.39:1, the cinema's own shape. It carries a faint frame — a
   letterbox edge catching the beam — and everything inside it is projected. */
.screen {
    position: relative;
    width: var(--screen-w);
    aspect-ratio: 2.39 / 1;
    max-height: calc(100vh - 220px);
    background: #050407;
    border-radius: 6px;
    overflow: hidden;
    box-shadow:
        0 40px 90px rgba(0, 0, 0, 0.7),
        0 0 0 1px rgba(242, 238, 233, 0.06),
        0 0 120px rgba(227, 169, 78, calc(0.10 * var(--beam)));
    isolation: isolate;
}

.screen-edge {
    position: absolute;
    inset: 0;
    pointer-events: none;
    border-radius: 6px;
    box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.55);
    z-index: 5;
}

.act {
    position: absolute;
    inset: 0;
}

.act[hidden] { display: none; }

/* Act 1 */

.montage { position: absolute; inset: 0; }

.montage img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    /* Very slow drift; each frame is on screen ~6s. */
    transform: scale(1.06);
    transition: opacity 1.6s ease, transform 7s linear;
}

.montage img.on { opacity: 1; transform: scale(1); }

/* Mirrored frames — see montage.js. Deliberately the `scale` property and not
   a scaleX() inside `transform`: the drift owns `transform`, and folding the
   mirror in there makes the flip itself animate — the frame interpolates from
   +1.06 to -1.06, passing through zero, and you watch it collapse to a line
   and reopen. `scale` composes with `transform` but is not in the transition
   list, so the mirror is simply a fact about the frame. */
.montage img.flip { scale: -1 1; }

@media (prefers-reduced-motion: reduce) {
    .montage img { transition: none; transform: none; }
}

.card-scrim {
    position: absolute;
    inset: 0;
    background:
        linear-gradient(180deg, rgba(5, 4, 7, 0.15) 0%, rgba(5, 4, 7, 0) 35%, rgba(5, 4, 7, 0.55) 70%, rgba(5, 4, 7, 0.92) 100%),
        linear-gradient(90deg, rgba(5, 4, 7, 0.82) 0%, rgba(5, 4, 7, 0.45) 45%, rgba(5, 4, 7, 0.05) 80%);
}

.title-card {
    position: absolute;
    left: 6%;
    right: 6%;
    bottom: 11%;
    max-width: 62%;
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.title-card .card-head {
    font-size: clamp(40px, 6.4vw, 96px);
    letter-spacing: 0.05em;
    text-shadow: 0 8px 40px rgba(0, 0, 0, 0.6);
    animation: card-in 1.1s var(--ease-out) both;
}

.subline {
    margin: 0;
    max-width: 46ch;
    font-size: clamp(14px, 1.3vw, 18px);
    line-height: 1.5;
    color: var(--text-soft);
    text-shadow: 0 2px 18px rgba(0, 0, 0, 0.7);
    animation: card-in 1.1s 0.18s var(--ease-out) both;
}

@keyframes card-in {
    from { opacity: 0; transform: translateY(14px); filter: blur(6px); }
    to { opacity: 1; transform: none; filter: blur(0); }
}

@media (prefers-reduced-motion: reduce) {
    .title-card .card-head, .subline { animation: none; }
}

/* Act 2 — the deck on the screen */

.act-demo {
    background:
        radial-gradient(ellipse 60% 80% at 30% 50%, rgba(227, 169, 78, 0.07), transparent 70%),
        #0A080D;
}

.demo-grid {
    position: absolute;
    inset: 0;
    display: grid;
    /* Was 1.15fr 1fr, which gave the panel 46.5% of the screen to hold about
       170px of content — 309px of it was empty black. The deck is the act; the
       panel reports on it. */
    grid-template-columns: 1.55fr 1fr;
    gap: 0;
}

.deck {
    position: relative;
    display: grid;
    /* minmax(0, …): a poster's intrinsic height must never win over the row,
       or the rating buttons fall off the bottom of the screen. */
    grid-template-rows: auto minmax(0, 1fr) auto;
    align-items: center;
    justify-items: center;
    padding: 4% 5% 4% 6%;
    gap: 10px;
    min-height: 0;
}

.deck-head {
    justify-self: start;
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.deck-counter {
    font-size: 11.5px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--text-muted);
    font-weight: 600;
}

/* Four circles were a guessing game for a first-time visitor. The prompt
   says what the buttons are for; the words say what each one means. */
.deck-prompt {
    margin: 0;
    font-size: 14px;
    color: var(--text-soft);
}

.deck-card {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: minmax(0, 1fr);
    align-items: end;
    gap: 22px;
    min-height: 0;
    height: 100%;
    width: 100%;
}

.deck-poster {
    height: 100%;
    min-height: 0;
    width: auto;
    aspect-ratio: 2 / 3;
    object-fit: cover;
    border-radius: 8px;
    border: 1px solid var(--line);
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.65);
    background: linear-gradient(160deg, var(--panel-2), var(--panel));
    animation: poster-in 0.55s var(--ease-out) both;
}

@keyframes poster-in {
    from { opacity: 0; transform: translateX(-10px) scale(0.985); filter: blur(4px); }
    to { opacity: 1; transform: none; filter: blur(0); }
}

.deck-meta { display: flex; flex-direction: column; gap: 6px; padding-bottom: 4px; min-width: 0; }

.deck-title {
    font-size: clamp(26px, 3.1vw, 46px);
    letter-spacing: 0.04em;
    text-wrap: balance;
}

.deck-sub {
    font-size: 13px;
    color: var(--text-muted);
}

.rate {
    justify-self: start;
    display: flex;
    align-items: center;
    gap: 10px;
}

/* The product's own scale, read left to right: Hated (red outline), Meh,
   Liked (gold outline), Loved (gold fill, largest). */
.rate-btn {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    color: var(--text-muted);
    transition: transform 0.15s var(--ease-out), color 0.15s ease;
}

.rate-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    /* 0.18 alpha measured 1.34:1 against the ground — the Meh circle was
       effectively invisible. 0.42 clears the 3:1 WCAG 1.4.11 asks of a
       control's own boundary. */
    border: 1.5px solid rgba(242, 238, 233, 0.42);
    transition: border-color 0.15s ease, background 0.15s ease;
}

.rate-word {
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.02em;
    white-space: nowrap;
}

.rate-btn:hover { transform: translateY(-2px); color: var(--text); }
.rate-btn:hover .rate-mark { border-color: rgba(242, 238, 233, 0.4); }
.rate-btn:active { transform: scale(0.96); }

.rate-hated { color: var(--red); }
/* 0.55 measured 2.22:1 on the deck ground; 0.8 clears 3:1. */
.rate-hated .rate-mark { border-color: rgba(212, 87, 77, 0.8); }
.rate-hated:hover { color: var(--red); }
.rate-hated:hover .rate-mark { border-color: var(--red); background: rgba(212, 87, 77, 0.1); }

.rate-liked { color: var(--gold); }
.rate-liked .rate-mark { border-color: rgba(227, 169, 78, 0.55); }
.rate-liked:hover { color: var(--gold); }
.rate-liked:hover .rate-mark { border-color: var(--gold); background: rgba(227, 169, 78, 0.1); }

.rate-loved { color: var(--gold); }
.rate-loved .rate-mark {
    width: 52px;
    height: 52px;
    border: 0;
    background: linear-gradient(160deg, var(--gold), var(--gold-grad));
    color: var(--on-gold);
    box-shadow: 0 10px 26px rgba(227, 169, 78, 0.28);
}
.rate-loved:hover { color: var(--gold-hover); }
.rate-loved:hover .rate-mark { background: linear-gradient(160deg, var(--gold-hover), var(--gold)); }

.rate-rule { width: 1px; height: 40px; background: var(--line); margin: 0 6px; align-self: center; }

.rate-skip {
    align-self: center;
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    font-size: 13px;
    color: var(--text-muted);
    padding: 0 14px;
    border-radius: 9px;
    border: 1.5px solid rgba(242, 238, 233, 0.42);
    transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}
.rate-skip:hover { border-color: rgba(242, 238, 233, 0.32); }
.rate-skip:hover { color: var(--text); background: rgba(242, 238, 233, 0.05); }

/* The profile, drawing itself as ratings land. */
.profile {
    border-left: 1px solid var(--line);
    padding: 5% 6% 5% 5%;
    display: flex;
    flex-direction: column;
    gap: 16px;
    min-height: 0;
    overflow: hidden;
}

.profile-head {
    font-size: 11.5px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--text-muted);
    font-weight: 600;
    /* The label stays at the top and everything else falls to the bottom, so
       the profile shares a baseline with the deck's card and rating row rather
       than trailing off into empty space. */
    margin-bottom: auto;
}

.profile-empty { font-size: 14px; color: var(--text-faint); }
.profile-empty[hidden] { display: none; }

.genres {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 9px;
}

.genre {
    display: grid;
    grid-template-columns: 110px 1fr;
    align-items: center;
    gap: 12px;
    font-size: 13px;
    animation: row-in 0.5s var(--ease-out) both;
}

@keyframes row-in {
    from { opacity: 0; transform: translateX(8px); }
    to { opacity: 1; transform: none; }
}

.genre-name { color: var(--text-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.genre-bar {
    position: relative;
    height: 6px;
    border-radius: 3px;
    background: rgba(242, 238, 233, 0.08);
}

.genre-bar::before {
    content: "";
    position: absolute;
    left: 50%;
    top: -2px;
    bottom: -2px;
    width: 1px;
    background: rgba(242, 238, 233, 0.28);
}

/* Each bar is a half-track scaled from the centre line: positive grows to
   the right from the middle, negative grows to the left. Transform, not
   width, so the five of them animate without relayout. */
.genre-bar span {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    width: 50%;
    border-radius: 3px;
    background: var(--gold);
    transform-origin: left center;
    transform: scaleX(0);
    transition: transform 0.7s var(--ease-out), background 0.3s ease;
}

.genre-bar span.neg { left: 0; transform-origin: right center; background: var(--red); }

.dial[hidden] { display: none; }
.dial { display: flex; flex-direction: column; gap: 14px; margin-top: 4px; }
.dial-label { display: flex; justify-content: space-between; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-faint); }
.dial-track { position: relative; height: 2px; background: rgba(242, 238, 233, 0.12); }
.dial-pin {
    position: absolute;
    top: 50%;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--gold);
    box-shadow: 0 0 0 4px rgba(227, 169, 78, 0.18);
    transform: translate(-50%, -50%);
    left: 50%;
    transition: left 0.8s var(--ease-out);
}

.people { display: flex; flex-wrap: wrap; gap: 6px; font-size: 12.5px; }
.people span {
    padding: 5px 10px;
    border-radius: 999px;
    border: 1px solid rgba(227, 169, 78, 0.35);
    color: var(--gold);
    animation: row-in 0.5s var(--ease-out) both;
}
.people span.neg { border-color: rgba(212, 87, 77, 0.4); color: var(--red); }

/* Act 3 — the reveal */

.act-reveal { background: #050407; }

.reveal-backdrop {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    filter: saturate(0.8);
    animation: fade-in 1.4s ease both;
}

.reveal-scrim {
    position: absolute;
    inset: 0;
    background:
        linear-gradient(180deg, rgba(5, 4, 7, 0.2), rgba(5, 4, 7, 0.6) 60%, rgba(5, 4, 7, 0.94)),
        linear-gradient(90deg, rgba(5, 4, 7, 0.9) 0%, rgba(5, 4, 7, 0.6) 50%, rgba(5, 4, 7, 0.15));
}

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

.reveal {
    position: absolute;
    inset: 0;
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 4%;
    padding: 5% 7%;
}

.reveal-poster {
    height: 80%;
    max-height: 100%;
    width: auto;
    aspect-ratio: 2 / 3;
    object-fit: cover;
    /* The deck card and the contact sheet both carry a placeholder under the
       art; the reveal was the one that did not, so a failed TMDB fetch left an
       empty bordered rectangle at the single moment that matters most. */
    background: linear-gradient(160deg, var(--panel-2), var(--panel));
    border-radius: 10px;
    border: 1px solid var(--line);
    box-shadow: 0 34px 74px rgba(0, 0, 0, 0.62);
    animation: card-in 0.9s 0.5s var(--ease-out) both;
    align-self: center;
}

.reveal-copy { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.reveal-copy > * { animation: card-in 0.9s var(--ease-out) both; }
.reveal-copy > :nth-child(1) { animation-delay: 0.6s; }
.reveal-copy > :nth-child(2) { animation-delay: 0.75s; }
.reveal-copy > :nth-child(3) { animation-delay: 0.9s; }
.reveal-copy > :nth-child(4) { animation-delay: 1.05s; }
.reveal-copy > :nth-child(5) { animation-delay: 1.2s; }

.reveal-title { font-size: clamp(30px, 4.2vw, 64px); }

.reveal-meta { font-size: 13.5px; color: var(--text-muted); display: flex; gap: 10px; flex-wrap: wrap; }
.reveal-meta .match {
    padding: 4px 11px;
    border-radius: 999px;
    background: rgba(227, 169, 78, 0.14);
    border: 1px solid rgba(227, 169, 78, 0.4);
    color: var(--gold);
    font-weight: 600;
    letter-spacing: 0.01em;
}

/* The dead-end title card: the room fully dark, one line on the screen.
   Same furniture as the hero title card, centred rather than bottom-left,
   because there is no backdrop underneath it to sit against. */
.act-nothing {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 6% 8%;
    background: radial-gradient(ellipse 90% 70% at 50% 45%, rgba(28, 24, 34, 0.9), var(--ground) 78%);
}

.nothing-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 14px;
    max-width: 58ch;
}

.nothing-card h2 {
    font-size: clamp(32px, 4.6vw, 68px);
    animation: card-in 0.9s var(--ease-out) both;
}

.nothing-sub {
    margin: 0;
    font-size: clamp(14px, 1.25vw, 17px);
    line-height: 1.55;
    color: var(--text-soft);
    animation: card-in 0.9s 0.15s var(--ease-out) both;
}

.nothing-actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 4px;
    animation: card-in 0.9s 0.3s var(--ease-out) both;
}

.reveal-because { margin: 0; font-size: clamp(16px, 1.5vw, 20px); line-height: 1.4; color: var(--text); }
.reveal-because em { font-style: normal; color: var(--gold); }
.reveal-because:empty { display: none; }

.reveal-overview {
    margin: 0;
    font-size: 14px;
    line-height: 1.6;
    color: var(--text-soft);
    max-width: 58ch;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.reveal-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 6px; }

/* The gate the gold button leads to, named where it is clicked rather than
   only in the closing section. */
.reveal-fine { margin: 2px 0 0; font-size: 12px; color: var(--text-faint); }

/* ---------- Buttons ---------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    min-height: 46px;
    padding: 0 20px;
    border-radius: 9px;
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.01em;
    border: 1.5px solid transparent;
    transition: transform 0.15s var(--ease-out), background 0.15s ease, border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
    white-space: nowrap;
}

.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0) scale(0.985); }

.btn-gold {
    background: linear-gradient(160deg, var(--gold), var(--gold-grad));
    color: var(--on-gold);
    box-shadow: 0 12px 30px rgba(227, 169, 78, 0.22);
}
.btn-gold:hover { color: var(--on-gold); background: linear-gradient(160deg, var(--gold-hover), var(--gold)); box-shadow: 0 16px 36px rgba(227, 169, 78, 0.3); }

.btn-ghost { border-color: rgba(242, 238, 233, 0.46); color: var(--text-soft); font-weight: 500; }
.btn-ghost:hover { border-color: rgba(242, 238, 233, 0.38); color: var(--text); }

.btn-dim { border-color: rgba(242, 238, 233, 0.42); color: var(--text-muted); font-weight: 500; }
.btn-dim:hover { color: var(--text); border-color: rgba(242, 238, 233, 0.24); }

.btn-lg { min-height: 54px; padding: 0 28px; font-size: 15px; }

/* The seats. */
.seats {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    justify-content: center;
    animation: card-in 1s 0.5s var(--ease-out) both;
}

@media (prefers-reduced-motion: reduce) { .seats { animation: none; } }

/* ---------- After the room ---------- */

.how, .sheet-section, .close {
    position: relative;
    z-index: 2;
}

.how {
    max-width: 1180px;
    margin: 0 auto;
    padding: 120px 32px 80px;
}

.section-title {
    font-size: clamp(34px, 4.6vw, 64px);
    margin-bottom: 44px;
}

.how-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 40px;
}

.how-item h3 {
    margin: 0 0 10px;
    font-size: 17px;
    font-weight: 700;
    letter-spacing: -0.005em;
    color: var(--text);
}

.how-item p {
    margin: 0;
    font-size: 14.5px;
    line-height: 1.65;
    color: var(--text-muted);
    max-width: 44ch;
}

.how-item em { font-style: normal; color: var(--gold); }

/* Anything projected shares the screen's frame. */
.screen-like {
    position: relative;
    background: #050407;
    border-radius: 6px;
    overflow: hidden;
    box-shadow:
        0 24px 60px rgba(0, 0, 0, 0.6),
        0 0 0 1px rgba(242, 238, 233, 0.06);
}

.frame {
    aspect-ratio: 2.39 / 1;
    margin-bottom: 18px;
    padding: 7% 8%;
    display: flex;
    align-items: center;
}

.mini-genres {
    list-style: none;
    margin: 0;
    padding: 0;
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.mini-genres li {
    display: grid;
    grid-template-columns: 62px 1fr;
    align-items: center;
    gap: 10px;
    font-size: 11px;
    color: var(--text-soft);
}

.mini-bar {
    position: relative;
    display: block;
    height: 5px;
    border-radius: 3px;
    background: rgba(242, 238, 233, 0.08);
}

.mini-bar::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    width: calc(50% * var(--w));
    border-radius: 3px;
    background: var(--gold);
}

.mini-bar.neg::after {
    left: auto;
    right: 50%;
    background: var(--red);
}

.mini-dial { width: 100%; display: flex; flex-direction: column; gap: 8px; }
.mini-dial .dial-track { position: relative; height: 2px; background: rgba(242, 238, 233, 0.12); }
.mini-dial .dial-pin { position: absolute; top: 50%; width: 10px; height: 10px; border-radius: 50%; background: var(--gold); box-shadow: 0 0 0 4px rgba(227, 169, 78, 0.18); transform: translate(-50%, -50%); }
.mini-dial .dial-label { display: flex; justify-content: space-between; font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-faint); }
.mini-filters { display: flex; gap: 6px; margin-top: 4px; }
.mini-filters span { padding: 3px 9px; border-radius: 999px; border: 1px solid rgba(227, 169, 78, 0.45); color: var(--gold); font-size: 10.5px; }

.mini-because { display: flex; flex-direction: column; gap: 4px; }
.mini-title { font-family: var(--display); font-size: 26px; letter-spacing: 0.04em; text-transform: uppercase; line-height: 1; }
.mini-line { font-size: 12px; color: var(--text-soft); }
.mini-line em { font-style: normal; color: var(--gold); }
/* The pill carries a sentence now, not a percentage, so it takes its own row
   rather than wrapping into two lines beside the year and out-shouting the
   film title it is describing. */
.mini-meta { font-size: 11px; color: var(--text-muted); display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.mini-meta .match { padding: 2px 8px; border-radius: 999px; background: rgba(227, 169, 78, 0.14); border: 1px solid rgba(227, 169, 78, 0.4); color: var(--gold); font-weight: 600; }

/* The contact sheet: the pool on one frame, still. */
.sheet-section {
    max-width: 1180px;
    margin: 0 auto;
    padding: 40px 32px 80px;
}

.sheet-caption {
    margin: -28px 0 22px;
    font-size: 14px;
    color: var(--text-muted);
    max-width: 60ch;
}

.sheet {
    padding: 2.2%;
}

.sheet-grid {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(10, 1fr);
    gap: 1.4%;
}

.sheet-card {
    position: relative;
    margin: 0;
    aspect-ratio: 2 / 3;
    border-radius: 4px;
    overflow: hidden;
    background: linear-gradient(160deg, var(--panel-2), var(--panel));
    outline: 1px solid rgba(242, 238, 233, 0.06);
}

.sheet-card img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.4s var(--ease-out); }

.sheet-card:hover img { transform: scale(1.04); }

.sheet-card figcaption {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 22px 7px 7px;
    font-family: var(--display);
    font-size: 12px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    line-height: 1;
    background: linear-gradient(180deg, transparent, rgba(5, 4, 7, 0.92));
    opacity: 0;
    transition: opacity 0.2s ease;
}

.sheet-card:hover figcaption,
.sheet-card:focus-within figcaption { opacity: 1; }

.close {
    max-width: 820px;
    margin: 0 auto;
    padding: 80px 32px 120px;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 18px;
}

.close-title { font-size: clamp(36px, 5.2vw, 74px); }

.close-sub { margin: 0; max-width: 52ch; font-size: 15.5px; line-height: 1.6; color: var(--text-muted); }

.close-fine { margin: 0; font-size: 12.5px; color: var(--text-faint); }

.foot {
    position: relative;
    z-index: 2;
    display: flex;
    justify-content: space-between;
    gap: 24px;
    flex-wrap: wrap;
    padding: 28px 32px;
    border-top: 1px solid var(--line);
    font-size: 12px;
    color: var(--text-faint);
}

/* The TMDB link measured 35x13, under the 24x24 minimum of WCAG 2.5.8. */
.foot a {
    display: inline-block;
    min-height: 24px;
    padding: 5px 2px;
}

/* ---------- Reduced motion ---------- */

/* The blocks above cover the room's ambient motion — the grain, the dust, the
   montage crossfade, the opening title card. Everything the demo and the reveal
   do was left running, which is precisely where the motion is densest: a
   visitor who asked for less got the calm hero and then the full choreography.
   The brief's own rule is that these become cuts, so they do.
   State still changes and hierarchy still reads; only the travel is removed.
   The beam's opacity fade is deliberately kept — it is a light level, not
   movement, and it carries the page's one authored moment. */
@media (prefers-reduced-motion: reduce) {
    .deck-poster,
    .genre,
    .people span,
    .reveal-backdrop,
    .reveal-poster,
    .reveal-copy > *,
    .nothing-card h2,
    .nothing-sub,
    .nothing-actions {
        animation: none;
    }

    .genre-bar span,
    .dial-pin,
    .sheet-card img {
        transition: none;
    }

    /* A hover that scales the artwork is movement, not feedback; the caption
       already reports the hover. */
    .sheet-card:hover img { transform: none; }
}

/* Seats follow the act: once the deck is running, Try it has been tried, and
   once a film has been found the gold seat asks for the only thing left. */
body[data-act="demo"] #seat-try,
body[data-act="reveal"] #seat-try,
body[data-act="nothing"] #seat-try { display: none; }
/* On the reveal and the dead end the screen carries its own gold action, so
   the seats stay quiet rather than offering a second route to the same URL
   under a different promise. */
body[data-act="reveal"] #seat-signin,
body[data-act="nothing"] #seat-signin { display: none; }


/* ---------- Signed-in ---------- */

body.signed-in .seats .btn-dim,
body.signed-in .close-fine { display: none; }

/* ---------- Narrow ---------- */

@media (max-width: 900px) {
    /* The demo stacks here, so the frame has to stop being a cinema letterbox:
       at 768px wide a 2.39:1 screen is 295px tall, which left the deck 125px
       once the panel took its share and rendered the poster at 2x2px with the
       rating row overlapping the panel. 3:2 gives the stack room to exist. */
    /* The ratio is the intent; the viewport is the limit. At 900x700 a 4:3
       frame is 654px tall and pushes the seats off the bottom, so height wins
       and the frame letterboxes a little wider than 4:3. */
    .screen { aspect-ratio: 4 / 3; max-height: calc(100vh - 230px); }
    .demo-grid { grid-template-columns: 1fr; grid-template-rows: 1fr auto; }
    .profile { border-left: 0; border-top: 1px solid var(--line); padding: 12px 6% 14px; gap: 10px; }
    .genres { gap: 6px; }
    .genre { grid-template-columns: 90px 1fr; font-size: 12px; }
    .people { display: none; }
    /* Three rows read the shape well enough once stacked; five took 170px of a
       469px frame and left the poster at 103px. */
    .profile .genre:nth-child(n + 4) { display: none; }
    .deck { padding: 4% 6%; }
    .how-grid { grid-template-columns: 1fr; gap: 28px; }
}

/* On a phone the room turns its screen portrait: a letterbox this narrow is
   a thumbnail, and the deck needs height more than width. */
@media (max-width: 640px) {
    :root { --screen-w: calc(100vw - 28px); }
    .top { padding: 16px 18px; }
    .mark { font-size: 20px; }
    .room { height: 175vh; }
    .stage { padding: 64px 14px 18px; gap: 16px; }
    /* DESIGN-SYSTEM AMENDMENT: the phone frame was 3:4, which at 347px wide
       left 179px of viewport unused and squeezed the poster — the one thing
       the first principle says is the interface — down to 46x68. 2:3 takes the
       proportions of the poster itself and hands the difference to the film. */
    .screen { aspect-ratio: 2 / 3; max-height: calc(100vh - 170px); }
    .title-card { left: 7%; right: 5%; bottom: 8%; max-width: none; gap: 10px; }
    .title-card .card-head { font-size: clamp(44px, 14vw, 64px); letter-spacing: 0.05em; }
    .subline { font-size: 14px; }
    .card-scrim {
        background: linear-gradient(180deg, rgba(5, 4, 7, 0.1) 0%, rgba(5, 4, 7, 0.2) 40%, rgba(5, 4, 7, 0.85) 75%, rgba(5, 4, 7, 0.96) 100%);
    }
    /* Two rows, no orphan: the gold seat owns the first, the quiet pair share
       the second. */
    .seats { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; width: var(--screen-w); }
    .seats .btn { min-height: 44px; padding: 0 14px; font-size: 13.5px; }
    .seats .btn-gold { grid-column: 1 / -1; }
    .deck { grid-template-rows: auto minmax(0, 1fr) auto; gap: 6px; padding: 4% 6% 3%; }
    /* Counter and prompt shared two lines and 41px of a 344px column. */
    .deck-head { flex-direction: row; align-items: baseline; gap: 8px; flex-wrap: wrap; }
    .deck-prompt { font-size: 12px; }
    .deck-card { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) auto; justify-items: center; gap: 8px; }
    .deck-poster { height: 100%; }
    .deck-meta { text-align: center; align-items: center; }
    .deck-title { font-size: 26px; }
    .rate { justify-self: center; gap: 7px; flex-wrap: wrap; justify-content: center; }
    .rate-mark { width: 44px; height: 44px; }
    .rate-loved .rate-mark { width: 44px; height: 44px; }
    .rate-word { font-size: 10.5px; }
    .rate-skip { min-height: 44px; padding: 0 9px; font-size: 11.5px; letter-spacing: 0; }
    .rate-rule { display: none; }
    .deck-prompt { font-size: 13px; }
    .profile { padding: 8px 6% 10px; gap: 8px; }
    .profile .dial { gap: 8px; margin-top: 0; }
    .profile .dial-label { font-size: 9.5px; }
    /* Rows sized from the screen, never from the poster's intrinsic height —
       that pushed the copy clean off the bottom. */
    /* fr, not percentages: Safari resolves a percentage row against an
       indefinite height as auto, the poster then takes its intrinsic height,
       and the copy lands on top of it. */
    /* Sized off width, never height. The poster used to be height: 100% in a
       grid row of indefinite height: Safari resolves that as auto, aspect-ratio
       then derives the width from the image's intrinsic size, and the poster
       balloons to fill the card with the copy printed on top of it. A
       percentage width against a definite-width column has no such ambiguity,
       so the poster cannot get away from us whatever the row does. */
    /* Centred, because the copy is shorter than the card and pinning it to the
       top crams five elements into the upper half while the lower half sits
       empty — which reads as crowded even though nothing overlaps. */
    .reveal { display: flex; flex-direction: column; justify-content: center; padding: 5% 6%; gap: 12px; }
    /* Credibility, not information, and it is what wraps the meta line onto a
       second row and orphans the word "votes" there. The rating stays. */
    .reveal-meta .votes { display: none; }
    .reveal-poster {
        width: 27%;
        max-width: 108px;
        height: auto;
        max-height: none;
        aspect-ratio: 2 / 3;
        align-self: flex-start;
        animation-delay: 0.3s;
    }
    /* No flex-grow: the copy takes its natural height so the centring above has
       something to centre. Grown, it swallows the slack and the block sits high
       in the card with the gap left underneath it. */
    .reveal-copy { gap: 8px; min-height: 0; flex: 0 0 auto; }
    .reveal-title { font-size: 28px; }
    .reveal-meta { font-size: 12px; gap: 6px; }
    .reveal-because { font-size: 15px; }
    .reveal-overview { -webkit-line-clamp: 2; font-size: 12.5px; line-height: 1.45; }
    .act-nothing { padding: 7% 7%; }
    .nothing-card { gap: 11px; }
    .nothing-card h2 { font-size: clamp(28px, 8.5vw, 40px); }
    .nothing-sub { font-size: 13.5px; line-height: 1.5; }
    .nothing-actions { gap: 8px; width: 100%; }
    .nothing-actions .btn { width: 100%; min-height: 44px; font-size: 13px; padding: 0 14px; }
    .reveal-actions { gap: 8px; margin-top: 2px; }
    .reveal-actions .btn { min-height: 44px; padding: 0 12px; font-size: 12.5px; }
    #reveal-again { padding: 0 10px; }
    .how { padding: 80px 20px 56px; }
    /* The third mini gained a row when the pill became a sentence; the frame
       height is fixed by its ratio, so the title gives the 2px back. */
    .mini-title { font-size: 23px; }
    .sheet-section { padding: 24px 14px 64px; }
    .sheet-caption { margin: -20px 6px 16px; }
    .sheet-grid { grid-template-columns: repeat(5, 1fr); gap: 2%; }
    .sheet-card figcaption { opacity: 1; font-size: 9px; padding: 14px 4px 4px; }
    .close { padding: 56px 20px 90px; }
    .foot { padding: 22px 20px; flex-direction: column; gap: 10px; }
}
