@import "tailwindcss"; @import "../../common/styles/tokens.css"; @import "../../common/styles/slides.css"; @source "../../common/components"; /* Design tokens, the `dark` variant, the two bases (light / dark) and the accents live in common/styles/tokens.css; the faces in common/styles/fonts.ts. A site opens on the reader's system base in its own accent (site.json, rendered as `html[data-accent]`); the hub opens dark in Signal. The header's toggle cycles the base; the accent is the site's. */ body { background: var(--background); color: var(--foreground); font-family: var(--font-sans), ui-sans-serif, system-ui, sans-serif; } /* Player geometry, in one place. The floating player (PlayerProvider) and the transcript modal's reserved slot (TranscriptModal) both have to agree on how wide the video is and where its top edge sits — they used to carry two copies of `min(92vw,960px)` and `top-16`, which is exactly the kind of pair that drifts. Below sm the player is full-bleed and tucked under the notch; from sm it is the centred card it has always been. */ :root { --player-w: 100vw; --player-top: env(safe-area-inset-top, 0px); } @media (min-width: 640px) { :root { --player-w: min(92vw, 960px); --player-top: 4rem; /* was `top-16` */ } } /* Safe-area padding for the sticky bars (composer, selection toolbar, the modal's control strip). Zero on every desktop browser, the home-indicator inset on an iPhone — with `viewportFit: "cover"` set in layout.tsx. */ @utility pb-safe { padding-bottom: env(safe-area-inset-bottom, 0px); } @utility pt-safe { padding-top: env(safe-area-inset-top, 0px); } /* Archive shelf: a restrained load stagger — the spines rise onto the shelf in sequence (per-spine delay set inline). Motion is the only animation on the hub, and it yields entirely to a reduced-motion preference. */ @keyframes shelf-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } } .shelf-spine { animation: shelf-rise 0.42s cubic-bezier(0.22, 1, 0.36, 1) both; } @media (prefers-reduced-motion: reduce) { .shelf-spine { animation: none; } } /* Downloads catalog: each channel record rises into place in sequence (per-card delay set inline). One quiet accent on the page; yields to reduced-motion. */ @keyframes record-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } } .record-rise { animation: record-rise 0.4s cubic-bezier(0.22, 1, 0.36, 1) both; } @media (prefers-reduced-motion: reduce) { .record-rise { animation: none; } } /* Ask chat: a terminal-flavoured blinking caret at the tail of the streaming answer, and a three-dot "thinking" pulse before the first token arrives. Both yield to a reduced-motion preference. */ @keyframes ai-caret { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } .ai-caret { display: inline-block; width: 0.5ch; margin-left: 1px; background: currentColor; animation: ai-caret 1s step-end infinite; } @keyframes ai-dot { 0%, 80%, 100% { opacity: 0.25; transform: translateY(0); } 40% { opacity: 1; transform: translateY(-2px); } } .ai-dot { animation: ai-dot 1.2s ease-in-out infinite; } .ai-dot:nth-child(2) { animation-delay: 0.15s; } .ai-dot:nth-child(3) { animation-delay: 0.3s; } @media (prefers-reduced-motion: reduce) { .ai-caret { animation: none; opacity: 1; } .ai-dot { animation: none; opacity: 0.6; } }