Web frontend

This commit is contained in:
2026-08-27 12:32:20 +02:00
parent d44c24ec97
commit edb6e5e027
97 changed files with 9535 additions and 195 deletions

188
web/src/styles/app.css Normal file
View File

@@ -0,0 +1,188 @@
/* Ported from claude-design/Dolphin Beats.dc.html. The palette is oklch throughout;
the tokens below are the handful of values that repeat. */
:root {
--ink: oklch(30% 0.04 210);
--paper: oklch(97% 0.01 210);
--accent: oklch(70% 0.16 340);
--accent-dim: oklch(78% 0.14 340);
--sea-deep: oklch(20% 0.045 210);
--shadow: oklch(15% 0.05 210 / 0.35);
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: "Nunito", system-ui, sans-serif;
overscroll-behavior: none;
/* Installed on a phone this is an app, not a document: no rubber-banding, no
long-press callout, no accidental text selection when a small hand misses a
button. Text inside the parent-mode inputs opts back in below. */
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
input,
textarea {
-webkit-user-select: text;
user-select: text;
}
button {
font-family: inherit;
}
@keyframes bubbleRise {
0% {
transform: translateY(0) scale(1);
opacity: 0.55;
}
100% {
transform: translateY(-120vh) scale(1.3);
opacity: 0;
}
}
@keyframes dolphinBob {
0%,
100% {
transform: translateY(0) rotate(-2deg);
}
50% {
transform: translateY(-10px) rotate(2deg);
}
}
@keyframes dolphinSwim {
0% { transform: translate(-20vw, 10vh) rotate(-3deg) scaleX(1); }
15% { transform: translate(2vw, 2vh) rotate(4deg) scaleX(1); }
30% { transform: translate(24vw, 12vh) rotate(-3deg) scaleX(1); }
45% { transform: translate(46vw, 2vh) rotate(4deg) scaleX(1); }
60% { transform: translate(68vw, 12vh) rotate(-3deg) scaleX(1); }
70% { transform: translate(84vw, 6vh) rotate(0deg) scaleX(1); }
74% { transform: translate(84vw, 6vh) rotate(0deg) scaleX(-1); }
85% { transform: translate(52vw, 18vh) rotate(-3deg) scaleX(-1); }
95% { transform: translate(16vw, 6vh) rotate(3deg) scaleX(-1); }
100% { transform: translate(-20vw, 10vh) rotate(-3deg) scaleX(-1); }
}
.stage {
position: relative;
width: 100%;
/* dvh follows the iOS toolbar as it collapses; vh is the fallback for older
browsers, where it would otherwise leave a strip of white at the bottom. */
height: 100vh;
height: 100dvh;
overflow: hidden;
background: linear-gradient(
180deg,
oklch(55% 0.07 210) 0%,
oklch(38% 0.06 210) 45%,
var(--sea-deep) 100%
);
}
.bubble {
position: absolute;
bottom: -40px;
border-radius: 50%;
animation-name: bubbleRise;
animation-timing-function: linear;
animation-iteration-count: infinite;
pointer-events: none;
}
.pill {
border: none;
cursor: pointer;
font-size: 14px;
font-weight: 800;
padding: 9px 15px;
border-radius: 999px;
white-space: nowrap;
background: oklch(97% 0.01 210 / 0.16);
color: var(--paper);
}
.pill[data-active="true"] {
background: var(--paper);
color: oklch(28% 0.04 210);
box-shadow: 0 4px 14px var(--shadow);
}
.card {
backdrop-filter: blur(6px);
overflow: hidden;
cursor: pointer;
transition: transform 0.12s ease;
border: none;
padding: 0;
text-align: left;
font: inherit;
display: block;
width: 100%;
}
.card[data-selected="true"] {
outline: 5px solid var(--paper);
outline-offset: 3px;
transform: translateY(-3px);
}
.card[data-current="true"]:not([data-selected="true"]) {
outline: 4px solid var(--accent);
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 24px;
max-width: 1180px;
margin: 0 auto;
}
.round {
border-radius: 999px;
border: none;
padding: 0;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.key-cap {
font: 800 12px ui-monospace, Menlo, monospace;
background: var(--ink);
color: #fff;
padding: 5px 12px;
border-radius: 8px;
min-width: 56px;
text-align: center;
}
.sheet {
background: var(--paper);
border-radius: 24px;
box-shadow: 0 20px 50px oklch(10% 0.04 210 / 0.5);
}
.overlay {
position: absolute;
inset: 0;
background: oklch(15% 0.03 210 / 0.55);
display: flex;
align-items: center;
justify-content: center;
padding: 40px;
}
input[type="number"],
input[type="range"] {
font: inherit;
}