/* ═══════════════════════════════════════════
   birthday-scrapbook — the shell (build contract §7.2)

   The stage (one viewport, phone or desktop, either orientation; never
   letterboxed), its three set-piece canvases, the camera world that holds
   every beat's <section>, and the stage chrome: the H E Y I L Y S M
   progress tape (top right) and the mute pill (top left). Beats style only
   their own prefixed classes and paint their own ground full-bleed; .screen,
   the stage, the canvases and the chrome belong here.

   Stacking inside #stage (all position: absolute over one box):
     0  #fx-back          the ambient bokeh (film.ambient; this film never raises it)
     1  #world            the camera world: every beat
     2  #fx-light         nameAssemble (the-letter)
     3  the hit flash     (.reveel-flash::after)
     4  #fx-burst         goldBurst (caution, reverse-card)
     6  .progress-tape    the stage chrome, outside the camera world
     8  #stage-mute

   The chrome band. Every beat leaves the top of the stage to the chrome:
   54-64 px on phone portrait (50 px on phones <= 680 px tall), 58-64 px on
   landscape / desktop, 44-52 px on landscape phones <= 480 px tall
   (caution.css, ten-things.css, love-note.css, photo-strips.css,
   reverse-card.css, special-delivery.css). the-letter's phone photo band and
   the-envelope's stars run under it by design (the chrome sits on them, it
   never covers copy). So the chrome ends at 42 px on phones, 38 px on short
   landscape phones and 50 px on desktop, plus the safe-area inset.
   ═══════════════════════════════════════════ */

html,
body {
    height: 100%;
    margin: 0;
}

body {
    background: var(--gingham-pink-base);
    color: var(--text-primary);
    font-family: var(--font-body);
    overflow: hidden;
    overscroll-behavior: none;
    -webkit-tap-highlight-color: transparent;
    -webkit-font-smoothing: antialiased;
}

button {
    font: inherit;
}

/* ───── The stage: ReveelStage's camera box, the whole viewport ─────
   Full bleed in both orientations: the beats compose themselves to the
   stage's box (portrait and landscape layouts of their own), so desktop is
   never letterboxed. `overflow: clip`, not hidden: a hidden box is still a
   scroll container, and a focus or scrollIntoView could scroll the film. */
#stage {
    width: 100%;
    height: 100vh;
    height: 100svh;
    height: 100dvh;
    overflow: hidden;
    overflow: clip;
    background: var(--gingham-pink-base);
    touch-action: manipulation;
    user-select: none;
    -webkit-user-select: none;
}

/* The hit's shake (ReveelStage.shake adds .reveel-shake to #stage itself)
   moves the stage box; clipped to that moved box, a strip of body showed
   along two edges for a frame. The beats' grounds bleed past the stage, so
   for the shake's 0.42 s the stage stops clipping and they cover the seam.
   body keeps overflow: hidden, so nothing scrolls. */
#stage.reveel-shake {
    overflow: visible;
}

/* The primitive's hit flash, its timing and gradient untouched: over the
   world and the name's light, under the burst's sparks. */
.reveel-stage.reveel-flash::after {
    z-index: 3;
}

/* ───── The set-piece canvases: inert, covering the stage ───── */
.fx {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
}
.fx-back { z-index: 0; }
.fx-light { z-index: 2; }
.fx-burst { z-index: 4; }

/* ───── The world the camera moves ───── */
.reveel-stage-world {
    z-index: 1;
}

/* Every beat is a full-bleed layer of the world, the stage's size. Only the
   director changes which one is active. Most changes are hard cuts (the
   beat runs its own handover from the previous beat's last frame); the
   three whose ground changes with no handover of their own (the-letter,
   love-note, reverse-card: the spine's entryFadeMs) fade in over the
   outgoing section for 0.22 s on the natural flow, opacity only. */
.screen {
    position: absolute;
    inset: 0;
    display: none;
    opacity: 0;
}
.screen.active {
    display: flex;
    opacity: 1;
}
/* The outgoing section, held under a fading-in one until its fade ends. */
.screen.screen-leaving {
    display: flex;
    opacity: 1;
    pointer-events: none;
}
.screen.active.screen-fading-in {
    animation: screen-fade-in var(--screen-fade-ms, 220ms) var(--ease-silk) both;
}
@keyframes screen-fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* Entry states are applied instantly (the spine's prepare on goto), never
   animated in. */
.instant,
.instant *,
.instant *::before,
.instant *::after {
    transition: none !important;
}

/* Text for assistive technology only. */
.stage-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ═══════════ The stage chrome ═══════════ */
.reveel-stage {
    --chrome-top: calc(8px + env(safe-area-inset-top, 0px));
    --chrome-h: 34px;
    --chrome-side: 8px;
    --chrome-letter: 1.1rem;
}

/* ───── H E Y I L Y S M: a strip of cream washi tape, top right ─────
   Cream paper on every ground (tokens.css › .progress-tape keeps its letters
   ink in the red scope too): the current letter ink 700 (15.98:1), the idle
   ones plum 400 (9.47:1). Each letter has a fixed cell, so the bold one
   never shifts its neighbours. Inert: pointer-events none, no links. */
.progress-tape {
    position: absolute;
    z-index: 6;
    top: var(--chrome-top);
    right: calc(var(--chrome-side) + env(safe-area-inset-right, 0px));
    pointer-events: none;
    transform: rotate(1.6deg);
    transform-origin: 100% 0;
    filter: drop-shadow(0 1px 1.5px rgba(70, 6, 24, 0.3));
}
.progress-tape-letters {
    display: flex;
    align-items: center;
    height: var(--chrome-h);
    padding: 0 13px 0 15px;
    background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.38), rgba(255, 255, 255, 0) 48%, rgba(124, 92, 40, 0.06)),
        var(--tape-fill);
    /* Torn ends: a zigzag on either side, like the washi on the polaroids. */
    clip-path: polygon(
        0 0, 100% 0,
        calc(100% - 5px) 16%, 100% 33%, calc(100% - 5px) 50%, 100% 67%, calc(100% - 5px) 84%,
        100% 100%, 0 100%,
        5px 84%, 0 67%, 5px 50%, 0 33%, 5px 16%);
    font-family: var(--font-body);
    font-size: var(--chrome-letter);
    font-weight: 400;
    line-height: 1;
    color: var(--letter-idle);
}
.progress-tape-letter {
    display: inline-block;
    width: 0.92em;
    text-align: center;
}
.progress-tape-letter.is-current {
    color: var(--letter-current);
    font-weight: 700;
}

/* ───── Mute: a cream tape pill with an ink speaker, top left ───── */
.stage-mute {
    position: absolute;
    z-index: 8;
    top: var(--chrome-top);
    left: calc(var(--chrome-side) + env(safe-area-inset-left, 0px));
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: var(--chrome-h);
    padding: 0;
    border: 0;
    border-radius: 999px;
    color: var(--ink);
    background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.38), rgba(255, 255, 255, 0) 48%, rgba(124, 92, 40, 0.06)),
        var(--paper-cream);
    box-shadow: 0 1px 1.5px rgba(70, 6, 24, 0.3), inset 0 0 0 1px var(--paper-edge);
    transform: rotate(-2deg);
    cursor: pointer;
}
/* The tap target is at least 44 px tall even where the pill is drawn smaller. */
.stage-mute::before {
    content: '';
    position: absolute;
    inset: -6px -2px;
}
.stage-mute-icon {
    width: 19px;
    height: 19px;
}
.stage-mute .stage-mute-x { display: none; }
.stage-mute[aria-pressed="true"] .stage-mute-x { display: inline; }
.stage-mute[aria-pressed="true"] .stage-mute-waves { display: none; }
.stage-mute[aria-pressed="true"] {
    color: var(--text-secondary);
}
.stage-mute:active {
    transform: rotate(-2deg) scale(0.95);
}
.stage-mute:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 3px;
}

/* ───── Landscape / desktop: a little larger, still inside the 58 px band ───── */
@media (orientation: landscape) and (min-width: 700px) and (min-height: 481px) {
    .reveel-stage {
        --chrome-top: calc(11px + env(safe-area-inset-top, 0px));
        --chrome-h: 38px;
        --chrome-side: 14px;
        --chrome-letter: 1.25rem;
    }
    .stage-mute { width: 52px; }
    .stage-mute-icon { width: 21px; height: 21px; }
}

/* ───── Landscape phones: the band is only 44 px ───── */
@media (orientation: landscape) and (max-height: 480px) {
    .reveel-stage {
        --chrome-top: calc(5px + env(safe-area-inset-top, 0px));
        --chrome-h: 32px;
        --chrome-letter: 1rem;
    }
}

/* ───── Very narrow phones: keep the tape clear of the pill ───── */
@media (max-width: 340px) {
    .reveel-stage { --chrome-letter: 0.95rem; }
}

/* ───── Reduced motion: the chrome never moves anyway; the press does not scale ───── */
@media (prefers-reduced-motion: reduce) {
    .stage-mute:active {
        transform: rotate(-2deg);
    }
}
