@import "tailwindcss"; @custom-variant dark (&:is(.dark, .dark *)); /* Window Controls Overlay: active when Electron exposes native titlebar control geometry. */ @custom-variant wco (&:is(.wco, .wco *)); /* On mobile, morph the expanded hero composer into the compact docked composer while it moves; the rest of the app remains visually stationary. */ html[data-mobile-composer-route-transition="true"]::view-transition-old(root), html[data-mobile-composer-route-transition="true"]::view-transition-new(root) { animation: none; } html[data-mobile-composer-route-transition="true"]::view-transition-group(t3-mobile-composer) { animation-duration: 180ms; animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } html[data-mobile-composer-route-transition="true"]::view-transition-image-pair(t3-mobile-composer) { isolation: isolate; } html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobile-composer) { animation: t3-mobile-composer-old 180ms linear both; mix-blend-mode: normal; } html[data-mobile-composer-route-transition="true"]::view-transition-new(t3-mobile-composer) { animation: t3-mobile-composer-new 180ms linear both; mix-blend-mode: normal; } /* The expanded and compact composers have different internal layouts. Use a brief crossfade while their shared wrapper moves so the layout change does not read as a single-frame cut. */ @keyframes t3-mobile-composer-old { 0%, 35% { opacity: 1; } 65%, 100% { opacity: 0; } } @keyframes t3-mobile-composer-new { 0%, 35% { opacity: 0; } 65%, 100% { opacity: 1; } } html[data-mobile-composer-route-transition="true"]::view-transition-group(t3-mobile-draft-headline) { animation-duration: 130ms; } html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobile-draft-headline) { animation: t3-mobile-draft-headline-exit 130ms cubic-bezier(0.4, 0, 1, 1) both; mix-blend-mode: normal; } @keyframes t3-mobile-draft-headline-exit { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-6px); } } :root { --app-scrollbar-width: 6px; --app-scrollbar-thumb: rgb(217 217 217); --app-scrollbar-thumb-hover: rgb(191 191 191); /* * Compact UI geometry. Keep these values semantic so sidebar, palette, * tooltip, and toolbar controls cannot quietly drift apart. */ --control-radius: 0.5rem; --sidebar-content-inset: 0.5rem; --sidebar-control-gap: 0.5rem; --sidebar-icon-color: color-mix(in srgb, var(--sidebar-muted-foreground) 60%, var(--sidebar)); --sidebar-row-content-inset: 0.625rem; --command-shell-inset: 0.5rem; --command-content-inset: 1rem; --floating-content-inset: 0.75rem; --glass-blur: 12px; --glass-opacity: 80%; --glass-saturation: 1.14; --desktop-window-right-resize-inset: 0px; --workspace-topbar-height: 52px; --workspace-controls-top: 0px; --workspace-controls-left: calc(env(safe-area-inset-left) + 0.75rem); --workspace-controls-right: calc(env(safe-area-inset-right) + 0.75rem); --workspace-native-controls-inset: 0px; --workspace-titlebar-control-size: 1.75rem; --workspace-titlebar-control-gap: 0.75rem; } .dark { --app-scrollbar-thumb: rgb(255 255 255 / 8%); --app-scrollbar-thumb-hover: rgb(255 255 255 / 12%); --glass-blur: 16px; --glass-saturation: 1.08; } [data-slot="sidebar-wrapper"] { --workspace-titlebar-content-left: calc( var(--workspace-controls-left) + var(--workspace-titlebar-control-size) + var(--workspace-titlebar-control-gap) ); } .wco { --workspace-topbar-height: env(titlebar-area-height, 52px); --workspace-controls-top: env(titlebar-area-y, 0px); --workspace-controls-left: calc(env(titlebar-area-x, 0px) + 0.75rem); --workspace-controls-right: calc( 100vw - env(titlebar-area-width, 100vw) - env(titlebar-area-x, 0px) + 0.75rem ); --workspace-native-controls-inset: calc( 100vw - env(titlebar-area-width, 100vw) - env(titlebar-area-x, 0px) + 0.75rem ); } /* The font tokens are declared outside the inline theme so utilities reference the variables and Settings -> Appearance can override them at runtime. The default stacks are mirrored in `appearanceFonts.ts`. */ @theme { --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; --font-mono: ui-monospace, "SF Mono", "SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace; } @theme inline { --color-zinc-25: oklch(99.2% 0 0); --animate-skeleton: skeleton 2s -1s infinite linear; /* Duty-cycled indicator animations: long holds with stepped ramps, so the compositor updates discrete frames instead of every vsync. */ --animate-status-pulse: status-pulse 2s infinite; --animate-status-ping: status-ping 2s infinite; --color-warning-foreground: var(--warning-foreground); --color-warning: var(--warning); --color-warning-surface: var(--warning-surface); --color-error-foreground: var(--error-foreground); --color-error: var(--error); --color-error-surface: var(--error-surface); --color-update-foreground: var(--update-foreground); --color-update: var(--update); --color-update-surface: var(--update-surface); --color-success-foreground: var(--success-foreground); --color-success: var(--success); --color-info-foreground: var(--info-foreground); --color-info: var(--info); --color-destructive-foreground: var(--destructive-foreground); --color-ring: var(--ring); --color-input: var(--input); --color-border: var(--border); --color-destructive: var(--destructive); --color-accent-foreground: var(--accent-foreground); --color-accent: var(--accent); --color-muted-foreground: var(--muted-foreground); --color-muted: var(--muted); --color-placeholder: var(--placeholder); --color-secondary-label: var(--secondary-label); --color-icon-muted: var(--icon-muted); --color-secondary-foreground: var(--secondary-foreground); --color-secondary: var(--secondary); --color-primary-foreground: var(--primary-foreground); --color-primary: var(--primary); --color-popover-foreground: var(--popover-foreground); --color-popover: var(--popover); --color-card-foreground: var(--card-foreground); --color-card: var(--card); --color-foreground: var(--foreground); --color-background: var(--background); --color-surface-raised: var(--surface-raised); --color-message: var(--message-surface); --color-message-foreground: var(--message-foreground); --color-message-action: var(--message-action); --color-message-action-foreground: var(--message-action-foreground); --color-message-action-hover: var(--message-action-hover); --color-sidebar: var(--sidebar); --color-sidebar-foreground: var(--sidebar-foreground); --color-sidebar-muted-foreground: var(--sidebar-muted-foreground); --color-sidebar-control-surface: var(--sidebar-control-surface); --color-sidebar-row-hover: var(--sidebar-row-hover); --color-sidebar-row-active: var(--sidebar-row-active); --color-sidebar-row-selected: var(--sidebar-row-selected); --color-sidebar-border: var(--sidebar-border); --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); --radius-xl: calc(var(--radius) + 4px); --radius-2xl: calc(var(--radius) + 8px); --radius-3xl: calc(var(--radius) + 12px); --radius-4xl: calc(var(--radius) + 16px); @keyframes skeleton { to { background-position: -200% 0; } } @keyframes status-pulse { 0%, 40% { opacity: 1; animation-timing-function: steps(6); } 50%, 90% { opacity: 0.5; animation-timing-function: steps(6); } 100% { opacity: 1; } } @keyframes status-ping { /* Burst first (immediate feedback for click ripples), then hold invisible for the rest of the cycle. Mirrors animate-ping's 75%-scale start. */ 0% { opacity: 0.9; scale: 0.75; animation-timing-function: steps(8); } 40%, 100% { opacity: 0; scale: 2; } } } @layer base { * { @apply border-border outline-ring/50; } :where([data-slot="menu-popup"], [data-slot="select-popup"], [data-slot="popover-popup"]):focus, :where( [data-slot="menu-popup"], [data-slot="select-popup"], [data-slot="popover-popup"] ):focus-visible { @apply outline-none ring-0; } html { background-color: var(--app-chrome-background); } body { @apply text-foreground relative; background-color: var(--app-chrome-background); } } @layer components { .sidebar-brand { display: none; } .sidebar-brand-stage { display: none; } @media (min-width: 48rem) { @container sidebar-header (min-width: 13.5rem) { .sidebar-brand { display: flex; } } @container sidebar-header (min-width: 15.75rem) { .sidebar-brand-stage { display: inline-flex; } } } /* Stage-channel sidebar art; ::after ramps to the sidebar bg color and the mask lets the surface grain show through at the boundary. Panels whose background differs from the app chrome (e.g. sidebar v2) override --sidebar-stage-fade so the art fades into their own surface color. */ .sidebar-stage-backdrop { --stage-fade: var(--sidebar-stage-fade, var(--app-chrome-background)); mask-image: linear-gradient(to bottom, black 0%, black 55%, transparent 92%); -webkit-mask-image: linear-gradient(to bottom, black 0%, black 55%, transparent 92%); } .sidebar-stage-backdrop::after { content: ""; position: absolute; inset: 0; background: linear-gradient( to bottom, transparent 0%, transparent 28%, color-mix(in srgb, var(--stage-fade) 10%, transparent) 40%, color-mix(in srgb, var(--stage-fade) 30%, transparent) 52%, color-mix(in srgb, var(--stage-fade) 58%, transparent) 64%, color-mix(in srgb, var(--stage-fade) 82%, transparent) 75%, color-mix(in srgb, var(--stage-fade) 96%, transparent) 85%, var(--stage-fade) 93% ); } .stage-blueprint { --stage-bp-top: #67c2ff; --stage-bp-mid: #347ff8; --stage-bp-bottom: #1538d0; } .dark .stage-blueprint { --stage-bp-top: #3a7ad1; --stage-bp-mid: #2050ae; --stage-bp-bottom: #101f6e; } .workspace-topbar { display: flex; height: var(--workspace-topbar-height); min-height: var(--workspace-topbar-height); flex-shrink: 0; align-items: center; } /* Fade rows themselves as they pass beneath the top chrome. A mask remains visible even when the header and timeline share the same background. */ .chat-timeline-scroll-fade, .settings-page-scroll-fade { --topbar-scroll-fade-height: 2.5rem; -webkit-mask-image: linear-gradient( to bottom, transparent 0%, rgb(0 0 0 / 10%) 10%, rgb(0 0 0 / 30%) 24%, rgb(0 0 0 / 58%) 42%, rgb(0 0 0 / 82%) 62%, rgb(0 0 0 / 96%) 82%, black 100% ), linear-gradient(black, black), linear-gradient(black, black); -webkit-mask-position: top, bottom, right; -webkit-mask-repeat: no-repeat; -webkit-mask-size: 100% var(--topbar-scroll-fade-height), 100% calc(100% - var(--topbar-scroll-fade-height)), var(--app-scrollbar-width) 100%; mask-image: linear-gradient( to bottom, transparent 0%, rgb(0 0 0 / 10%) 10%, rgb(0 0 0 / 30%) 24%, rgb(0 0 0 / 58%) 42%, rgb(0 0 0 / 82%) 62%, rgb(0 0 0 / 96%) 82%, black 100% ), linear-gradient(black, black), linear-gradient(black, black); mask-position: top, bottom, right; mask-repeat: no-repeat; mask-size: 100% var(--topbar-scroll-fade-height), 100% calc(100% - var(--topbar-scroll-fade-height)), var(--app-scrollbar-width) 100%; } @keyframes settings-search-target-pulse { 0%, 100% { box-shadow: 0 0 0 0 transparent; } 50% { box-shadow: 0 0 0 2px color-mix(in oklab, var(--color-primary) 70%, transparent); } } .settings-page-scroll-fade div.settings-search-target-pulse, .settings-page-scroll-fade section.settings-search-target-pulse > div:first-child { animation: settings-search-target-pulse 650ms ease-in-out 2; border-radius: 0.75rem; } /* The pulse is the destination indicator; without it (reduced motion), the focus outline takes over, so exactly one indicator shows at a time. */ .settings-page-scroll-fade .settings-search-target-pulse:focus { outline: none; } .workspace-titlebar-controls { position: absolute; top: var(--workspace-controls-top); right: var(--workspace-controls-right); display: flex; height: var(--workspace-topbar-height); align-items: center; -webkit-app-region: no-drag; } .surface-subheader { @apply flex h-10 min-h-10 shrink-0 items-center border-b border-border/60 bg-background; } [data-preview-panel-mode="inline"] [data-right-panel-surface-content] [data-surface-subheader] { height: calc(var(--spacing) * 7); min-height: calc(var(--spacing) * 7); margin-bottom: calc(var(--spacing) * 3); border-bottom-color: transparent; } .chat-composer-horizontal-inset { padding-inline-start: calc(env(safe-area-inset-left) + 0.75rem); padding-inline-end: calc(env(safe-area-inset-right) + 0.75rem); } .chat-composer-glass { background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent); -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation)); backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation)); } .chat-composer-glass-shell { --chat-composer-glass-surface: var(--card); --chat-composer-outline: rgb(0 0 0 / 8%); position: relative; isolation: isolate; } .chat-composer-glass-shell::before { pointer-events: none; position: absolute; z-index: 0; inset: 0; border-radius: 22px; background: color-mix( in srgb, var(--chat-composer-glass-surface) var(--glass-opacity), transparent ); -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation)); backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation)); content: ""; } .chat-composer-glass-shell-with-context { --chat-composer-context-extension: 2.25rem; } .chat-composer-glass-shell-with-context::before { border-radius: 0; /* * One continuous glass layer: a 22px composer joined to a 16px strip, * whose visible sides align with the composer's bottom tangents. */ clip-path: shape( from 0 22px, curve to 22px 0 with 0 9.85px / 9.85px 0, line to calc(100% - 22px) 0, curve to 100% 22px with calc(100% - 9.85px) 0 / 100% 9.85px, line to 100% calc(100% - var(--chat-composer-context-extension) - 22px), curve to calc(100% - 22px) calc(100% - var(--chat-composer-context-extension)) with 100% calc(100% - var(--chat-composer-context-extension) - 9.85px) / calc(100% - 9.85px) calc(100% - var(--chat-composer-context-extension)), line to calc(100% - 22px) calc(100% - 16px), curve to calc(100% - 38px) 100% with calc(100% - 22px) calc(100% - 7.16px) / calc(100% - 29.16px) 100%, line to 38px 100%, curve to 22px calc(100% - 16px) with 29.16px 100% / 22px calc(100% - 7.16px), line to 22px calc(100% - var(--chat-composer-context-extension)), curve to 0 calc(100% - var(--chat-composer-context-extension) - 22px) with 9.85px calc(100% - var(--chat-composer-context-extension)) / 0 calc(100% - var(--chat-composer-context-extension) - 9.85px), line to 0 22px, close ); } @media (min-width: 40rem) { .chat-composer-glass-shell-with-context { /* The xs toolbar controls shrink by 4px at Tailwind's sm breakpoint. */ --chat-composer-context-extension: 2rem; } } .chat-composer-glass-host { position: relative; box-shadow: 0 12px 28px -18px rgb(0 0 0 / 40%); } .chat-composer-glass-host::after { pointer-events: none; position: absolute; z-index: 1; inset: 0; border: 1px solid var(--chat-composer-outline); border-radius: inherit; content: ""; } .chat-composer-glass-shell-with-context .chat-composer-glass-host::after { /* Keep the attachment seam open; the strip continues the outer outline below. */ clip-path: polygon( 0 0, 100% 0, 100% 100%, calc(100% - 22px) 100%, calc(100% - 22px) calc(100% - 2px), 22px calc(100% - 2px), 22px 100%, 0 100% ); } .chat-composer-context-strip { position: relative; isolation: isolate; } .chat-composer-context-strip::before { position: absolute; z-index: -1; inset: 0; border: 1px solid var(--chat-composer-outline); border-radius: 0 0 16px 16px; /* Start the strip outline exactly where the composer's 22px curve reaches its tangent. */ -webkit-mask-image: linear-gradient(to bottom, transparent 0 1rem, black 1rem); mask-image: linear-gradient(to bottom, transparent 0 1rem, black 1rem); box-shadow: 0 12px 28px -18px rgb(0 0 0 / 40%); content: ""; } .dark .chat-composer-glass-shell { --chat-composer-glass-surface: color-mix(in srgb, var(--background) 96%, var(--color-white)); --chat-composer-outline: color-mix(in srgb, var(--color-white) 5%, transparent); --chat-composer-highlight: rgb(255 255 255 / 3%); } .dark .chat-composer-glass-host { box-shadow: none; } .dark .chat-composer-glass-host::after { box-shadow: inset 0 1px var(--chat-composer-highlight); } .dark .chat-composer-context-strip::before { border-color: rgb(255 255 255 / 7%); background: linear-gradient( to bottom, transparent 0 1rem, rgb(0 0 0 / 18%) 1rem, transparent calc(1rem + 10px) ), rgb(255 255 255 / 2%); box-shadow: 0 14px 32px -18px rgb(0 0 0 / 75%); } @supports not (clip-path: shape(from 0 0, line to 1px 1px)) { .chat-composer-glass-shell-with-context::before { inset-block-end: var(--chat-composer-context-extension); border-radius: 22px; clip-path: none; } .chat-composer-context-strip::before { background: color-mix( in srgb, var(--chat-composer-glass-surface) var(--glass-opacity), transparent ); -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation)); backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation)); } .dark .chat-composer-context-strip::before { background: linear-gradient( to bottom, transparent 0 1rem, rgb(0 0 0 / 18%) 1rem, transparent calc(1rem + 10px) ), linear-gradient(rgb(255 255 255 / 2%), rgb(255 255 255 / 2%)), color-mix(in srgb, var(--chat-composer-glass-surface) var(--glass-opacity), transparent); } } .alert-glass { --alert-glass-tint: transparent; background: linear-gradient( color-mix(in srgb, var(--alert-glass-tint) 4%, transparent), color-mix(in srgb, var(--alert-glass-tint) 4%, transparent) ), color-mix(in srgb, var(--background) var(--glass-opacity), transparent) !important; -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation)); backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation)); } .alert-glass[data-variant="error"] { --alert-glass-tint: var(--destructive); } .alert-glass[data-variant="info"] { --alert-glass-tint: var(--info); } .alert-glass[data-variant="success"] { --alert-glass-tint: var(--success); } .alert-glass[data-variant="warning"] { --alert-glass-tint: var(--warning); } .dialog-glass { background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent); -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation)); backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation)); } .dialog-backdrop { background: color-mix(in srgb, var(--background) 60%, transparent); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); } .dropdown-glass { /* * Elevated glass needs a denser tint than broad ambient surfaces. Nesting * the user-controlled mix inside an 18% popover tint preserves the full * opacity setting range (40% -> 51%, 80% -> 84%, 100% -> 100%) while * keeping high-contrast page content from blooming through menus. */ background: color-mix( in srgb, var(--popover) 18%, color-mix(in srgb, var(--popover) var(--glass-opacity), transparent) ); -webkit-backdrop-filter: blur(var(--glass-blur)); backdrop-filter: blur(var(--glass-blur)); border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); box-shadow: 0 16px 40px -18px rgb(0 0 0 / 55%); } .dialog-glass { border-color: color-mix(in srgb, var(--foreground) 10%, transparent); box-shadow: 0 24px 64px -24px rgb(0 0 0 / 65%); } .dark .dropdown-glass { box-shadow: 0 18px 44px -18px rgb(0 0 0 / 80%); } .dark .model-picker-surface.model-picker-surface { background: color-mix( in srgb, var(--popover) 18%, color-mix(in srgb, var(--popover) var(--glass-opacity), transparent) ); } .dark .dialog-glass { border-color: color-mix(in srgb, var(--color-white) 8%, transparent); box-shadow: inset 0 1px rgb(255 255 255 / 4%), 0 24px 72px -20px rgb(0 0 0 / 90%); } .dark .dialog-backdrop { background: color-mix(in srgb, var(--background) 64%, transparent); } .settings-slider { --settings-slider-progress: 0%; --settings-slider-fill-offset: 0.5rem; --settings-slider-fill-position: calc( var(--settings-slider-progress) + var(--settings-slider-fill-offset) ); appearance: none; height: 1.5rem; background: transparent; cursor: pointer; } .settings-slider::-webkit-slider-runnable-track { height: 0.375rem; border-radius: 9999px; background: linear-gradient( to right, var(--primary) 0 var(--settings-slider-fill-position), color-mix(in srgb, var(--muted-foreground) 22%, transparent) var(--settings-slider-fill-position) 100% ); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border) 55%, transparent); } .settings-slider::-moz-range-track { height: 0.375rem; border-radius: 9999px; background: color-mix(in srgb, var(--muted-foreground) 22%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border) 55%, transparent); } .settings-slider::-moz-range-progress { height: 0.375rem; border-radius: 9999px; background: var(--primary); } .settings-slider::-webkit-slider-thumb { appearance: none; width: 1rem; height: 1rem; margin-top: -0.3125rem; border: 2px solid var(--primary); border-radius: 9999px; background: var(--background); box-shadow: 0 1px 2px color-mix(in srgb, var(--foreground) 12%, transparent); transition: transform 120ms ease, box-shadow 120ms ease; } .settings-slider::-moz-range-thumb { width: 1rem; height: 1rem; border: 2px solid var(--primary); border-radius: 9999px; background: var(--background); box-shadow: 0 1px 2px color-mix(in srgb, var(--foreground) 12%, transparent); transition: transform 120ms ease, box-shadow 120ms ease; } .settings-slider:hover::-webkit-slider-thumb { transform: scale(1.08); box-shadow: 0 1px 3px color-mix(in srgb, var(--foreground) 20%, transparent); } .settings-slider:hover::-moz-range-thumb { transform: scale(1.08); box-shadow: 0 1px 3px color-mix(in srgb, var(--foreground) 20%, transparent); } .settings-slider:active::-webkit-slider-thumb { transform: scale(0.94); } .settings-slider:active::-moz-range-thumb { transform: scale(0.94); } .settings-slider:focus-visible { outline: none; } .settings-slider:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--background), 0 0 0 5px var(--ring); } .settings-slider:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 3px var(--background), 0 0 0 5px var(--ring); } @media (forced-colors: active) { .settings-slider { appearance: auto; accent-color: Highlight; } .settings-slider::-webkit-slider-runnable-track, .settings-slider::-webkit-slider-thumb { all: revert; } .settings-slider::-moz-range-track, .settings-slider::-moz-range-progress, .settings-slider::-moz-range-thumb { all: revert; } } @media (min-width: 40rem) { .chat-timeline-scroll-fade, .settings-page-scroll-fade { --topbar-scroll-fade-height: 3rem; } .chat-composer-horizontal-inset { padding-inline-start: calc(env(safe-area-inset-left) + 1.25rem); padding-inline-end: calc(env(safe-area-inset-right) + 1.25rem); } } @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) { .chat-composer-glass, .alert-glass { background: var(--background) !important; } .chat-composer-glass-shell::before { background: var(--chat-composer-glass-surface); } .dialog-glass, .dropdown-glass { background: var(--popover) !important; } } } /* Safe-area inset utilities for surfaces that opt into edge-to-edge rendering. Insets resolve to 0 when the browser already constrains the layout viewport to the safe area. */ @utility pt-safe { padding-top: max(env(safe-area-inset-top), 0px); } @utility pb-safe { padding-bottom: max(env(safe-area-inset-bottom), 0px); } @utility pl-safe { padding-left: max(env(safe-area-inset-left), 0px); } @utility pr-safe { padding-right: max(env(safe-area-inset-right), 0px); } /* Grain texture for chrome surfaces that float above the body (see the --surface-grain note). Layered over the element's background-color, so it composes with bg-* utilities. */ @utility surface-grain { background-image: var(--surface-grain); background-repeat: repeat; background-size: var(--surface-grain-size); } /* Suppress all transitions during theme changes */ .no-transitions, .no-transitions *, .no-transitions *::before, .no-transitions *::after { transition-duration: 0s !important; animation-duration: 0s !important; } :root { color-scheme: light; --radius: 0.625rem; --background: var(--color-zinc-25); --app-chrome-background: var(--background); --toolbar-background: var(--app-chrome-background); --toolbar-foreground: var(--foreground); --toolbar-border: var(--border); --toolbar-control: var(--popover); --toolbar-control-foreground: var(--foreground); --toolbar-control-hover: var(--accent); --surface-raised: color-mix(in srgb, var(--card) 20%, transparent); --foreground: var(--color-zinc-800); --card: var(--color-white); --card-foreground: var(--color-zinc-800); --popover: var(--color-white); --popover-foreground: var(--color-zinc-800); --primary: oklch(0.488 0.217 264); --primary-foreground: var(--color-white); --secondary: var(--color-zinc-50); --secondary-foreground: var(--color-zinc-800); --muted: var(--color-zinc-50); --muted-foreground: var(--color-zinc-500); --placeholder: var(--muted-foreground); --secondary-label: var(--muted-foreground); --icon-muted: var(--muted-foreground); --message-surface: var(--accent); --message-foreground: var(--foreground); --message-action: var(--primary); --message-action-foreground: var(--primary-foreground); --message-action-hover: color-mix(in srgb, var(--primary) 90%, var(--background)); --accent: var(--color-zinc-100); --accent-foreground: var(--color-zinc-900); --error: var(--color-red-500); --error-foreground: var(--color-red-700); --error-surface: color-mix(in srgb, var(--error) 8%, transparent); --destructive: var(--error); --border: var(--color-zinc-200); --input: var(--color-zinc-300); --ring: oklch(0.488 0.217 264); --destructive-foreground: var(--error-foreground); --info: var(--color-blue-500); --info-foreground: var(--color-blue-700); --success: var(--color-emerald-500); --success-foreground: var(--color-emerald-700); --warning: var(--color-amber-500); --warning-foreground: var(--color-amber-700); --warning-surface: color-mix(in srgb, var(--warning) 8%, transparent); --update: var(--primary); --update-foreground: var(--primary); --update-surface: color-mix(in srgb, var(--update) 12%, transparent); /* Keep every sidebar primitive on the same light surface hierarchy, including portaled mobile sheets and settings navigation outside the app sidebar. */ --sidebar: var(--color-zinc-50); --sidebar-foreground: var(--foreground); --sidebar-muted-foreground: var(--muted-foreground); --sidebar-control-surface: var(--color-zinc-100); --sidebar-row-hover: var(--color-zinc-25); --sidebar-row-active: var(--color-white); --sidebar-row-selected: var(--color-white); --sidebar-border: var(--border); --sidebar-stage-fade: var(--sidebar); --code-background: color-mix(in srgb, var(--card) 90%, var(--background)); --code-foreground: var(--foreground); --terminal-background: var(--background); --terminal-foreground: var(--foreground); --terminal-cursor: rgb(38 56 78); --terminal-selection-background: rgb(37 63 99 / 20%); --terminal-scrollbar: rgb(0 0 0 / 15%); --terminal-scrollbar-hover: rgb(0 0 0 / 25%); @variant dark { color-scheme: dark; /* Keep the workspace in the same neutral-black family as sidebar v2. Surfaces lift from this base instead of starting from a milky gray. */ --background: var(--color-neutral-950); --app-chrome-background: var(--background); --surface-raised: var(--secondary); --foreground: var(--color-neutral-100); --card: color-mix(in srgb, var(--background) 97%, var(--color-white)); --card-foreground: var(--color-neutral-100); --popover: color-mix(in srgb, var(--background) 94%, var(--color-white)); --popover-foreground: var(--color-neutral-100); --primary: oklch(0.588 0.217 264); --primary-foreground: var(--color-white); --secondary: --alpha(var(--color-white) / 4%); --secondary-foreground: var(--color-neutral-100); --muted: --alpha(var(--color-white) / 4%); --muted-foreground: color-mix(in srgb, var(--color-neutral-500) 90%, var(--color-white)); --placeholder: var(--muted-foreground); --secondary-label: var(--muted-foreground); --icon-muted: var(--muted-foreground); --message-surface: var(--accent); --message-foreground: var(--foreground); --message-action: var(--primary); --message-action-foreground: var(--primary-foreground); --message-action-hover: color-mix(in srgb, var(--primary) 90%, var(--background)); --accent: --alpha(var(--color-white) / 4%); --accent-foreground: var(--color-neutral-100); --error: color-mix(in srgb, var(--color-red-500) 90%, var(--color-white)); --error-foreground: var(--color-red-400); --error-surface: color-mix(in srgb, var(--error) 16%, transparent); --destructive: var(--error); --border: --alpha(var(--color-white) / 6%); --input: --alpha(var(--color-white) / 8%); --ring: oklch(0.588 0.217 264); --destructive-foreground: var(--error-foreground); --info: var(--color-blue-500); --info-foreground: var(--color-blue-400); --success: var(--color-emerald-500); --success-foreground: var(--color-emerald-400); --warning: var(--color-amber-500); --warning-foreground: var(--color-amber-400); --warning-surface: color-mix(in srgb, var(--warning) 16%, transparent); --update: var(--primary); --update-foreground: var(--primary); --update-surface: color-mix(in srgb, var(--update) 18%, transparent); --sidebar: var(--card); --sidebar-foreground: var(--foreground); --sidebar-muted-foreground: var(--muted-foreground); --sidebar-control-surface: var(--muted); --sidebar-row-hover: var(--accent); --sidebar-row-active: var(--accent); --sidebar-row-selected: var(--muted); --sidebar-border: var(--border); --sidebar-stage-fade: var(--card); --terminal-background: var(--background); --terminal-foreground: var(--foreground); --terminal-cursor: rgb(180 203 255); --terminal-selection-background: rgb(180 203 255 / 25%); --terminal-scrollbar: rgb(255 255 255 / 10%); --terminal-scrollbar-hover: rgb(255 255 255 / 18%); } } /* Keep both navigation implementations on the same quiet zinc hierarchy: zinc-50 navigation, zinc-25 hover, and white selected/raised surfaces. The version attribute remains useful for layout-specific styling without changing the color system when the beta is toggled. */ [data-sidebar-version="v1"], [data-sidebar-version="v2"] { --background: var(--color-zinc-25); --foreground: var(--color-zinc-800); --card: var(--color-white); --card-foreground: var(--color-zinc-800); --accent: var(--color-zinc-100); --accent-foreground: var(--color-zinc-900); --muted: var(--color-zinc-50); --muted-foreground: var(--color-zinc-500); --border: var(--color-zinc-200); --input: var(--color-zinc-300); --sidebar: var(--color-zinc-50); --sidebar-foreground: var(--color-zinc-800); --sidebar-muted-foreground: var(--color-zinc-500); --sidebar-control-surface: var(--color-zinc-100); --sidebar-row-hover: var(--color-zinc-25); --sidebar-row-active: var(--color-white); --sidebar-row-selected: var(--color-white); --sidebar-border: var(--color-zinc-200); --sidebar-stage-fade: var(--sidebar); background-color: var(--sidebar); } .dark [data-sidebar-version="v1"], .dark [data-sidebar-version="v2"] { --background: #000; --foreground: #f1f3f7; --card: #000; --card-foreground: var(--foreground); --accent: #191a1d; --accent-foreground: #f7f9ff; --muted: #0a0a0a; --muted-foreground: #a3a3a3; --border: rgb(255 255 255 / 8%); --input: rgb(255 255 255 / 18%); --sidebar: var(--card); --sidebar-foreground: var(--foreground); --sidebar-muted-foreground: var(--muted-foreground); --sidebar-control-surface: var(--muted); --sidebar-row-hover: color-mix(in srgb, var(--foreground) 8%, transparent); --sidebar-row-active: color-mix(in srgb, var(--foreground) 11%, transparent); --sidebar-row-selected: color-mix(in srgb, var(--foreground) 7%, transparent); --sidebar-border: var(--border); /* The stage-channel header art must ramp to THIS panel's surface, not the global chrome background, or the fade shows a seam (same rule as the light palette above). */ --sidebar-stage-fade: var(--card); } /* Theme files are expressed in app color roles and mapped to the existing semantic surface tokens here. Keep this block after the sidebar-version compatibility overrides so both navigation implementations receive the same palette. Success, info, provider, and channel identity colors remain independent; error, warning, and update roles are themeable below. */ html[data-theme-id], html.dark[data-theme-id] { --background: var(--app-theme-canvas); --app-chrome-background: var(--app-theme-chrome); --toolbar-background: var(--app-theme-toolbar); --toolbar-foreground: var(--app-theme-toolbar-foreground); --toolbar-border: var(--app-theme-toolbar-border); --toolbar-control: var(--app-theme-toolbar-control); --toolbar-control-foreground: var(--app-theme-toolbar-control-foreground); --toolbar-control-hover: var(--app-theme-toolbar-control-hover); --surface-raised: var(--app-theme-surface-raised); --foreground: var(--app-theme-text); --card: var(--app-theme-surface); --card-foreground: var(--app-theme-text); --popover: var(--app-theme-surface-overlay); --popover-foreground: var(--app-theme-text); /* Solid controls (CTAs, switches, sliders, the send button) all share the theme's action color so every "act now" affordance reads consistently; the accent keeps driving surfaces, selection, and focus. Palettes without a distinct action color make these identical. */ --primary: var(--app-theme-message-action); --primary-foreground: var(--app-theme-message-action-foreground); --secondary: var(--app-theme-secondary); --secondary-foreground: var(--app-theme-secondary-foreground); --muted: var(--app-theme-muted); --muted-foreground: var(--app-theme-text-muted); --placeholder: var(--app-theme-placeholder); --secondary-label: var(--app-theme-secondary-label); --icon-muted: var(--app-theme-icon-muted); --accent: var(--app-theme-accent-surface); --accent-foreground: var(--app-theme-accent-surface-foreground); --message-surface: var(--app-theme-message-surface); --message-foreground: var(--app-theme-message-foreground); --message-action: var(--app-theme-message-action); --message-action-foreground: var(--app-theme-message-action-foreground); --message-action-hover: var(--app-theme-message-action-hover); --error: var(--app-theme-error); --error-foreground: var(--app-theme-error-foreground); --error-surface: var(--app-theme-error-surface); --destructive: var(--error); --destructive-foreground: var(--error-foreground); --warning: var(--app-theme-warning); --warning-foreground: var(--app-theme-warning-foreground); --warning-surface: var(--app-theme-warning-surface); --update: var(--app-theme-update); --update-foreground: var(--app-theme-update-foreground); --update-surface: var(--app-theme-update-surface); --border: var(--app-theme-border); --input: var(--app-theme-input); --ring: var(--app-theme-focus); --sidebar: var(--app-theme-sidebar); --sidebar-foreground: var(--app-theme-sidebar-foreground); --sidebar-muted-foreground: var(--app-theme-sidebar-muted-foreground); --sidebar-control-surface: var(--app-theme-sidebar-control-surface); --sidebar-row-hover: var(--app-theme-sidebar-row-hover); --sidebar-row-active: var(--app-theme-sidebar-row-active); --sidebar-row-selected: var(--app-theme-sidebar-row-selected); --sidebar-border: var(--app-theme-sidebar-border); --sidebar-stage-fade: var(--app-theme-sidebar); --code-background: var(--app-theme-code-background); --code-foreground: var(--app-theme-code-foreground); --terminal-background: var(--app-theme-terminal-background); --terminal-foreground: var(--app-theme-terminal-foreground); --terminal-cursor: var(--app-theme-terminal-cursor); --terminal-selection-background: var(--app-theme-terminal-selection-background); --terminal-scrollbar: var(--app-theme-terminal-scrollbar); --terminal-scrollbar-hover: var(--app-theme-terminal-scrollbar-hover); } /* T3 Chat's composer is a translucent lift over --chat-background. Route its measured flattened color through the raised-surface role instead of deriving another tint from the canvas, which made the dark composer too red. */ html[data-theme-id] .chat-composer-glass-shell { --chat-composer-glass-surface: var(--app-theme-surface-raised); } html[data-theme-id]:not(.dark) .chat-composer-glass-shell { --chat-composer-outline: var(--app-theme-toolbar-border); } html.dark[data-theme-id] .chat-composer-glass-shell { --chat-composer-outline: color-mix(in srgb, var(--app-theme-input) 30%, var(--background)); --chat-composer-highlight: color-mix(in srgb, var(--app-theme-input) 12%, transparent); } html.dark[data-theme-id="t3-chat"] .chat-composer-glass-shell { /* T3 Chat's visible composer edge is a dark plum, not the stock translucent white outline. Its highlight is derived from --chat-input-gradient. */ --chat-composer-outline: #241e28; --chat-composer-highlight: color-mix(in srgb, #432d48 12%, transparent); } html[data-theme-id]:not(.dark) { color-scheme: light; } html.dark[data-theme-id] { color-scheme: dark; } html[data-theme-id] body { background-color: var(--app-chrome-background); color: var(--foreground); } /* Theme-token dependency probes are restored synchronously, before paint. Keep transitions from observing the temporary sentinel color in between. */ html[data-theme-token-probe], html[data-theme-token-probe] *, html[data-theme-token-probe] *::before, html[data-theme-token-probe] *::after { transition: none !important; } #theme-inspector-spotlight { position: fixed; z-index: 100; inset: 0; width: 100vw; height: 100dvh; overflow: visible; pointer-events: none; } [data-slot="popover-positioner"]:has([data-theme-editor-panel]), [data-slot="tooltip-positioner"]:has([data-theme-editor-panel]) { z-index: 120; } .theme-inspector-spotlight-dimmer { fill: rgb(2 3 8 / 54%); } .theme-inspector-spotlight-glow { fill: color-mix(in oklab, var(--ring) 9%, transparent); stroke: color-mix(in oklab, var(--ring) 72%, white); stroke-width: 2px; opacity: 0.92; vector-effect: non-scaling-stroke; } #theme-inspector-hover { position: fixed; z-index: 101; pointer-events: none; background: color-mix(in oklab, var(--ring) 7%, transparent); border: 1px solid color-mix(in oklab, var(--ring) 70%, white); box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring) 16%, transparent), 0 0 18px color-mix(in oklab, var(--ring) 38%, transparent); } #theme-inspector-hover [data-theme-inspector-hover-label] { position: absolute; left: 0; max-width: 12rem; overflow: hidden; padding: 0.2rem 0.45rem; border: 1px solid color-mix(in oklab, var(--ring) 48%, var(--border)); border-radius: 0.4rem; background: var(--popover); box-shadow: 0 4px 16px rgb(0 0 0 / 24%); color: var(--popover-foreground); font-size: 0.6875rem; font-weight: 600; line-height: 1rem; text-overflow: ellipsis; white-space: nowrap; } #theme-inspector-hover[data-placement="above"] [data-theme-inspector-hover-label] { bottom: calc(100% + 0.35rem); } #theme-inspector-hover[data-placement="below"] [data-theme-inspector-hover-label] { top: calc(100% + 0.35rem); } /* Workspace actions use a small, separate chrome hierarchy. Keeping these roles explicit prevents the thread toolbar from falling back to the default dark-mode whites while preserving the same theme file across clients. */ html[data-theme-id] [data-chat-header] { background-color: var(--toolbar-background); color: var(--toolbar-foreground); } html[data-theme-id] [data-chat-header] [data-slot="button"], html[data-theme-id] [data-chat-header] [data-slot="menu-trigger"], html[data-theme-id] [data-chat-header] [data-toolbar-control] { --control-icon-color: var(--toolbar-control-foreground); background-color: var(--toolbar-control); border-color: var(--toolbar-border); color: var(--toolbar-control-foreground); } /* The panel layout toggles stay ghost: they render both inside the header and in the titlebar strip, so filling them would make them change appearance as the panel opens. They only take the themed foreground; hover and pressed keep the base ghost accent. The tooltip trigger's data-slot wins over the toggle's when the trigger renders the toggle, so match both. */ html[data-theme-id] [data-panel-layout-controls] [data-slot="toggle"], html[data-theme-id] [data-panel-layout-controls] [data-slot="tooltip-trigger"], html[data-theme-id] .workspace-titlebar-controls [data-slot="toggle"], html[data-theme-id] .workspace-titlebar-controls [data-slot="tooltip-trigger"] { --control-icon-color: var(--toolbar-foreground); color: var(--toolbar-foreground); } html[data-theme-id] [data-chat-header] [data-slot="button"]:hover, html[data-theme-id] [data-chat-header] [data-slot="button"][data-pressed], html[data-theme-id] [data-chat-header] [data-slot="menu-trigger"]:hover, html[data-theme-id] [data-chat-header] [data-slot="menu-trigger"][data-pressed], html[data-theme-id] [data-chat-header] [data-toolbar-control]:hover, html[data-theme-id] [data-chat-header] [data-toolbar-control][data-pressed] { background-color: var(--toolbar-control-hover); } html[data-theme-id] [data-chat-header] [data-slot="separator"] { background-color: var(--toolbar-border); } /* Chat code blocks join the other code surfaces on the code tokens. The default look keeps its secondary/input tints; themed palettes tint those surfaces with the accent, which fights syntax highlighting. */ html[data-theme-id] .chat-markdown .chat-markdown-codeblock { background-color: var(--code-background); border-color: var(--border); color: var(--code-foreground); } html[data-theme-id] .chat-markdown pre { background-color: var(--code-background); color: var(--code-foreground); } html[data-theme-id] .chat-markdown .chat-markdown-codeblock pre { background-color: transparent; } html[data-theme-id] .chat-markdown .chat-markdown-codeblock-header, html[data-theme-id] .chat-markdown .chat-markdown-chrome-action { color: color-mix(in srgb, var(--code-foreground) 72%, transparent); } /* T3 Chat renders inline code and compact chat artifacts with its translucent secondary surface flattened over the light chat canvas. The raw muted and secondary tokens are substantially darker than those visible pixels. */ html[data-theme-id="t3-chat"]:not(.dark) .chat-markdown :not(pre) > code, html[data-theme-id="t3-chat"]:not(.dark) [data-changed-files-state], html[data-theme-id="t3-chat"]:not(.dark) [data-changed-files-header] { background-color: var(--message-surface); } html[data-theme-id="t3-chat"]:not(.dark) .chat-markdown :not(pre) > code, html[data-theme-id="t3-chat"]:not(.dark) [data-changed-files-state] { border-color: transparent; } html[data-theme-id="t3-chat"]:not(.dark) .chat-markdown :not(pre) > code { color: var(--message-foreground); } html[data-theme-id] .chat-markdown .chat-markdown-chrome-action:hover, html[data-theme-id] .chat-markdown .chat-markdown-chrome-action[aria-pressed="true"] { color: var(--code-foreground); } html[data-theme-id] [data-sidebar-version="v1"], html[data-theme-id] [data-sidebar-version="v2"] { --background: var(--app-theme-canvas); --foreground: var(--app-theme-text); --card: var(--app-theme-surface); --card-foreground: var(--app-theme-text); --accent: var(--app-theme-accent-surface); --accent-foreground: var(--app-theme-accent-surface-foreground); --muted: var(--app-theme-muted); --muted-foreground: var(--app-theme-text-muted); --border: var(--app-theme-border); --input: var(--app-theme-input); --sidebar: var(--app-theme-sidebar); --sidebar-foreground: var(--app-theme-sidebar-foreground); --sidebar-muted-foreground: var(--app-theme-sidebar-muted-foreground); --sidebar-control-surface: var(--app-theme-sidebar-control-surface); --sidebar-row-hover: var(--app-theme-sidebar-row-hover); --sidebar-row-active: var(--app-theme-sidebar-row-active); --sidebar-row-selected: var(--app-theme-sidebar-row-selected); --sidebar-border: var(--app-theme-sidebar-border); --sidebar-stage-fade: var(--app-theme-sidebar); background-color: var(--sidebar); } /* Keep the navigation edge as quiet as the standard palettes. Theme files may still use sidebarBorder for controls and internal separators, but the outer divider should not become more prominent just because a palette is vivid. */ html[data-theme-id] [data-app-sidebar] { border-color: color-mix(in srgb, var(--sidebar-foreground) 10%, transparent); } html.dark[data-theme-id] [data-app-sidebar] { border-color: color-mix(in srgb, var(--sidebar-foreground) 8%, transparent); } /* T3 Chat's panel divider is deliberately pink, and its resize affordance keeps that color while hovered. Do not neutralize this branded edge. */ html.dark[data-theme-id="t3-chat"] [data-app-sidebar] { border-color: var(--sidebar-border); } .theme-json-key { color: var(--app-theme-accent, var(--color-blue-600)); } .theme-json-string { color: var(--app-theme-message-action, var(--color-emerald-600)); } .theme-json-number { color: var(--app-theme-secondary-foreground, var(--color-amber-600)); } .theme-json-constant { color: var(--app-theme-accent-surface-foreground, var(--color-violet-600)); } body { /* Reference the theme token (not a literal stack) so the Settings -> Appearance runtime override of --font-sans reaches all interface text. */ font-family: var(--font-sans); margin: 0; padding: 0; } html, body { min-height: calc(100svh + env(safe-area-inset-top)); overscroll-behavior: none; } body { height: 100%; overflow: hidden; } #root { height: 100%; width: 100%; max-width: 100%; overflow-x: hidden; overflow-x: clip; overflow-y: hidden; overscroll-behavior-y: none; padding-top: max(env(safe-area-inset-top), 0px); padding-right: var(--desktop-window-right-resize-inset); } .electron-windows { --desktop-window-right-resize-inset: 6px; } /* App-chrome grain. Baked into each surface's own background (behind content) rather than a fixed overlay on top: a full-viewport overlay forces the compositor to re-blend every frame any animation produces, which multiplied idle GPU cost. The overlay's 0.035 opacity lives in the SVG rect instead. Surfaces that float over the body (e.g. the inset main card) must opt in via the surface-grain utility, or they lose the grain's subtle brightening and stand out against the body. */ :root { --surface-grain: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E"); --surface-grain-size: 256px 256px; } /* The live T3 Chat texture is a 128px grayscale tile with a constant 7/255 alpha. Keep our procedural texture cheap while matching that scale and strength for the branded preset. */ html[data-theme-id="t3-chat"] { --surface-grain: url("data:image/svg+xml,%3Csvg viewBox='0 0 128 128' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.02745'/%3E%3C/svg%3E"); --surface-grain-size: 128px 128px; } body { background-image: var(--surface-grain); background-repeat: repeat; background-size: var(--surface-grain-size); } pre, code { font-family: var(--font-mono); } /* Code blocks in chat carry the size preference. Scoped to chat markdown rather than every pre: a global rule would beat text-size utilities and inherited sizes on unrelated pre surfaces (terminal previews, approvals). Inline code stays relative to its sentence so it never towers over prose; diffs and file previews take the size through --diffs-font-size. */ .chat-markdown .chat-markdown-shiki .shiki, .chat-markdown pre code { font-size: var(--font-size-code, inherit); } /* @pierre/diffs surfaces (diffs, file previews, annotatable code, search lines) render inside shadow roots but consult these hooks with their own literal stacks as fallback. Custom properties inherit across the shadow boundary, so defining them once here routes every code surface through the appearance font tokens. */ :root { --diffs-font-family: var(--font-mono); --diffs-header-font-family: var(--font-sans); } /* Window drag region (frameless titlebar) */ .drag-region { -webkit-app-region: drag; } .drag-region button, .drag-region input, .drag-region textarea, .drag-region select, .drag-region a { -webkit-app-region: no-drag; } /* Scrollbar styling */ ::-webkit-scrollbar { width: var(--app-scrollbar-width); } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--app-scrollbar-thumb); border-radius: 3px; } ::-webkit-scrollbar-thumb:hover { background: var(--app-scrollbar-thumb-hover); } /* Settings -> Appearance can point the composer at its own face (for example a mono font); default follows the sans stack. Applied on the surface wrapper so the editor and its placeholder inherit together. */ .composer-editor-surface { font-family: var(--font-composer, var(--font-sans)); font-size: var(--font-size-prompt, 0.875rem); } /* Touch browsers zoom the page when a focused field is under 16px, so keep the floor there regardless of the preference. Gated on a coarse pointer: the zoom quirk does not exist on desktop, where a narrow window must not silently override a smaller chosen prompt size. */ @media (max-width: 39.999rem) and (pointer: coarse) { .composer-editor-surface { font-size: max(var(--font-size-prompt, 1rem), 16px); } } .t3-ghostty-canvas { cursor: text; } .t3-ghostty-scrollbar { position: absolute; z-index: 1; top: 4px; right: 1px; bottom: 4px; width: var(--app-scrollbar-width); cursor: default; touch-action: none; } .t3-ghostty-scrollbar-thumb { position: absolute; top: 0; right: 1px; left: 1px; border-radius: 3px; background: var(--app-scrollbar-thumb); transition: background-color 120ms ease-out; } .t3-ghostty-scrollbar:hover .t3-ghostty-scrollbar-thumb, .t3-ghostty-scrollbar:focus-visible .t3-ghostty-scrollbar-thumb { background: var(--app-scrollbar-thumb-hover); } .model-picker-list::-webkit-scrollbar-track { margin-block: 0.5rem; } .model-picker-list-scroll-fade-top, .model-picker-list-scroll-fade-bottom { -webkit-mask-image: var(--model-picker-list-scroll-mask), linear-gradient(black, black); -webkit-mask-position: left, right; -webkit-mask-repeat: no-repeat; -webkit-mask-size: calc(100% - var(--app-scrollbar-width)) 100%, var(--app-scrollbar-width) 100%; mask-image: var(--model-picker-list-scroll-mask), linear-gradient(black, black); mask-position: left, right; mask-repeat: no-repeat; mask-size: calc(100% - var(--app-scrollbar-width)) 100%, var(--app-scrollbar-width) 100%; } .model-picker-list-scroll-fade-top { --model-picker-list-scroll-mask: linear-gradient(to bottom, transparent, black var(--fade-size)); } .model-picker-list-scroll-fade-bottom { --model-picker-list-scroll-mask: linear-gradient( to bottom, black calc(100% - var(--fade-size)), transparent ); } .model-picker-list-scroll-fade-top.model-picker-list-scroll-fade-bottom { --model-picker-list-scroll-mask: linear-gradient( to bottom, transparent, black var(--fade-size), black calc(100% - var(--fade-size)), transparent ); } .turn-chip-strip { scrollbar-width: none; -ms-overflow-style: none; overscroll-behavior-x: contain; } .turn-chip-strip::-webkit-scrollbar { display: none; } /* Reasoning select -- clickable label surface */ label:has(> select#reasoning-effort) { position: relative; } label:has(> select#reasoning-effort) select { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; } /* Chat markdown rendering */ .chat-markdown { min-width: 0; overflow-wrap: anywhere; word-break: break-word; } .chat-markdown > :first-child { margin-top: 0; } .chat-markdown > :last-child { margin-bottom: 0; } .chat-markdown p, .chat-markdown ul, .chat-markdown ol, .chat-markdown blockquote, .chat-markdown pre, .chat-markdown .chat-markdown-table-container { margin: 0.65rem 0; } .chat-markdown h1, .chat-markdown h2, .chat-markdown h3, .chat-markdown h4, .chat-markdown h5, .chat-markdown h6 { margin: 1.25rem 0 0.5rem; font-weight: 600; line-height: 1.3; color: var(--foreground); } .chat-markdown h1 { font-size: 1.25rem; } .chat-markdown h2 { font-size: 1.125rem; } .chat-markdown h3 { font-size: 1rem; } .chat-markdown h4, .chat-markdown h5, .chat-markdown h6 { font-size: 0.875rem; } .chat-markdown h6 { color: var(--muted-foreground); } .chat-markdown ul { padding-left: 1.25rem; list-style-type: disc; } .chat-markdown ol { padding-left: 1.25rem; list-style-type: decimal; } .chat-markdown ul ul { list-style-type: circle; } .chat-markdown ul ul ul { list-style-type: square; } .chat-markdown ol ol { list-style-type: lower-alpha; } .chat-markdown ol ol ol { list-style-type: lower-roman; } .chat-markdown li + li { margin-top: 0.25rem; } /* GitHub-style task lists: the checkbox replaces the list marker. */ .chat-markdown li.task-list-item { list-style-type: none; } .chat-markdown li.task-list-item input[type="checkbox"] { margin: 0 0.35em 0.15em -1.25rem; vertical-align: middle; } .chat-markdown a { color: var(--info-foreground); text-decoration: none; } .chat-markdown a:not(.chat-markdown-file-link):hover, .chat-markdown a:not(.chat-markdown-file-link):focus-visible { background-image: radial-gradient(circle, currentcolor 0.75px, transparent 1px); background-position: left bottom; background-repeat: repeat-x; background-size: 4px 2px; } .chat-markdown .chat-markdown-link-favicon { @apply inline-flex; width: 14px; height: 14px; margin-inline: 0.25em 0.2em; vertical-align: -0.125em; } .chat-markdown .chat-markdown-link-leading { white-space: nowrap; } .chat-markdown blockquote { border-left: 2px solid var(--border); padding-left: 0.8rem; color: var(--muted-foreground); } .chat-markdown section[data-footnotes] { margin-top: 1.25rem; border-top: 1px solid var(--border); padding-top: 0.75rem; color: var(--muted-foreground); font-size: 0.75rem; } .chat-markdown section[data-footnotes] ol { margin: 0; padding-left: 1.25rem; } .chat-markdown section[data-footnotes] li + li { margin-top: 0.35rem; } .chat-markdown a[data-footnote-ref], .chat-markdown a[data-footnote-backref] { display: inline-flex; min-width: 1rem; align-items: center; justify-content: center; border-radius: 0.25rem; text-decoration: none; font-size: 0.6875rem; font-weight: 600; } .chat-markdown :not(pre) > code { border: 1px solid var(--border); border-radius: 0.375rem; background: var(--muted); padding: 0.1rem 0.35rem; color: var(--foreground); font-size: 0.75rem; } .chat-markdown a.chat-markdown-file-link { color: var(--foreground); text-decoration: none; } .chat-markdown a.chat-markdown-file-link:hover { color: var(--foreground); text-decoration: none; } .chat-markdown a.chat-markdown-file-link:focus-visible { outline: none; box-shadow: 0 0 0 2px color-mix(in srgb, var(--ring) 70%, transparent); } .chat-markdown pre { max-width: 100%; overflow-x: auto; border: 1px solid var(--border); border-radius: 0.75rem; background: var(--muted); padding: 0.8rem 0.9rem; } .chat-markdown pre code { border: none; background: transparent; padding: 0; font-size: 0.75rem; } .chat-markdown pre { scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--border) 78%, transparent) transparent; } .chat-markdown pre::-webkit-scrollbar { height: 7px; } .chat-markdown pre::-webkit-scrollbar-track { background: transparent; } .chat-markdown pre::-webkit-scrollbar-thumb { border-radius: 999px; background: color-mix(in srgb, var(--border) 78%, transparent); } .markdown-file-link-tooltip-scroll { scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--border) 78%, transparent) transparent; } .markdown-file-link-tooltip-scroll::-webkit-scrollbar { height: 6px; } .markdown-file-link-tooltip-scroll::-webkit-scrollbar-track { background: transparent; } .markdown-file-link-tooltip-scroll::-webkit-scrollbar-thumb { border-radius: 999px; background: color-mix(in srgb, var(--border) 78%, transparent); } .chat-markdown .chat-markdown-codeblock { margin: 0.65rem 0; overflow: hidden; border-radius: var(--radius); } .chat-markdown .chat-markdown-codeblock-header { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; padding: 0.375rem 0.375rem 0 0.75rem; color: color-mix(in srgb, var(--foreground) 72%, transparent); } .chat-markdown .chat-markdown-codeblock-title { display: inline-flex; min-width: 0; align-items: center; gap: 0.4rem; font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace); font-size: 0.6875rem; } .chat-markdown .chat-markdown-chrome-action { color: color-mix(in srgb, var(--foreground) 72%, transparent); } .chat-markdown .chat-markdown-chrome-action:hover { color: var(--foreground); } .chat-markdown .chat-markdown-chrome-action[aria-pressed="true"] { color: var(--foreground); background: color-mix(in srgb, var(--foreground) 8%, transparent); } .chat-markdown .chat-markdown-codeblock pre { margin: 0; border: none; border-radius: 0; background: transparent; } .chat-markdown .chat-markdown-codeblock[data-wrap="true"] pre { white-space: pre-wrap; overflow-wrap: anywhere; } .chat-markdown .chat-markdown-shiki .shiki { background: transparent !important; } /* Diagnostics-style tables: row separators only, uppercase headers, and a scroll-fade container for horizontal overflow. The root chat-markdown wrapping rules (overflow-wrap: anywhere) would let columns shrink to single characters and defeat the overflow — restore word-boundary wrapping so the min column width is the longest word. */ .chat-markdown table { width: 100%; min-width: max-content; border-collapse: collapse; overflow-wrap: normal; word-break: normal; font-size: 0.75rem; } .chat-markdown th, .chat-markdown td { padding: 0.45rem 0.75rem; text-align: left; } .chat-markdown thead th { border-bottom: 1px solid color-mix(in srgb, var(--border) 60%, transparent); padding-block: 0.55rem; font-weight: 600; white-space: nowrap; } .chat-markdown tbody td { border-bottom: 1px solid color-mix(in srgb, var(--border) 60%, transparent); } /* Collapsed (default): single-line cells truncate so arbitrary chat content cannot blow the column out; the footer toggle expands cells to wrap. */ .chat-markdown .chat-markdown-table-container[data-expanded="false"] th, .chat-markdown .chat-markdown-table-container[data-expanded="false"] td { overflow: hidden; max-width: 24rem; text-overflow: ellipsis; white-space: nowrap; } .chat-markdown .chat-markdown-table-container[data-expanded="true"] td { max-width: 24rem; overflow-wrap: anywhere; } .chat-markdown .chat-markdown-table-footer { display: flex; align-items: center; justify-content: space-between; margin-top: 0.125rem; } /* Prompt-stash save acknowledgement: the new count fades up from just below its resting position, once, then stops. One-shot and event-driven (React remounts the element by key on each stash) — no continuous animation. */ @keyframes prompt-stash-count-enter { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: translateY(0); } } .prompt-stash-count-enter { animation: prompt-stash-count-enter 180ms ease-out both; } @media (prefers-reduced-motion: reduce) { .prompt-stash-count-enter { animation: none; } } @keyframes provider-update-pill-countdown { from { transform: scaleX(1); } to { transform: scaleX(0); } } .provider-update-pill-progress { animation: provider-update-pill-countdown var(--provider-update-pill-dismiss-ms) linear forwards; } /* Diffs theme bridge (match diff surfaces to app palette) */ .diff-panel-viewport { background: var(--background); } /* Diffs live directly on the panel canvas. Normal chat code blocks may use a raised code surface, but carrying that fill into the diff creates a card-like rectangle that does not belong in the panel. */ .diff-render-surface { --code-background: var(--background); } .diff-render-file, .diff-render-surface diffs-container { border: 0; border-radius: 0; overflow: clip; background: var(--code-background); color: var(--code-foreground); } /* Pierre's file renderer owns its own surface tokens. Keep the root of a file preview on the same themeable code surface as the diff panel. */ .diff-render-surface diffs-container, .file-preview-virtualizer diffs-container { --diffs-bg: var(--code-background) !important; --diffs-light-bg: var(--code-background) !important; --diffs-dark-bg: var(--code-background) !important; background-color: var(--code-background) !important; color: var(--code-foreground) !important; } @keyframes ultrathink-rainbow { 0% { background-position: 0% 50%; } 100% { background-position: 200% 50%; } } @keyframes ultrathink-chroma-shift { 0% { filter: hue-rotate(0deg) saturate(1.2); } 100% { filter: hue-rotate(360deg) saturate(1.2); } } .ultrathink-frame { --ultrathink-spectrum: linear-gradient( 120deg, #ff6b6b 0%, #f59e0b 18%, #22c55e 36%, #14b8a6 54%, #3b82f6 72%, #ec4899 90%, #ff6b6b 100% ); position: relative; background: transparent; } .ultrathink-frame::before { content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 2px; background-image: var(--ultrathink-spectrum); background-size: 220% 220%; pointer-events: none; filter: saturate(0.82) brightness(0.92); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude; animation: ultrathink-rainbow 10s linear infinite; } .ultrathink-frame > * { position: relative; } .ultrathink-chroma { animation: ultrathink-chroma-shift 10s linear infinite; } .ultrathink-pill { background: linear-gradient(var(--card), var(--card)) padding-box, var(--ultrathink-spectrum) border-box; background-size: 100% 100%, 220% 220%; background-position: 0 0, 0% 50%; animation: ultrathink-rainbow 10s linear infinite; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--card) 82%, transparent); } .ultrathink-word { display: inline-block; color: transparent; background-image: var(--ultrathink-spectrum); background-size: 220% 220%; background-position: 0% 50%; background-clip: text; -webkit-background-clip: text; animation: ultrathink-rainbow 10s linear infinite; } /* Composer chips are non-editable decorators, so the browser skips them when painting text selection; this overlay stands in for the native highlight. */ .composer-inline-chip[data-composer-chip-selected]::after { content: ""; position: absolute; inset: 0; border-radius: 6px; background-color: Highlight; opacity: 0.3; pointer-events: none; }