/* ==========================================================================
   CinderTek - Effects (every page)
   First-visit intro, rising cinders and decoding headlines.
   Scripts: js/intro.js, js/atmosphere.js, js/decode.js
   ========================================================================== */

/* --------------------------------------------------------------------------
   Pre-paint cover
   The <head> script adds .ct-intro before first paint; js/intro.js replaces
   this cover with its own overlay. If it never runs, the cover lifts
   itself after 3s.
   -------------------------------------------------------------------------- */

.ct-intro body::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: var(--z-max);
    background: var(--intro-bg);
    animation: ct-cover-timeout 0.3s linear 3s forwards;
}

@keyframes ct-cover-timeout {
    to { opacity: 0; visibility: hidden; }
}

/* --------------------------------------------------------------------------
   First-visit intro
   -------------------------------------------------------------------------- */

.ct-intro-overlay {
    position: fixed;
    inset: 0;
    z-index: var(--z-max);
    display: grid;
    place-items: center;
    overflow: hidden;
    color: var(--intro-fg);
}

.ct-intro-overlay__half {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 50.5%;
    background: var(--intro-bg);
}

.ct-intro-overlay__half--left { left: 0; }
.ct-intro-overlay__half--right { right: 0; }

.ct-intro-overlay__center {
    position: relative;
    display: grid;
    justify-items: center;
    gap: var(--space-6);
    width: min(72vw, 520px);
}

.ct-intro-overlay__spark {
    width: 12px;
    height: 12px;
    background: var(--ct-ember);
    box-shadow: 0 0 24px 6px rgba(223, 97, 32, 0.45);
    opacity: 0;
}

.ct-intro-overlay__word {
    width: 100%;
    height: auto;
    overflow: visible;
    clip-path: inset(0 100% 0 0);
}

.ct-intro-overlay__cinder { fill: var(--intro-fg); }
.ct-intro-overlay__tek { fill: var(--ct-ember); }

.ct-intro-overlay__bar {
    width: 100%;
    height: 1px;
    background: rgba(var(--rgb-warm), 0.14);
}

.ct-intro-overlay__bar span {
    display: block;
    height: 100%;
    background: var(--ct-ember);
    transform: scaleX(0);
    transform-origin: left;
}

.ct-intro-overlay__count {
    position: absolute;
    left: var(--gutter);
    bottom: calc(var(--space-8) + env(safe-area-inset-bottom, 0px));
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    letter-spacing: var(--ls-caps);
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    color: rgba(var(--rgb-warm), 0.6);
}

/* --------------------------------------------------------------------------
   Rising cinders - canvas behind the content of each dark section
   -------------------------------------------------------------------------- */

.has-embers {
    position: relative;
    isolation: isolate;
}

.ember-field {
    position: absolute;
    inset: 0;
    z-index: -1;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

/* --------------------------------------------------------------------------
   Decoding headlines (js/decode.js)
   Each character keeps its real glyph in the layout (hidden until resolved),
   with the scramble glyph drawn over it, so lines never reflow.
   -------------------------------------------------------------------------- */

.decode-word {
    display: inline-block;
    white-space: nowrap;
}

.decode-char {
    position: relative;
    display: inline-block;
}

.decode-char.is-pending {
    color: transparent;
}

.decode-char[data-glyph]::after {
    content: attr(data-glyph);
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    font-family: var(--font-mono);
    font-size: 0.62em;
    font-weight: var(--fw-regular);
    color: var(--color-accent);
}

@media (prefers-reduced-motion: reduce) {
    .ct-intro body::after {
        display: none;
    }
}

/* --------------------------------------------------------------------------
   Film grain - a fine texture over every page so large dark areas never
   read as flat. Sits above content, never intercepts input.
   -------------------------------------------------------------------------- */


/* --------------------------------------------------------------------------
   Glass
   Panels are frosted, slightly lit from the top-left, with a hairline edge
   that catches light (::before, masked to the border) and a soft glare that
   follows the pointer (--gx/--gy, set by main.js). The ambient lights fixed
   behind the page (body::before) give the blur something to refract.
   -------------------------------------------------------------------------- */

body::before {
    content: "";
    position: fixed;
    inset: -10vh -10vw;
    z-index: -1;
    pointer-events: none;
    transform: translateZ(0);   /* own layer: never repainted while scrolling */
    will-change: transform;
    background:
        radial-gradient(40vw 34vw at 82% 8%, rgba(var(--rgb-accent), 0.13), transparent 70%),
        radial-gradient(46vw 40vw at 6% 46%, rgba(110, 140, 180, 0.11), transparent 70%),
        radial-gradient(38vw 30vw at 70% 96%, rgba(224, 85, 26, 0.09), transparent 70%);
}

.glass-panel {
    position: relative;
    isolation: isolate;
    /* Paint-cheap glass: no backdrop-filter (a live blur is recomputed on
       every scroll frame) and no masked pseudo-element. The lit edge is a
       gradient painted in the border box behind a near-opaque fill. */
    border: 1px solid transparent;
    border-radius: 14px;
    background:
        radial-gradient(520px circle at var(--gx, 18%) var(--gy, -20%), rgba(var(--rgb-hi), var(--glass-glare, 0.06)), transparent 55%) padding-box,
        var(--glass-bg) padding-box,
        linear-gradient(rgba(var(--rgb-well), 0.9), rgba(var(--rgb-well), 0.9)) padding-box,
        linear-gradient(135deg, rgba(var(--rgb-hi), 0.4), rgba(var(--rgb-hi), 0.09) 32%, rgba(var(--rgb-hi), 0.06) 62%, rgba(255, 138, 74, 0.38)) border-box;
    box-shadow:
        var(--glass-highlight),
        0 28px 56px -32px rgba(var(--rgb-shadow), calc(0.7 * var(--shadow-k)));
}

.glass-panel.is-glaring {
    --glass-glare: 0.1;
}

@property --glass-glare {
    syntax: "<number>";
    inherits: true;
    initial-value: 0.06;
}




/* --------------------------------------------------------------------------
   Drawing sheets (built by main.js): a faint drafting grid behind each
   content section, fading out at the edges, with registration marks, a
   sheet number and the section name running down the margin.
   -------------------------------------------------------------------------- */

.has-sheet {
    position: relative;
    isolation: isolate;
}

.sheet {
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
}

.sheet::before {
    content: "";
    position: absolute;
    inset: 0;
    /* The fade is baked into the image, so nothing is masked while scrolling. */
    background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='1792' height='1088'><defs><pattern id='g' width='64' height='64' patternUnits='userSpaceOnUse'><path d='M64 0H0V64' fill='none' stroke='%23B4B9BF' stroke-opacity='.075'/></pattern><radialGradient id='f' cx='.5' cy='.42' r='.6'><stop offset='.2' stop-color='white'/><stop offset='1' stop-color='white' stop-opacity='0'/></radialGradient><mask id='m'><rect width='100%' height='100%' fill='url(%23f)'/></mask></defs><rect width='100%' height='100%' fill='url(%23g)' mask='url(%23m)'/></svg>") center top / 1792px 1088px no-repeat;
}

.sheet__frame {
    position: absolute;
    top: var(--space-6);
    bottom: var(--space-6);
    left: 0;
    right: 0;
    width: 100%;
    max-width: calc(var(--container-default) + var(--gutter) * 2);
    margin-inline: auto;
}

.has-sheet--pinned > .sheet .sheet__frame {
    top: calc(var(--header-height) + var(--space-3));
}

.sheet__reg {
    position: absolute;
    top: 0;
    width: 14px;
    height: 14px;
    border: 0 solid rgba(var(--rgb-line), 0.32);
}

.sheet__reg--start {
    left: var(--gutter);
    border-top-width: 1px;
    border-left-width: 1px;
}

.sheet__reg--end {
    right: var(--gutter);
    border-top-width: 1px;
    border-right-width: 1px;
}

.sheet__no {
    position: absolute;
    top: -4px;
    right: calc(var(--gutter) + 26px);
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--color-text-muted);
    opacity: 0.8;
}

/* Margin rail: a short tick, the section label, then the rule running
   down the margin. The rule stops short of the label on both sides, so the
   line never crosses the text and stays centred on the label's column. */
.sheet__rail {
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(var(--gutter) - 43px);
    width: 14px;
    display: none;
    flex-direction: column;
    align-items: center;
    gap: 14px;
}

.sheet__rail::before,
.sheet__rail::after {
    content: "";
    flex: none;
    width: 1px;
    background: rgba(var(--rgb-line), 0.3);
}

.sheet__rail::before {
    height: 18px;
}

.sheet__rail::after {
    flex: 1;
    background: linear-gradient(rgba(var(--rgb-line), 0.3), transparent 70%);
}

.sheet__rail span {
    flex: none;
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font-family: var(--font-mono);
    font-size: 10px;
    line-height: 14px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--color-text-muted);
}

/* The margin rail only where the margin is wide enough to hold it. */
@media (min-width: 1440px) {
    .sheet__rail {
        display: flex;
    }
}

@media (max-width: 767.98px) {
    .sheet__no {
        right: calc(var(--gutter) + 22px);
        font-size: 9px;
    }

    .sheet::before {
        background-size: 1344px 816px;
    }
}


/* While the page is scrolling, on-screen loops hold still so every frame
   goes to the scroll itself; they resume as soon as it settles. main.js
   toggles .is-paused on the few containers that are playing. */
.is-playing.is-paused * {
    animation-play-state: paused !important;
}

/* Lighter mode for devices that cannot keep up (set by smooth-scroll.js). */
html.ct-lite .is-playing *,
html.ct-lite .pui * {
    animation: none !important;
}
