/* ==========================================================================
   /time/ — adrift.
   A flat field pretending to be infinite: depth is blur + scale + opacity +
   drift-rate parallax, never geometry. Warm paper fog, photocopy static,
   dithered scraps, one neon. Loaded ONLY by time/index.html after style.css;
   reads the site tokens, defines nothing global. Without JS the page stays a
   plain readable list (body.museum-on gates every immersive rule).
   ========================================================================== */

/* ---- the page's own tokens ---- */
.museum {
    /* the ONE neon — scoped here, never global */
    --time-neon: #f0468c;
    --time-neon-text: #b3275f;
    --time-neon-rgb: 240, 70, 140;
}

html[data-theme="dark"] .museum {
    --time-neon: #ff5da2;
    --time-neon-text: #ff7ab5;
    --time-neon-rgb: 255, 93, 162;
}

html[data-palette="cosmic"] .museum {
    --time-neon: #ff5ce0;
    --time-neon-text: #ff8de9;
    --time-neon-rgb: 255, 92, 224;
}

/* ==========================================================================
   No-JS / fallback shell: a quiet list of everything in the collection.
   ========================================================================== */
.museum-list {
    max-width: 640px;
    margin: 0 0 3rem;
}

.museum-list ol {
    list-style: none;
    display: grid;
    gap: 1.1rem;
    padding: 0;
}

.museum-list .list-year {
    font-family: var(--mono);
    font-size: 0.62rem;
    letter-spacing: 0.1em;
    text-transform: lowercase;
    font-variant-numeric: tabular-nums;
    color: var(--link);
    display: block;
}

.museum-list .list-title {
    font-family: var(--header);
    font-size: 1.05rem;
    color: var(--ink);
}

.museum-list .list-blurb {
    font-family: var(--body);
    font-size: 0.9rem;
    color: var(--text);
}

/* immersive chrome stays out of the way until JS builds the field */
.museum .drift,
.museum .dust,
.museum .museum-rail,
.museum .museum-era,
.museum .museum-hint,
.museum .museum-whisper,
.museum .museum-curtain,
.museum .museum-exit,
.museum .time-hum {
    display: none;
}

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

/* ==========================================================================
   Drift mode (JS on): the page becomes the static.
   ========================================================================== */
body.museum-on {
    overflow: hidden;
}

body.museum-on nav,
body.museum-on footer {
    display: none;
}

body.museum-on .museum-list {
    display: none;
}

body.museum-on .museum {
    position: fixed;
    inset: 0;
    overflow: hidden;
    background: var(--paper);
    /* main's column layout must not box the void */
    margin: 0;
    padding: 0;
    width: 100vw;
    max-width: none;
}

/* cosmic: the field floats in the nebula (glint shows through) */
html[data-palette="cosmic"] body.museum-on .museum {
    background: transparent;
}

/* ---- the field ---- */
body.museum-on .drift {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 1;
    touch-action: none;
}

/* photocopy tooth: one turbulence tile, slid by JS on its own 240px period so
   the paper itself streams past as you drift (transform-only, no repaint) */
body.museum-on .tooth {
    position: absolute;
    inset: -260px 0;
    pointer-events: none;
    opacity: 0.05;
    will-change: transform;     /* its own layer: per-frame slide stays compositor-only */
    background-image: url("/assets/images/time/noise.svg");
    background-size: 240px 240px;
}

/* the fog: huge blurred era-hued strata. JS slides each at its own rate with
   travel (plus a slow weather sway), so drifting deeper feels like moving
   THROUGH the weather. Constant blur + transform-only = compositor work. */
.fog {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

/* no blur filter on purpose: the air recolors continuously with travel, and
   gradient repaints stay cheap only while there's no blur pass to redo —
   the softness lives in the gradient stops themselves */
.fog-a,
.fog-b {
    position: absolute;
    inset: -42% -16%;
    /* promoted on purpose: without a layer each per-frame transform repaints
       a ~2.4-viewport gradient (this was the sub-12fps bug after the blur,
       which had been promoting them implicitly, was removed) */
    will-change: transform;
}

.fog-a {
    background:
        radial-gradient(46% 38% at 30% 36%,
            color-mix(in srgb, var(--haze-a) var(--haze-d), transparent), transparent 72%),
        radial-gradient(38% 44% at 74% 62%,
            color-mix(in srgb, var(--haze-b) calc(var(--haze-d) * 0.7), transparent), transparent 74%),
        radial-gradient(30% 26% at 52% 88%,
            color-mix(in srgb, var(--haze-a) calc(var(--haze-d) * 0.5), transparent), transparent 75%);
}

.fog-b {
    background:
        radial-gradient(32% 30% at 56% 22%,
            color-mix(in srgb, var(--haze-b) calc(var(--haze-d) * 0.5), transparent), transparent 75%),
        radial-gradient(40% 36% at 22% 76%,
            color-mix(in srgb, var(--haze-a) calc(var(--haze-d) * 0.6), transparent), transparent 70%),
        radial-gradient(26% 30% at 84% 12%,
            color-mix(in srgb, var(--haze-b) calc(var(--haze-d) * 0.4), transparent), transparent 72%);
}

/* era atmosphere — felt, never built */
.drift[data-era="0"] { --haze-a: var(--cat-olive);  --haze-b: var(--cat-teal);   --haze-d: 14%; }
.drift[data-era="1"] { --haze-a: var(--cat-amber);  --haze-b: var(--cat-coral);  --haze-d: 20%; }
.drift[data-era="2"] { --haze-a: var(--cat-green);  --haze-b: var(--cat-amber);  --haze-d: 26%; }
.drift[data-era="3"] { --haze-a: var(--cat-violet); --haze-b: var(--cat-teal);   --haze-d: 15%; }

.drift.is-cosmic .fog,
.drift.is-cosmic .tooth {
    display: none;            /* nebula-glint owns the ambience there */
}

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

/* ==========================================================================
   The memories — bodies in the fog.
   ========================================================================== */
.mem {
    position: absolute;
    left: 50%;
    top: 50%;
    opacity: 0;
    /* no filter transition here: with 23 pieces crossing blur buckets during
       a scroll there is ALWAYS some transition running, and each one
       re-rasterizes its element every frame. Buckets step instantly; only
       the held sharpen (below) eases. */
    cursor: default;
}

.mem.is-held {
    transition: filter 0.28s ease;
}

/* quantized blur — constant within a bucket so rasters stay cached */
.b0 { filter: none; }
.b1 { filter: blur(1.5px); }
.b2 { filter: blur(3px); }
.b3 { filter: blur(5px); }
.b4 { filter: blur(8px); }

.mem.is-held {
    z-index: 6;
    filter: drop-shadow(0 0 22px color-mix(in srgb, var(--era-c, var(--link)) 40%, transparent));
}

/* per-era lamp color */
.mem[data-era="0"] { --era-c: var(--cat-olive); }
.mem[data-era="1"] { --era-c: var(--cat-amber); }
.mem[data-era="2"] { --era-c: var(--cat-green); }
.mem[data-era="3"] { --era-c: var(--cat-violet); }

.mem:focus-visible {
    outline: none;
}

.mem:focus-visible .face {
    outline: 3px solid var(--link);
    outline-offset: 5px;
}

.mem.is-neon:focus-visible .face {
    outline-color: var(--time-neon);
}

/* ---- form: the zeroth memory ---- */
.mem-title {
    pointer-events: none;
}

.face-title {
    font-family: var(--header);
    font-weight: 300;
    font-size: clamp(4rem, 12vw, 7rem);
    letter-spacing: 0.02em;
    line-height: 1;
    color: var(--muted);
}

/* ---- form: photo-scrap (dithered, torn, tilted) ---- */
.mem-scrap { width: clamp(170px, 16vw, 280px); }

.scrap-face {
    position: relative;
    background: var(--paper-raised);
    padding: 4.5%;
    box-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.06);
}

.scrap-dither {
    position: absolute;
    inset: 4.5%;
    width: 91%;
    height: 91%;
    image-rendering: pixelated;
}

.museum .drawn-slot {
    display: block;            /* a hanging place even while empty */
    position: absolute;
    inset: 4.5%;
    width: 91%;
    height: 91%;
}

.museum .drawn-slot:not(.has-art)::after {
    content: attr(data-cap);
    position: absolute;
    inset: auto 6% 6% 6%;
    font-family: var(--mono);
    font-size: 0.5rem;
    letter-spacing: 0.08em;
    text-transform: lowercase;
    color: var(--muted);
    text-align: center;
    background: color-mix(in srgb, var(--paper-raised) 80%, transparent);
    padding: 0.15rem 0.2rem;
}

.museum .drawn-slot.has-art {
    background: var(--slot-src) center / cover no-repeat;
}

html[data-theme="dark"] .museum .drawn-slot.has-art {
    background-image: var(--slot-src-dark, var(--slot-src));
}

.museum .drawn-slot.has-art ~ .scrap-dither {
    display: none;
}

/* one hangs crooked; holding it straightens it (you fixed it) */
.mem.is-crooked .face {
    transform: rotate(-2.4deg);
    transition: transform 0.45s cubic-bezier(0.3, 1.25, 0.5, 1);
}

.mem.is-crooked.is-held .face,
.mem.is-crooked:focus-within .face {
    transform: rotate(0deg);
}

/* ---- form: neon dithered still (the struck matches) ---- */
.mem-neon { width: clamp(160px, 15vw, 260px); }

.mem-neon .scrap-face {
    border: 1px solid color-mix(in srgb, var(--time-neon) 70%, transparent);
}

.mem-neon .scrap-face::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
        repeating-linear-gradient(0deg,
            rgba(var(--time-neon-rgb), 0.10) 0 1px, transparent 1px 3px),
        radial-gradient(80% 60% at 50% 30%,
            rgba(var(--time-neon-rgb), 0.12), transparent 75%);
}

.mem-neon.is-held .scrap-face {
    box-shadow: 0 0 18px rgba(var(--time-neon-rgb), 0.35);
}

/* ---- form: .txt note (typewritten on fog) ---- */
.mem-note { width: clamp(200px, 18vw, 300px); }

.face-note {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    padding: 0.7rem 0.85rem;
    background: color-mix(in srgb, var(--paper-raised) 92%, transparent);
    border: 1px solid rgba(var(--text-rgb), 0.14);
    font-family: var(--mono);
}

.note-line {
    font-size: 0.78rem;
    letter-spacing: 0.03em;
    text-transform: lowercase;
    color: var(--ink-soft);
}

.note-body {
    color: var(--text);
    font-size: 0.72rem;
}

.note-redact::after {
    content: '';
    display: inline-block;
    width: 3.2em;
    height: 0.72em;
    margin-left: 0.35em;
    vertical-align: baseline;
    background: var(--redact);
}

.note-caret {
    display: inline-block;
    width: 0.5em;
    height: 0.95em;
    background: var(--link);
    animation: noteBlink 1.05s steps(1) infinite;
}

@keyframes noteBlink {
    50% { opacity: 0; }
}

/* ---- form: color blob (a planet or a mote — who can say) ---- */
.mem-blob {
    width: clamp(180px, 17vw, 300px);
}

.face-blob {
    aspect-ratio: 1;
    background:
        radial-gradient(closest-side circle at 50% 46%,
            color-mix(in srgb, var(--era-c, var(--link)) 52%, transparent),
            color-mix(in srgb, var(--era-c, var(--link)) 22%, transparent) 52%,
            transparent 96%);
}

/* ---- form: the one browser window ---- */
.mem-window { width: clamp(200px, 17vw, 270px); }

.win {
    background: var(--paper-raised);
    border: 1px solid rgba(var(--text-rgb), 0.4);
    box-shadow: 2px 2px 0 rgba(var(--shadow-rgb), 0.12);
}

.win-bar {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.25rem 0.4rem;
    border-bottom: 1px solid rgba(var(--text-rgb), 0.25);
}

.win-dots {
    width: 5px;
    height: 5px;
    background: var(--text);
    box-shadow: 8px 0 0 0 rgba(var(--text-rgb), 0.55), 16px 0 0 0 rgba(var(--text-rgb), 0.3);
}

.win-title {
    font-family: var(--mono);
    font-size: 0.58rem;
    letter-spacing: 0.05em;
    color: var(--ink-soft);
    margin-left: 0.5rem;
}

.win-addr {
    font-family: var(--mono);
    font-size: 0.52rem;
    color: var(--muted);
    padding: 0.2rem 0.45rem;
    border-bottom: 1px solid rgba(var(--text-rgb), 0.18);
}

.win .scrap-face {
    position: relative;
    aspect-ratio: 16 / 10;
    box-shadow: none;
    padding: 0;
}

.win .scrap-dither,
.win .drawn-slot {
    inset: 0;
    width: 100%;
    height: 100%;
}

/* ---- form: whisper (Caveat, barely there) ---- */
.mem-whisper { max-width: 340px; }

.face-whisper {
    font-family: 'Caveat', cursive;
    font-weight: 500;
    font-size: clamp(1.6rem, 2.6vw, 2rem);
    line-height: 1.2;
    color: var(--muted);
    opacity: 0.55;
    text-align: center;
    transition: opacity 0.3s ease;
}

.mem-whisper.is-held .face-whisper,
.mem-whisper:focus-within .face-whisper {
    opacity: 1;
    color: var(--text);
}

/* ---- form: dashed ghost (the futures) ---- */
.mem-ghost { width: clamp(170px, 15vw, 260px); }

.ghost-pane {
    aspect-ratio: 4 / 5;
    border: 1.5px dashed color-mix(in srgb, var(--time-neon) 80%, transparent);
    background: radial-gradient(70% 60% at 50% 40%,
        rgba(var(--time-neon-rgb), 0.10), transparent 75%);
}

.mem-ghost .plaque {
    border-style: dashed;
    opacity: 1;                 /* the futures speak unprompted */
    transform: translateX(-50%);
    pointer-events: auto;
}

.mem-ghost .plaque-year {
    color: var(--time-neon-text);
}

.mem-ghost .plaque-title {
    font-style: italic;
    font-weight: 400;
}

.mem-small { width: clamp(130px, 11vw, 190px); }

/* ---- the plaque: surfaces when held ---- */
.plaque {
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%) translateY(6px);
    margin-top: 0.6rem;
    min-width: 150px;
    max-width: 230px;
    padding: 0.5rem 0.7rem;
    background: var(--paper-raised);
    border: 1px solid rgba(var(--text-rgb), 0.2);
    box-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.05);
    display: flex;
    flex-direction: column;
    gap: 0.12rem;
    text-align: center;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.6s ease, transform 0.6s ease;
}

.mem.is-held .plaque,
.mem:focus-within .plaque {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
    pointer-events: auto;
    transition: opacity 0.28s cubic-bezier(0.3, 1.1, 0.4, 1),
                transform 0.28s cubic-bezier(0.3, 1.1, 0.4, 1);
}

/* a note already says its piece — its plaque only adds the year (and a link) */
.mem-note .plaque-title,
.mem-note .plaque-blurb {
    display: none;
}

.mem-note .plaque {
    min-width: 0;
}

.plaque-year {
    font-family: var(--mono);
    font-size: 0.6rem;
    letter-spacing: 0.1em;
    text-transform: lowercase;
    font-variant-numeric: tabular-nums;
    color: var(--link);
}

.plaque-title {
    font-family: var(--header);
    font-weight: 500;
    font-size: 0.98rem;
    line-height: 1.25;
    color: var(--ink);
}

.plaque-blurb {
    font-family: var(--body);
    font-size: 0.8rem;
    line-height: 1.45;
    color: var(--text);
}

.plaque-read {
    font-family: var(--mono);
    font-size: 0.64rem;
    letter-spacing: 0.05em;
    text-transform: lowercase;
    color: var(--link);
    text-decoration: none;
    background: none;
    border: 0;
    padding: 0.15rem 0 0;
    cursor: pointer;
    align-self: center;
}

.mem.is-neon .plaque-read {
    color: var(--time-neon-text);
}

.plaque-read:hover,
.plaque-read:focus-visible {
    text-decoration: underline;
}

.plaque-read:focus-visible {
    outline: 2px solid var(--link);
    outline-offset: 2px;
}

/* ==========================================================================
   Chrome: exit, hum, era label, rail, hints, grain canvas.
   ========================================================================== */
body.museum-on .museum-exit {
    display: inline-flex;
    align-items: center;
    position: fixed;
    top: 16px;
    left: 16px;
    z-index: 40;
    height: 2.1rem;
    padding: 0 0.8rem;
    font-family: var(--mono);
    font-size: 0.7rem;
    letter-spacing: 0.05em;
    text-transform: lowercase;
    text-decoration: none;
    color: var(--text);
    background: var(--paper-raised);
    border: 1px solid rgba(var(--text-rgb), 0.2);
}

body.museum-on .museum-exit:hover {
    border-color: rgba(var(--text-rgb), 0.5);
    color: var(--ink);
}

body.museum-on .museum-exit:focus-visible {
    outline: 3px solid var(--link);
    outline-offset: 3px;
}

body.museum-on .time-hum {
    display: inline-flex;
    align-items: center;
    position: fixed;
    bottom: 16px;
    right: 16px;
    z-index: 40;
    height: 2.1rem;
    padding: 0 0.8rem;
    font-family: var(--mono);
    font-size: 0.7rem;
    letter-spacing: 0.05em;
    text-transform: lowercase;
    color: var(--text);
    background: var(--paper-raised);
    border: 1px solid rgba(var(--text-rgb), 0.2);
    cursor: pointer;
}

body.museum-on .time-hum:hover {
    border-color: rgba(var(--text-rgb), 0.5);
    color: var(--ink);
}

body.museum-on .time-hum:focus-visible {
    outline: 3px solid var(--link);
    outline-offset: 3px;
}

body.museum-on .museum-era {
    display: block;
    position: fixed;
    top: 24px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 40;
    margin: 0;
    font-family: var(--mono);
    font-size: 0.68rem;
    letter-spacing: 0.12em;
    text-transform: lowercase;
    color: var(--muted);
    transition: opacity 0.26s ease, transform 0.26s ease;
}

body.museum-on .museum-era.is-swapping {
    opacity: 0;
    transform: translateX(-50%) translateY(-3px);
}

body.museum-on .museum-rail {
    display: block;
    position: fixed;
    top: 14vh;
    bottom: 14vh;
    right: 14px;
    width: 3px;
    z-index: 20;
    background: rgba(var(--text-rgb), 0.12);
}

body.museum-on .museum-rail-fill {
    display: block;
    width: 100%;
    height: 100%;
    background: var(--time-neon);
    transform: scaleY(0);
    transform-origin: top;
}

body.museum-on .museum-hint {
    display: block;
    position: fixed;
    bottom: 22px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 40;
    margin: 0;
    font-family: var(--mono);
    font-size: 0.62rem;
    letter-spacing: 0.08em;
    text-transform: lowercase;
    color: var(--muted);
    transition: opacity 1s ease;
}

body.museum-on .museum-hint.is-done {
    opacity: 0;
    pointer-events: none;
}

body.museum-on .museum-whisper {
    display: block;
    position: fixed;
    bottom: 60px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 30;
    margin: 0;
    font-family: 'Caveat', cursive;
    font-weight: 500;
    font-size: 1.6rem;
    color: var(--muted);
    opacity: 0;
    pointer-events: none;
    transition: opacity 1.4s ease;
}

body.museum-on .museum-whisper.is-on {
    opacity: 0.6;
}

body.museum-on .dust {
    display: block;
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    z-index: 4;
    pointer-events: none;
    image-rendering: pixelated;
}

/* ==========================================================================
   The curtain — a held threshold. It waits, breathing, with the fog drifting
   faintly behind the veil, until the visitor scrolls. Then it becomes the fog.
   ========================================================================== */
body.museum-on .museum-curtain {
    display: grid;
    place-items: center;
    position: fixed;
    inset: 0;
    z-index: 60;
    /* a veil, not a wall: the living field shows through */
    background: color-mix(in srgb, var(--paper) 58%, transparent);
    text-align: center;
    cursor: pointer;
    transition: opacity 0.9s ease, filter 0.9s ease;
}

body.museum-on .museum-curtain.is-lifting {
    opacity: 0;
    filter: blur(20px) brightness(1.06);
    pointer-events: none;
}

/* hidden must win over display:grid (reduced motion skips the lift) */
body.museum-on .museum-curtain[hidden] {
    display: none;
}

/* while the threshold holds, the field's own hints stay out of the veil */
body.museum-on .museum:has(.museum-curtain:not([hidden])) .museum-hint,
body.museum-on .museum:has(.museum-curtain:not([hidden])) .museum-whisper {
    opacity: 0;
}

/* the text sits on solid ground even while the veil is sheer */
.curtain-inner {
    position: relative;
    padding: 5.5rem 7rem;
    background: radial-gradient(closest-side,
        var(--paper) 58%,
        color-mix(in srgb, var(--paper) 72%, transparent) 82%,
        transparent 100%);
}

/* a slow warm halo behind the word — the inviting, unexplained light */
.curtain-inner::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 38%;
    width: 130%;
    aspect-ratio: 5 / 3;
    transform: translate(-50%, -50%);
    pointer-events: none;
    background: radial-gradient(closest-side,
        color-mix(in srgb, var(--cat-amber) 16%, transparent),
        color-mix(in srgb, var(--cat-olive) 7%, transparent) 55%,
        transparent 100%);
    animation: curtainHalo 8s ease-in-out infinite;
}

@keyframes curtainHalo {
    0%, 100% { opacity: 0.45; transform: translate(-50%, -50%) scale(0.94); }
    50%      { opacity: 0.95; transform: translate(-50%, -50%) scale(1.08); }
}

.curtain-title {
    position: relative;
    font-family: var(--header);
    font-weight: 300;
    font-size: clamp(3.2rem, 9vw, 5.5rem);
    line-height: 1;
    letter-spacing: 0.02em;
    color: var(--ink);
    margin: 0 0 1.1rem;
    animation: fadeIn 0.9s ease-out both,
        curtainBreathe 7s ease-in-out 0.9s infinite;
}

@keyframes curtainBreathe {
    0%, 100% { transform: scale(1); opacity: 1; }
    50%      { transform: scale(1.025); opacity: 0.88; }
}

.curtain-line {
    position: relative;
    font-family: var(--body);
    font-style: italic;
    font-size: 1.1rem;
    color: var(--text);
    margin: 0 0 2.4rem;
    animation: fadeIn 0.9s ease-out 0.25s both;
}

.curtain-hint {
    position: relative;
    font-family: var(--mono);
    font-size: 0.62rem;
    letter-spacing: 0.1em;
    text-transform: lowercase;
    color: var(--muted);
    margin: 0;
    animation: fadeIn 0.9s ease-out 0.6s both,
        curtainBeckon 3.4s ease-in-out 1.5s infinite;
}

@keyframes curtainBeckon {
    0%, 100% { opacity: 1; transform: translateY(0); }
    50%      { opacity: 0.45; transform: translateY(2px); }
}

@media (max-width: 600px) {
    .curtain-inner {
        padding: 4rem 2.5rem;
    }
}

/* ==========================================================================
   Fingers need bigger doors.
   ========================================================================== */
@media (pointer: coarse) {
    body.museum-on .museum-exit,
    body.museum-on .time-hum {
        min-height: 44px;
    }

    .plaque-read {
        padding: 0.6rem 0.8rem 0.45rem;
        margin: -0.3rem auto -0.35rem;
    }
}

/* ==========================================================================
   Small screens: the same fog, closer in.
   ========================================================================== */
@media (max-width: 600px) {
    .mem-scrap,
    .mem-neon { width: clamp(150px, 44vw, 210px); }
    .mem-note { width: clamp(180px, 56vw, 240px); }
    .mem-blob { width: clamp(150px, 46vw, 220px); }
    .mem-window { width: clamp(180px, 52vw, 230px); }
    .mem-ghost { width: clamp(150px, 44vw, 210px); }
    .mem-whisper { max-width: 260px; }

    .face-title {
        font-size: clamp(3rem, 18vw, 4.5rem);
    }

    body.museum-on .museum-era {
        top: 70px;
    }
}
