@import url('https://fonts.googleapis.com/css2?family=Fredoka+One&family=Patrick+Hand&family=Gloria+Hallelujah&display=swap');
:root {
--paper: #fffef7;
--paper-edge: #f5ecd9;
--crayon-blue: #4a90d9;
--crayon-red: #e15a4e;
--crayon-yellow: #f5c842;
--crayon-green: #6fb05e;
--crayon-purple: #9b6db5;
--crayon-pink: #f08ab0;
--crayon-orange: #f0934a;
--ink: #2d3142;
/* dark mode surface tokens (overridden below when .dark is active) */
--surface-card: var(--paper);
--surface-input: rgba(255, 255, 255, 0.6);
--surface-dropdown: #ffffff;
--surface-canvas: #f3f1ec;
--surface-canvas-inner: #fdfbf3;
}
/* ── Dark mode ── */
.dark {
--paper: #1a1b2e;
--paper-edge: #16172a;
--ink: #e8e4d9;
--crayon-blue: #6aabf0;
--crayon-yellow: #f5c842;
--crayon-red: #f07066;
--crayon-orange: #f5a060;
--crayon-green: #80cc6e;
--surface-card: #1e2040;
--surface-input: rgba(30, 32, 64, 0.85);
--surface-dropdown: #252848;
--surface-canvas: #1c1e38;
--surface-canvas-inner: #23264a;
}
/* Dark mode toggle button */
.dark-toggle-label {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: 'Fredoka One', cursive !important;
font-size: 1.05rem !important;
color: var(--ink) !important;
cursor: pointer !important;
}
#dark-toggle-btn {
width: 40px;
height: 22px;
appearance: none;
-webkit-appearance: none;
background: var(--ink);
border: 2.5px solid var(--ink);
border-radius: 22px;
position: relative;
cursor: pointer;
transition: background .2s;
}
#dark-toggle-btn:checked {
background: var(--crayon-blue) !important;
}
#dark-toggle-btn::before {
content: '';
position: absolute;
left: 0px;
top: -0.5px;
width: 18px;
height: 18px;
background: var(--paper);
border-radius: 50%;
transition: transform .2s;
}
#dark-toggle-btn:checked::before {
transform: translateX(16px);
}
#dark-toggle-btn:hover {
transform: translate(-1px, -1px) !important;
box-shadow: 4px 4px 0 rgba(45, 49, 66, 0.2) !important;
}
.gradio-container {
background: var(--paper) !important;
background-image:
radial-gradient(circle at 20% 30%, rgba(245, 200, 66, 0.06) 0%, transparent 30%),
radial-gradient(circle at 80% 70%, rgba(74, 144, 217, 0.06) 0%, transparent 30%),
radial-gradient(circle at 50% 90%, rgba(240, 138, 176, 0.06) 0%, transparent 30%);
font-family: 'Patrick Hand', sans-serif !important;
color: var(--ink) !important;
min-height: 100vh;
transition: background 0.3s ease, color 0.3s ease;
}
.dark .gradio-container {
background-image:
radial-gradient(circle at 20% 30%, rgba(106, 171, 240, 0.05) 0%, transparent 30%),
radial-gradient(circle at 80% 70%, rgba(155, 109, 181, 0.05) 0%, transparent 30%),
radial-gradient(circle at 50% 90%, rgba(245, 200, 66, 0.04) 0%, transparent 30%) !important;
}
#title-block {
text-align: center;
padding: 40px 24px 16px;
}
#title-block::after {
content: "";
display: block;
width: 280px;
height: 18px;
margin: 20px auto 0;
/* Hand-drawn squiggle. Inline SVG as a background image — single ink color,
gentle wave reading as "kid drew a wavy line under the title". */
background-image: url("data:image/svg+xml;utf8,");
background-repeat: no-repeat;
background-size: contain;
background-position: center;
opacity: 0.85;
}
#title-block h1 {
font-family: 'Fredoka One', cursive !important;
font-weight: 400 !important;
font-size: 4.2rem !important;
color: var(--ink) !important;
margin: 0 !important;
letter-spacing: 0.02em;
line-height: 1 !important;
text-shadow: 3px 3px 0px rgba(245, 200, 66, 0.45);
}
#title-block .subtitle {
font-family: 'Patrick Hand', sans-serif !important;
font-size: 1.4rem !important;
color: var(--crayon-blue) !important;
margin-top: 12px;
font-weight: 400;
transform: rotate(-1.5deg);
display: inline-block;
}
/* Orientation copy under the title. Sits BELOW the sketched squiggle (the
#title-block::after pseudo-element) so the squiggle reads as the title's
closing flourish, and the explainer is its own calmer block underneath.
Plain warm prose — what the app IS and who does WHAT, in two lines. */
#title-explainer {
font-family: 'Patrick Hand', sans-serif;
font-size: 1.15rem;
color: var(--ink);
opacity: 0.82;
margin: 12px auto 24px;
max-width: 640px;
line-height: 1.5;
text-align: center;
}
#title-explainer .explainer-roles {
display: block;
margin-top: 6px;
font-size: 1.0rem;
opacity: 0.85;
font-style: italic;
}
/* ---------- Step diagram (1 ➜ 2 ➜ 3) -------------------------------------
Wayfinding strip just under the title. Three larger pills connected
by dashed lines, hand-drawn aesthetic to match the rest of the app.
Each pill contains: number badge (popped above), emoji icon, short
heading, and a two-line description. Step 1 is styled with a dashed
border to visually telegraph "optional" — backs up the explicit
"(optional)" text underneath.
The diagram doubles as the main explainer of how Lolaby works, so it
sits prominently above the cards. */
#step-diagram {
display: flex;
align-items: flex-start;
/* top-align so descs of different
lengths don't shift the headings */
justify-content: center;
gap: 6px;
margin: 8px auto 32px;
flex-wrap: wrap;
max-width: 920px;
}
#step-diagram .step {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 14px 18px 16px;
background: rgba(255, 255, 255, 0.35);
border: 2.5px solid var(--ink);
border-radius: 16px 20px 14px 18px;
/* irregular to look hand-drawn */
width: 240px;
flex: 0 1 240px;
box-shadow: 2px 2px 0 rgba(45, 49, 66, 0.12);
position: relative;
}
.dark #step-diagram .step {
background: rgba(30, 32, 64, 0.75);
box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.3);
}
#step-diagram .step-optional {
border-style: dashed;
background: rgba(255, 255, 255, 0.35);
opacity: 0.92;
}
.dark #step-diagram .step-optional {
background: rgba(30, 32, 64, 0.35);
}
#step-diagram .step-num {
font-family: 'Fredoka One', sans-serif;
font-size: 1.25rem;
color: var(--ink);
background: var(--crayon-yellow);
width: 30px;
height: 30px;
border-radius: 50%;
border: 2.5px solid var(--ink);
display: inline-flex;
align-items: center;
justify-content: center;
line-height: 1;
margin-top: -28px;
/* pop the number above the pill */
margin-bottom: 4px;
box-shadow: 1px 1px 0 rgba(45, 49, 66, 0.15);
}
.dark #step-diagram .step-num {
color: var(--paper);
}
#step-diagram .step-icon {
font-size: 1.7rem;
line-height: 1;
margin: 2px 0;
}
#step-diagram .step-label {
font-family: 'Fredoka One', sans-serif;
font-size: 1.05rem;
color: var(--ink);
text-align: center;
line-height: 1.2;
margin-bottom: 4px;
}
#step-diagram .step-sub {
font-family: 'Patrick Hand', sans-serif;
font-weight: normal;
font-size: 0.85rem;
opacity: 0.7;
display: block;
margin-top: 2px;
}
#step-diagram .step-desc {
font-family: 'Patrick Hand', sans-serif;
font-size: 0.95rem;
color: var(--ink);
opacity: 0.82;
text-align: center;
line-height: 1.35;
margin-top: 2px;
}
#step-diagram .step-connector {
flex: 0 0 24px;
height: 0;
border-top: 2.5px dashed var(--ink);
opacity: 0.5;
/* Connector centred vertically against the pill's vertical mid-point.
The pills are ~150px tall now, so the connector needs a ~70px top
offset so it visually meets the middle of the boxes, not the top. */
margin-top: 70px;
border-radius: 50%;
}
@media (max-width: 880px) {
/* When the strip can't fit three pills + connectors, stack vertically.
Hide connectors so the pills stack cleanly. */
#step-diagram {
flex-direction: column;
align-items: center;
gap: 24px;
}
#step-diagram .step-connector {
display: none;
}
#step-diagram .step {
width: 280px;
flex: 0 0 auto;
}
}
/* ---------- Step badge (small number inside a card tab) ------------------
Reinforces the diagram by anchoring each card to its step number. Once
a user scrolls past the diagram they can still see which step they're
on by looking at the card tab. */
.step-badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
margin-right: 6px;
background: #2d3142 !important;
color: #ffffff !important;
border-radius: 50%;
font-family: 'Fredoka One', sans-serif;
font-size: 0.85rem;
line-height: 1;
vertical-align: middle;
position: relative;
top: -1px;
}
.step-badge-inline {
background: var(--crayon-yellow);
color: var(--ink);
border: 2px solid var(--ink);
margin-right: 8px;
}
/* ---------- Step-3 hint above the Sing button ---------------------------
Sits right above the primary CTA so a non-power user sees a clear
"this is the next thing to do" line before the button itself. */
.step-3-hint {
font-family: 'Patrick Hand', sans-serif;
font-size: 1.05rem;
color: var(--ink);
text-align: center;
margin: 18px 0 8px;
opacity: 0.9;
}
label>span,
.gradio-container label>span:first-child {
font-family: 'Fredoka One', cursive !important;
font-size: 1.2rem !important;
color: var(--ink) !important;
font-weight: 400 !important;
}
input[type="text"],
input[type="number"],
textarea,
select {
background: rgba(255, 255, 255, 0.6) !important;
border: 2.5px solid var(--ink) !important;
border-radius: 14px 18px 12px 16px !important;
color: var(--ink) !important;
font-family: 'Patrick Hand', sans-serif !important;
font-size: 1.15rem !important;
padding: 10px 14px !important;
box-shadow: 3px 3px 0 rgba(45, 49, 66, 0.12);
transition: all 0.18s ease;
}
.dark input[type="text"],
.dark input[type="number"],
.dark textarea,
.dark select,
.dark input[role="listbox"] {
background: var(--paper) !important;
color: var(--ink) !important;
}
input[type="text"]:focus,
textarea:focus,
select:focus {
border-color: var(--crayon-blue) !important;
box-shadow: 4px 4px 0 var(--crayon-yellow);
outline: none;
}
/* Dropdown: style only the visible trigger input, leave the options panel
with Gradio defaults so it stays usable. */
.gradio-dropdown input {
background: rgba(255, 255, 255, 0.6) !important;
border: 2.5px solid var(--ink) !important;
border-radius: 14px 18px 12px 16px !important;
color: var(--ink) !important;
font-family: 'Patrick Hand', sans-serif !important;
font-size: 1.15rem !important;
padding: 10px 14px !important;
box-shadow: 3px 3px 0 rgba(45, 49, 66, 0.12);
}
.dark .gradio-dropdown input {
background: rgba(30, 32, 64, 0.85) !important;
color: var(--ink) !important;
}
/* Make sure the options popup is on top and uses readable defaults */
.gradio-dropdown ul,
.gradio-dropdown [role="listbox"],
ul.options {
background: #ffffff !important;
border: 2px solid var(--ink) !important;
border-radius: 8px !important;
font-family: 'Patrick Hand', sans-serif !important;
color: var(--ink) !important;
z-index: 9999 !important;
box-shadow: 4px 4px 0 rgba(45, 49, 66, 0.18);
}
.gradio-dropdown ul li,
.gradio-dropdown [role="option"],
ul.options li {
padding: 8px 14px !important;
font-family: 'Patrick Hand', sans-serif !important;
font-size: 1.05rem !important;
color: var(--ink) !important;
background: transparent !important;
cursor: pointer;
}
.gradio-dropdown ul li:hover,
.gradio-dropdown [role="option"]:hover,
ul.options li:hover {
background: var(--crayon-yellow) !important;
}
.dark .gradio-dropdown ul,
.dark .gradio-dropdown [role="listbox"],
.dark ul.options {
color: var(--paper) !important;
}
.dark .reset-button {
color: var(--ink) !important;
}
.dark ul.options li,
.dark .gradio-dropdown ul li,
.dark .gradio-dropdown [role="option"] {
color: var(--paper) !important;
}
input[type="range"] {
accent-color: var(--crayon-red) !important;
}
.form-card {
background: var(--paper) !important;
border: 3px solid var(--ink) !important;
border-radius: 22px 18px 26px 16px !important;
padding: 28px !important;
box-shadow: 5px 5px 0 rgba(45, 49, 66, 0.08);
/* No transform here — it creates a stacking context that breaks
Gradio's fixed-position dropdown popup. */
}
.dark .form-card {
box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.3) !important;
}
.dark .form-card div {
background-color: var(--paper) !important;
}
.form-card-right {
background: var(--paper) !important;
border: 3px solid var(--ink) !important;
border-radius: 18px 24px 16px 22px !important;
padding: 28px !important;
box-shadow: 5px 5px 0 rgba(245, 200, 66, 0.25);
}
.dark .form-card-right {
box-shadow: 5px 5px 0 rgba(245, 200, 66, 0.12) !important;
}
.dark .form-card-right div {
background-color: var(--paper) !important;
}
/* ---- Drawing canvas card ----------------------------------------------- */
/* Matches the form-card aesthetic: paper background, ink border, wobbly
corners that read as "drawn by hand". Sits above the form as the hero. */
.draw-card {
background: var(--paper) !important;
border: 3px solid var(--ink) !important;
border-radius: 24px 16px 22px 18px !important;
padding: 24px 28px 18px !important;
box-shadow: 5px 5px 0 rgba(74, 144, 217, 0.18);
margin-bottom: 12px;
}
.dark .draw-card {
box-shadow: 5px 5px 0 rgba(106, 171, 240, 0.12) !important;
}
.draw-card .card-heading {
margin-bottom: 4px;
}
.draw-hint {
font-family: 'Patrick Hand', sans-serif;
color: var(--ink);
opacity: 0.7;
font-size: 1.0rem;
margin: 0 0 14px 0;
}
/* ---------- Split draw card: canvas | upload ------------------------------
The draw card hosts two side-by-side image inputs so trackpad-allergic
users (i.e. small children) can upload a phone photo of a paper drawing
instead of trying to draw with a mouse. Soft vertical divider between
the two halves; on narrow viewports the columns stack and the divider
becomes a horizontal one. */
#draw-row {
gap: 18px;
align-items: stretch;
}
.draw-half {
padding: 0 6px;
position: relative;
z-index: 2;
/* sit above background doodles */
}
/* Vertical divider between the two halves, drawn on the canvas-side's
right edge so it sits exactly between the two columns. */
.draw-half-canvas {
border-right: 2px dashed rgba(45, 49, 66, 0.18);
padding-right: 18px;
}
.draw-half-upload {
padding-left: 6px;
}
/* Small label above each half so people immediately see "this side OR
that side." Same font as the hint, slightly bolder, slightly darker. */
.draw-half-label {
font-family: 'Patrick Hand', sans-serif;
font-size: 1.05rem;
color: var(--ink);
opacity: 0.85;
margin: 0 0 8px;
font-weight: 700;
letter-spacing: 0.01em;
}
/* The little caption under the upload widget, mirroring the canvas's
`.draw-hint` styling but scoped to the upload half. */
.draw-half-hint {
font-family: 'Patrick Hand', sans-serif;
color: var(--ink);
opacity: 0.65;
font-size: 0.95rem;
margin: 8px 2px 0;
text-align: center;
}
/* Match the upload widget's idle look to the canvas surround so the two
halves feel like siblings. Gradio's default upload widget has a plain
white drop-zone — we soften that to the same warm taupe used for the
canvas surround. */
.gradio-container #draw-upload {
background-color: transparent !important;
background-image: none !important;
}
.gradio-container #draw-upload .upload-container,
.gradio-container #draw-upload .image-container {
border: 2px dashed rgba(45, 49, 66, 0.25) !important;
border-radius: 14px !important;
background-color: #fdfbf3 !important;
}
.dark .gradio-container #draw-upload .upload-container,
.dark .gradio-container #draw-upload .image-container {
border: 2px dashed rgba(232, 228, 217, 0.25) !important;
background-color: var(--paper) !important;
}
/* When viewport gets narrow, stack the two halves vertically and convert
the vertical divider into a horizontal one so it still reads as a
"this OR that" separator. */
@media (max-width: 760px) {
.draw-half-canvas {
border-right: none;
border-bottom: 2px dashed rgba(45, 49, 66, 0.18);
padding-right: 0;
padding-bottom: 18px;
margin-bottom: 6px;
}
.draw-half-upload {
padding-left: 0;
}
}
/* Tab-pill labels that sit ABOVE each card heading, classifying who the
section is for. Visually paired (same shape) but color-differentiated
(kid = playful crayon-yellow accent, grown-up = grounded ink) so the
two roles are recognisable at a glance. */
.card-tab {
font-family: 'Patrick Hand', sans-serif;
font-size: 0.95rem;
font-weight: 400;
display: inline-block;
padding: 4px 14px;
border-radius: 12px 16px 14px 12px;
margin: 0 0 10px;
letter-spacing: 0.02em;
transform: rotate(-1.2deg);
border: 2px solid var(--ink);
}
.card-tab-kid {
background: #f4d8a0;
color: var(--ink);
}
.dark .card-tab-kid {
color: var(--paper) !important;
}
/* Grown-up tab: soft crayon-blue wash — paired with yellow (the kid tab) as
a classic complementary crayon pair, calm enough to read as the more
sober adult-facing section. Solid border (the dashed border had it
visually colliding with the Clear-drawing button). */
.card-tab-grownup {
background: rgba(74, 144, 217, 0.22) !important;
/* crayon-blue @ 22% */
color: var(--ink);
}
.dark .card-tab-grownup.card-tab-grownup {
background: rgba(74, 144, 217, 0.22) !important;
color: var(--ink);
}
/* Subtitle under the form heading — explains what's required vs optional in
one breath, so the user isn't surprised by validation errors. Same
visual weight as .draw-hint so the two cards feel like siblings. */
.form-role-hint {
font-family: 'Patrick Hand', sans-serif;
color: var(--ink);
opacity: 0.7;
font-size: 1.0rem;
margin: 0 0 14px 0;
}
/* The Sketchpad itself: give the inner canvas a creamy "paper" feel and
the surround a dashed-ink border so it sits inside our card. The actual
surround color is set lower in the taupe block below. */
#draw-canvas .image-container,
#draw-canvas canvas {
background: #fdfbf3 !important;
border-radius: 14px !important;
}
#draw-canvas {
border: 2.5px dashed var(--ink) !important;
border-radius: 14px !important;
overflow: hidden;
}
#draw-canvas canvas {
border: none !important;
}
/* Drawing canvas surround.
IDLE state: warm muted taupe — repainted via CSS-variable overrides
scoped to #draw-canvas (Gradio uses --block-background-fill and friends
to color component surrounds; overriding them recolors the idle wrapper
without fighting Sketchpad's active editor chrome).
ACTIVE editing state: Gradio's Sketchpad swaps in its own dark editor
surface using inline styles / different variables, which I'm accepting
as a constraint — fighting it produced more side effects than wins. */
.gradio-container #draw-canvas {
--block-background-fill: #f3f1ec !important;
--background-fill-primary: #f3f1ec !important;
--background-fill-secondary: #f3f1ec !important;
--neutral-50: #f3f1ec !important;
--neutral-100: #f3f1ec !important;
background-color: #f3f1ec !important;
background-image: none !important;
}
.dark .gradio-container #draw-canvas {
--block-background-fill: var(--paper) !important;
--background-fill-primary: var(--paper) !important;
--background-fill-secondary: var(--paper) !important;
--neutral-50: var(--paper) !important;
--neutral-100: var(--paper) !important;
background-color: var(--paper) !important;
}
/* Also paint direct child wrappers (Gradio's Sketchpad nests several divs;
any "inactive" surround inherits/uses these surfaces). The active editor
uses a deeper element with its own inline style that we no longer fight. */
.gradio-container #draw-canvas>div,
.gradio-container #draw-canvas .block,
.gradio-container #draw-canvas .wrap,
.gradio-container #draw-canvas .container {
background-color: #f3f1ec !important;
background-image: none !important;
}
.dark .gradio-container #draw-canvas>div,
.dark .gradio-container #draw-canvas .block,
.dark .gradio-container #draw-canvas .wrap,
.dark .gradio-container #draw-canvas .container {
background-color: var(--paper) !important;
}
/* The inner