@import 'tailwindcss'; @import 'tw-animate-css'; @import 'shadcn-svelte/tailwind.css'; @import './fonts.css'; @variant dark (&:where(.dark, .dark *)); /* The values below are the Mono color theme; applyColorTheme in color-theme-util.ts overrides the neutrals and the primary family at runtime */ :root { --h-xs: 26px; --h-sm: 34px; --h-md: 42px; --h-xl: 48px; --row-dense: 36px; --row: 44px; --row-2line: 52px; --col-auth: 464px; --col-content: 1120px; /* The identity column gives way on tablets so the other columns fit without scrolling sideways */ --table-id-max: clamp(240px, 36vw, 440px); --sidebar-w: 256px; --rail-w: 60px; --dialog-sm: 420px; --dialog-md: 480px; --dialog-lg: 560px; --sheet-w: 560px; --palette-w: 640px; --toast-w: 340px; --clear-end-phone: 148px; --z-sticky: 10; --z-nav: 20; --z-bar: 30; --z-overlay: 40; --z-popover: 50; --z-toast: 60; --z-tooltip: 70; --z-progress: 80; --halo-width: 3px; --selection-border: 1.5px; --dur-fast: 120ms; --dur-base: 200ms; --dur-slow: 280ms; --rise: 4px; --nudge: 2px; --slide: 24px; --press-scale: 0.98; --pop-scale: 0.96; --t-success-hold: 800ms; --t-redirect-fallback: 3000ms; --t-dim-delay: 200ms; --t-skeleton-delay: 150ms; --t-pulse: 1600ms; --t-spin: 800ms; --t-caret: 1000ms; --t-progress: 3000ms; --canvas: #ffffff; --surface: #ffffff; --surface-raised: #ffffff; --surface-sunken: #f5f5f5; --fill-muted: #f2f2f2; --fill-hover: #eeeeee; --fill-row-hover: #fafafa; --fill-selected: #ebebeb; --fill-skeleton: #eeeeee; --border: #e8e8e8; --border-strong: #8c8c8c; --border-control: #8c8c8c; --fg: #0a0a0a; --fg-muted: #5e5e5e; --fg-subtle: #a3a3a3; --inverse: #111111; --inverse-fg: #fafafa; --inverse-muted: #b5b5b5; --inverse-hover: #262626; --inverse-border: transparent; --success-solid: #319751; --success-fg: #206b38; --success-soft: #ebfaed; --success-border: #bcdfc2; --warning-solid: #c5770f; --warning-fg: #864e18; --warning-soft: #fff6e4; --warning-border: #eecf9c; --danger-solid: #d73337; --danger-fg: #b32228; --danger-soft: #fff1f0; --danger-border: #f9c1bf; --danger-fill: #c52b30; --danger-fill-hover: #b32228; --danger-fill-fg: #ffffff; --info-solid: #5e5e5e; --info-fg: #0a0a0a; --info-soft: #f5f5f5; --info-border: #e8e8e8; --well-light: #f5f5f5; --well-dark: #000000; --well-light-fg: #5e5e5e; --well-dark-fg: #a1a1a1; --qr-bg: #ffffff; --scrim: rgb(18 17 16 / 0.4); --ring: #0a0a0a; --ring-halo: rgb(10 10 10 / 0.24); --elevation-popover: 0 1px 2px rgb(31 29 27 / 0.06), 0 8px 24px -6px rgb(31 29 27 / 0.14); --elevation-overlay: 0 2px 4px rgb(31 29 27 / 0.06), 0 16px 40px -8px rgb(31 29 27 / 0.2); --elevation-thumb: 0 1px 2px rgb(0 0 0 / 0.2); --elevation-selected: 0 1px 2px rgb(31 29 27 / 0.06), 0 4px 14px rgb(31 29 27 / 0.07); --border-selected: color-mix(in oklab, var(--border-strong) 40%, var(--border)); --sidebar: #fafafa; --primary: #0a0a0a; --primary-foreground: #ffffff; --primary-hover: #262626; --primary-control: #0a0a0a; --primary-control-foreground: #ffffff; --primary-edge: transparent; --fw-display-l: 600; --fw-display-m: 600; /* shadcn-svelte variables, mapped onto the tokens above so the ui/ components keep their class names */ --background: var(--canvas); --foreground: var(--fg); --card: var(--surface); --card-foreground: var(--fg); --popover: var(--surface-raised); --popover-foreground: var(--fg); --secondary: var(--fill-muted); --secondary-foreground: var(--fg); --muted: var(--fill-muted); --muted-foreground: var(--fg-muted); --accent: var(--fill-hover); --accent-foreground: var(--fg); --destructive: var(--danger-fg); --destructive-foreground: var(--danger-fill-fg); --warning: var(--warning-soft); --warning-foreground: var(--warning-fg); --input: var(--fill-muted); --sidebar-foreground: var(--fg); --sidebar-primary: var(--primary); --sidebar-primary-foreground: var(--primary-foreground); --sidebar-accent: var(--fill-selected); --sidebar-accent-foreground: var(--fg); --sidebar-border: var(--border); --sidebar-ring: var(--ring); } .dark { --canvas: #000000; --surface: #000000; --surface-raised: #121212; --surface-sunken: #0e0e0e; --fill-muted: #171717; --fill-hover: #1a1a1a; --fill-row-hover: #0c0c0c; --fill-selected: #1f1f1f; --fill-skeleton: #1a1a1a; --border: #1f1f1f; --border-strong: #6b6b6b; --border-control: #6b6b6b; --fg: #ededed; --fg-muted: #a1a1a1; --fg-subtle: #5c5c5c; --inverse: #1f1f1f; --inverse-fg: #ededed; --inverse-muted: #b5b5b5; --inverse-hover: #2e2e2e; --inverse-border: #333333; --success-solid: #54b66e; --success-fg: #83d494; --success-soft: #1a2b1d; --success-border: #2c5235; --warning-solid: #eba941; --warning-fg: #f5ca7a; --warning-soft: #342611; --warning-border: #68491a; --danger-solid: #f45a56; --danger-fg: #ff8f8d; --danger-soft: #3b1e1e; --danger-border: #813939; --danger-fill: #c52b30; --danger-fill-hover: #b32228; --danger-fill-fg: #ffffff; --info-solid: #a1a1a1; --info-fg: #ededed; --info-soft: #0e0e0e; --info-border: #1f1f1f; --well-light: #f5f5f5; --well-dark: #000000; --well-light-fg: #5e5e5e; --well-dark-fg: #a1a1a1; --qr-bg: #ffffff; --scrim: rgb(0 0 0 / 0.6); --ring: #ededed; --ring-halo: rgb(237 237 237 / 0.24); --elevation-popover: 0 1px 2px rgb(0 0 0 / 0.4), 0 8px 24px -6px rgb(0 0 0 / 0.6); --elevation-overlay: 0 2px 4px rgb(0 0 0 / 0.4), 0 16px 40px -8px rgb(0 0 0 / 0.7); --elevation-thumb: 0 1px 2px rgb(0 0 0 / 0.5); --elevation-selected: 0 1px 2px rgb(0 0 0 / 0.4), 0 4px 14px rgb(0 0 0 / 0.5); --sidebar: #0a0a0a; --primary: #ededed; --primary-foreground: #000000; --primary-hover: #d6d6d6; --primary-control: #ededed; --primary-control-foreground: #000000; --primary-edge: transparent; --fw-display-l: 500; --fw-display-m: 500; color-scheme: dark; } /* Reduced motion: the OS setting and the Disable animations setting share one token collapse */ html.no-motion { --dur-fast: 0ms; --dur-base: 0ms; --dur-slow: 0ms; --rise: 0px; --nudge: 0px; --slide: 0px; --press-scale: 1; --pop-scale: 1; --t-pulse: 0ms; --t-caret: 0ms; --t-progress: 0ms; --t-spin: 1200ms; } @media (prefers-reduced-motion: reduce) { :root { --dur-fast: 0ms; --dur-base: 0ms; --dur-slow: 0ms; --rise: 0px; --nudge: 0px; --slide: 0px; --press-scale: 1; --pop-scale: 1; --t-pulse: 0ms; --t-caret: 0ms; --t-progress: 0ms; --t-spin: 1200ms; } } /* Safety net for motion that does not read the tokens (bits-ui keyframes, svelte-sonner): only spinners and scroll-linked fades keep moving */ html.no-motion *:not(.animate-spin, .scroll-fade-x, .scroll-fade-y), html.no-motion *::before, html.no-motion *::after { animation-duration: 0s !important; transition-duration: 0s !important; } @media (prefers-reduced-motion: reduce) { *:not(.animate-spin, .scroll-fade-x, .scroll-fade-y), *::before, *::after { animation-duration: 0s !important; transition-duration: 0s !important; } } /* Set for two frames by setModeInstantly, so light and dark swap at once instead of fading control by control */ html.theme-switching *, html.theme-switching *::before, html.theme-switching *::after { transition: none !important; } @theme { --font-sans: 'Funnel Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif; --font-display: 'Funnel Display', 'Funnel Sans', ui-sans-serif, system-ui, sans-serif; --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace; --radius-xs: 6px; --radius-sm: 10px; --radius-md: 12px; --radius-lg: 16px; --radius-xl: 20px; --radius-2xl: 24px; --radius-3xl: 28px; --radius-4xl: 32px; --ease-out: cubic-bezier(0.2, 0, 0, 1); --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); --ease-in: cubic-bezier(0.4, 0, 1, 1); } @theme inline { --text-display-l: 34px; --text-display-l--line-height: 42px; --text-display-l--font-weight: var(--fw-display-l); --text-display-l--letter-spacing: -0.02em; --text-display-m: 28px; --text-display-m--line-height: 36px; --text-display-m--font-weight: var(--fw-display-m); --text-display-m--letter-spacing: -0.015em; --text-display-s: 22px; --text-display-s--line-height: 30px; --text-display-s--font-weight: 500; --text-display-s--letter-spacing: -0.01em; --text-title: 18px; --text-title--line-height: 26px; --text-title--font-weight: 600; --text-title--letter-spacing: 0em; --text-heading: 15px; --text-heading--line-height: 22px; --text-heading--font-weight: 600; --text-heading--letter-spacing: 0em; --text-body-l: 17px; --text-body-l--line-height: 26px; --text-body-l--font-weight: 400; --text-body-l--letter-spacing: 0em; --text-body: 15px; --text-body--line-height: 22px; --text-body--font-weight: 400; --text-body--letter-spacing: 0em; --text-control: 14px; --text-control--line-height: 20px; --text-control--font-weight: 400; --text-control--letter-spacing: 0em; --text-caption: 13px; --text-caption--line-height: 18px; --text-caption--font-weight: 400; --text-caption--letter-spacing: 0em; --text-micro: 12px; --text-micro--line-height: 16px; --text-micro--font-weight: 500; --text-micro--letter-spacing: 0.01em; --text-mono: 14px; --text-mono--line-height: 20px; --text-mono--font-weight: 400; --text-mono--letter-spacing: 0em; --text-mono-s: 13px; --text-mono-s--line-height: 18px; --text-mono-s--font-weight: 400; --text-mono-s--letter-spacing: 0em; --text-code-cell: 30px; --text-code-cell--line-height: 38px; --text-code-cell--font-weight: 500; --text-code-cell--letter-spacing: 0em; --shadow-popover: var(--elevation-popover); --shadow-overlay: var(--elevation-overlay); --shadow-thumb: var(--elevation-thumb); --shadow-selected: var(--elevation-selected); --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-destructive-foreground: var(--destructive-foreground); --color-warning: var(--warning); --color-warning-foreground: var(--warning-foreground); --color-input: var(--input); --color-ring: var(--ring); --color-ring-halo: var(--ring-halo); --color-sidebar-foreground: var(--sidebar-foreground); --color-sidebar-primary: var(--sidebar-primary); --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); --color-sidebar-accent: var(--sidebar-accent); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); --color-scrim: var(--scrim); --color-canvas: var(--canvas); --color-surface: var(--surface); --color-surface-raised: var(--surface-raised); --color-surface-sunken: var(--surface-sunken); --color-fill-muted: var(--fill-muted); --color-fill-hover: var(--fill-hover); --color-fill-row-hover: var(--fill-row-hover); --color-fill-selected: var(--fill-selected); --color-fill-skeleton: var(--fill-skeleton); --color-border: var(--border); --color-border-strong: var(--border-strong); --color-border-selected: var(--border-selected); --color-fg: var(--fg); --color-fg-muted: var(--fg-muted); --color-fg-subtle: var(--fg-subtle); --color-inverse: var(--inverse); --color-inverse-fg: var(--inverse-fg); --color-inverse-muted: var(--inverse-muted); --color-inverse-hover: var(--inverse-hover); --color-inverse-border: var(--inverse-border); --color-success-solid: var(--success-solid); --color-success-soft: var(--success-soft); --color-warning-solid: var(--warning-solid); --color-warning-fg: var(--warning-fg); --color-danger-solid: var(--danger-solid); --color-danger-fg: var(--danger-fg); --color-danger-soft: var(--danger-soft); --color-danger-fill: var(--danger-fill); --color-danger-fill-hover: var(--danger-fill-hover); --color-danger-fill-fg: var(--danger-fill-fg); --color-well-light: var(--well-light); --color-well-dark: var(--well-dark); --color-well-light-fg: var(--well-light-fg); --color-well-dark-fg: var(--well-dark-fg); --color-qr-bg: var(--qr-bg); --color-sidebar: var(--sidebar); --color-primary-hover: var(--primary-hover); --color-primary-control: var(--primary-control); --color-primary-control-foreground: var(--primary-control-foreground); } @layer base { * { @apply border-border outline-ring; } /* Links and anything else without its own focus style get the standard ring, which component utilities override */ :focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; } html { font-family: var(--font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; } /* Names are interpolated into sentences, so a long unbroken one wraps instead of pushing the page sideways */ body { @apply bg-canvas text-body text-fg wrap-break-word; } button { @apply cursor-pointer; } /* Text selection stays neutral: the accent marks state, never decoration */ ::selection { background: var(--fill-selected); color: var(--fg); } /* Lucide icons keep a constant stroke regardless of their rendered size */ svg.lucide { stroke-width: 1.5px; } svg.lucide * { vector-effect: non-scaling-stroke; } } /* A link inside a sentence stands out by contrast and weight rather than a permanent underline, which appears only on hover */ @utility inline-link { color: var(--fg); font-weight: 500; text-underline-offset: 3px; text-decoration-line: underline; text-decoration-thickness: 1px; text-decoration-color: transparent; transition: text-decoration-color var(--dur-fast) var(--ease-out); &:hover { text-decoration-color: color-mix(in srgb, currentColor 40%, transparent); } } /* A key in a shortcut hint, in the palette footer and on its trigger */ @utility keycap { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding-inline: 4px; border-radius: var(--radius-xs); background: var(--fill-muted); box-shadow: inset 0 0 0 1px var(--border); font-family: var(--font-sans); font-size: 11px; line-height: 14px; font-weight: 500; color: var(--fg-muted); } /* Compositor-only motion; Tailwind 4 writes scale and translate as their own properties, so they are listed next to transform */ @utility transition-motion { transition-property: opacity, transform, translate, scale; transition-duration: var(--dur-fast); transition-timing-function: var(--ease-out); } /* A scroller fades at the edge that has more content; without overflow its scroll timeline is inactive, so nothing fades */ @property --fade-start { syntax: ''; inherits: false; initial-value: 0px; } @property --fade-end { syntax: ''; inherits: false; initial-value: 0px; } @supports (animation-timeline: scroll()) { .scroll-fade-x, .scroll-fade-y { animation: scroll-fade-start linear both, scroll-fade-end linear both; animation-range: 0 24px, calc(100% - 24px) 100%; } .scroll-fade-x { mask-image: linear-gradient( to right, transparent, #000 var(--fade-start), #000 calc(100% - var(--fade-end)), transparent ); animation-timeline: scroll(self inline), scroll(self inline); } .scroll-fade-y { mask-image: linear-gradient( transparent, #000 var(--fade-start), #000 calc(100% - var(--fade-end)), transparent ); animation-timeline: scroll(self), scroll(self); } } @keyframes scroll-fade-start { to { --fade-start: 24px; } } @keyframes scroll-fade-end { from { --fade-end: 24px; } to { --fade-end: 0px; } } .settings-content { view-transition-name: settings-content; } ::view-transition-old(root), ::view-transition-new(root) { animation: none; } /* The phone bars swap instantly and stay above the content, whose new snapshot is otherwise painted last because only the new page has its name */ ::view-transition-group(phone-top-bar), ::view-transition-group(phone-nav), ::view-transition-group(phone-search) { z-index: 1; animation: none; } ::view-transition-old(settings-content-old) { animation: 120ms ease-out both vt-fade-out; } ::view-transition-new(settings-content) { animation: 180ms ease-out both vt-fade-in; } @keyframes vt-fade-out { to { opacity: 0; } } @keyframes vt-fade-in { from { opacity: 0; } } @keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-10px); } 50% { transform: translateX(10px); } 75% { transform: translateX(-10px); } } .animate-shake { animation: shake 0.5s ease-in-out; } /* Marks a moment: the bar arriving, a save settling, a blocked navigation */ @keyframes bloom { 0% { opacity: 0; } 30% { opacity: 1; } 100% { opacity: 0; } } .animate-bloom { animation: bloom 1.5s ease-out forwards; } /* The bar's inverse surface is dark in both themes, so one glow rising from under it reads as light in light and dark mode */ .bloom-pending, .bloom-success, .bloom-error { background: radial-gradient( 130% 150% at 50% 130%, color-mix(in srgb, var(--bloom-tint) 30%, transparent), transparent 70% ); } .bloom-pending { --bloom-tint: var(--warning-solid); } /* Waits out the bar's 380ms motion so it blooms after the width settles, and `both` keeps it hidden during that wait */ .bloom-success { --bloom-tint: var(--success-solid); animation-delay: 380ms; animation-fill-mode: both; } .bloom-error { --bloom-tint: var(--danger-solid); } /* bits-ui moves the highlight in selects and the palette with aria-activedescendant, so :focus-visible never applies there and the tint alone fails 3:1 */ /* It sits outside the layers so it beats the items' outline-hidden utility */ html[data-modality='keyboard'] :is( [role='option'][data-highlighted], [role^='menuitem'][data-highlighted], [data-slot='command-item'][data-selected] ) { outline: 2px solid var(--ring); outline-offset: -2px; } /* Alt+T focuses svelte-sonner's toast list, which has no height, so the newest toast shows the ring instead */ [data-sonner-toaster]:focus-visible > [data-front='true'] { outline: 2px solid var(--inverse-fg); outline-offset: -2px; } /* Phones show every dropdown as a bottom sheet with its groups as cards */ /* These rules sit outside the layers so they beat the menu's utility classes, and !important beats bits-ui's inline floating position */ @media (width < 40rem) { [data-bits-floating-content-wrapper]:has(> [data-slot='dropdown-menu-content']) { position: fixed !important; inset: auto 0 0 !important; transform: none !important; min-width: 0 !important; } [data-slot='dropdown-menu-content'] { display: flex; flex-direction: column; width: 100%; max-height: 85dvh; padding: 0 16px calc(16px + env(safe-area-inset-bottom)); border: 0; border-radius: var(--radius-lg) var(--radius-lg) 0 0; background: var(--surface-raised); backdrop-filter: none; /* The spread shadow doubles as the scrim, so tapping it lands outside the menu and closes it */ box-shadow: 0 0 0 100vmax var(--scrim); scale: 1; transition-property: translate, box-shadow; transition-duration: var(--dur-slow); transition-timing-function: var(--ease-out); @starting-style { translate: 0 100%; box-shadow: 0 0 0 100vmax transparent; } &[data-state='closed'] { translate: 0 100%; opacity: 1; box-shadow: 0 0 0 100vmax transparent; transition-duration: var(--dur-base); transition-timing-function: var(--ease-in); } /* An item that navigates sets data-jumping before the menu closes, because the page crossfade would otherwise show the sheet sliding away under the new page */ &[data-state='closed'][data-jumping] { transition: none; } &::before { content: ''; flex-shrink: 0; align-self: center; width: 36px; height: 4px; margin: 8px 0 12px; border-radius: 2px; background: var(--border-strong); } [data-slot='dropdown-menu-group'] + [data-slot='dropdown-menu-group'] { margin-top: 8px; } [data-slot='dropdown-menu-separator'] { height: 8px; margin: 0; background: none; } [data-slot='dropdown-menu-group-heading'], [data-slot='dropdown-menu-label'] { padding: 8px 4px 6px; } /* Consecutive items form one card: each draws its sides and top line, the last one closes the card */ [role^='menuitem'] { min-height: var(--h-xl); padding-inline: 16px; border-radius: 0; background-color: var(--surface); box-shadow: inset 1px 0 var(--border), inset -1px 0 var(--border), inset 0 1px var(--border); font-size: var(--text-body); line-height: var(--text-body--line-height); } [role^='menuitem']:not([role^='menuitem'] + [role^='menuitem']) { border-top-left-radius: var(--radius-md); border-top-right-radius: var(--radius-md); } [role^='menuitem']:not(:has(+ [role^='menuitem'])) { border-bottom-left-radius: var(--radius-md); border-bottom-right-radius: var(--radius-md); box-shadow: inset 1px 0 var(--border), inset -1px 0 var(--border), inset 0 1px var(--border), inset 0 -1px var(--border); } [role^='menuitem'][data-highlighted] { background-color: var(--fill-hover); } [data-variant='destructive'][data-highlighted] { background-color: var(--danger-soft); } } }