@tailwind base; @tailwind components; @tailwind utilities; @layer base { :root { /* Sidebar tokens (consumed by tailwind colors in tailwind.config.js). Brutalist system: near-black surfaces, white/15 hairlines, white accent for selection/focus. */ --sidebar-bg: #1e1f22; --sidebar-fg: rgba(255, 255, 255, 0.72); --sidebar-border: rgba(255, 255, 255, 0.15); --sidebar-accent: rgba(255, 255, 255, 0.08); --sidebar-accent-fg: #ffffff; --sidebar-ring: rgba(255, 255, 255, 0.4); /* shadcn design tokens consumed by @storyteller/ui-video-editor. Dark-theme palette (the editor only runs in dark mode today). --primary matches the webapp's brand blue (#2d81ff). */ --background: 240 5% 8%; --foreground: 0 0% 98%; --card: 240 11% 7%; --card-foreground: 0 0% 98%; --popover: 240 4% 5%; --popover-foreground: 0 0% 98%; --primary: 216 100% 59%; --primary-foreground: 0 0% 98%; --secondary: 240 4% 9%; --secondary-foreground: 0 0% 98%; --muted: 240 4% 9%; --muted-foreground: 0 0% 64%; --accent: 240 4% 9%; --accent-foreground: 0 0% 98%; --destructive: 0 70% 50%; --destructive-foreground: 0 0% 98%; --border: 0 0% 100% / 0.15; --input: 0 0% 100% / 0.15; --ring: 0 0% 100% / 0.6; /* ── Motion tokens ────────────────────────────────────────────────── Shared easing curves + durations so motion across the app feels like one system, not a pile of one-offs. Mirrored as JS constants in src/lib/motion.ts for framer-motion. "Balanced" direction: chrome and navigation stay restrained; the creative canvas gets to be expressive. Curves: --ease-out decelerate-to-rest; the default for things entering. --ease-in-out symmetric; for things that move and settle in place. --ease-spring gentle overshoot; for playful, tactile moments. --ease-emphasis Apple/Linear "expo" curve; for confident, premium slides. */ --ease-out: cubic-bezier(0.22, 1, 0.36, 1); --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); --ease-emphasis: cubic-bezier(0.32, 0.72, 0, 1); /* Durations — keep chrome snappy, reserve longer ones for canvas drama. */ --dur-fast: 140ms; --dur-base: 220ms; --dur-slow: 340ms; --dur-page: 260ms; } /* Respect users who ask for less motion: collapse durations to near-zero so transitions become instant cuts rather than being removed wholesale. */ @media (prefers-reduced-motion: reduce) { :root { --dur-fast: 1ms; --dur-base: 1ms; --dur-slow: 1ms; --dur-page: 1ms; } } } /* Subtle 3px corners on interactive controls. The brutalist system keeps large surfaces (panels, modals, tables), passive labels, and media tiles square; anything pressable gets a soft corner for affordance. Explicit rounding elsewhere uses the same rounded-[3px] value. Tailwind radius utilities (rounded-full, rounded-none) still win — the utilities layer is emitted after base. */ @layer base { button, select, textarea, input:not([type="radio"]):not([type="range"]), [role="button"] { border-radius: 3px; } } * { scrollbar-width: thin; scrollbar-color: #2c2c2f #0b0b0c; } *::-webkit-scrollbar { width: 8px; } *::-webkit-scrollbar-track { background: #0b0b0c; } *::-webkit-scrollbar-thumb { background-color: #2c2c2f; border: 2px solid #0b0b0c; } .scrollbar-hidden { -ms-overflow-style: none; scrollbar-width: none; } .scrollbar-hidden::-webkit-scrollbar { display: none; } @layer base { /* Display face: Archivo — an industrial grotesque that matches the hard-edged HUD system better than Bricolage's editorial quirk. Weights still come from per-usage utilities. */ h1, h2, h3, h4, h5, h6 { font-family: "Archivo", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; letter-spacing: -0.03em; } /* Only display-scale headings run expanded on the wdth axis; compact headings (card titles, section labels) stay at normal width so they don't truncate in tight layouts. */ h1, h2 { font-stretch: 118%; } body { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; color: #ffffff; background-color: #0b0b0c; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } /* Mono HUD micro-label, matching the marketing site's .hud-label. */ .hud-label { font-family: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.15em; text-transform: uppercase; } .font-serif-italic { font-family: "Instrument Serif", Georgia, "Times New Roman", serif; font-style: italic; font-weight: 400; letter-spacing: -0.01em; } } /* Display utility carries the ax-display reference treatment (Archivo at weight 620, 118% width), matching the marketing site. Plain (unlayered) rule so it wins over font-weight utilities like font-semibold, which are emitted at the top of the file. */ .font-display { font-stretch: 118%; font-weight: 620; } /* Shared component utilities (matching desktop app theme) */ .bg-ui-badge { background-color: rgba(0, 0, 0, 0.4); } .icon-auto-contrast { filter: invert(1); } /* Panel surface used by pagescene + lib components. Brutalist system: flat near-black, no gradient (class name kept for lib compatibility). */ .bg-ui-panel-gradient { background: #101014; background-color: #101014; } .prevent-select { -webkit-user-select: none; -ms-user-select: none; user-select: none; pointer-events: none; } /* three-dots loading spinners — used inside upload-modal + pagescene. */ .dot-typing { position: relative; left: -9999px; width: 10px; height: 10px; border-radius: 5px; background-color: #ffffff30; color: #ffffff30; box-shadow: 9984px 0 0 0 #ffffff30, 9999px 0 0 0 #ffffff30, 10014px 0 0 0 #ffffff30; animation: dot-typing 1.5s infinite linear; } @keyframes dot-typing { 0% { box-shadow: 9984px 0 0 0 #ffffff30, 9999px 0 0 0 #ffffff30, 10014px 0 0 0 #ffffff30; } 16.667% { box-shadow: 9984px -10px 0 0 #ffffff30, 9999px 0 0 0 #ffffff30, 10014px 0 0 0 #ffffff30; } 33.333% { box-shadow: 9984px 0 0 0 #ffffff30, 9999px 0 0 0 #ffffff30, 10014px 0 0 0 #ffffff30; } 50% { box-shadow: 9984px 0 0 0 #ffffff30, 9999px -10px 0 0 #ffffff30, 10014px 0 0 0 #ffffff30; } 66.667% { box-shadow: 9984px 0 0 0 #ffffff30, 9999px 0 0 0 #ffffff30, 10014px 0 0 0 #ffffff30; } 83.333% { box-shadow: 9984px 0 0 0 #ffffff30, 9999px 0 0 0 #ffffff30, 10014px -10px 0 0 #ffffff30; } 100% { box-shadow: 9984px 0 0 0 #ffffff30, 9999px 0 0 0 #ffffff30, 10014px 0 0 0 #ffffff30; } } /* Brutalist system: former "glass" surfaces are now flat near-black panels with white/15 hairline borders (class names kept for lib compatibility). */ @layer components { .glass { @apply relative border border-white/15 transition-colors duration-300 rounded-[3px] backdrop-blur-sm; @apply bg-[#1E1F22]/95 !important; } } .liquid-glass { background: rgba(11, 11, 12, 0.92); border: 1px solid rgba(255, 255, 255, 0.15); } /* Minimal thin scrollbar for prompt textareas */ .promptbox-scrollbar { scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, 0.2) transparent; } .promptbox-scrollbar::-webkit-scrollbar { width: 4px; } .promptbox-scrollbar::-webkit-scrollbar-track { background: transparent; } .promptbox-scrollbar::-webkit-scrollbar-thumb { background-color: rgba(255, 255, 255, 0.2); } .promptbox-scrollbar::-webkit-scrollbar-thumb:hover { background-color: rgba(255, 255, 255, 0.35); } /* Hide the native resize grip (Chromium draws different grips for textareas vs contenteditable divs) and draw one consistent grip on the wrapper so the plain and @-mention prompt editors look identical. */ .promptbox-scrollbar::-webkit-resizer { background: transparent; border: none; } .promptbox-resize-wrap { position: relative; } .promptbox-resize-wrap::after { content: ""; position: absolute; bottom: 2px; right: 2px; width: 10px; height: 10px; pointer-events: none; background: linear-gradient( 135deg, transparent 40%, rgba(255, 255, 255, 0.5) 40%, rgba(255, 255, 255, 0.5) 45%, transparent 45%, transparent 55%, rgba(255, 255, 255, 0.5) 55%, rgba(255, 255, 255, 0.5) 60%, transparent 60%, transparent 70%, rgba(255, 255, 255, 0.5) 70%, rgba(255, 255, 255, 0.5) 75%, transparent 75% ); } @keyframes fade-in-up { from { opacity: 0; translate: 0 12px; } to { opacity: 1; translate: 0 0; } } /* backdrop blur bug on site so i disabled - bflat */ /* .animate-fade-in-up { animation: fade-in-up 0.2s ease-out both; } */ @keyframes toast-in { from { opacity: 0; translate: 0 8px; } to { opacity: 1; translate: 0 0; } } .animate-toast-in { animation: toast-in 0.2s ease-out both; } /* Prompt box drag & drop overlay */ @keyframes promptbox-drop-overlay-in { from { opacity: 0; } to { opacity: 1; } } .promptbox-drop-overlay { animation: promptbox-drop-overlay-in 0.15s ease-out both; } @keyframes promptbox-drop-chip-in { from { opacity: 0; translate: 0 6px; scale: 0.9; } to { opacity: 1; translate: 0 0; scale: 1; } } .promptbox-drop-chip { animation: promptbox-drop-chip-in 0.25s cubic-bezier(0.22, 1, 0.36, 1) both; } @keyframes marquee { 0% { transform: translateX(0); } 100% { transform: translateX(-100%); } } @keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } .animate-shimmer { background: linear-gradient( 90deg, transparent 0%, rgba(255, 255, 255, 0.08) 50%, transparent 100% ); background-size: 200% 100%; animation: shimmer 1.5s ease-in-out infinite; } .bg-dots { background-image: radial-gradient(#ffffff20 1px, transparent 0); background-color: #101014; background-size: 33px 33px; @apply bg-center; } /* Mobile bottom-sheet (SettingsDrawer). The shadcn Sheet relies on the tailwindcss-animate plugin, which isn't enabled here, so we drive the slide ourselves off Radix's data-state. Defining a closed-state animation lets Radix delay unmount until the slide-down finishes. */ @keyframes ac-drawer-up { from { transform: translateY(100%); } to { transform: translateY(0); } } /* --ac-drawer-drag-y: set inline by SettingsDrawer when a pull-down gesture dismisses the sheet, so the slide-down starts from where the finger let go instead of snapping back to the top first. Fresh mounts default to 0. */ @keyframes ac-drawer-down { from { transform: translateY(var(--ac-drawer-drag-y, 0px)); } to { transform: translateY(100%); } } .ac-drawer-content[data-state="open"] { animation: ac-drawer-up 0.32s cubic-bezier(0.32, 0.72, 0, 1); } .ac-drawer-content[data-state="closed"] { animation: ac-drawer-down 0.22s ease-in forwards; } @keyframes ac-sheet-fade { from { opacity: 0; } to { opacity: 1; } } .ac-sheet-overlay[data-state="open"] { animation: ac-sheet-fade 0.25s ease-out; } .ac-sheet-overlay[data-state="closed"] { animation: ac-sheet-fade 0.2s ease-in reverse forwards; } /* Lucide icons ship with a fixed 24px default; size them relative to the surrounding font so they scale with text like the previous icons did. 1.2em (not 1em) because Lucide draws glyphs on a 20px grid inside a 24px viewBox — the extra 20% makes the visible mark fill a full 1em, matching the old solid-glyph sizing. Explicit w-/h-/size- utilities still win (the utilities layer is emitted later). */ @layer base { .lucide { display: inline-block; width: 1.2em; height: 1.2em; vertical-align: -0.2em; } }