Spaces:
Running
Running
| @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 ; | |
| font-size: 1.05rem ; | |
| color: var(--ink) ; | |
| cursor: pointer ; | |
| } | |
| #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) ; | |
| } | |
| #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) ; | |
| box-shadow: 4px 4px 0 rgba(45, 49, 66, 0.2) ; | |
| } | |
| .gradio-container { | |
| background: var(--paper) ; | |
| 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 ; | |
| color: var(--ink) ; | |
| 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%) ; | |
| } | |
| #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,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 280 18' fill='none' stroke='%232d3142' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><path d='M4 9 C 22 2, 38 16, 56 9 S 90 2, 108 9 S 142 16, 160 9 S 194 2, 212 9 S 246 16, 276 9'/></svg>"); | |
| background-repeat: no-repeat; | |
| background-size: contain; | |
| background-position: center; | |
| opacity: 0.85; | |
| } | |
| #title-block h1 { | |
| font-family: 'Fredoka One', cursive ; | |
| font-weight: 400 ; | |
| font-size: 4.2rem ; | |
| color: var(--ink) ; | |
| margin: 0 ; | |
| letter-spacing: 0.02em; | |
| line-height: 1 ; | |
| text-shadow: 3px 3px 0px rgba(245, 200, 66, 0.45); | |
| } | |
| #title-block .subtitle { | |
| font-family: 'Patrick Hand', sans-serif ; | |
| font-size: 1.4rem ; | |
| color: var(--crayon-blue) ; | |
| 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 ; | |
| color: #ffffff ; | |
| 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 ; | |
| font-size: 1.2rem ; | |
| color: var(--ink) ; | |
| font-weight: 400 ; | |
| } | |
| input[type="text"], | |
| input[type="number"], | |
| textarea, | |
| select { | |
| background: rgba(255, 255, 255, 0.6) ; | |
| border: 2.5px solid var(--ink) ; | |
| border-radius: 14px 18px 12px 16px ; | |
| color: var(--ink) ; | |
| font-family: 'Patrick Hand', sans-serif ; | |
| font-size: 1.15rem ; | |
| padding: 10px 14px ; | |
| 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) ; | |
| color: var(--ink) ; | |
| } | |
| input[type="text"]:focus, | |
| textarea:focus, | |
| select:focus { | |
| border-color: var(--crayon-blue) ; | |
| 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) ; | |
| border: 2.5px solid var(--ink) ; | |
| border-radius: 14px 18px 12px 16px ; | |
| color: var(--ink) ; | |
| font-family: 'Patrick Hand', sans-serif ; | |
| font-size: 1.15rem ; | |
| padding: 10px 14px ; | |
| box-shadow: 3px 3px 0 rgba(45, 49, 66, 0.12); | |
| } | |
| .dark .gradio-dropdown input { | |
| background: rgba(30, 32, 64, 0.85) ; | |
| color: var(--ink) ; | |
| } | |
| /* Make sure the options popup is on top and uses readable defaults */ | |
| .gradio-dropdown ul, | |
| .gradio-dropdown [role="listbox"], | |
| ul.options { | |
| background: #ffffff ; | |
| border: 2px solid var(--ink) ; | |
| border-radius: 8px ; | |
| font-family: 'Patrick Hand', sans-serif ; | |
| color: var(--ink) ; | |
| z-index: 9999 ; | |
| 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 ; | |
| font-family: 'Patrick Hand', sans-serif ; | |
| font-size: 1.05rem ; | |
| color: var(--ink) ; | |
| background: transparent ; | |
| cursor: pointer; | |
| } | |
| .gradio-dropdown ul li:hover, | |
| .gradio-dropdown [role="option"]:hover, | |
| ul.options li:hover { | |
| background: var(--crayon-yellow) ; | |
| } | |
| .dark .gradio-dropdown ul, | |
| .dark .gradio-dropdown [role="listbox"], | |
| .dark ul.options { | |
| color: var(--paper) ; | |
| } | |
| .dark .reset-button { | |
| color: var(--ink) ; | |
| } | |
| .dark ul.options li, | |
| .dark .gradio-dropdown ul li, | |
| .dark .gradio-dropdown [role="option"] { | |
| color: var(--paper) ; | |
| } | |
| input[type="range"] { | |
| accent-color: var(--crayon-red) ; | |
| } | |
| .form-card { | |
| background: var(--paper) ; | |
| border: 3px solid var(--ink) ; | |
| border-radius: 22px 18px 26px 16px ; | |
| padding: 28px ; | |
| 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) ; | |
| } | |
| .dark .form-card div { | |
| background-color: var(--paper) ; | |
| } | |
| .form-card-right { | |
| background: var(--paper) ; | |
| border: 3px solid var(--ink) ; | |
| border-radius: 18px 24px 16px 22px ; | |
| padding: 28px ; | |
| 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) ; | |
| } | |
| .dark .form-card-right div { | |
| background-color: var(--paper) ; | |
| } | |
| /* ---- 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) ; | |
| border: 3px solid var(--ink) ; | |
| border-radius: 24px 16px 22px 18px ; | |
| padding: 24px 28px 18px ; | |
| 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) ; | |
| } | |
| .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 ; | |
| background-image: none ; | |
| } | |
| .gradio-container #draw-upload .upload-container, | |
| .gradio-container #draw-upload .image-container { | |
| border: 2px dashed rgba(45, 49, 66, 0.25) ; | |
| border-radius: 14px ; | |
| background-color: #fdfbf3 ; | |
| } | |
| .dark .gradio-container #draw-upload .upload-container, | |
| .dark .gradio-container #draw-upload .image-container { | |
| border: 2px dashed rgba(232, 228, 217, 0.25) ; | |
| background-color: var(--paper) ; | |
| } | |
| /* 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) ; | |
| } | |
| /* 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) ; | |
| /* crayon-blue @ 22% */ | |
| color: var(--ink); | |
| } | |
| .dark .card-tab-grownup.card-tab-grownup { | |
| background: rgba(74, 144, 217, 0.22) ; | |
| 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 ; | |
| border-radius: 14px ; | |
| } | |
| #draw-canvas { | |
| border: 2.5px dashed var(--ink) ; | |
| border-radius: 14px ; | |
| overflow: hidden; | |
| } | |
| #draw-canvas canvas { | |
| border: none ; | |
| } | |
| /* 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 ; | |
| --background-fill-primary: #f3f1ec ; | |
| --background-fill-secondary: #f3f1ec ; | |
| --neutral-50: #f3f1ec ; | |
| --neutral-100: #f3f1ec ; | |
| background-color: #f3f1ec ; | |
| background-image: none ; | |
| } | |
| .dark .gradio-container #draw-canvas { | |
| --block-background-fill: var(--paper) ; | |
| --background-fill-primary: var(--paper) ; | |
| --background-fill-secondary: var(--paper) ; | |
| --neutral-50: var(--paper) ; | |
| --neutral-100: var(--paper) ; | |
| background-color: var(--paper) ; | |
| } | |
| /* 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 ; | |
| background-image: none ; | |
| } | |
| .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) ; | |
| } | |
| /* The inner <canvas> stays cream-white when idle (it's the "paper") */ | |
| .gradio-container #draw-canvas canvas { | |
| background-color: #fdfbf3 ; | |
| background-image: none ; | |
| } | |
| .dark .gradio-container #draw-canvas canvas { | |
| background-color: #23264a ; | |
| } | |
| /* The "Clear drawing" button β secondary visual weight; the primary CTA is | |
| still "Sing with Lola" further down the page. */ | |
| #reset-drawing-btn { | |
| margin-top: 10px ; | |
| background: transparent ; | |
| border: 2px dashed var(--ink) ; | |
| color: var(--ink) ; | |
| font-family: 'Patrick Hand', sans-serif ; | |
| font-size: 1.0rem ; | |
| padding: 6px 16px ; | |
| border-radius: 12px ; | |
| box-shadow: none ; | |
| width: auto ; | |
| align-self: flex-start; | |
| } | |
| #reset-drawing-btn:hover { | |
| background: rgba(45, 49, 66, 0.04) ; | |
| } | |
| /* ---- "What Lola saw" hint ---------------------------------------------- | |
| Sits between the audio player and the lyrics. Reserves a CONSTANT amount | |
| of vertical space whether or not it has content, so the layout never | |
| reflows when content arrives (which was previously pushing the lyrics | |
| down on the first generation and overlapping them mid-transition). | |
| The visual chrome (background, border, padding) only renders when the | |
| inner .saw-hint div exists; the outer container keeps its margin always. */ | |
| #saw-hint { | |
| margin: 4px 0 14px ; | |
| /* ALWAYS reserve this space */ | |
| min-height: 12px; | |
| /* small constant gap when empty */ | |
| } | |
| .saw-hint { | |
| font-family: 'Patrick Hand', sans-serif; | |
| color: var(--ink); | |
| background: rgba(245, 200, 66, 0.10); | |
| /* faint crayon-yellow */ | |
| border-left: 4px solid var(--crayon-yellow); | |
| border-radius: 8px 12px 10px 8px; | |
| padding: 10px 14px; | |
| margin: 0; | |
| /* margin lives on #saw-hint now */ | |
| font-size: 1.0rem; | |
| line-height: 1.45; | |
| } | |
| .dark .saw-label { | |
| color: var(--ink) ; | |
| } | |
| .saw-hint .saw-label { | |
| font-weight: 700; | |
| margin-right: 4px; | |
| } | |
| .saw-hint em { | |
| font-style: italic; | |
| color: var(--ink); | |
| opacity: 0.85; | |
| } | |
| .saw-hint .saw-plus { | |
| margin-left: 6px; | |
| opacity: 0.75; | |
| font-size: 0.95rem; | |
| } | |
| .saw-hint-empty { | |
| background: rgba(74, 144, 217, 0.08); | |
| /* faint crayon-blue */ | |
| border-left-color: var(--crayon-blue); | |
| font-style: italic; | |
| } | |
| /* Instrument grid β auto-fill so cells wrap naturally at any container width. | |
| minmax(80px, 1fr): each cell is at least 80px wide (enough for the 56px | |
| icon + padding) and expands to fill available space. No fixed column count | |
| means the grid reflows on its own: 4 across on desktop, 3 on tablet, 2 on | |
| narrow mobile β without ever squashing the buttons. */ | |
| .instr-grid { | |
| display: grid ; | |
| grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)) ; | |
| gap: 12px ; | |
| margin: 0 0 14px ; | |
| } | |
| /* Gradio wraps every gr.HTML in a div with default margin; squash the gap | |
| between the picker title and the instrument grid. */ | |
| .form-card-left .gr-block:has(> .instr-grid), | |
| .form-card-left .gr-block:has(> #picker-hint) { | |
| display: contents ; | |
| margin: 0 ; | |
| padding: 0 ; | |
| } | |
| /* Slider β Gradio's value bubble sits in a small box that can clip the | |
| rounded border at the bottom. Give the wrapper extra padding so the | |
| bubble's full outline shows, and force no overflow clipping on the | |
| surrounding block. */ | |
| .gr-slider, | |
| .gradio-slider, | |
| [data-testid="slider"] { | |
| overflow: visible ; | |
| padding-bottom: 14px ; | |
| } | |
| .gr-slider input[type="number"], | |
| .gradio-slider input[type="number"], | |
| [data-testid="slider"] input[type="number"] { | |
| overflow: visible ; | |
| } | |
| /* The bubble container itself */ | |
| .gr-slider .wrap, | |
| .gradio-slider .wrap { | |
| overflow: visible ; | |
| } | |
| .instr-cell { | |
| background: var(--paper) ; | |
| border: 2.5px solid var(--ink) ; | |
| border-radius: 16px 20px 14px 18px ; | |
| padding: 10px 6px 8px ; | |
| cursor: pointer; | |
| box-shadow: 3px 3px 0 rgba(45, 49, 66, 0.12); | |
| transition: all 0.2s ease; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 4px; | |
| font-family: 'Patrick Hand', sans-serif; | |
| font-size: 1rem; | |
| color: var(--ink); | |
| } | |
| .dark .instr-cell { | |
| box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.3) ; | |
| } | |
| .dark .instr-cell span { | |
| color: var(--ink) ; | |
| } | |
| .instr-cell:hover { | |
| transform: translate(-2px, -2px) rotate(-2deg); | |
| box-shadow: 5px 5px 0 rgba(45, 49, 66, 0.18); | |
| } | |
| .instr-cell.selected { | |
| background: var(--crayon-yellow) ; | |
| box-shadow: 3px 3px 0 var(--ink) ; | |
| transform: rotate(-1deg); | |
| } | |
| .instr-cell img { | |
| width: 56px; | |
| height: 56px; | |
| object-fit: contain; | |
| pointer-events: none; | |
| } | |
| .instr-cell span { | |
| pointer-events: none; | |
| } | |
| /* The little "1" / "2" badge in the corner of selected cells β tells the | |
| user which slot they've assigned the instrument to. */ | |
| .instr-cell { | |
| position: relative; | |
| } | |
| .slot-badge { | |
| position: absolute; | |
| top: -8px; | |
| right: -8px; | |
| background: #2d3142 ; | |
| color: #ffffff ; | |
| font-family: 'Fredoka One', cursive; | |
| font-size: 0.85rem; | |
| width: 22px; | |
| height: 22px; | |
| border-radius: 50%; | |
| display: flex ; | |
| align-items: center; | |
| justify-content: center; | |
| box-shadow: 2px 2px 0 rgba(45, 49, 66, 0.25); | |
| pointer-events: none; | |
| } | |
| #picker-hint { | |
| font-family: 'Patrick Hand', sans-serif; | |
| font-size: 0.95rem; | |
| color: var(--crayon-blue); | |
| margin: -2px 0 10px; | |
| padding: 4px 8px; | |
| border-radius: 6px; | |
| transition: background 0.25s ease, color 0.25s ease; | |
| min-height: 0; | |
| } | |
| #picker-hint:empty { | |
| margin: 0; | |
| padding: 0; | |
| } | |
| #picker-hint.hint-flash { | |
| background: var(--crayon-red); | |
| color: #fff; | |
| transform: rotate(-0.5deg); | |
| } | |
| #picker-hint.hint-error { | |
| font-weight: 700; | |
| box-shadow: 2px 2px 0 rgba(45, 49, 66, 0.2); | |
| } | |
| .section-title { | |
| font-family: 'Fredoka One', cursive; | |
| font-size: 1.4rem; | |
| color: var(--ink); | |
| margin: 18px 0 2px; | |
| display: flex; | |
| align-items: baseline; | |
| gap: 10px; | |
| } | |
| .section-title .which { | |
| font-family: 'Patrick Hand', sans-serif; | |
| font-size: 1rem; | |
| color: var(--crayon-blue); | |
| font-style: italic; | |
| } | |
| button#generate-btn, | |
| .primary { | |
| background: var(--crayon-red) ; | |
| color: #fffef7 ; | |
| border: 3px solid var(--ink) ; | |
| border-radius: 26px 18px 28px 22px ; | |
| font-family: 'Fredoka One', cursive ; | |
| font-size: 1.7rem ; | |
| font-weight: 400 ; | |
| padding: 18px 36px ; | |
| cursor: pointer; | |
| box-shadow: 5px 5px 0 var(--ink); | |
| transition: all 0.18s ease; | |
| letter-spacing: 0.03em; | |
| width: 100%; | |
| margin-top: 16px; | |
| } | |
| button#generate-btn:hover { | |
| transform: translate(-2px, -2px) rotate(-1deg); | |
| box-shadow: 7px 7px 0 var(--ink); | |
| background: var(--crayon-orange) ; | |
| } | |
| button#generate-btn:active { | |
| transform: translate(2px, 2px); | |
| box-shadow: 2px 2px 0 var(--ink); | |
| } | |
| #output-lyrics textarea { | |
| background: | |
| linear-gradient(transparent 28px, rgba(74, 144, 217, 0.18) 28px, rgba(74, 144, 217, 0.18) 29px, transparent 29px) ; | |
| background-size: 100% 30px ; | |
| background-color: rgba(255, 255, 255, 0.7) ; | |
| font-family: 'Gloria Hallelujah', cursive ; | |
| font-size: 1.25rem ; | |
| line-height: 30px ; | |
| color: var(--ink) ; | |
| border: 2.5px solid var(--ink) ; | |
| border-radius: 16px 20px 14px 18px ; | |
| padding: 12px 18px ; | |
| } | |
| .dark #output-lyrics textarea { | |
| background: | |
| linear-gradient(transparent 28px, rgba(106, 171, 240, 0.15) 28px, rgba(106, 171, 240, 0.15) 29px, transparent 29px) ; | |
| background-size: 100% 30px ; | |
| background-color: rgba(30, 32, 64, 0.8) ; | |
| } | |
| audio { | |
| border-radius: 18px ; | |
| border: 2.5px solid var(--ink) ; | |
| background: var(--paper) ; | |
| box-shadow: 3px 3px 0 rgba(45, 49, 66, 0.1); | |
| } | |
| .dark audio { | |
| box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.3) ; | |
| } | |
| .attribution { | |
| text-align: center; | |
| font-family: 'Patrick Hand', sans-serif; | |
| font-size: 0.85rem; | |
| color: rgba(45, 49, 66, 0.55); | |
| margin: 24px auto 16px; | |
| padding: 12px; | |
| } | |
| .attribution a { | |
| color: var(--crayon-blue); | |
| text-decoration: none; | |
| border-bottom: 1px dashed var(--crayon-blue); | |
| } | |
| /* Footer container β wraps the icon attribution and a professional credit | |
| block. A subtle divider sits between them. */ | |
| .footer { | |
| text-align: center; | |
| padding: 8px 12px 20px; | |
| } | |
| .footer .attribution { | |
| padding: 6px 12px 14px; | |
| border-bottom: 1px dashed rgba(45, 49, 66, 0.18); | |
| margin-bottom: 12px; | |
| } | |
| /* Powered-by line β sits below the icon attribution. Small and quiet: | |
| names the open-source models behind the app and gives judges one-click | |
| access to each on the Hub. */ | |
| .footer .powered-by { | |
| font-family: 'Patrick Hand', sans-serif; | |
| font-size: 0.92rem; | |
| color: var(--ink); | |
| opacity: 0.72; | |
| letter-spacing: 0.01em; | |
| padding: 0 0 4px; | |
| } | |
| .footer .powered-by a { | |
| color: var(--ink); | |
| text-decoration: none; | |
| border-bottom: 1px dotted rgba(45, 49, 66, 0.45); | |
| padding: 0 1px; | |
| transition: color 0.2s ease, border-color 0.2s ease; | |
| } | |
| .footer .powered-by a:hover { | |
| color: var(--crayon-blue); | |
| border-bottom-color: var(--crayon-blue); | |
| } | |
| footer { | |
| display: none ; | |
| } | |
| .gr-form { | |
| gap: 14px ; | |
| } | |
| /* Hide the state-holder textboxes β they're in the DOM only so JS can write to them */ | |
| .pick-state { | |
| display: none ; | |
| } | |
| /* Card-level section headings (replaces inline gr.Markdown "###" usage) */ | |
| .card-heading { | |
| font-family: 'Fredoka One', cursive; | |
| font-size: 1.5rem; | |
| color: var(--ink); | |
| margin: 4px 0 14px; | |
| letter-spacing: 0.01em; | |
| } | |
| /* Advanced settings accordion */ | |
| #advanced-accordion { | |
| margin-top: 22px ; | |
| border: 2.5px dashed var(--ink) ; | |
| border-radius: 14px 18px 12px 16px ; | |
| background: rgba(255, 255, 255, 0.3) ; | |
| overflow: visible ; | |
| } | |
| .dark #advanced-accordion { | |
| background: rgba(30, 32, 64, 0.5) ; | |
| } | |
| #advanced-accordion>button, | |
| #advanced-accordion .label-wrap, | |
| #advanced-accordion summary { | |
| font-family: 'Fredoka One', cursive ; | |
| font-size: 1.1rem ; | |
| color: var(--ink) ; | |
| padding: 10px 14px ; | |
| } | |
| /* Inline error banner β appears under the form when validation fails */ | |
| #error-banner { | |
| margin-top: 12px; | |
| } | |
| .error-banner-inner { | |
| background: #fde6e3; | |
| border: 2.5px solid var(--crayon-red); | |
| border-radius: 14px 18px 12px 16px; | |
| color: #8a2a22; | |
| font-family: 'Patrick Hand', sans-serif; | |
| font-size: 1.1rem; | |
| padding: 12px 16px; | |
| box-shadow: 3px 3px 0 rgba(225, 90, 78, 0.25); | |
| transform: rotate(-0.4deg); | |
| } | |
| .dark .error-banner-inner { | |
| background: #3a1a1a; | |
| color: #f0a0a0; | |
| } | |
| /* ---------- Success banner (between heading and audio in lullaby card) --- | |
| Sits between "Your lullaby" heading and the audio player. One-liner, | |
| no emoji β quiet confirmation that generation completed. Friendly | |
| green palette to mirror the error-banner's red, but smaller padding | |
| and no rotation since it's an inline confirmation, not a celebration. */ | |
| #success-banner { | |
| margin: 4px 0 10px; | |
| } | |
| .success-banner-inner { | |
| background: #e5f3dc; | |
| border: 2.5px solid #6fb05e; | |
| border-radius: 12px 16px 10px 14px; | |
| color: #2f5a26; | |
| font-family: 'Patrick Hand', sans-serif; | |
| font-size: 1.05rem; | |
| padding: 8px 14px; | |
| text-align: center; | |
| box-shadow: 2px 2px 0 rgba(111, 176, 94, 0.2); | |
| } | |
| .dark .success-banner-inner { | |
| background: #1a3020; | |
| color: #90cc80; | |
| } | |
| /* Idle (pre-generation) variant β soft yellow "waiting" tone, no green | |
| to avoid implying success before there's a song. Same shape as the | |
| success state so the slot doesn't jump around when content changes. */ | |
| .success-banner-idle { | |
| background: rgba(245, 200, 66, 0.14); | |
| /* whisper of crayon-yellow */ | |
| border: 2px dashed rgba(45, 49, 66, 0.40); | |
| border-radius: 12px 16px 10px 14px; | |
| color: var(--ink); | |
| opacity: 0.78; | |
| font-family: 'Patrick Hand', sans-serif; | |
| font-size: 1.0rem; | |
| padding: 8px 14px; | |
| text-align: center; | |
| } | |
| /* ---------- "Both inputs" notice (in the draw card) --------------------- | |
| Soft, low-key, informational tone. Not red/alarming because the user | |
| didn't do anything wrong β we're just being transparent about which | |
| input wins when both are present. */ | |
| #both-inputs-notice { | |
| margin: 14px 18px 0; | |
| } | |
| .both-inputs-inner { | |
| background: rgba(245, 200, 66, 0.18); | |
| /* whisper of crayon-yellow */ | |
| border: 2px dashed rgba(45, 49, 66, 0.45); | |
| border-radius: 12px 16px 10px 14px; | |
| color: var(--ink); | |
| font-family: 'Patrick Hand', sans-serif; | |
| font-size: 1.0rem; | |
| padding: 10px 14px; | |
| line-height: 1.4; | |
| } | |
| .both-inputs-inner .both-inputs-icon { | |
| margin-right: 4px; | |
| font-size: 1.1rem; | |
| } | |
| #lola-status:empty { | |
| display: none; | |
| } |