/* SPDX-License-Identifier: Apache-2.0 * Copyright 2026 alvations (Melon Lab) */ :root { --bg: #0b0c0e; --fg: #d7d7d2; /* Two tiers of secondary text, both WCAG AA on the dark bg but kept gloomy: --dim = reading tier (lead, taglines, asides, intro/win) ~6.7:1 --dim2 = chrome tier (HUD, give-up link, language row) ~5.0:1 */ --dim: #93979f; --dim2: #7d818a; --accent: #9fd0c0; --danger: #c98b8b; --hue: 210; /* base hue of the ambient background glow */ --panel-w: 40rem; /* These are nudged by JS a fraction at a time -- a drift you feel rather than see. */ --bg-shift: 0; --weight: 340; --tracking: 0.01em; } /* --- skins --------------------------------------------------------------- Each arc sets data-skin on ; these override the palette. */ body[data-skin="hallway"] { --bg: #0b0c0e; --fg: #d7d7d2; --dim: #93979f; /* cool blue-grey, reading tier */ --dim2: #7d818a; /* cool blue-grey, chrome tier */ --accent: #6fb2ee; /* cold fluorescent blue */ --hue: 212; } body[data-skin="stairway"] { --bg: #0a0d0b; --fg: #d3d8d2; --dim: #949c96; /* green-grey, reading tier */ --dim2: #7e857f; /* green-grey, chrome tier */ --accent: #8fc79a; /* emergency-exit green */ --hue: 150; } body[data-skin="coach"] { --bg: #0c0a08; --fg: #e0d7c8; --dim: #9d978a; /* warm amber-grey, reading tier */ --dim2: #877f72; /* warm amber-grey, chrome tier */ --accent: #d8b06a; /* warm cabin amber */ --hue: 35; } * { box-sizing: border-box; } html, body { margin: 0; height: 100%; background: var(--bg); color: var(--fg); font-family: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif; -webkit-font-smoothing: antialiased; } body { display: flex; align-items: center; justify-content: center; min-height: 100vh; /* the 1%-at-a-time background shift lives here */ background: radial-gradient( 120% 90% at 50% -10%, hsl(var(--hue) 13% calc(9.5% + var(--bg-shift) * 1%)), var(--bg) 70% ); transition: background 2.4s ease; } #screen { width: var(--panel-w); max-width: 92vw; padding: 2rem; } .panel { animation: fade-in 1.1s ease both; } .hidden { display: none !important; } /* --- title --- */ .title { font-size: 3rem; font-weight: 300; letter-spacing: 0.18em; text-align: center; margin: 0 0 1.5rem; color: var(--fg); } .select-lead { text-align: center; color: var(--dim); line-height: 1.9; margin: 0 0 1rem; } /* The ways through fade in a beat after the title, so the first paint is just "8" and the two lead lines: a deliberate, empty-of-chrome splash. */ /* The ways-through occupy their space from first paint (so the title/lead sit at their final position immediately); they only fade opacity in on reveal. The min-height reserves the arcs' room even before they have loaded, so nothing shifts between the splash and the loaded screen. */ .select-reveal { transition: opacity 1.3s ease; min-height: 19rem; } .select-reveal.pre { opacity: 0; visibility: hidden; } .brief { white-space: pre-wrap; line-height: 1.9; color: var(--dim); font-size: 1.02rem; min-height: 12rem; margin-bottom: 2rem; } .brief .lead { color: var(--fg); letter-spacing: 0.14em; } /* --- arc cards --- */ .arcs { display: flex; flex-direction: column; gap: 1rem; margin-top: 2rem; } .arc-card { text-align: left; background: transparent; color: var(--fg); border: 1px solid #2a2d31; border-left: 5px solid #2a2d31; padding: 1.1rem 1.3rem; font-family: inherit; cursor: pointer; transition: border-color 0.3s ease, background 0.3s ease, transform 0.15s ease, box-shadow 0.3s ease; } .arc-card:hover { transform: translateX(4px); } /* Each arc wears its own colour prominently: tinted card, coloured title and edge, a glow on hover. */ .arc-card[data-skin="hallway"] { border-color: rgba(111, 178, 238, 0.35); border-left-color: #6fb2ee; background: rgba(111, 178, 238, 0.06); } .arc-card[data-skin="hallway"] .arc-title { color: #6fb2ee; } .arc-card[data-skin="hallway"]:hover { border-color: #6fb2ee; background: rgba(111, 178, 238, 0.12); box-shadow: 0 0 24px -8px rgba(111, 178, 238, 0.6); } .arc-card[data-skin="stairway"] { border-color: rgba(143, 199, 154, 0.35); border-left-color: #8fc79a; background: rgba(143, 199, 154, 0.06); } .arc-card[data-skin="stairway"] .arc-title { color: #8fc79a; } .arc-card[data-skin="stairway"]:hover { border-color: #8fc79a; background: rgba(143, 199, 154, 0.12); box-shadow: 0 0 24px -8px rgba(143, 199, 154, 0.5); } .arc-card[data-skin="coach"] { border-color: rgba(216, 176, 106, 0.35); border-left-color: #d8b06a; background: rgba(216, 176, 106, 0.06); } .arc-card[data-skin="coach"] .arc-title { color: #d8b06a; } .arc-card[data-skin="coach"]:hover { border-color: #d8b06a; background: rgba(216, 176, 106, 0.12); box-shadow: 0 0 24px -8px rgba(216, 176, 106, 0.5); } .arc-title { font-size: 1.3rem; font-weight: 300; letter-spacing: 0.12em; } .arc-tag { color: var(--dim); font-size: 0.95rem; margin-top: 0.4rem; line-height: 1.5; } /* --- hud --- */ .hud { display: flex; justify-content: space-between; font-size: 0.72rem; letter-spacing: 0.24em; text-transform: uppercase; color: var(--dim2); /* chrome tier */ margin-bottom: 2.5rem; gap: 1rem; } /* The progress word ("HALLWAY 3 / 8") takes a hair of the arc's accent, so the HUD carries the arc's colour too, not just the prose. */ .progress { color: color-mix(in srgb, var(--accent) 42%, var(--dim2)); } .best { opacity: 0.72; white-space: nowrap; } /* --- the loop --- */ .heading { text-align: center; font-weight: 300; letter-spacing: 0.22em; font-size: 1.5rem; margin: 0 0 2.5rem; color: var(--fg); } .prose { line-height: 2.1; font-size: 1.12rem; font-weight: var(--weight); letter-spacing: var(--tracking); min-height: 11rem; } .prose .line { opacity: 0; cursor: default; transition: opacity 0.9s ease, color 0.4s ease; } .prose .line.show { opacity: 1; } .prose .line:hover { color: #fff; } /* Hard mode double-change reveal: the two changed lines briefly glow the accent after a correct turn-back, teaching "two moved here". Silent, no text. */ .prose .line.flare { animation: flare 1.4s ease; } @keyframes flare { 0% { color: inherit; text-shadow: none; } 25% { color: var(--accent); text-shadow: 0 0 10px color-mix(in srgb, var(--accent) 55%, transparent); } 70% { color: var(--accent); text-shadow: 0 0 6px color-mix(in srgb, var(--accent) 35%, transparent); } 100% { color: inherit; text-shadow: none; } } html.motion-reduce .prose .line.flare { animation: none; color: var(--accent); /* still reveal which two, just without the pulse */ } .aside { color: var(--dim); font-style: italic; min-height: 1.6rem; opacity: 0; transition: opacity 1.2s ease; margin-top: 0.5rem; } .aside.show { opacity: 0.85; } /* --- controls --- */ .controls { display: flex; gap: 1rem; margin-top: 2.5rem; animation: fade-in 1s ease both; } .confidence { margin-top: 1.6rem; animation: fade-in 0.6s ease both; } .conf-q { color: var(--dim); font-size: 0.92rem; margin: 0 0 0.7rem; } .conf-row { display: flex; gap: 0.5rem; flex-wrap: wrap; } .row { display: flex; gap: 1rem; align-items: center; justify-content: center; } /* --- buttons --- */ .btn { background: transparent; color: var(--fg); border: 1px solid #2a2d31; padding: 0.8rem 1.4rem; font-family: inherit; font-size: 1rem; letter-spacing: 0.06em; cursor: pointer; transition: border-color 0.3s ease, background 0.3s ease, color 0.3s ease; } .btn:hover { border-color: var(--accent); color: #fff; } .btn:active { transform: translateY(1px); } /* The go-on / turn-back buttons carry a whisper of the arc's accent even at rest, so the place keeps its colour identity in-loop (blue hallway, amber coach, green stair) instead of collapsing to the same neutral pair. */ .choice { flex: 1; border-color: color-mix(in srgb, var(--accent) 26%, #2a2d31); } .choice:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); } .btn.ghost { border-color: #23262a; color: var(--dim); padding: 0.55rem 1rem; font-size: 0.86rem; } .btn.ghost:hover { color: var(--fg); border-color: var(--dim); } #begin, #again { min-width: 12rem; } /* always-visible mute toggle */ .sound-toggle { position: fixed; top: 1rem; right: 1rem; z-index: 10; background: transparent; border: none; line-height: 0; padding: 0.4rem; cursor: pointer; color: var(--dim2); /* chrome tier, per skin */ opacity: 0.55; transition: opacity 0.3s ease, color 0.3s ease; } .sound-toggle svg { display: block; } .sound-toggle:hover { opacity: 1; color: var(--fg); } /* language picker: a quiet fixed corner control, top-left (mirrors the sound toggle top-right). Only shown on the selection screen. */ .visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; } .lang-corner { position: fixed; top: 1rem; left: 1rem; z-index: 10; display: flex; align-items: center; gap: 0.3rem; color: var(--dim2); /* chrome tier, per skin */ opacity: 0.55; transition: opacity 0.3s ease, color 0.3s ease; } .lang-corner:hover { opacity: 1; color: var(--fg); } .lang-globe { display: block; flex: none; } /* Strip the OS chrome: no native border/background/arrow, a drawn caret instead, so the resting control reads as part of the mood. */ .lang-select { appearance: none; -webkit-appearance: none; background: transparent; border: none; color: inherit; font-family: inherit; font-size: 0.8rem; letter-spacing: 0.04em; cursor: pointer; padding: 0.1rem 0.9rem 0.1rem 0.1rem; background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%); background-size: 4px 4px, 4px 4px; background-position: right 3px center, right calc(3px - 4px) center; background-repeat: no-repeat; } .lang-select:focus-visible { outline: 1px solid var(--accent); outline-offset: 2px; } /* The open dropdown is OS-native; keep its rows legible on the dark bg. */ .lang-select option { background: #16181c; color: #d7d7d2; } /* a quiet text link to abandon the run */ .give-up-row { text-align: center; margin-top: 3.5rem; } .link-btn { background: none; border: none; color: var(--dim2); /* chrome tier */ font-family: inherit; font-size: 0.8rem; letter-spacing: 0.14em; text-transform: uppercase; cursor: pointer; opacity: 0.78; transition: opacity 0.3s ease, color 0.3s ease; } .link-btn:hover { opacity: 1; color: var(--fg); } /* On the win screen the brief needs no reserved height (that spacer is for the intro); collapse it so the fleeting "look back" line sits close under the win text rather than across a dead gap. */ #win .brief { min-height: 0; margin-bottom: 1.6rem; } /* --- fleeting "look back" (how many runs it took) --- */ .look { text-align: center; margin: 0 0 1.4rem; min-height: 1.5rem; } .look-toggle { background: none; border: none; color: var(--dim2); /* chrome tier */ font-family: inherit; font-size: 0.82rem; letter-spacing: 0.04em; cursor: pointer; opacity: 0.7; padding: 0 0 1px; border-bottom: 1px dotted rgba(255, 255, 255, 0.18); transition: opacity 0.3s ease, color 0.3s ease; } .look-toggle:hover { opacity: 1; color: var(--fg); } .look-line { color: var(--dim); font-style: italic; font-size: 0.95rem; line-height: 1.55; margin: 0; opacity: 0; transition: opacity 0.8s ease; } .look-line.show { opacity: 1; } /* --- win-screen share (the reward: pass the place on) --- Kept quiet and compact so it reads as part of the win, not a bolted-on box. */ .share { margin: 1.6rem auto 1.8rem; max-width: 30rem; text-align: center; } /* Progressive disclosure: the arc prompt is a quiet trigger (matches the "look back" toggle); the panel with the line and share targets stays folded until tapped, so the reward moment is calm by default. */ .share-open { background: none; border: none; color: var(--dim2); /* chrome tier */ font-family: inherit; font-size: 0.82rem; letter-spacing: 0.04em; cursor: pointer; opacity: 0.7; padding: 0 0 1px; border-bottom: 1px dotted rgba(255, 255, 255, 0.18); transition: opacity 0.3s ease, color 0.3s ease; } .share-open:hover { opacity: 1; color: var(--fg); } .share-panel { opacity: 0; transition: opacity 0.7s ease; } .share-panel.show { opacity: 1; } .share-line { color: var(--dim); /* reading tier: a quiet aside, not a headline */ font-style: italic; font-size: 0.95rem; line-height: 1.6; margin: 0 auto 1.1rem; } .share-row { display: flex; flex-wrap: wrap; gap: 0.5rem 1.4rem; align-items: baseline; justify-content: center; } .share-platforms { display: inline-flex; flex-wrap: wrap; gap: 0.5rem 1.4rem; justify-content: center; } .share-btn { margin: 0; text-decoration: none; } @media (max-width: 640px) { .share { margin: 1.2rem auto 1.4rem; } .share-row { gap: 0.4rem 1.1rem; } .share-line { font-size: 0.95rem; } } /* --- Act 2: touch the room --- */ .touch-line { text-align: center; color: var(--dim); font-style: italic; line-height: 1.35; min-height: 1.2rem; margin: 0.2rem auto 0.5rem; max-width: 34rem; /* Balance the wrap so a single short word never orphans onto its own line. */ text-wrap: balance; opacity: 0; transition: opacity 0.7s ease; } .touch-line.show { opacity: 1; } .touch-row { display: flex; flex-wrap: wrap; gap: 0.5rem 1.3rem; justify-content: center; margin: 0 0 1.4rem; } .touch-btn { margin: 0; } .touch-btn:disabled { opacity: 0.35; cursor: default; } /* The rare false way out: visually distinct (accent), never a misclick. */ .exit-offer { text-align: center; margin: 0 auto 1.6rem; max-width: 30rem; padding: 0.9rem 1rem; border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); border-radius: 2px; background: color-mix(in srgb, var(--accent) 7%, transparent); animation: fade-in 0.9s ease both; } .exit-tempt { color: var(--fg); line-height: 1.6; margin: 0 0 0.7rem; } .exit-take { color: var(--accent) !important; letter-spacing: 0.14em; text-transform: uppercase; font-size: 0.82rem; opacity: 0.95; } .exit-take:hover { opacity: 1; } /* Reduced motion: the game leans on slow fades and reveals. For users who ask for less motion (OS setting or the in-app toggle, JS sets html.motion-reduce), drop the transitions/animations and make sure nothing that carries meaning starts invisible or fades away. JS also shows loop text at once and skips the timed "look back" fade-out. */ html.motion-reduce *, html.motion-reduce *::before, html.motion-reduce *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; animation-delay: 0ms !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; } html.motion-reduce .line, html.motion-reduce .aside, html.motion-reduce .look-line, html.motion-reduce .share-panel, html.motion-reduce .select-reveal, html.motion-reduce .exit-offer { opacity: 1 !important; } /* .touch-line is deliberately NOT forced visible here: it toggles on/off via the `show` class (cleared each loop), so it must be free to return to opacity 0. But it DOES keep a gentle opacity fade even under reduced motion (opacity is not motion): a flavour line appears, holds long enough to read, then fades on a timer instead of snapping in and out. */ html.motion-reduce .touch-line { transition-duration: 0.7s !important; } /* --- text size (opt-in; default is unchanged) --- */ :root[data-text="1"] { font-size: 112.5%; } :root[data-text="2"] { font-size: 125%; } /* --- brightness (opt-in; lifts the dim reading + chrome tiers toward the foreground for legibility; default leaves the skin values untouched) --- */ body[data-bright="1"] { --dim: #b8bbc1 !important; --dim2: #a2a6ad !important; } body[data-bright="2"] { --dim: #dcdcd7 !important; --dim2: #c6cad0 !important; } /* --- settings gear + panel --- */ .settings-toggle { position: fixed; top: 1rem; right: 3.4rem; z-index: 10; background: transparent; border: none; line-height: 0; padding: 0.4rem; cursor: pointer; color: var(--dim2); opacity: 0.55; transition: opacity 0.3s ease, color 0.3s ease; } .settings-toggle:hover { opacity: 1; color: var(--fg); } /* Collection corner button: a quiet icon, third in the top-right cluster. */ .ach-corner-toggle { position: fixed; top: 1rem; right: 5.8rem; z-index: 10; background: transparent; border: none; line-height: 0; padding: 0.4rem; cursor: pointer; color: var(--dim2); opacity: 0.55; transition: opacity 0.3s ease, color 0.3s ease; } .ach-corner-toggle svg { display: block; } .ach-corner-toggle:hover { opacity: 1; color: var(--fg); } .settings-panel { text-align: left; } .set-group { margin: 0 0 1.3rem; } .set-label { display: block; color: var(--dim2); font-size: 0.78rem; letter-spacing: 0.14em; text-transform: uppercase; margin: 0 0 0.6rem; } .set-opts { display: flex; gap: 0.6rem; } .set-opts button { flex: 1; background: transparent; border: 1px solid rgba(255, 255, 255, 0.14); color: var(--fg); border-radius: 2px; padding: 0.5rem; cursor: pointer; font-family: inherit; display: flex; align-items: center; justify-content: center; min-height: 2.4rem; } .set-opts button:hover { border-color: var(--accent); } .set-opts button.on { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); } .set-opts .swatch { width: 1.4rem; height: 1.4rem; border-radius: 50%; background: var(--fg); display: block; } /* Text-size and brightness as 3-stop sliders, with a small cue at each end. */ .set-slider { display: flex; align-items: center; gap: 0.7rem; } .set-end { color: var(--dim2); line-height: 1; flex: none; font-weight: 300; letter-spacing: 0; } .set-end.swatch { width: 0.9rem; height: 0.9rem; border-radius: 50%; background: var(--fg); } /* The track carries a thin line plus a notch at each of the three stops (left, centre, right), so it reads as three positions, not a continuous dial. */ .set-range { flex: 1; -webkit-appearance: none; appearance: none; height: 0.9rem; background: transparent; outline: none; cursor: pointer; margin: 0.5rem 0; } .set-range::-webkit-slider-runnable-track { height: 0.9rem; background: linear-gradient(rgba(255,255,255,0.5),rgba(255,255,255,0.5)) 0% 50% / 2px 0.55rem no-repeat, linear-gradient(rgba(255,255,255,0.5),rgba(255,255,255,0.5)) 50% 50% / 2px 0.55rem no-repeat, linear-gradient(rgba(255,255,255,0.5),rgba(255,255,255,0.5)) 100% 50% / 2px 0.55rem no-repeat, linear-gradient(rgba(255,255,255,0.18),rgba(255,255,255,0.18)) 50% 50% / 100% 2px no-repeat; } .set-range::-moz-range-track { height: 0.9rem; background: linear-gradient(rgba(255,255,255,0.5),rgba(255,255,255,0.5)) 0% 50% / 2px 0.55rem no-repeat, linear-gradient(rgba(255,255,255,0.5),rgba(255,255,255,0.5)) 50% 50% / 2px 0.55rem no-repeat, linear-gradient(rgba(255,255,255,0.5),rgba(255,255,255,0.5)) 100% 50% / 2px 0.55rem no-repeat, linear-gradient(rgba(255,255,255,0.18),rgba(255,255,255,0.18)) 50% 50% / 100% 2px no-repeat; } .set-range::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 0.95rem; height: 0.95rem; border-radius: 50%; margin-top: -0.025rem; background: var(--accent); border: none; cursor: pointer; } .set-range::-moz-range-thumb { width: 0.95rem; height: 0.95rem; border-radius: 50%; background: var(--accent); border: none; cursor: pointer; } .set-range:focus-visible { box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 40%, transparent); } /* --- achievements / collection page --- */ .ach-open-row { text-align: center; margin-top: 2.4rem; } .ach-overlay { position: fixed; inset: 0; z-index: 70; background: rgba(0, 0, 0, 0.72); display: flex; align-items: flex-start; justify-content: center; overflow-y: auto; padding: 2rem 1rem; animation: fade-in 0.5s ease both; } .ach-panel { background: var(--bg); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 3px; max-width: 40rem; width: 100%; padding: 1.8rem 1.8rem 2.2rem; margin: auto; } .ach-head { display: flex; align-items: baseline; justify-content: space-between; } .ach-title { font-size: 1.3rem; font-weight: 300; letter-spacing: 0.14em; color: var(--fg); margin: 0; } .ach-close { background: none; border: none; color: var(--dim2); font-size: 1.6rem; line-height: 1; cursor: pointer; padding: 0 0.2rem; } .ach-close:hover { color: var(--fg); } .ach-progress { color: var(--dim2); font-size: 0.82rem; letter-spacing: 0.12em; text-transform: uppercase; margin: 0.4rem 0 1.6rem; } /* Section headers set the two halves apart: a small hairline-underlined label. */ .ach-section-head { color: var(--dim); font-size: 0.72rem; letter-spacing: 0.22em; text-transform: uppercase; margin: 0 0 0.9rem; padding-bottom: 0.4rem; border-bottom: 1px solid rgba(255, 255, 255, 0.09); } .ach-section { margin-bottom: 1.9rem; } .ach-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.7rem; } .ach-tile { display: flex; align-items: flex-start; gap: 0.7rem; border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 2px; padding: 0.8rem 0.9rem; opacity: 0.5; } .ach-tile.on { opacity: 1; border-color: color-mix(in srgb, var(--accent) 45%, transparent); background: color-mix(in srgb, var(--accent) 6%, transparent); } .ach-icon { flex: none; margin-top: 0.05rem; color: var(--dim2); } .ach-tile.on .ach-icon { color: var(--accent); } /* The empty-state line lives inside the 2-column grid; without this it is boxed into one column (half width), so a short English line fits but longer translations wrap to two. Span the full row so every locale reads as one line. */ .ach-empty { grid-column: 1 / -1; color: var(--dim); font-size: 0.9rem; } .ach-body { min-width: 0; } .ach-name { color: var(--fg); font-size: 0.98rem; letter-spacing: 0.03em; margin-bottom: 0.2rem; } .ach-tile:not(.on) .ach-name { color: var(--dim2); letter-spacing: 0.14em; text-transform: uppercase; font-size: 0.8rem; } .ach-desc { color: var(--dim); font-size: 0.85rem; line-height: 1.4; } .ach-tile:not(.on) .ach-desc { visibility: hidden; } /* Collectibles read as a quieter, inset ledger, distinct from the badge grid. */ .col-section { margin-bottom: 1.9rem; padding: 1.1rem 1.1rem 0.5rem; border: 1px solid rgba(255, 255, 255, 0.06); border-radius: 3px; background: rgba(255, 255, 255, 0.015); } .col-section .ach-section-head { border-bottom-color: rgba(255, 255, 255, 0.06); } .ach-flash { margin-bottom: 0; } .flash-row { display: flex; align-items: center; gap: 0.7rem; margin: 0 0 0.6rem; } .flash-arc { color: var(--dim); font-size: 0.85rem; min-width: 6.5rem; } .flash-dots { display: flex; gap: 0.35rem; flex: 1; flex-wrap: wrap; } .flash-dot { width: 0.72rem; height: 0.72rem; border-radius: 50%; padding: 0; border: 1px solid rgba(255, 255, 255, 0.22); background: transparent; } .flash-dot.on { cursor: pointer; transition: transform 0.12s ease, box-shadow 0.12s ease; } .flash-dot.on:hover, .flash-dot.on:focus-visible { transform: scale(1.25); outline: none; } .flash-dot.on.recall { box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px currentColor; } .flash-row[data-skin="hallway"] .flash-dot.on { background: #6fb2ee; border-color: #6fb2ee; color: #6fb2ee; } .flash-row[data-skin="stairway"] .flash-dot.on { background: #8fc79a; border-color: #8fc79a; color: #8fc79a; } .flash-row[data-skin="coach"] .flash-dot.on { background: #d8b06a; border-color: #d8b06a; color: #d8b06a; } .flash-num { color: var(--dim2); font-size: 0.78rem; font-variant-numeric: tabular-nums; } /* The recalled fragment's own words, surfacing quietly under the ledger. */ .col-recap { color: var(--dim); font-size: 0.9rem; line-height: 1.5; font-style: italic; margin: 0.9rem 0 0.2rem; min-height: 1.2rem; opacity: 0; transition: opacity 0.5s ease; } .col-recap.show { opacity: 1; } /* Records: a quiet self-competition ledger, one row per how-you-won mark. */ .rec-section { margin-bottom: 1.9rem; } .ach-rec { display: flex; flex-direction: column; gap: 0.55rem; } .rec-row { display: flex; align-items: center; gap: 0.7rem; opacity: 0.5; } .rec-row.on { opacity: 1; } .rec-dot { width: 0.65rem; height: 0.65rem; border-radius: 50%; flex: none; border: 1px solid rgba(255, 255, 255, 0.22); background: transparent; } .rec-dot.on { background: var(--accent); border-color: var(--accent); } .rec-label { color: var(--dim); font-size: 0.88rem; line-height: 1.4; } .rec-row.on .rec-label { color: var(--fg); } /* The fleeting "how you did" record under the win-screen look-back line. */ .look-record { display: inline-block; margin-top: 0.5rem; color: var(--dim2); font-style: italic; } /* Let the win-screen ending breathe: the look-back and share surfaces stay hushed for a beat after the ending, then fade up, so the benediction lands alone first. */ #look, #share { transition: opacity 1s ease; } .win-hush #look, .win-hush #share, .win-hush #nudge { opacity: 0; pointer-events: none; } /* Difficulty / reading (register) settings: word buttons + a hint line. */ .set-group-wide { display: block; } .set-group-wide .set-label { display: block; margin-bottom: 0.5rem; } .set-words { display: flex; gap: 0.5rem; } .set-words button { flex: 1; background: transparent; color: var(--dim); border: 1px solid rgba(255, 255, 255, 0.14); border-radius: 2px; font-family: inherit; font-size: 0.9rem; letter-spacing: 0.04em; padding: 0.5rem 0.4rem; cursor: pointer; } .set-words button.on { color: var(--fg); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); } .set-words button.locked { opacity: 0.4; cursor: not-allowed; } .set-hint { color: var(--dim2); font-size: 0.78rem; margin: 0.4rem 0 0.6rem; min-height: 1rem; } /* Coach: the passenger's fixed utterance. Quiet by default; louder in easy. */ .npc-utterance { color: var(--dim); font-style: italic; line-height: 1.5; margin: 0.6rem 0 0; opacity: 0; transition: opacity 0.6s ease; max-width: 34rem; } .npc-utterance.show { opacity: 1; } .npc-utterance.prominent { color: var(--fg); font-style: normal; font-size: 1.08rem; border-left: 2px solid var(--accent); padding-left: 0.9rem; } /* Simple reading register: the plain rule, set apart quietly from the prose. */ .intro-plain, .learn-plain { color: var(--dim); font-size: 0.9rem; line-height: 1.5; border-left: 2px solid color-mix(in srgb, var(--accent) 55%, transparent); padding-left: 0.9rem; margin-top: 1.1rem; } .ach-actions { display: flex; gap: 1rem; justify-content: center; margin: 0 0 1.4rem; } .ach-actions .btn { min-width: 7rem; } @media (max-width: 640px) { .ach-grid { grid-template-columns: 1fr; } .flash-arc { min-width: 5rem; } } /* --- verdict flashes --- */ .flash-good { animation: good 1.4s ease; } .flash-bad { animation: bad 1.4s ease; } @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } @keyframes good { 0% { box-shadow: none; } 30% { box-shadow: inset 0 0 120px -40px var(--accent); } 100% { box-shadow: none; } } @keyframes bad { 0% { box-shadow: none; } 20% { box-shadow: inset 0 0 160px -30px var(--danger); } 100% { box-shadow: none; } } /* --- onboarding panel (first reset only) --- */ .learn-overlay { position: fixed; inset: 0; z-index: 20; display: flex; align-items: center; justify-content: center; padding: 1.5rem; background: rgba(6, 7, 8, 0.72); backdrop-filter: blur(2px); animation: fade-in 0.7s ease both; } .learn-panel { width: 34rem; max-width: 92vw; /* Never taller than the viewport: scroll inside the panel so the button at the bottom (Begin / Go on) is always reachable, even on a short phone. */ max-height: calc(100dvh - 2rem); overflow-y: auto; -webkit-overflow-scrolling: touch; background: color-mix(in srgb, var(--bg) 82%, #16181c); border: 1px solid #2a2d31; border-top: 2px solid var(--accent); padding: 2.4rem 2.2rem 2rem; box-shadow: 0 24px 80px -30px #000; animation: fade-in 0.9s ease both; } /* Settings uses the same overlay; keep its panel scrollable too. */ .learn-overlay > .settings-panel, #settings .learn-panel { max-height: calc(100dvh - 2rem); overflow-y: auto; } .learn-title { color: var(--fg); font-size: 1.15rem; letter-spacing: 0.06em; line-height: 1.6; margin: 0 0 1.4rem; } .learn-lines { margin: 0 0 1.9rem; } .learn-lines p { color: var(--dim); line-height: 1.65; margin: 0 0 0.85rem; padding-left: 1rem; border-left: 1px solid #2a2d31; } .learn-lines p:last-child { margin-bottom: 0; } .learn-panel .btn { display: block; width: 100%; } /* First-reset rule cutscene: a full-screen quiet line that fades into the instructions. */ .cutscene-overlay { position: fixed; inset: 0; z-index: 60; background: var(--bg); display: flex; align-items: center; justify-content: center; padding: 2rem; cursor: pointer; animation: fade-in 1.3s ease both; } .cutscene-text { max-width: 32rem; text-align: center; color: var(--fg); font-size: 1.15rem; line-height: 2; letter-spacing: 0.04em; } .cutscene-text p { margin: 0 0 1.2rem; } .cutscene-text p:last-child { margin-bottom: 0; color: var(--dim); font-size: 1rem; } /* --- ending credits (landing-quiet style, slow upward roll) --- */ .credits-overlay { position: fixed; inset: 0; z-index: 80; background: var(--bg); cursor: pointer; animation: fade-in 1.3s ease both; } .credits-scroller { position: absolute; inset: 0; overflow: hidden; } .credits-roll { position: absolute; top: 100%; left: 50%; transform: translateX(-50%); width: min(32rem, 88vw); text-align: center; color: var(--dim); animation: credits-scroll 52s linear forwards; } @keyframes credits-scroll { from { transform: translate(-50%, 0); } to { transform: translate(-50%, calc(-100% - 100vh)); } } /* One deliberate type scale; spacing lives on the blocks, not on manual gaps. */ .credits-block { margin: 0 0 3.6rem; } /* Small "a Melon Lab game" presenter above the title. */ .credits-presenter { color: var(--dim2); font-size: 0.8rem; letter-spacing: 0.3em; text-transform: uppercase; margin: 0 0 1.3rem; } /* Closing studio splash: the wordmark alone, a small copyright beneath. */ .credits-splash { color: var(--fg); font-size: 1.9rem; font-weight: 300; letter-spacing: 0.34em; text-transform: uppercase; margin: 0 0 0.9rem; } .credits-copy { color: var(--dim2); font-size: 0.82rem; letter-spacing: 0.12em; margin: 0; } .credits-title { color: var(--fg); font-size: 2.6rem; font-weight: 300; letter-spacing: 0.32em; margin: 0; } .credits-head { color: var(--dim2); font-size: 0.78rem; letter-spacing: 0.36em; margin: 0 0 1.4rem; } .credits-cast + .credits-cast { margin-top: 1.5rem; } .credits-role { color: var(--dim2); font-size: 0.86rem; letter-spacing: 0.05em; line-height: 1.5; } .credits-name { color: var(--fg); font-size: 1.15rem; letter-spacing: 0.06em; line-height: 1.55; } /* A secondary name (the AI collaborators) sits one notch smaller than the lead. */ .credits-name-sm { font-size: 1rem; } .credits-note { color: var(--dim); font-style: italic; font-size: 1rem; line-height: 1.6; } /* Dedication: each "the ..." gets room to breathe. */ .credits-ded + .credits-ded { margin-top: 2rem; } .credits-ded .credits-role { margin-bottom: 0.35rem; } .credits-quote { color: var(--dim); font-style: italic; font-size: 1.06rem; line-height: 1.95; } .credits-by { color: var(--dim2); font-size: 0.9rem; letter-spacing: 0.14em; margin: 1.4rem 0 0; } /* The closing quote stands alone, well after the rest, before it fades out. */ .credits-solo { margin-top: 46vh; margin-bottom: 42vh; } .credits-done { position: fixed; top: 1rem; right: 1rem; z-index: 81; background: none; border: none; color: var(--dim2); font-size: 1.8rem; line-height: 1; cursor: pointer; opacity: 0.6; transition: opacity 0.3s ease, color 0.3s ease; } .credits-done:hover { opacity: 1; color: var(--fg); } /* Reduced motion: no roll. Read it statically, scrolled by hand. */ html.motion-reduce .credits-roll { position: static; transform: translateX(-50%); animation: none; margin: 0 auto; padding: 3rem 0; } html.motion-reduce .credits-scroller { overflow-y: auto; } /* Erase confirmation: a small centered modal over a dim scrim. */ .confirm-overlay { position: fixed; inset: 0; z-index: 90; display: flex; align-items: center; justify-content: center; padding: 2rem; background: rgba(0, 0, 0, 0.72); animation: fade-in 0.4s ease both; } .confirm-panel { max-width: 26rem; background: var(--bg); border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 3px; padding: 1.8rem 1.6rem 1.4rem; text-align: center; } .confirm-title { color: var(--fg); font-size: 1.15rem; letter-spacing: 0.06em; margin: 0 0 0.9rem; } .confirm-body { color: var(--dim); line-height: 1.65; font-size: 0.96rem; margin: 0 0 1.6rem; } .confirm-row { display: flex; gap: 0.9rem; justify-content: center; } .btn-danger { border-color: color-mix(in srgb, var(--danger) 55%, #2a2d31); color: var(--danger); } .btn-danger:hover { border-color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, transparent); color: #fff; } .btn-erase { color: color-mix(in srgb, var(--danger) 70%, var(--dim)); } .btn-erase:hover { border-color: var(--danger); color: var(--danger); } /* Win-screen adaptive nudge: a quiet line, chrome tier. */ .nudge { text-align: center; color: var(--dim2); font-size: 0.95rem; font-style: italic; line-height: 1.6; margin: 0 0 1.4rem; } .credits-row { margin-top: 1.4rem; text-align: center; } .view-credits { font-size: 0.8rem; letter-spacing: 0.14em; text-transform: uppercase; } .fold-hide-row { display: flex; align-items: center; gap: 0.5rem; color: var(--dim2); font-size: 0.82rem; margin: 0 0 1.4rem; cursor: pointer; } .fold-hide-row input { accent-color: var(--accent); cursor: pointer; } @media (max-width: 640px) { /* Top-align instead of centering: centered flex clips the top (the HUD) when content is taller than the viewport. Top-align keeps the progress bar visible and lets the page scroll naturally. */ body { align-items: flex-start; justify-content: flex-start; } #screen { padding: 1.1rem; /* Clear the fixed toggle cluster, and add the notch inset so on a notched iPhone (Safari) the content shifts down with the toggles instead of sliding under them. */ padding-top: calc(3rem + env(safe-area-inset-top)); } .title { font-size: 2.2rem; } .select-lead { font-size: 0.95rem; } .select-reveal { min-height: 23.5rem; } /* arcs stack taller on narrow screens */ .brief { min-height: 0; margin-bottom: 1.5rem; } /* progress bar: smaller, always readable, wraps rather than hides */ .hud { font-size: 0.68rem; letter-spacing: 0.12em; margin-bottom: 1.6rem; flex-wrap: wrap; gap: 0.2rem 0.8rem; /* Keep "Furthest" out from under the sound/settings toggles at the top-right. space-between otherwise pins it to the very edge, directly beneath the icons; on Safari the vertical clearance is tight and they touched. */ padding-right: 4rem; } .progress { font-weight: 500; } .heading { font-size: 1.25rem; margin-bottom: 1.6rem; } .prose { font-size: 1.02rem; line-height: 1.95; min-height: 7rem; } .controls { margin-top: 1.8rem; } .give-up-row { margin-top: 2.2rem; } .arcs { margin-top: 1.5rem; } /* Memory action row: on a 375px phone three 7rem buttons overflow the panel. Let them share the width evenly and shrink instead of running off-edge. */ .ach-actions { gap: 0.5rem; } .ach-actions .btn { min-width: 0; flex: 1 1 0; padding-left: 0.6rem; padding-right: 0.6rem; } /* Follow the notch on the fixed corner controls so they never hide under it. */ .sound-toggle { top: calc(0.5rem + env(safe-area-inset-top)); right: 0.5rem; opacity: 0.6; } .settings-toggle { top: calc(0.5rem + env(safe-area-inset-top)); right: 2.7rem; opacity: 0.6; } .ach-corner-toggle { top: calc(0.5rem + env(safe-area-inset-top)); right: 4.9rem; opacity: 0.6; } .lang-corner { top: calc(0.5rem + env(safe-area-inset-top)); left: 0.5rem; opacity: 0.6; } } /* Very small phones (iPhone 12/13 mini, ~375px and under): tighten type and spacing so prose stays compact and nothing overflows its box. The larger phones (390-430px: 14/15/16/17 and the Pro / Pro Max) keep the 640px sizing. */ @media (max-width: 380px) { #screen { padding: 0.9rem; padding-top: calc(2.8rem + env(safe-area-inset-top)); } .title { font-size: 2rem; } .heading { font-size: 1.15rem; letter-spacing: 0.16em; } .prose { font-size: 0.97rem; line-height: 1.85; } .hud { font-size: 0.64rem; letter-spacing: 0.09em; padding-right: 3.6rem; } .ach-panel { padding: 1.4rem 1.2rem 1.8rem; } .ach-title { font-size: 1.15rem; } .ach-actions .btn { padding-left: 0.4rem; padding-right: 0.4rem; font-size: 0.85rem; } .learn-panel { padding-left: 1.3rem; padding-right: 1.3rem; } }