/* ═══════════════════════════════════════════
   best-boyfriend — beat 4: the-letter
   The quiet centre. One warm lamp over the open envelope; the letter is
   drawn out, unfolds in thirds onto lined cream paper, and writes itself
   in Pinyon. State classes live on .the-letter-container (all prefixed
   the-letter--), toggled by the beat's cues and its handler:
     --arrived · --intro · --armed · --placed · --drawn · --out ·
     --unfolded · --opened · --reading · --closed · --away · --lamp-off ·
     --threading
   The screen-space controls carry their own: --asking · --rereading.
   Only transform and opacity move. The writing (clip wipes, the nib, the
   auto-scroll) and the sheet's travel are Web Animations started by the
   module; the hinges, fades and exit are transitions keyed to the classes.

   Geometry: the envelope lays out from tokens.css's continuity variables
   (--env-x/-y/-w/-h, --flap-depth, --letter-peek). The open sheet sits in
   a band chosen for the camera's pushed pose { y: -90, z: 60 } (P = 900),
   so on screen it spans 9% + 28px to 100% - 128px:
     section y = H/2 + 90 + (screen Y - H/2) * 840/900
   Reduced motion leaves the camera still, so the band is the screen band.
   ═══════════════════════════════════════════ */

.the-letter-container {
    position: absolute;
    inset: 0;
    color: var(--text-primary);
    font-family: var(--font-body);
    pointer-events: none;
}

/* ───── The room: one lamp over the envelope ─────
   Entry = three-gifts' exit, gradient for gradient (its room box spans
   -50%..150% of the stage vertically; this one too, so the same stops land
   on the same pixels; narrower sideways, the camera only needs a little).
   Pre-painted layers; only opacity moves, one or two at a time. */
.the-letter-room {
    position: absolute;
    left: -6%;
    right: -6%;
    top: -50%;
    height: 200%;
    z-index: 0;
    pointer-events: none;
}
.the-letter-lit,
.the-letter-dim,
.the-letter-lamp,
.the-letter-dark {
    position: absolute;
    inset: 0;
}
/* The dim and the lamp go out together (one layer fading, not two). */
.the-letter-lit { transition: opacity 3.4s var(--ease-silk); }
.the-letter-dim {
    background: radial-gradient(calc(var(--env-w) * 1.05) calc(var(--env-w) * 0.82) at 50% 54%,
        rgba(22, 5, 9, 0) 0%, rgba(22, 5, 9, 0.2) 46%, var(--scrim) 100%);
}
.the-letter-lamp {
    background:
        radial-gradient(calc(var(--env-w) * 0.78) calc(var(--env-w) * 0.5) at 50% 52%, var(--lamp-cone), rgba(246, 220, 154, 0) 100%),
        conic-gradient(from 180deg at 50% 25%, rgba(246, 220, 154, 0) 168deg, var(--lamp-cone) 177deg 183deg, rgba(246, 220, 154, 0) 192deg);
}
.the-letter-dark {
    background: var(--void);
    opacity: 0;
    transition: opacity 3s var(--ease-silk);
}
/* 0-3 s: the room dims the last step to a single lamp. */
.the-letter-container.the-letter--arrived .the-letter-dark { opacity: 0.12; }
/* The exit: the lamp goes out, and the dark is reasons' entry (--void at 0.9). */
.the-letter-container.the-letter--lamp-off .the-letter-lit { opacity: 0; }
.the-letter-container.the-letter--lamp-off .the-letter-dark {
    opacity: 0.9;
    transition: opacity 3.4s var(--ease-silk);
}

/* ───── The exit's afterglow (F + 3.6 -> F + 10.8, keyed to --away) ─────
   As the envelope drifts away, the warmth where the letter lay stays and
   goes out slowly, and two drifts of gold motes rise from it toward the top
   of the dark, where the thread begins: the frame is never empty before the
   cut. All three are back to nothing by F + 10.8, before reasons opens on
   the bare dark. Pre-painted; each layer moves only transform and opacity. */
.the-letter-ember {
    position: absolute;
    inset: 0;
    background: radial-gradient(calc(var(--env-w) * 0.95) calc(var(--env-w) * 0.62) at 50% 55%,
        rgba(246, 220, 154, 0.26) 0%, rgba(226, 177, 85, 0.1) 46%, rgba(226, 177, 85, 0) 100%);
    opacity: 0;
}
.the-letter-container.the-letter--away .the-letter-ember {
    animation: the-letter-ember 7.2s var(--ease-silk) both;
}
@keyframes the-letter-ember {
    0% { opacity: 0; }
    18% { opacity: 1; }
    45% { opacity: 0.8; }
    100% { opacity: 0; }
}
/* Room box: stage y = 2 * top - 50%. These sit around the envelope's spot
   (stage 52-72%) and rise about a third of the frame. */
.the-letter-motes {
    position: absolute;
    left: 30%;
    width: 40%;
    top: 51%;
    height: 10%;
    opacity: 0;
}
.the-letter-motes > span {
    position: absolute;
    width: 4px;
    height: 4px;
    margin: -2px 0 0 -2px;
    border-radius: 50%;
    background: radial-gradient(circle, var(--accent-warm-bright) 0%, rgba(226, 177, 85, 0.55) 45%, rgba(226, 177, 85, 0) 72%);
    box-shadow: 0 0 6px 1px var(--accent-warm-glow-soft);
}
.the-letter-motes > span:nth-child(even) { width: 3px; height: 3px; opacity: 0.7; }
.the-letter-motes-a > span:nth-child(1) { left: 8%; top: 70%; }
.the-letter-motes-a > span:nth-child(2) { left: 22%; top: 30%; }
.the-letter-motes-a > span:nth-child(3) { left: 35%; top: 82%; }
.the-letter-motes-a > span:nth-child(4) { left: 46%; top: 12%; }
.the-letter-motes-a > span:nth-child(5) { left: 55%; top: 58%; }
.the-letter-motes-a > span:nth-child(6) { left: 66%; top: 92%; }
.the-letter-motes-a > span:nth-child(7) { left: 74%; top: 26%; }
.the-letter-motes-a > span:nth-child(8) { left: 86%; top: 66%; }
.the-letter-motes-a > span:nth-child(9) { left: 95%; top: 40%; }
.the-letter-motes-b > span:nth-child(1) { left: 4%; top: 22%; }
.the-letter-motes-b > span:nth-child(2) { left: 18%; top: 88%; }
.the-letter-motes-b > span:nth-child(3) { left: 30%; top: 50%; }
.the-letter-motes-b > span:nth-child(4) { left: 50%; top: 76%; }
.the-letter-motes-b > span:nth-child(5) { left: 62%; top: 34%; }
.the-letter-motes-b > span:nth-child(6) { left: 80%; top: 84%; }
.the-letter-motes-b > span:nth-child(7) { left: 92%; top: 8%; }
.the-letter-container.the-letter--away .the-letter-motes-a {
    animation: the-letter-motes-a 6.6s linear 0.5s both;
}
.the-letter-container.the-letter--away .the-letter-motes-b {
    animation: the-letter-motes-b 5.8s linear 1.4s both;
}
@keyframes the-letter-motes-a {
    0% { opacity: 0; transform: translate3d(0, 0, 0); }
    22% { opacity: 1; }
    70% { opacity: 0.75; }
    100% { opacity: 0; transform: translate3d(0, -190%, 0); }
}
@keyframes the-letter-motes-b {
    0% { opacity: 0; transform: translate3d(0, 0, 0); }
    25% { opacity: 0.85; }
    70% { opacity: 0.6; }
    100% { opacity: 0; transform: translate3d(5%, -150%, 0); }
}

/* ───── The rig: everything on the table, moved together at the exit ───── */
.the-letter-rig {
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
}
.the-letter-container.the-letter--away .the-letter-rig {
    transform: translate3d(0, 75%, 0);
    transition: transform 2.2s var(--ease-stage);
}

/* ───── The envelope (continuity geometry; the-seal's shape) ─────
   No z-index or transform here: the back, the flap, the sheet and the
   pocket stack in the rig's context, so the letter sits between the
   lining and the pocket. */
.the-letter-env {
    position: absolute;
    left: calc(var(--env-x) - var(--env-w) / 2);
    top: calc(var(--env-y) - var(--env-h) / 2);
    width: var(--env-w);
    height: var(--env-h);
}

/* The inside, seen through the V mouth; it carries the drop shadow. */
.the-letter-back {
    position: absolute;
    z-index: 1;
    inset: 0;
    border-radius: 3px;
    background: linear-gradient(180deg, var(--void) 0%, var(--bg-primary) 58%, var(--bg-elevated) 100%);
    box-shadow: var(--envelope-shadow);
}

/* The flap, open since the seal broke: hinged on the top edge, standing
   up --flap-depth above it, its champagne lining toward us (the-seal's
   lining stops, mirrored, since this one is rotated up rather than drawn
   up). It closes down over the pocket at the fold. */
.the-letter-flap {
    position: absolute;
    z-index: 1;
    left: 0;
    top: 0;
    width: 100%;
    height: var(--flap-depth);
    clip-path: polygon(0 0, 100% 0, 50% 100%);
    transform-origin: 50% 0;
    transform: perspective(900px) rotateX(180deg);
    transition: opacity 0.6s var(--ease-silk);
}
/* While the letter is open the flap steps back: a short sheet (a one-line
   note) would otherwise show its gold tip above the paper, behind the
   ribbon. It is back before the fold closes it (F + 2.6 s). */
.the-letter-container.the-letter--opened .the-letter-flap { opacity: 0; }
.the-letter-flap-in,
.the-letter-flap-out,
.the-letter-flap .the-letter-flap-sweep {
    position: absolute;
    inset: 0;
}
.the-letter-flap-in {
    background: linear-gradient(0deg, var(--accent-celestial) -78.57%, var(--accent-warm) 100%);
    opacity: 1;
}
.the-letter-flap-out {
    background: var(--envelope-flap);
    opacity: 0;
}
.the-letter-flap-edge {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    fill: none;
    stroke: var(--envelope-edge);
    stroke-width: 2px;
    stroke-linejoin: round;
}
.the-letter-flap-edge path { vector-effect: non-scaling-stroke; }
.the-letter-flap .the-letter-flap-sweep { pointer-events: none; }

.the-letter-container.the-letter--closed .the-letter-flap {
    z-index: 4;
    transform: perspective(900px) rotateX(0deg);
    transition: transform 0.8s var(--ease-stage), z-index 0s linear 0.4s;
}
.the-letter-container.the-letter--closed .the-letter-flap-in {
    opacity: 0;
    transition: opacity 0.05s linear 0.38s;
}
.the-letter-container.the-letter--closed .the-letter-flap-out {
    opacity: 1;
    transition: opacity 0.05s linear 0.38s;
}

/* Invisible: --letter-peek tall, so the module reads the peek from the token. */
.the-letter-peekline {
    position: absolute;
    left: 0;
    top: 0;
    width: 1px;
    height: var(--letter-peek);
    visibility: hidden;
    pointer-events: none;
}

/* The pocket: the envelope's front with its V mouth (38% deep), in front of the letter. */
.the-letter-pocket {
    position: absolute;
    z-index: 3;
    inset: 0;
    background: var(--envelope-body);
    clip-path: polygon(0 0, 50% 38%, 100% 0, 100% 100%, 0 100%);
    pointer-events: none;
}
.the-letter-pocket-lines {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    fill: none;
}
.the-letter-pocket-lines rect,
.the-letter-pocket-lines path { vector-effect: non-scaling-stroke; }
.the-letter-pocket-edge { stroke: var(--envelope-edge); stroke-width: 2px; }
.the-letter-pocket-fold { stroke: var(--foil-line-soft); stroke-width: 1px; }
.the-letter-pocket-mouth { stroke: var(--foil-line-strong); stroke-width: 2px; stroke-linejoin: round; }

/* The swipe-up surface over the letter's edge and the envelope. Not a
   control (the button is), so it declares itself to the badge guard. */
.the-letter-grip {
    position: absolute;
    z-index: 4;
    left: -6%;
    right: -6%;
    top: calc(-1 * var(--flap-depth) - 6%);
    bottom: -8%;
    pointer-events: none;
    touch-action: none;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
    cursor: grab;
}
.the-letter-container.the-letter--armed .the-letter-grip { pointer-events: auto; }
.the-letter-container.the-letter--drawn .the-letter-grip { pointer-events: none; }

/* "{fromName} wrote you something." — above the open flap's tip, under
   the sheet as it is drawn out past it. */
.the-letter-intro {
    position: absolute;
    z-index: 1;
    left: 50%;
    bottom: calc(100% + var(--flap-depth) + 18px);
    width: min(86vw, 24rem);
    margin: 0 0 0 calc(min(86vw, 24rem) / -2);
    text-align: center;
    text-wrap: balance;
    overflow-wrap: anywhere;
    font: italic 400 clamp(1.2rem, 5.2vw, 1.5rem)/1.35 var(--font-display);
    color: var(--text-primary);
    text-shadow: 0 0 18px var(--accent-warm-glow-soft);
    opacity: 0;
    transform: translateY(10px);
    transition: opacity 1.2s var(--ease-expo-out), transform 1.4s var(--ease-expo-out);
    pointer-events: none;
}
.the-letter-container.the-letter--intro .the-letter-intro {
    opacity: 1;
    transform: none;
}
.the-letter-container.the-letter--drawn .the-letter-intro {
    opacity: 0;
    transform: translateY(-8px);
    transition-duration: 0.7s;
}

/* ───── The band the open sheet is centred in ───── */
.the-letter-band {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(11.733% + 116px);
    bottom: calc(3.333% + 29.5px);
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}

/* ───── The sheet ─────
   Its layout box is the open letter, as tall as its writing (46vh at the
   least, so a one-line note is still a letter). One that fits is lifted in
   the band (--the-letter-lift) until its writing stays above 62%; one that
   does not is a window a whole number of lines tall
   (--the-letter-sheet-max), so it scrolls by whole lines and never rests
   with a line sliced at its edge. Both are set by the module's layout pass. Folded and in the envelope it is
   the same element under a transform the module computes once (scale k,
   only the middle third showing, the folded stack's top edge
   --letter-peek above the mouth). It carries no paper of its own: the
   three panels are the paper, so the folded stack has no ghost rectangle. */
.the-letter-sheet {
    --the-letter-rules: repeating-linear-gradient(180deg,
        rgba(184, 134, 47, 0) 0 calc(1.55em * 0.72 - 1px),
        var(--paper-rule) calc(1.55em * 0.72 - 1px) calc(1.55em * 0.72),
        rgba(184, 134, 47, 0) calc(1.55em * 0.72) 1.55em);
    position: relative;
    z-index: 2;
    display: flex;
    flex-direction: column;
    top: calc(-1 * var(--the-letter-lift, 0px));
    width: min(84vw, 26rem);
    min-height: min(46vh, 100%);
    max-height: var(--the-letter-sheet-max, 100%);
    font-size: clamp(1.45rem, 6.2vw, 1.9rem);
    transform-origin: 50% 50%;
    visibility: hidden;
    pointer-events: none;
}
.the-letter-container.the-letter--placed .the-letter-sheet { visibility: visible; }
.the-letter-container.the-letter--out .the-letter-sheet { z-index: 5; }

/* The shadow it casts once it is out: compressed to the folded third,
   full size open. A static shadow, scaled. */
.the-letter-shadow {
    position: absolute;
    inset: 0;
    box-shadow: var(--paper-shadow);
    transform: scaleY(0.3334);
    opacity: 0;
    transition: opacity 0.8s var(--ease-silk), transform 1.3s var(--ease-stage);
    pointer-events: none;
}
.the-letter-container.the-letter--out .the-letter-shadow { opacity: 1; }
.the-letter-container.the-letter--unfolded .the-letter-shadow {
    transform: none;
    transition: opacity 0.8s var(--ease-silk), transform 1.6s var(--ease-stage);
}

/* Paper under the seams once it is flat (identical to the panels). */
.the-letter-underlay {
    position: absolute;
    inset: 0;
    background-color: var(--paper);
    background-image: var(--the-letter-rules);
    background-position: 0 calc(var(--the-letter-page-top, 0px) + 1.55em);
    opacity: 0;
    pointer-events: none;
}
.the-letter-container.the-letter--opened .the-letter-underlay { opacity: 1; }

/* The three thirds. Each face is the whole sheet's paper, offset, so the
   ruling runs on unbroken across the hinges. The folded panels lie face
   down over the middle; their backs are plain paper (::after), swapped in
   as each passes 90 degrees. */
.the-letter-panel {
    position: absolute;
    left: 0;
    width: 100%;
    height: 33.3334%;
    overflow: hidden;
    pointer-events: none;
}
.the-letter-panel-top {
    top: 0;
    transform-origin: 50% 100%;
    transform: perspective(1200px) rotateX(-180deg);
    transition: transform 1.3s var(--ease-stage) 0.5s;
}
/* The middle third reaches 1 px under each hinge, so no seam opens while
   the outer thirds turn; its face is shifted back so the ruling still
   starts at the sheet's top. */
.the-letter-panel-mid {
    top: calc(33.3333% - 1px);
    height: calc(33.3334% + 2px);
}
.the-letter-panel-bot {
    top: 66.6666%;
    transform-origin: 50% 0;
    transform: perspective(1200px) rotateX(180deg);
    transition: transform 1.3s var(--ease-stage) 0s;
}
.the-letter-face {
    position: absolute;
    left: 0;
    width: 100%;
    height: 300%;
    background-color: var(--paper);
    background-image: var(--the-letter-rules);
    background-position: 0 calc(var(--the-letter-page-top, 0px) + 1.55em);
    box-shadow: inset 1px 0 0 var(--paper-edge), inset -1px 0 0 var(--paper-edge);
}
.the-letter-panel-top .the-letter-face { top: 0; box-shadow: inset 1px 0 0 var(--paper-edge), inset -1px 0 0 var(--paper-edge), inset 0 1px 0 var(--paper-edge); }
.the-letter-panel-mid .the-letter-face { top: calc(-100% + 3px); }
.the-letter-panel-bot .the-letter-face { top: -200%; box-shadow: inset 1px 0 0 var(--paper-edge), inset -1px 0 0 var(--paper-edge), inset 0 -1px 0 var(--paper-edge); }
.the-letter-panel-top::after,
.the-letter-panel-bot::after {
    content: '';
    position: absolute;
    inset: 0;
    background:
        linear-gradient(180deg, var(--paper-shade), rgba(184, 134, 47, 0.06) 55%, var(--paper-shade)),
        var(--paper);
    box-shadow: inset 0 0 0 1px var(--paper-edge);
    opacity: 1;
}
/* The folded letter's outside is the top third's back, seen through the
   envelope's V exactly as the-seal drew its letter: 14 px rules from 10 px
   down, paper warming to champagne, a hairline top edge. The panel is
   mirrored (rotated 180deg), so every gradient runs from the element's
   bottom; the sheet's scale is undone by --the-letter-fy (1 / its
   vertical scale in the envelope), so the rules are 14 px on screen. */
.the-letter-panel-top::after {
    background:
        repeating-linear-gradient(0deg, rgba(184, 134, 47, 0) 0 calc(13px * var(--the-letter-fy, 1)), var(--paper-rule) calc(13px * var(--the-letter-fy, 1)) calc(14px * var(--the-letter-fy, 1))) 0 0 / 100% calc(100% - 10px * var(--the-letter-fy, 1)) no-repeat,
        linear-gradient(0deg, var(--paper) 0%, var(--paper) 55%, var(--accent-celestial) 100%);
    box-shadow: inset 0 calc(-1px * var(--the-letter-fy, 1)) 0 var(--paper-edge);
}
/* Folding (the default state's transitions): the bottom third first, the
   top third over it; each back is swapped in at its 90 degrees. */
.the-letter-panel-bot::after { transition: opacity 0.06s linear 0.62s; }
.the-letter-panel-top::after { transition: opacity 0.06s linear 1.12s; }
/* Unfolding: the top third opens first, the bottom one 0.4 s behind it. */
.the-letter-container.the-letter--unfolded .the-letter-panel-top {
    transform: perspective(1200px) rotateX(0deg);
    transition: transform 1.2s var(--ease-stage) 0s;
}
.the-letter-container.the-letter--unfolded .the-letter-panel-bot {
    transform: perspective(1200px) rotateX(0deg);
    transition: transform 1.2s var(--ease-stage) 0.4s;
}
.the-letter-container.the-letter--unfolded .the-letter-panel-top::after {
    opacity: 0;
    transition: opacity 0.06s linear 0.57s;
}
.the-letter-container.the-letter--unfolded .the-letter-panel-bot::after {
    opacity: 0;
    transition: opacity 0.06s linear 0.97s;
}

/* The letter's top edge, glowing in the envelope, behind the paper: three-
   gifts' glow at its exit (16% past each side, 1.6 peeks above the edge,
   4.4 peeks tall). The folded stack's top edge is the middle third's;
   --the-letter-peek-y is the peek before the sheet's vertical scale.
   Pre-painted; only opacity moves. */
.the-letter-glow {
    position: absolute;
    left: -16%;
    right: -16%;
    top: calc(33.3333% - 1.6 * var(--the-letter-peek-y, 14px));
    height: calc(4.4 * var(--the-letter-peek-y, 14px));
    background: radial-gradient(ellipse at 50% 50%, rgba(246, 220, 154, 0.55) 0%, rgba(226, 177, 85, 0.16) 42%, rgba(226, 177, 85, 0) 70%);
    opacity: 1;
    transition: opacity 0.6s var(--ease-silk);
    pointer-events: none;
}
.the-letter-container.the-letter--armed .the-letter-glow {
    animation: the-letter-glow-breathe 2.67s var(--ease-breath) infinite;
}
.the-letter-container.the-letter--drawn .the-letter-glow {
    opacity: 0;
    animation: none;
}
@keyframes the-letter-glow-breathe {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.6; }
}

/* ───── The writing surface ───── */
.the-letter-scroll {
    position: relative;
    z-index: 1;
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    scrollbar-width: thin;
    scrollbar-color: var(--paper-edge) rgba(184, 134, 47, 0);
}
.the-letter-container.the-letter--reading .the-letter-sheet,
.the-letter-container.the-letter--reading .the-letter-scroll { pointer-events: auto; }
.the-letter-container.the-letter--reading .the-letter-scroll {
    overflow-y: auto;
    overscroll-behavior: contain;
    touch-action: pan-y;
    -webkit-overflow-scrolling: touch;
}

/* The page carries its own paper and ruling, so a scrolled page keeps
   its lines under its words. Every line box is 1.55em (one rule). */
/* --the-letter-tail (set by the module) is blank ruled paper below the
   signature of a letter that scrolls: enough for the last lines to rise
   to 62% too, and to make the scroll range whole lines. */
.the-letter-page {
    position: relative;
    flex: none;
    margin: auto 0;
    padding: 1.55em 1em calc(1.55em + var(--the-letter-tail, 0px)) 1.15em;
    background-color: var(--paper);
    background-image: var(--the-letter-rules);
    background-position: 0 1.55em;
    opacity: 0;
    transition: opacity 0.35s var(--ease-silk);
}
.the-letter-container.the-letter--opened .the-letter-page { opacity: 1; }

.the-letter-salute,
.the-letter-body,
.the-letter-sign {
    margin: 0;
    font-family: var(--font-script);
    font-weight: 400;
}
.the-letter-body {
    font-size: 1em;
    line-height: 1.55;
    color: var(--ink);
}
.the-letter-para + .the-letter-para { margin-top: 1.55em; }
.the-letter-hard { overflow-wrap: anywhere; }
/* 1.12em on a 1.55em line box (each row one rule) */
.the-letter-salute {
    font-size: 1.12em;
    line-height: 1.3839;
    color: var(--ink-script);
}
/* The signature at --the-letter-sign-s (1.3x, fitted down to the body's
   1x so it stays one line) on a two-rule line box, one rule below the
   last line. A name too long even at 1x wraps tight instead: 1x rows of
   one rule each, the first where the single line's words sit, so no
   blank rule ever opens between them. */
.the-letter-sign {
    --the-letter-sign-s: 1.3;
    margin-top: calc(1.55em / var(--the-letter-sign-s));
    padding-right: 0.3em;
    font-size: calc(var(--the-letter-sign-s) * 1em);
    line-height: calc(3.1 / var(--the-letter-sign-s));
    text-align: right;
    color: var(--ink-script);
}
.the-letter-sign.the-letter-sign--wrapped {
    margin-top: calc(3.1em / var(--the-letter-sign-s));
    line-height: calc(1.55 / var(--the-letter-sign-s));
}
.the-letter-salute.the-letter--omitted,
.the-letter-sign.the-letter--omitted { display: none; }
.the-letter-src { display: none; }
.the-letter-line { white-space: nowrap; }
.the-letter-row { display: block; }
/* The layout pass's measuring states: the whole text as one row, unwrapped
   (its natural width) or wrapped (where the browser breaks it). */
.the-letter-measure .the-letter-pen,
.the-letter-measure .the-letter-ink { max-width: none; }
.the-letter-wrapping .the-letter-line { white-space: normal; }
.the-letter-pen {
    display: inline-block;
    max-width: 100%;
    vertical-align: top;
}
.the-letter-ink {
    display: inline-block;
    max-width: 100%;
    overflow-wrap: anywhere;
    clip-path: inset(-0.6em calc(100% + 0.8em) -0.6em -0.8em);
}
.the-letter-line[data-ink="done"] .the-letter-ink { clip-path: none; }

/* One nib of warm light for the whole sheet, riding each wipe head (its
   run is a Web Animation; this is only its look). */
.the-letter-nib {
    position: absolute;
    z-index: 1;
    left: -13px;
    top: -13px;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: radial-gradient(circle, var(--accent-warm-bright) 0%, var(--nib-glow) 26%, rgba(226, 177, 85, 0) 70%);
    opacity: 0;
    pointer-events: none;
}

/* Edge shading and the lamp's warmth on the open paper (fixed: the page
   scrolls under it). */
.the-letter-vignette {
    position: absolute;
    inset: 0;
    z-index: 2;
    background:
        linear-gradient(180deg, rgba(246, 220, 154, 0.12), rgba(246, 220, 154, 0) 26%),
        radial-gradient(ellipse 80% 72% at 50% 46%, rgba(184, 134, 47, 0) 58%, var(--paper-shade) 100%);
    box-shadow: inset 0 0 0 1px var(--paper-edge);
    opacity: 0;
    transition: opacity 0.5s var(--ease-silk);
    pointer-events: none;
}
.the-letter-container.the-letter--opened .the-letter-vignette { opacity: 1; }

.the-letter-sheet .the-letter-sweep {
    position: absolute;
    inset: 0;
    z-index: 3;
    pointer-events: none;
}

/* ───── The gold ribbon tab (swipe down to fold) ─────
   A satin tab standing up from the top edge, overlapping the paper by a
   hair so it never covers a written word. Its box is the hit area
   (48 x 70 px); the face is the ribbon. Not a control (the button is). */
.the-letter-ribbon {
    position: absolute;
    z-index: 4;
    left: 50%;
    top: -60px;
    width: 48px;
    height: 70px;
    margin-left: -24px;
    opacity: 0;
    transition: opacity 0.6s var(--ease-silk);
    pointer-events: none;
    touch-action: none;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
    cursor: grab;
}
.the-letter-ribbon .the-letter-ribbon-face {
    position: absolute;
    left: 12px;
    top: 4px;
    width: 24px;
    height: 60px;
    background: linear-gradient(90deg, var(--accent-warm-deep) 0%, var(--accent-warm-bright) 42%, var(--accent-warm) 70%, var(--accent-warm-deep) 100%);
    clip-path: polygon(0 0, 50% 16%, 100% 0, 100% 100%, 0 100%);
}
/* A small cream chevron on the ribbon: this way down. */
.the-letter-ribbon::before {
    content: '';
    position: absolute;
    z-index: 1;
    left: 50%;
    top: 28px;
    width: 8px;
    height: 8px;
    margin-left: -4px;
    border-right: 1.5px solid var(--paper);
    border-bottom: 1.5px solid var(--paper);
    transform: rotate(45deg);
    opacity: 0.85;
}
.the-letter-container.the-letter--opened .the-letter-ribbon { opacity: 1; }
.the-letter-container.the-letter--reading .the-letter-ribbon { pointer-events: auto; }
.the-letter-container.the-letter--reading .the-letter-ribbon-face {
    animation: the-letter-ribbon-nudge 2.67s var(--ease-breath) 0.8s infinite;
}
@keyframes the-letter-ribbon-nudge {
    0%, 60%, 100% { transform: translateY(0); }
    30% { transform: translateY(5px); }
}

/* ───── The exit: a thin gold thread begins across the top of the dark ─────
   The reasons beat's own thread, to the pixel: the low stretch of one
   circle (centre and radius from reasons' formula, written by the module
   as --the-letter-lx/-ly/-r), a 1.5 px --thread border. Its draw is a
   transform-only wipe: a window from -30% to +30% of the width slides
   right while its content slides back, so the arc is drawn from the left
   edge to 30% of the frame -- the state reasons opens on. */
.the-letter-thread {
    position: absolute;
    inset: 0;
    z-index: 4;
    box-sizing: border-box;
    padding-top: env(safe-area-inset-top, 0px);   /* read by the module: the arc hangs below it */
    opacity: 0;
    pointer-events: none;
}
.the-letter-thread-wipe {
    position: absolute;
    left: -30%;
    top: -30%;
    width: 60%;
    height: 160%;
    overflow: hidden;
    transform: translateX(-50%);
}
.the-letter-thread-inner {
    position: absolute;
    inset: 0;
    transform: translateX(50%);
}
.the-letter-thread-loop {
    position: absolute;
    left: var(--the-letter-lx, -130px);
    top: var(--the-letter-ly, -420px);
    width: calc(2 * var(--the-letter-r, 340px));
    height: calc(2 * var(--the-letter-r, 340px));
    box-sizing: border-box;
    border: 1.5px solid var(--thread);
    border-radius: 50%;
}
.the-letter-container.the-letter--threading .the-letter-thread {
    opacity: 1;
    transition: opacity 0.3s var(--ease-silk);
}
.the-letter-container.the-letter--threading .the-letter-thread-wipe,
.the-letter-container.the-letter--threading .the-letter-thread-inner {
    transform: translateX(0);
    transition: transform 2.4s var(--ease-silk);
}

/* ───── Screen space: the hints and the gestures' buttons ─────
   #the-letter-controls is moved into #stage by the module (outside the
   camera's world), so nothing here travels with a camera move. Once
   shown it only fades. */
.the-letter-controls {
    position: absolute;
    inset: 0;
    z-index: 6;
    pointer-events: none;
}
.the-letter-ask,
.the-letter-reread {
    position: absolute;
    left: 0;
    right: 0;
    bottom: calc(24px + env(safe-area-inset-bottom, 0px));
    display: grid;
    justify-items: center;
    gap: 12px;
    padding-inline: 16px;
    text-align: center;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.5s var(--ease-silk), visibility 0s linear 0.5s;
}
.the-letter-controls.the-letter--asking .the-letter-ask,
.the-letter-controls.the-letter--rereading .the-letter-reread {
    opacity: 1;
    visibility: visible;
    transition: opacity 0.9s var(--ease-expo-out);
}
.the-letter-hint {
    margin: 0;
    max-width: 22rem;
    font: 500 0.78rem/1.4 var(--font-body);
    letter-spacing: 0.16em;
    text-transform: uppercase;
    text-wrap: balance;
    color: var(--text-secondary);
}
.the-letter-btn {
    min-height: 44px;
    margin: 0;
    padding: 0.75rem 1.6rem;
    font: 500 0.92rem/1 var(--font-body);
    letter-spacing: 0.04em;
    color: var(--text-primary);
    background: var(--glass);
    border: 1px solid var(--btn-ghost-border);
    border-radius: 999px;
    box-shadow: 0 0 22px var(--accent-warm-glow-soft);
    cursor: pointer;
    pointer-events: auto;
    -webkit-tap-highlight-color: transparent;
}
.the-letter-btn:disabled { cursor: default; }
.the-letter-btn:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 3px;
}
.the-letter-chevs {
    display: grid;
    justify-items: center;
    height: 22px;
}
.the-letter-chev {
    width: 12px;
    height: 12px;
    margin-top: -5px;
    border-left: 2px solid var(--accent-warm);
    border-top: 2px solid var(--accent-warm);
    transform: rotate(45deg);
}
.the-letter-chev + .the-letter-chev { opacity: 0.45; }
.the-letter-controls.the-letter--asking .the-letter-chev {
    animation: the-letter-chev-nudge 1.6s var(--ease-breath) infinite;
}
.the-letter-controls.the-letter--asking .the-letter-chev + .the-letter-chev { animation-delay: 0.15s; }
@keyframes the-letter-chev-nudge {
    50% { transform: translateY(-6px) rotate(45deg); }
}

/* Entry states are applied instantly (prepare), never animated in. */
.the-letter-instant,
.the-letter-instant *,
.the-letter-instant *::before,
.the-letter-instant *::after {
    transition: none !important;
    animation: none !important;
}

/* ───── Reduced motion: every step still plays, calmly ─────
   The camera stays still (ReveelStage), so the band is the screen band.
   The sheet fades up out of the envelope and unfolds as a cross-fade
   (the module fades it; the hinges switch while it is invisible); each
   line fades in whole (400 ms) on the same schedule, with no nib; the
   fold is a fade to the closed envelope, which then fades away. */
@media (prefers-reduced-motion: reduce) {
    .the-letter-band {
        top: calc(9% + 28px);
        bottom: 128px;
    }
    .the-letter-sheet { width: min(88vw, 27rem); }

    .the-letter-panel-top,
    .the-letter-panel-bot,
    .the-letter-panel-top::after,
    .the-letter-panel-bot::after,
    .the-letter-shadow {
        transition: none !important;
    }

    .the-letter-ink {
        clip-path: none;
        opacity: 0;
    }
    .the-letter-line[data-ink="done"] .the-letter-ink { opacity: 1; }
    .the-letter-nib { display: none; }

    .the-letter-intro,
    .the-letter-container.the-letter--intro .the-letter-intro,
    .the-letter-container.the-letter--drawn .the-letter-intro {
        transform: none;
        transition: opacity 0.8s var(--ease-silk);
    }

    /* The flap closes as a fade: out, swap, in. */
    .the-letter-container.the-letter--closed .the-letter-flap {
        transition: none;
        animation: the-letter-flap-calm 0.8s var(--ease-silk) both;
    }
    .the-letter-container.the-letter--closed .the-letter-flap-in,
    .the-letter-container.the-letter--closed .the-letter-flap-out {
        transition-duration: 0s;
    }

    .the-letter-container.the-letter--away .the-letter-rig {
        transform: none;
        opacity: 0;
        transition: opacity 2.2s var(--ease-silk);
    }

    /* The motes glow and fade where they are; they do not rise. */
    .the-letter-container.the-letter--away .the-letter-motes-a,
    .the-letter-container.the-letter--away .the-letter-motes-b { animation-name: the-letter-motes-calm; }

    .the-letter-container.the-letter--armed .the-letter-glow,
    .the-letter-controls.the-letter--asking .the-letter-chev,
    .the-letter-container.the-letter--reading .the-letter-ribbon-face {
        animation: none;
    }

    /* The thread fades in whole, at the 0.35 reasons opens on under reduced motion. */
    .the-letter-thread-wipe,
    .the-letter-thread-inner,
    .the-letter-container.the-letter--threading .the-letter-thread-wipe,
    .the-letter-container.the-letter--threading .the-letter-thread-inner {
        transform: none;
        transition: none;
    }
    .the-letter-container.the-letter--threading .the-letter-thread {
        opacity: 0.35;
        transition: opacity 1.6s var(--ease-silk);
    }
}

@keyframes the-letter-motes-calm {
    0% { opacity: 0; }
    30% { opacity: 0.8; }
    100% { opacity: 0; }
}

@keyframes the-letter-flap-calm {
    0% { opacity: 1; transform: perspective(900px) rotateX(180deg); z-index: 1; }
    49% { opacity: 0; transform: perspective(900px) rotateX(180deg); z-index: 1; }
    51% { opacity: 0; transform: perspective(900px) rotateX(0deg); z-index: 4; }
    100% { opacity: 1; transform: perspective(900px) rotateX(0deg); z-index: 4; }
}
