LoFinity / frontend /style.css
eloigil6's picture
Refactor overlay behavior to improve user interaction during descent transition. The start button now has its pointer events disabled when the overlay is hidden, preventing click interference with underlying elements while maintaining a smooth fade-out effect.
a58205b
Raw
History Blame Contribute Delete
36.5 kB
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);
}