html,
body {
margin: 0;
height: 100%;
overflow: hidden;
background: #0e2a63;
}
#scene {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
display: block;
}
#title-overlay {
position: fixed;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
pointer-events: none;
z-index: 10;
opacity: 1;
transform: translateY(0);
transition:
opacity 1.8s ease,
transform 1.8s cubic-bezier(0.55, 0, 0.55, 1);
}
#title-overlay.hidden {
opacity: 0;
transform: translateY(-48px);
}
/* Once the intro is dismissed the overlay only fades (no display:none, so the
exit animation can play). But the spent start button keeps .ready, i.e.
pointer-events: auto, leaving an invisible button on top that swallows clicks
on the scene behind it (e.g. the bench / cassette collection). Drop its
pointer-events the instant the overlay hides — it still fades out in place,
so there's no layout jump. */
#title-overlay.hidden #start-btn {
pointer-events: none;
}
#title {
font-family: "Baloo 2", sans-serif;
font-weight: 800;
font-size: clamp(3.5rem, 11vw, 8rem);
color: #ffffff;
margin: 0;
letter-spacing: 0.06em;
text-shadow:
0 3px 0 rgba(173, 211, 255, 0.55),
0 8px 28px rgba(13, 47, 110, 0.5),
0 2px 6px rgba(13, 47, 110, 0.35);
animation:
title-in 1.6s cubic-bezier(0.22, 1, 0.36, 1) both,
title-bob 5.5s ease-in-out 1.6s infinite alternate;
}
/* The transform-based entrance lives on the wrapper, NOT on #subtitle: an
element with backdrop-filter loses its blur the moment it also carries a
transform (and title-in's `both` fill left one on permanently), which is
what wiped the glass during the intro. A 2D transform on an ancestor is not
a backdrop root, so the pill's blur survives. */
.subtitle-wrap {
margin-top: 1.1rem;
animation: subtitle-slide 1.6s cubic-bezier(0.22, 1, 0.36, 1) 0.5s both;
}
#subtitle {
font-family: "Baloo 2", sans-serif;
font-weight: 600;
font-size: clamp(0.95rem, 2.2vw, 1.3rem);
color: rgba(255, 255, 255, 0.95);
margin: 0;
letter-spacing: 0.14em;
text-transform: lowercase;
padding: 0.45em 1.4em;
border-radius: 999px;
background: rgba(255, 255, 255, 0.14);
border: 1px solid rgba(255, 255, 255, 0.3);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
/* opacity-only fade — opacity on the element itself does not break its own
backdrop-filter, unlike a transform would */
animation: subtitle-fade 1.6s cubic-bezier(0.22, 1, 0.36, 1) 0.5s both;
}
@keyframes title-in {
from {
opacity: 0;
transform: translateY(28px) scale(0.96);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes subtitle-slide {
from {
transform: translateY(28px) scale(0.96);
}
to {
transform: translateY(0) scale(1);
}
}
@keyframes subtitle-fade {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
#start-btn {
margin-top: 2.4rem;
pointer-events: none; /* the overlay ignores pointers; we opt in once ready */
cursor: pointer;
font-family: "Baloo 2", sans-serif;
font-weight: 700;
font-size: clamp(0.95rem, 2.2vw, 1.25rem);
letter-spacing: 0.12em;
text-transform: lowercase;
color: #ffffff;
padding: 0.7em 1.9em;
border-radius: 999px;
background: rgba(255, 255, 255, 0.16);
border: 1px solid rgba(255, 255, 255, 0.4);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
/* fades in via opacity only (a transform here would kill the backdrop blur,
per the subtitle note above); the glow uses box-shadow, also transform-free */
opacity: 0;
transition:
opacity 0.8s ease,
background 0.2s ease,
border-color 0.2s ease;
}
#start-btn.ready {
opacity: 1;
pointer-events: auto;
animation: start-glow 2.6s ease-in-out 0.6s infinite;
}
#start-btn:hover {
background: rgba(255, 255, 255, 0.3);
border-color: rgba(255, 255, 255, 0.7);
}
#start-btn:active {
background: rgba(255, 255, 255, 0.4);
}
@keyframes start-glow {
0%,
100% {
box-shadow: 0 0 0 0 rgba(173, 211, 255, 0);
}
50% {
box-shadow: 0 0 22px 2px rgba(173, 211, 255, 0.5);
}
}
/* Global mute toggle, top-right. z-index above the modals (max 20) so it stays
clickable while a tape plays. No transform on this backdrop-filter element. */
.mute-btn {
position: fixed;
top: 1rem;
right: 1rem;
z-index: 50;
width: 2.6rem;
height: 2.6rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: 999px;
color: #ffffff;
background: rgba(18, 28, 58, 0.34);
border: 1px solid rgba(255, 255, 255, 0.32);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
cursor: pointer;
transition:
background 0.2s ease,
border-color 0.2s ease,
color 0.2s ease;
}
.mute-btn:hover {
background: rgba(18, 28, 58, 0.55);
border-color: rgba(255, 255, 255, 0.6);
}
.mute-btn.muted {
color: rgba(255, 255, 255, 0.55);
}
.mute-btn .mute-slash {
display: none;
}
.mute-btn.muted .mute-waves {
display: none;
}
.mute-btn.muted .mute-slash {
display: inline;
}
@keyframes title-bob {
from {
transform: translateY(0);
}
to {
transform: translateY(-12px);
}
}
.hover-label {
position: fixed;
left: 0;
top: 0;
transform: translate(-50%, -118%);
pointer-events: none;
z-index: 8;
opacity: 0;
transition: opacity 0.18s ease;
}
.hover-label.visible {
opacity: 1;
}
/* the lamp label sits to the LEFT of the pole (which runs up the right edge),
vertically centred on the anchor, with its tail pointing right at the post */
#lamp-label {
transform: translate(calc(-100% - 14px), -50%);
}
#lamp-label span::after {
left: auto;
right: -5px;
bottom: auto;
top: 50%;
transform: translateY(-50%) rotate(45deg);
}
.hover-label span {
position: relative;
display: inline-block;
font-family: "Baloo 2", sans-serif;
font-weight: 800;
font-size: clamp(1rem, 1.8vw, 1.3rem);
color: #3a2c14;
background: #ffd84a;
padding: 0.4em 1.1em;
border-radius: 999px;
box-shadow: 0 6px 20px rgba(18, 44, 96, 0.28);
white-space: nowrap;
}
.hover-label span::after {
content: "";
position: absolute;
left: 50%;
bottom: -5px;
width: 12px;
height: 12px;
background: #ffd84a;
border-radius: 2px;
transform: translateX(-50%) rotate(45deg);
z-index: -1;
}
.hover-label.visible span {
animation:
label-pop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) both,
label-bob 2s ease-in-out 0.45s infinite alternate;
}
@keyframes label-pop {
from {
opacity: 0;
transform: scale(0.55) translateY(12px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
@keyframes label-bob {
from {
transform: translateY(0) rotate(-1.2deg);
}
to {
transform: translateY(-7px) rotate(1.2deg);
}
}
/* --- vending machine modal: machine control panel -------------------------- */
#machine-modal {
position: fixed;
top: 50%;
right: 5vw;
transform: translateY(-50%);
transform-origin: 100% 0%;
width: min(390px, 86vw);
box-sizing: border-box;
background: linear-gradient(165deg, #8ed0f4, #6db8e0 70%, #5fabd6);
border: 3px solid #4f9cc8;
border-radius: 20px;
box-shadow:
inset 0 2px 0 rgba(255, 255, 255, 0.55),
inset 0 -3px 8px rgba(31, 79, 116, 0.35),
0 24px 70px rgba(15, 40, 90, 0.4);
padding: 22px 20px 18px;
font-family: "DotGothic16", "Baloo 2", monospace;
color: #1d2a40;
z-index: 20;
}
#machine-modal.hidden {
display: none;
}
/* "play while waiting" nudge — a frosted pill below the card, shown only while
the machine is brewing. Clicking it whisks you off to the garden mini-game. */
#play-while-waiting {
position: absolute;
top: calc(100% + 16px);
left: 50%;
transform: translateX(-50%);
display: inline-flex;
align-items: center;
gap: 9px;
white-space: nowrap;
padding: 9px 17px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.5);
background: rgba(255, 255, 255, 0.18);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
color: #fff;
font-family: "Baloo 2", sans-serif;
font-weight: 600;
font-size: 0.9rem;
cursor: pointer;
text-shadow: 0 1px 3px rgba(15, 40, 90, 0.45);
box-shadow: 0 8px 24px rgba(15, 40, 90, 0.28);
transition:
background 0.18s ease,
transform 0.18s ease;
animation: pww-in 0.4s ease;
}
#play-while-waiting svg {
flex: none;
opacity: 0.95;
}
#play-while-waiting:hover {
background: rgba(255, 255, 255, 0.32);
transform: translateX(-50%) translateY(-2px);
}
#play-while-waiting.hidden {
display: none;
}
@keyframes pww-in {
from {
opacity: 0;
transform: translateX(-50%) translateY(8px);
}
to {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
}
/* anime panel entrance: a thin slash from the corner that springs open */
#machine-modal.opening {
animation: modal-in 0.55s cubic-bezier(0.25, 0.9, 0.35, 1.2) both;
}
@keyframes modal-in {
0% {
transform: translateY(-50%) scale(0.04, 0.04) skewX(-10deg);
opacity: 0;
}
35% {
transform: translateY(-50%) scale(1.06, 0.07) skewX(-7deg);
opacity: 1;
}
62% {
transform: translateY(-50%) scale(0.99, 1.06) skewX(2.5deg);
}
80% {
transform: translateY(-50%) scale(1.01, 0.98) skewX(-1deg);
}
100% {
transform: translateY(-50%) scale(1, 1) skewX(0deg);
}
}
#machine-modal.closing {
animation: modal-out 0.3s ease-in both;
}
@keyframes modal-out {
0% {
transform: translateY(-50%) scale(1, 1);
opacity: 1;
}
45% {
transform: translateY(-50%) scale(1.04, 0.06) skewX(-6deg);
opacity: 1;
}
100% {
transform: translateY(-50%) scale(0.03, 0.03) skewX(-10deg);
opacity: 0;
}
}
#modal-close {
position: absolute;
top: 12px;
right: 14px;
width: 32px;
height: 32px;
border-radius: 50%;
border: 2px solid #16243c;
background: #2b3a55;
color: #cfe0f0;
font-size: 1.05rem;
line-height: 1;
cursor: pointer;
z-index: 2;
}
#modal-close:hover {
background: #16243c;
}
/* --- LED screen ------------------------------------------------------------ */
#led-screen {
position: relative;
background: #0b120d;
border: 2px solid #14241a;
border-radius: 12px;
box-shadow: inset 0 0 22px rgba(0, 0, 0, 0.85);
padding: 14px 14px 12px;
overflow: hidden;
}
#led-screen::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
background: repeating-linear-gradient(
to bottom,
transparent 0 2px,
rgba(0, 0, 0, 0.22) 2px 3px
);
border-radius: inherit;
}
.led-title {
margin: 0;
font-family: "DotGothic16", monospace;
font-size: 1.3rem;
letter-spacing: 0.1em;
color: #5dff8d;
text-shadow:
0 0 8px rgba(93, 255, 141, 0.7),
0 0 2px rgba(93, 255, 141, 0.9);
animation: led-flicker 4s steps(1) infinite;
}
.led-cursor {
animation: blink 1.1s steps(1) infinite;
}
.led-sub {
margin: 4px 0 0;
font-family: "DotGothic16", monospace;
font-size: 0.78rem;
letter-spacing: 0.12em;
color: #3fd470;
opacity: 0.8;
}
@keyframes led-flicker {
0%,
93%,
100% {
opacity: 1;
}
94% {
opacity: 0.75;
}
95% {
opacity: 1;
}
97% {
opacity: 0.85;
}
98% {
opacity: 1;
}
}
#prompt-input {
width: 100%;
box-sizing: border-box;
margin-top: 12px;
font-family: "DotGothic16", monospace;
font-size: 1rem;
letter-spacing: 0.04em;
color: #5dff8d;
caret-color: #5dff8d;
text-shadow: 0 0 6px rgba(93, 255, 141, 0.5);
padding: 10px 12px;
border-radius: 8px;
border: 1px dashed rgba(93, 255, 141, 0.35);
background: rgba(0, 0, 0, 0.3);
resize: none;
outline: none;
}
#prompt-input::placeholder {
color: rgba(93, 255, 141, 0.3);
text-shadow: none;
}
#prompt-input:focus {
border: 1px solid rgba(93, 255, 141, 0.8);
}
#prompt-input:disabled {
opacity: 0.5;
}
/* tape-length slider — green-on-LED to match the dot-matrix screen */
#length-row {
display: flex;
align-items: center;
gap: 10px;
margin-top: 12px;
font-family: "DotGothic16", monospace;
}
.len-label {
flex: none;
font-size: 0.74rem;
letter-spacing: 0.12em;
color: #3fd470;
opacity: 0.85;
}
#length-value {
flex: none;
min-width: 34px;
text-align: right;
font-size: 0.84rem;
letter-spacing: 0.06em;
color: #5dff8d;
text-shadow: 0 0 6px rgba(93, 255, 141, 0.5);
}
#length-slider {
flex: 1;
-webkit-appearance: none;
appearance: none;
height: 6px;
border-radius: 3px;
background: rgba(93, 255, 141, 0.18);
border: 1px solid rgba(93, 255, 141, 0.25);
outline: none;
cursor: pointer;
}
#length-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: #5dff8d;
box-shadow: 0 0 8px rgba(93, 255, 141, 0.85);
border: none;
cursor: pointer;
}
#length-slider::-moz-range-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background: #5dff8d;
box-shadow: 0 0 8px rgba(93, 255, 141, 0.85);
border: none;
cursor: pointer;
}
#length-slider:disabled {
opacity: 0.45;
cursor: default;
}
#prompt-input.shake {
animation: shake 0.4s ease;
}
@keyframes shake {
0%,
100% {
transform: translateX(0);
}
25% {
transform: translateX(-6px);
}
50% {
transform: translateX(6px);
}
75% {
transform: translateX(-4px);
}
}
#generating {
margin-top: 14px;
text-align: center;
}
.led-status {
margin: 10px 0 4px;
font-family: "DotGothic16", monospace;
font-size: 0.95rem;
letter-spacing: 0.1em;
color: #5dff8d;
text-shadow: 0 0 8px rgba(93, 255, 141, 0.7);
}
.dots {
animation: blink 1.4s steps(1) infinite;
}
@keyframes blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.15;
}
}
/* cozy loader: a little row of plants that sprout, bloom, and loop while the
beat brews — green-on-LED to match the machine's dot-matrix screen. */
.grow-loader {
position: relative;
display: flex;
gap: 13px;
justify-content: center;
align-items: flex-end;
height: 40px;
}
/* a soft soil line the sprouts rise from */
.grow-loader::after {
content: "";
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
width: 118px;
height: 3px;
border-radius: 2px;
background: linear-gradient(#2aa85a, #115f30);
box-shadow: 0 0 6px rgba(93, 255, 141, 0.25);
}
/* brew progress — fills in whole 30s-chunk steps as the backend stitches them
(e.g. a 1 min tape jumps to 50% when the first chunk lands). */
#brew-bar {
position: relative;
width: 80%;
max-width: 230px;
height: 6px;
margin: 16px auto 0;
border-radius: 3px;
background: rgba(93, 255, 141, 0.15);
border: 1px solid rgba(93, 255, 141, 0.25);
overflow: hidden;
}
#brew-bar-fill {
display: block;
width: 0%;
height: 100%;
border-radius: 3px;
background: #5dff8d;
box-shadow: 0 0 8px rgba(93, 255, 141, 0.6);
transition: width 0.3s ease;
}
.sprout {
position: relative;
width: 18px;
height: 34px;
transform-origin: 50% 100%;
animation: sprout-grow 2.8s ease-in-out infinite;
}
.sprout:nth-child(2) {
animation-delay: 0.4s;
}
.sprout:nth-child(3) {
animation-delay: 0.8s;
}
.sprout:nth-child(4) {
animation-delay: 1.2s;
}
.sprout .stem {
position: absolute;
left: 50%;
bottom: 0;
width: 2px;
height: 22px;
margin-left: -1px;
border-radius: 2px;
background: linear-gradient(#5dff8d, #1f8f4a);
box-shadow: 0 0 6px rgba(93, 255, 141, 0.5);
}
.sprout .leaf {
position: absolute;
bottom: 9px;
width: 7px;
height: 4px;
background: #43e07f;
box-shadow: 0 0 4px rgba(93, 255, 141, 0.4);
}
.sprout .leaf.l {
right: 50%;
border-radius: 60% 0 60% 0;
transform: rotate(22deg);
transform-origin: right center;
}
.sprout .leaf.r {
left: 50%;
border-radius: 0 60% 0 60%;
transform: rotate(-22deg);
transform-origin: left center;
}
/* a 5-dot flower head: pale centre + four green petals via box-shadow */
.sprout .bloom {
position: absolute;
left: 50%;
bottom: 19px;
width: 5px;
height: 5px;
margin-left: -2.5px;
border-radius: 50%;
background: #d9ffe7;
box-shadow:
0 -4px 0 #5dff8d,
0 4px 0 #5dff8d,
-4px 0 0 #5dff8d,
4px 0 0 #5dff8d,
0 0 8px rgba(93, 255, 141, 0.8);
}
@keyframes sprout-grow {
0% {
transform: scale(0);
opacity: 0;
}
12% {
opacity: 1;
}
50% {
transform: scale(1.08);
}
62% {
transform: scale(1);
}
88% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 0;
}
}
/* --- coin slot + red button -------------------------------------------------- */
#controls-row {
margin-top: 16px;
display: flex;
align-items: center;
gap: 14px;
}
#coin-slot-area {
position: relative;
flex: none;
width: 64px;
height: 64px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(#5fabd6, #4f9cc8);
border: 2px solid #3f86ad;
border-radius: 10px;
box-shadow:
inset 0 2px 4px rgba(255, 255, 255, 0.35),
inset 0 -3px 5px rgba(31, 79, 116, 0.4);
perspective: 300px;
}
#coin-slot {
width: 10px;
height: 40px;
background: #0e1116;
border-radius: 5px;
box-shadow:
inset 0 0 5px #000,
0 1px 0 rgba(255, 255, 255, 0.4);
}
#wado-coin {
position: absolute;
width: 46px;
height: 46px;
left: 50%;
top: 50%;
margin: -23px 0 0 -23px;
opacity: 0;
pointer-events: none;
filter: drop-shadow(0 3px 4px rgba(20, 30, 50, 0.45));
}
.wado-kanji {
font-family: "Hiragino Mincho ProN", "Yu Mincho", serif;
font-size: 21px;
fill: #5d431d;
}
.inserting #wado-coin {
animation: coin-insert 1.15s ease-in-out forwards;
}
@keyframes coin-insert {
0% {
opacity: 0;
transform: translate(-58px, 16px) rotate(-30deg) scale(0.3);
}
18% {
opacity: 1;
transform: translate(-40px, -8px) rotate(-10deg) scale(1.25);
}
42% {
opacity: 1;
transform: translate(0, -16px) rotate(0deg) scale(1.1);
}
60% {
opacity: 1;
transform: translate(0, -16px) rotateY(80deg) scale(1);
}
78% {
opacity: 1;
transform: translate(0, 6px) rotateY(80deg) scale(0.95);
}
100% {
opacity: 0;
transform: translate(0, 26px) rotateY(80deg) scale(0.9);
}
}
.inserting #coin-slot {
animation: slot-flash 0.35s ease 0.95s;
}
@keyframes slot-flash {
50% {
box-shadow:
inset 0 0 5px #000,
0 0 12px rgba(93, 255, 141, 0.9);
}
}
#coin-button {
flex: 1;
padding: 16px 10px;
border-radius: 12px;
border: 2px solid #a32119;
background: linear-gradient(#ff6a5e, #e03a30 55%, #c52d24);
color: #fff;
font-family: "DotGothic16", monospace;
font-size: 1.2rem;
letter-spacing: 0.12em;
text-shadow: 0 2px 3px rgba(0, 0, 0, 0.45);
cursor: pointer;
box-shadow:
0 5px 0 #8f1d18,
inset 0 2px 3px rgba(255, 255, 255, 0.4);
}
#coin-button:active {
transform: translateY(3px);
box-shadow:
0 2px 0 #8f1d18,
inset 0 2px 3px rgba(255, 255, 255, 0.4);
}
#coin-button:disabled {
cursor: default;
filter: saturate(0.7) brightness(0.92);
}
#cassette-stage {
margin-top: 16px;
}
#cassette {
background: linear-gradient(#3a4254, #2c3344);
border-radius: 14px;
padding-bottom: 4px;
box-shadow:
inset 0 2px 6px rgba(255, 255, 255, 0.12),
0 8px 22px rgba(15, 40, 90, 0.3);
animation: cassette-out 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes cassette-out {
from {
transform: translateY(56px) scale(0.92);
opacity: 0;
}
to {
transform: translateY(0) scale(1);
opacity: 1;
}
}
.cassette-label {
background: #f6f1e3;
border: 2px solid #e0d6bd;
border-radius: 8px;
margin: 12px 14px 10px;
padding: 8px 10px;
text-align: center;
font-weight: 800;
font-size: 0.95rem;
color: #4a3b2c;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.reels {
display: flex;
align-items: center;
padding: 0 28px 12px;
}
.reel {
width: 34px;
height: 34px;
border-radius: 50%;
border: 5px dashed #cfd6e4;
background: #1d232f;
box-sizing: border-box;
}
#cassette.playing .reel,
.cassette-card.playing .reel {
animation: reel-spin 2.4s linear infinite;
}
@keyframes reel-spin {
to {
transform: rotate(360deg);
}
}
.tape-window {
flex: 1;
height: 16px;
margin: 0 12px;
background: #1d232f;
border-radius: 8px;
}
#player {
display: flex;
align-items: center;
gap: 10px;
margin-top: 14px;
}
#play-btn,
#np-toggle,
#deck-play {
width: 44px;
height: 44px;
flex: none;
border-radius: 50%;
border: none;
background: #ffd84a;
color: #3a2c14;
font-size: 1rem;
cursor: pointer;
box-shadow: 0 3px 0 #d9ab1e;
}
#play-btn:active,
#np-toggle:active,
#deck-play:active {
transform: translateY(2px);
box-shadow: 0 1px 0 #d9ab1e;
}
#progress {
flex: 1;
height: 8px;
border-radius: 4px;
background: rgba(13, 26, 43, 0.4);
cursor: pointer;
overflow: hidden;
}
#progress-fill {
height: 100%;
width: 0%;
border-radius: 4px;
background: linear-gradient(90deg, #5dff8d, #2ea65a);
box-shadow: 0 0 6px rgba(93, 255, 141, 0.6);
}
#time {
font-family: "DotGothic16", monospace;
font-size: 0.85rem;
color: #16213a;
min-width: 38px;
text-align: right;
}
#again-btn {
margin-top: 14px;
width: 100%;
padding: 11px;
border-radius: 12px;
border: 2px solid #16213a;
background: linear-gradient(#3a4c6e, #2b3a55);
color: #cfe0f0;
font-family: "DotGothic16", monospace;
font-size: 1rem;
letter-spacing: 0.12em;
cursor: pointer;
box-shadow:
0 4px 0 #16213a,
inset 0 2px 2px rgba(255, 255, 255, 0.18);
}
#again-btn:hover {
background: linear-gradient(#465a80, #324466);
}
#again-btn:active {
transform: translateY(2px);
box-shadow:
0 2px 0 #16213a,
inset 0 2px 2px rgba(255, 255, 255, 0.18);
}
#error-msg {
font-family: "DotGothic16", monospace;
color: #ff6b5e;
text-shadow: 0 0 8px rgba(255, 107, 94, 0.7);
font-size: 0.88rem;
letter-spacing: 0.08em;
margin: 10px 0 0;
text-align: center;
}
#error-msg.hidden,
#controls-row.hidden,
#prompt-input.hidden,
#length-row.hidden,
#generating.hidden,
#cassette-stage.hidden {
display: none;
}
/* --- now playing pill ------------------------------------------------------ */
#now-playing {
position: fixed;
left: 18px;
bottom: 18px;
z-index: 15;
display: flex;
align-items: center;
gap: 10px;
padding: 8px 18px 8px 8px;
background: rgba(255, 255, 255, 0.92);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border-radius: 999px;
box-shadow: 0 8px 24px rgba(15, 40, 90, 0.25);
font-family: "Baloo 2", sans-serif;
font-weight: 700;
font-size: 0.95rem;
color: #2b3a55;
transition:
opacity 0.3s ease,
transform 0.3s ease;
}
#now-playing.hidden {
opacity: 0;
transform: translateY(12px);
pointer-events: none;
}
#now-playing #np-toggle {
width: 36px;
height: 36px;
font-size: 0.85rem;
}
#np-title {
max-width: 230px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* --- cassette collection: carousel ------------------------------------------ */
#collection-panel {
position: fixed;
top: 6vh;
left: 50%;
transform: translateX(-50%);
width: min(780px, 94vw);
z-index: 18;
text-align: center;
transition:
opacity 0.35s ease,
transform 0.35s ease;
}
#collection-panel.hidden {
opacity: 0;
transform: translate(-50%, -18px);
pointer-events: none;
}
#collection-heading {
margin: 0;
}
#collection-heading span {
display: inline-block;
font-family: "Baloo 2", sans-serif;
font-weight: 800;
font-size: clamp(1.1rem, 2.2vw, 1.5rem);
color: #3a2c14;
background: #ffd84a;
padding: 0.4em 1.3em;
border-radius: 999px;
box-shadow: 0 8px 24px rgba(18, 44, 96, 0.3);
}
#collection-close {
position: absolute;
top: -6px;
right: 0;
width: 36px;
height: 36px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.5);
background: rgba(255, 255, 255, 0.22);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
color: #fff;
font-size: 1.15rem;
line-height: 1;
cursor: pointer;
text-shadow: 0 1px 3px rgba(15, 40, 90, 0.4);
}
#collection-close:hover {
background: rgba(255, 255, 255, 0.38);
}
#collection-status {
margin: 26px 0 0;
font-family: "Baloo 2", sans-serif;
font-weight: 700;
font-size: 1.05rem;
color: #fff;
text-shadow: 0 2px 8px rgba(15, 40, 90, 0.45);
}
#collection-status.hidden {
display: none;
}
#carousel-row {
display: flex;
align-items: center;
gap: 10px;
margin-top: 16px;
}
.caro-btn {
flex: none;
position: relative;
z-index: 2;
width: 40px;
height: 40px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.5);
background: rgba(255, 255, 255, 0.22);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
color: #fff;
font-size: 1.5rem;
line-height: 1;
padding-bottom: 4px;
cursor: pointer;
text-shadow: 0 1px 3px rgba(15, 40, 90, 0.4);
}
.caro-btn:hover {
background: rgba(255, 255, 255, 0.38);
}
.caro-btn:disabled {
opacity: 0.32;
cursor: default;
}
.caro-btn:disabled:hover {
background: rgba(255, 255, 255, 0.22);
}
#carousel {
position: relative;
flex: 1;
height: 232px;
perspective: 1200px;
overflow: hidden;
}
.cassette-card {
position: absolute;
left: 50%;
top: 50%;
width: 184px;
box-sizing: border-box;
cursor: pointer;
border: none;
padding: 0 0 4px;
font-family: "Baloo 2", sans-serif;
background: linear-gradient(var(--shell1, #3a4254), var(--shell2, #2c3344));
border-radius: 14px;
box-shadow:
inset 0 2px 6px rgba(255, 255, 255, 0.14),
0 10px 24px rgba(15, 40, 90, 0.32);
transform-origin: center center;
transform: translate(-50%, -50%) scale(0.5); /* pre-layout: centred + small */
opacity: 0;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
transition:
transform 0.44s cubic-bezier(0.22, 0.61, 0.36, 1),
opacity 0.44s ease,
box-shadow 0.25s ease,
filter 0.2s ease;
will-change: transform, opacity;
}
.cassette-card:hover {
filter: brightness(1.08);
}
.cassette-card.selected {
box-shadow:
0 0 0 3px #ffd84a,
inset 0 2px 6px rgba(255, 255, 255, 0.16),
0 16px 34px rgba(15, 40, 90, 0.46);
}
.cassette-card .cassette-label {
margin: 10px 12px 8px;
padding: 6px 8px;
font-size: 0.85rem;
}
.cassette-card .reels {
padding: 0 22px 10px;
}
.cassette-card .reel {
width: 28px;
height: 28px;
border-width: 4px;
}
.cassette-card .tape-window {
height: 13px;
margin: 0 10px;
}
/* --- tape deck: glass pill player -------------------------------------------- */
#tape-deck {
position: fixed;
bottom: 26px;
left: 50%;
transform: translateX(-50%);
z-index: 18;
display: flex;
align-items: center;
gap: 13px;
width: min(620px, 92vw);
box-sizing: border-box;
padding: 9px 10px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.16);
border: 1px solid rgba(255, 255, 255, 0.42);
backdrop-filter: blur(16px) saturate(1.35);
-webkit-backdrop-filter: blur(16px) saturate(1.35);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.5),
0 16px 44px rgba(15, 40, 90, 0.38);
font-family: "Baloo 2", sans-serif;
transition:
opacity 0.35s ease,
transform 0.35s ease;
}
#tape-deck.hidden {
opacity: 0;
transform: translate(-50%, 18px);
pointer-events: none;
}
#deck-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 5px;
}
#deck-title {
font-weight: 800;
font-size: 0.98rem;
color: #fff;
text-shadow: 0 1px 4px rgba(15, 40, 90, 0.45);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-right: 6px;
}
#deck-progress {
height: 8px;
border-radius: 4px;
background: rgba(13, 26, 43, 0.35);
cursor: pointer;
overflow: hidden;
}
#deck-fill {
height: 100%;
width: 0%;
border-radius: 4px;
background: linear-gradient(90deg, #ffd84a, #e8a13c);
box-shadow: 0 0 8px rgba(255, 216, 74, 0.65);
}
#deck-time {
flex: none;
font-family: "DotGothic16", monospace;
font-size: 0.85rem;
color: #fff;
text-shadow: 0 1px 3px rgba(15, 40, 90, 0.45);
min-width: 38px;
text-align: right;
}
#deck-cassette {
flex: none;
position: relative;
width: 64px;
height: 42px;
border-radius: 8px;
background: linear-gradient(var(--shell1, #3a4254), var(--shell2, #2c3344));
box-shadow:
inset 0 1px 2px rgba(255, 255, 255, 0.2),
0 3px 10px rgba(15, 40, 90, 0.3);
display: flex;
align-items: flex-end;
justify-content: center;
gap: 12px;
padding-bottom: 6px;
box-sizing: border-box;
}
#deck-cassette::before {
content: "";
position: absolute;
top: 5px;
left: 7px;
right: 7px;
height: 12px;
background: #f6f1e3;
border: 1px solid #e0d6bd;
border-radius: 3px;
box-sizing: border-box;
}
.mini-reel {
width: 15px;
height: 15px;
border-radius: 50%;
border: 3px dashed #cfd6e4;
background: #1d232f;
box-sizing: border-box;
}
#tape-deck.playing .mini-reel {
animation: reel-spin 2.4s linear infinite;
}
#deck-loop {
flex: none;
width: 44px;
height: 44px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(255, 255, 255, 0.5);
background: rgba(255, 255, 255, 0.22);
color: #fff;
cursor: pointer;
box-sizing: border-box;
transition:
background 0.15s ease,
color 0.15s ease,
box-shadow 0.15s ease,
border-color 0.15s ease;
}
#deck-loop:hover {
background: rgba(255, 255, 255, 0.4);
}
/* lit up = this tape loops; off = roll through the whole shelf */
#deck-loop.on {
background: #ffd84a;
border-color: #ffd84a;
color: #3a2c14;
box-shadow: 0 0 10px rgba(255, 216, 74, 0.7);
}
#deck-loop:active {
transform: translateY(1px);
}
#deck-download {
flex: none;
width: 44px;
height: 44px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(255, 255, 255, 0.5);
background: rgba(255, 255, 255, 0.22);
color: #fff;
cursor: pointer;
text-decoration: none;
box-sizing: border-box;
transition: background 0.15s ease;
}
#deck-download:hover {
background: rgba(255, 255, 255, 0.4);
}
#deck-download.disabled {
opacity: 0.4;
pointer-events: none;
}
/* --- game boy modal --------------------------------------------------------- */
#gameboy-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 19;
}
/* display:none (like #machine-modal) — an opacity fade here would let the inner
.gb-shell snap back to full opacity as .closing is removed, causing a flicker. */
#gameboy-modal.hidden {
display: none;
}
#gameboy-modal.opening .gb-shell {
animation: gb-pop 0.42s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
#gameboy-modal.closing .gb-shell {
animation: gb-drop 0.28s ease both;
}
@keyframes gb-pop {
from {
opacity: 0;
transform: translateY(26px) scale(0.85);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes gb-drop {
to {
opacity: 0;
transform: translateY(20px) scale(0.92);
}
}
#gameboy-close {
position: absolute;
top: -14px;
right: -14px;
z-index: 2;
width: 36px;
height: 36px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.5);
background: rgba(255, 255, 255, 0.22);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
color: #fff;
font-size: 1.15rem;
line-height: 1;
cursor: pointer;
text-shadow: 0 1px 3px rgba(15, 40, 90, 0.4);
}
#gameboy-close:hover {
background: rgba(255, 255, 255, 0.38);
}
.gb-shell {
width: min(330px, 86vw);
padding: 22px 22px 26px;
background: linear-gradient(160deg, #d3d0c6, #b7b4a9);
border-radius: 16px 16px 64px 16px;
box-shadow:
inset 0 2px 0 rgba(255, 255, 255, 0.6),
inset 0 -6px 14px rgba(0, 0, 0, 0.18),
0 24px 60px rgba(15, 40, 90, 0.45);
font-family: "Baloo 2", sans-serif;
}
.gb-screen-frame {
background: linear-gradient(160deg, #585d56, #3f443d);
border-radius: 10px 10px 30px 10px;
padding: 14px 26px 22px;
box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.5);
}
.gb-power {
display: flex;
align-items: center;
gap: 7px;
margin: 0 0 8px 2px;
font-size: 0.52rem;
letter-spacing: 0.14em;
font-weight: 700;
color: #c4c8d0;
}
.gb-power i {
width: 8px;
height: 8px;
border-radius: 50%;
background: #d9534f;
box-shadow: 0 0 6px #ff6a64;
}
.gb-screen {
position: relative;
aspect-ratio: 10 / 9;
border-radius: 4px;
overflow: hidden;
box-shadow:
inset 0 0 0 2px #1d2540,
inset 0 2px 10px rgba(0, 0, 0, 0.35);
}
#gb-canvas {
display: block;
width: 100%;
height: 100%;
image-rendering: pixelated;
image-rendering: crisp-edges;
}
/* faint scanlines for retro feel — subtle so the colour reads */
.gb-screen::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
background: repeating-linear-gradient(
0deg,
rgba(0, 0, 0, 0.07) 0 1px,
transparent 1px 3px
);
}
.gb-caption {
margin: 9px 2px 0;
min-height: 14px;
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 8px;
font-family: "DotGothic16", monospace;
transition: filter 0.15s ease;
}
.gb-caption .gb-seed {
font-size: 0.72rem;
letter-spacing: 0.06em;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.35);
}
.gb-caption .gb-hint {
font-size: 0.5rem;
letter-spacing: 0.03em;
color: #6a6f86;
}
.gb-caption.gb-flash {
animation: gb-flash 0.3s ease;
}
@keyframes gb-flash {
0% { filter: brightness(1.9); }
100% { filter: brightness(1); }
}
.gb-logo {
margin: 12px 0 14px;
text-align: center;
font-weight: 800;
font-style: italic;
font-size: 1.05rem;
color: #2c3550;
}
.gb-logo span {
color: #8c2d52;
margin-left: 2px;
}
.gb-controls {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 6px;
}
.gb-dpad {
position: relative;
width: 56px;
height: 56px;
}
.gb-pad-v,
.gb-pad-h {
position: absolute;
background: #2b2f2b;
border-radius: 4px;
box-shadow: inset 0 2px 3px rgba(255, 255, 255, 0.12);
}
.gb-pad-v {
left: 19px;
top: 0;
width: 18px;
height: 56px;
}
.gb-pad-h {
top: 19px;
left: 0;
width: 56px;
height: 18px;
}
/* transparent click targets over each arm of the d-pad */
.gb-pad-hit {
position: absolute;
z-index: 2;
margin: 0;
padding: 0;
border: 0;
background: transparent;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.gb-pad-hit:active {
background: rgba(255, 255, 255, 0.14);
border-radius: 3px;
}
.gb-up { left: 19px; top: 0; width: 18px; height: 19px; }
.gb-down { left: 19px; top: 37px; width: 18px; height: 19px; }
.gb-left { left: 0; top: 19px; width: 19px; height: 18px; }
.gb-right { left: 37px; top: 19px; width: 19px; height: 18px; }
.gb-ab {
display: flex;
gap: 14px;
transform: rotate(-18deg);
}
.gb-btn {
width: 40px;
height: 40px;
border-radius: 50%;
border: 0;
padding: 0;
cursor: pointer;
font-family: inherit;
background: radial-gradient(circle at 35% 30%, #a8406b, #7a1f45);
color: #f2d6e2;
font-weight: 800;
font-size: 0.95rem;
display: flex;
align-items: center;
justify-content: center;
box-shadow:
0 3px 5px rgba(0, 0, 0, 0.3),
inset 0 2px 3px rgba(255, 255, 255, 0.25);
-webkit-tap-highlight-color: transparent;
transition: transform 0.06s ease, box-shadow 0.06s ease;
}
.gb-btn:active {
transform: translateY(2px);
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.3),
inset 0 2px 3px rgba(255, 255, 255, 0.25);
}
.gb-startsel {
display: flex;
justify-content: center;
gap: 18px;
margin-top: 18px;
}
.gb-startsel .gb-ss {
width: 34px;
height: 11px;
border: 0;
padding: 0;
cursor: pointer;
border-radius: 999px;
background: #6b7280;
transform: rotate(-22deg);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3);
-webkit-tap-highlight-color: transparent;
transition: background 0.1s ease;
}
.gb-startsel .gb-ss:active {
background: #565d6b;
}
.gb-speaker {
display: flex;
gap: 6px;
justify-content: flex-end;
margin: 16px 10px 0 0;
transform: rotate(-22deg);
}
.gb-speaker i {
width: 4px;
height: 26px;
border-radius: 999px;
background: rgba(0, 0, 0, 0.22);
}