@import "tailwindcss"; @custom-variant dark (&:is(.dark *)); /* * THE SKIN — wave 5. Design tokens, the single place to restyle the app. * * Mathematical soundness, per the owner's brief: * - Tailwind's built-in 0.25rem (4px) spacing scale IS the rhythm — every * padding/margin/gap in the app uses whole units of a 4px base. * - The type scale below is a declared modular ladder (each --text-* token * carries its own line-height); components never invent pixel sizes. * - Radii ladder is 4 / 8 / 12 / 16px multiples of the 4px base. * - Emphasis colors live in tokens (--lh-ink-strong / --lh-ink-italic) so a * future settings UI reassigns them (configurable per the owner). */ /* Palette (owner's hexes): main #141414, raised #1C1C1C, edges #434343. Mono chrome = JetBrains Mono; prose = Source Sans 3; titles = CMU Serif. */ :root { --radius: 8px; --background: #141414; --foreground: #e6e6e6; --card: #1c1c1c; --card-foreground: #e6e6e6; --popover: #1c1c1c; --popover-foreground: #e6e6e6; --primary: #e6e6e6; --primary-foreground: #141414; --secondary: #1c1c1c; --secondary-foreground: #e6e6e6; --muted: #1c1c1c; --muted-foreground: #8d8d8d; --accent: #2a2a2a; --accent-foreground: #e6e6e6; --destructive: oklch(0.704 0.191 22.216); --border: #434343; --input: #434343; --ring: #8d8d8d; /* Emphasis coloring in teaching prose — ready for the settings UI. */ --lh-ink-strong: #c96b4a; --lh-ink-italic: #6fa9d6; --lh-prose-size: 16.5px; --lh-prose-leading: 1.5; } /* The app is always dark (); mirror the same values so the block stays coherent under Tailwind's variant. */ .dark { --background: #141414; --foreground: #e6e6e6; --card: #1c1c1c; --card-foreground: #e6e6e6; --popover: #1c1c1c; --popover-foreground: #e6e6e6; --primary: #e6e6e6; --primary-foreground: #141414; --secondary: #1c1c1c; --secondary-foreground: #e6e6e6; --muted: #1c1c1c; --muted-foreground: #8d8d8d; --accent: #2a2a2a; --accent-foreground: #e6e6e6; --destructive: oklch(0.704 0.191 22.216); --border: #434343; --input: #434343; --ring: #8d8d8d; --lh-ink-strong: #c96b4a; --lh-ink-italic: #6fa9d6; --lh-prose-size: 16.5px; --lh-prose-leading: 1.5; } /* CMU Serif bold (titles) — bundled local file, no CDN dependency. */ @font-face { font-family: "lh-cmu"; src: url("/fonts/cmunbx.ttf") format("truetype"); font-weight: 700; font-style: normal; font-display: swap; } @theme inline { /* Fonts: chrome = JetBrains Mono, prose = Source Sans 3, titles = CMU Serif bold. The JetBrains/SourceSans variables attach in app/layout.tsx. (owner, typography round) The Tailwind defaults are ALIASED to the app fonts: `font-mono`/`font-sans` utilities would otherwise fall to the SYSTEM stacks — a fourth font the app never chose — and streamdown's code-block chrome + mermaid-error + demo-card all use them. */ --font-ui: var(--font-jetbrains), ui-monospace, SFMono-Regular, monospace; --font-prose: var(--font-sans3), ui-sans-serif, system-ui, sans-serif; --font-title: "lh-cmu", serif; --font-mono: var(--font-jetbrains), ui-monospace, SFMono-Regular, monospace; --font-sans: var(--font-sans3), ui-sans-serif, system-ui, sans-serif; /* The modular type ladder — each size carries its own line-height. Component sizes must pull from this ladder. */ --text-xxs: 10px; --text-xxs--line-height: 1.4; --text-xs: 11px; --text-xs--line-height: 1.4; --text-sm: 12.5px; --text-sm--line-height: 1.4; --text-base: 14px; --text-base--line-height: 1.45; --text-lg: 16px; --text-lg--line-height: 1.45; --text-xl: 20px; --text-xl--line-height: 1.4; --text-2xl: 25px; --text-2xl--line-height: 1.3; --text-3xl: 32px; --text-3xl--line-height: 1.25; /* Radii ladder — 4/8/12/16 multiples of the 4px base. */ --radius-sm: 4px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px; --radius: 8px; --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); --color-card-foreground: var(--card-foreground); --color-popover: var(--popover); --color-popover-foreground: var(--popover-foreground); --color-primary: var(--primary); --color-primary-foreground: var(--primary-foreground); --color-secondary: var(--secondary); --color-secondary-foreground: var(--secondary-foreground); --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground); --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive); --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); } * { border-color: var(--color-border); } body { background: var(--color-background); color: var(--color-foreground); font-family: var(--font-ui); } /* Teaching prose (the markdown renderer's container gets .lh-prose): Source Sans 3 with the size/leading tokens; emphasis color bound to the settings-ready tokens. */ .lh-prose { font-family: var(--font-prose); font-size: var(--lh-prose-size); line-height: var(--lh-prose-leading); } .lh-prose :where(strong, b) { color: var(--lh-ink-strong); font-weight: 700; } .lh-prose :where(em, i) { color: var(--lh-ink-italic); } /* Thin dark native scrollbar (app-wide): dark thumb, transparent track. */ * { scrollbar-width: thin; scrollbar-color: var(--color-border) transparent; } ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--color-border); border-radius: 8px; } ::-webkit-scrollbar-thumb:hover { background: var(--color-muted-foreground); } /* Dock variant swap — quick fade/rise on content change; the shell's HEIGHT morph is separate (WAAPI in DockOverlay). Re-applied via class toggling, no remount, so collapsed-question state survives. */ @keyframes dock-variant-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } .dock-variant { animation: dock-variant-in 160ms cubic-bezier(0.2, 0.8, 0.2, 1); } /* ============================================================ * W3 — presentation fixes (appended 2026-08-22) * ============================================================ */ /* Streamdown's internal UI chrome (code blocks, tables, mermaid containers, copy/download buttons) is styled with Tailwind utilities living in its dist * bundle. Tailwind v4 never scans node_modules, so without this line those * classes are dead — code blocks and mermaid containers rendered unstyled. * (Streamdown README, "Tailwind CSS setup".) */ @source "../node_modules/streamdown/dist/*.js"; /* Token the streamdown utilities reference (code-block/mermaid chrome uses * bg-sidebar / border-sidebar): map it onto the app's card surface. */ @theme inline { --color-sidebar: var(--card); } /* Mermaid diagrams: keep wide charts inside the content column. Mermaid * inlines a px max-width on the svg, so !important is required to win. */ [data-streamdown="mermaid"] svg { max-width: 100% !important; height: auto !important; } /* Display math: long equations scroll instead of overflowing the column. */ .katex-display { overflow-x: auto; overflow-y: hidden; } /* ============================================================ * Session status — the braille spinner (the one working indicator) * ============================================================ */ /* (owner, minimal-color round) Phase color-coding is RETIRED: phases only advance (every session ends in Teaching), so phase-as-signal was decoration; the --phase-* tokens and every phase-dot state are gone. What state deserved — "a turn is in flight" — is carried by MOTION alone: the terminal braille spinner (components/braille-spinner.tsx), monochrome, existing only while true. */ /* One keyframes, all ten frames: each frame holds opacity 1 for its 10% slice of the 800ms cycle and 0 elsewhere; frames stagger via positive animation-delay (frame k starts at k×80ms). Static contexts (SSR, reduced motion) show frame 0 — see the base rules below. */ @keyframes braille-frame { 0%, 10% { opacity: 1; } 10.001%, 100% { opacity: 0; } } .braille-frame { animation: braille-frame 800ms linear infinite; opacity: 0; } /* Static fallback: frame 0 (⠋) is the glyph a non-animated context shows. */ .braille-frame[data-frame="0"] { opacity: 1; } @media (prefers-reduced-motion: reduce) { .braille-frame { animation: none; } }