Add 3D clamshell phone dialer spike + persistent-board design
A diegetic 90s handset rendered as a small three.js scene in a fixed 1:2 portrait stage (visual spike at /?phone=1, three.js lazy-loaded so the board bundle is unchanged). Blocky flat-shaded clamshell with glowing keys, a flip-open animation with an intro camera orbit that settles head-on before the DOM screen appears, a hinge barrel on the pivot axis, and a chubby antenna. Pressable 3D keypad (raycast + keyboard) with DTMF tones drives a dialer: connect / voicemail / SIT "unobtainable", against a stub number->node directory with flag-gated node enablement. The screen UI is real DOM positioned in percent of the stage, kept pixel-exact by the head-on ortho camera. Also documents the persistent-board flow model (docs/persistent-boards.md): board_key reuse across level nodes, present-but-hidden flag-gated exhibits with live arrival reveals, reset/new-game semantics, and A/B/M citation codes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -592,3 +592,39 @@ button:focus-visible { outline: 2px solid #e99a44; outline-offset: 2px; }
|
||||
.audio-toggle { position: fixed; top: 14px; right: 14px; z-index: 300; width: 34px; height: 34px; display: grid; place-items: center; border: 1px solid #3c5a52; background: #0a211de6; color: #d58a46; font-size: 16px; line-height: 1; cursor: pointer; }
|
||||
.audio-toggle:hover { border-color: #6f8f85; color: #e7b57e; }
|
||||
.audio-toggle.muted { color: #5f7b73; text-decoration: line-through; }
|
||||
|
||||
/* === 90s handset spike (src/phone.tsx) ============================ */
|
||||
.phone-backdrop { position: fixed; inset: 0; z-index: 500; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px;
|
||||
background: radial-gradient(120% 90% at 50% 30%, #10201c 0%, #060b0a 70%, #020403 100%); }
|
||||
/* Fixed 1:2 portrait stage. The 3D scene and the DOM screen both live here, so
|
||||
percentages resolve against the same box at every size. */
|
||||
.phone-stage { position: relative; height: min(88vh, 720px); aspect-ratio: 1 / 2; max-width: 92vw; }
|
||||
.phone-canvas { position: absolute; inset: 0; }
|
||||
/* The interactive screen, positioned in % of the stage over the 3D glass. */
|
||||
.phone-screen { position: absolute; box-sizing: border-box; padding: 4% 6%; overflow: hidden;
|
||||
background: linear-gradient(180deg, #12200b, #0a1607); color: #cdea6a; font-family: "DejaVu Sans Mono", ui-monospace, monospace;
|
||||
text-shadow: 0 0 6px #7fa02988; opacity: 0; transition: opacity .1s ease; pointer-events: none;
|
||||
box-shadow: inset 0 0 18px #0006, inset 0 0 0 1px #2c3d16; }
|
||||
.phone-screen.on { opacity: 1; pointer-events: auto; transition: opacity .28s ease .1s; }
|
||||
.phone-screen::after { content: ""; position: absolute; inset: 0; pointer-events: none;
|
||||
background: repeating-linear-gradient(180deg, #0000 0 2px, #00000022 2px 3px); mix-blend-mode: multiply; }
|
||||
.pscr-status { display: flex; justify-content: space-between; font-size: 9px; letter-spacing: .5px; opacity: .85; }
|
||||
.pscr-clock { text-align: center; font-size: 34px; font-weight: 700; margin-top: 14%; letter-spacing: 2px; }
|
||||
.pscr-date { text-align: center; font-size: 11px; opacity: .8; letter-spacing: 3px; }
|
||||
.pscr-soft { position: absolute; left: 6%; right: 6%; bottom: 4%; display: flex; justify-content: space-between; font-size: 10px; opacity: .9; }
|
||||
.pscr-dir { text-align: center; font-size: 9px; opacity: .55; margin-top: 8%; letter-spacing: 1px; }
|
||||
.pscr-num { text-align: center; font-size: 22px; letter-spacing: 3px; margin-top: 16%; word-break: break-all; }
|
||||
.pscr-num.sm { font-size: 13px; margin-top: 4%; opacity: .8; }
|
||||
.pscr-big { text-align: center; font-size: 17px; letter-spacing: 1px; margin-top: 12%; font-weight: 700; }
|
||||
.pscr-big.warn { color: #e88a4a; text-shadow: 0 0 6px #e88a4a66; }
|
||||
.pscr-big.ok { color: #8fe86a; }
|
||||
.pscr-big:nth-of-type(3) { margin-top: 2%; }
|
||||
.pscr-callee { text-align: center; font-size: 12px; margin-top: 6%; opacity: .95; }
|
||||
.pscr-line { text-align: center; font-size: 10px; opacity: .7; margin-top: 3%; }
|
||||
.pscr-dots::after { content: "…"; animation: pscr-blink 1s steps(1) infinite; }
|
||||
@keyframes pscr-blink { 50% { opacity: .2; } }
|
||||
.phone-open-btn { border: 1px solid #6f8f85; background: #0a211de6; color: #cdea6a; font-family: ui-monospace, monospace;
|
||||
letter-spacing: 2px; padding: 8px 22px; cursor: pointer; }
|
||||
.phone-open-btn:hover { border-color: #cdea6a; }
|
||||
.phone-hint { color: #5f7b73; font-family: ui-monospace, monospace; font-size: 11px; margin: 0; }
|
||||
.phone-hint code { color: #8fae4a; }
|
||||
|
||||
Reference in New Issue
Block a user