Spaces:
Running
Running
| @tailwind base; | |
| @tailwind components; | |
| @tailwind utilities; | |
| /* βββ Theme tokens βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| Palette is stored as space-separated RGB triplets so Tailwind's | |
| `rgb(var(--brand-500) / <alpha-value>)` pattern works. The Settings page | |
| overrides these at runtime to re-skin the whole app. | |
| The remaining tokens (surface / line / text) are also exposed as CSS vars | |
| so we can tune the system once and have every surface react. Light and | |
| dark are both intentionally near-monochrome β the brand color does the | |
| accenting; surfaces stay quiet. */ | |
| :root { | |
| /* Default brand: refined indigo (#4f46e5 scale). Settings can override. */ | |
| --brand-50: 238 242 255; | |
| --brand-100: 224 231 255; | |
| --brand-200: 199 210 254; | |
| --brand-300: 165 180 252; | |
| --brand-400: 129 140 248; | |
| --brand-500: 99 102 241; | |
| --brand-600: 79 70 229; | |
| --brand-700: 67 56 202; | |
| --brand-800: 55 48 163; | |
| --brand-900: 49 46 129; | |
| /* Neutral surfaces (light). */ | |
| --bg-app: 250 250 252; /* page background */ | |
| --bg-surface: 255 255 255; /* cards, sidebar, inputs */ | |
| --bg-muted: 246 247 250; /* hover, subtle backgrounds */ | |
| --bg-strong: 244 244 248; /* selected rows, kbd */ | |
| --line: 226 230 238; /* primary border */ | |
| --line-soft: 235 238 245; /* hairline / divider */ | |
| --text-strong: 15 23 42; /* headings */ | |
| --text: 30 41 59; /* body */ | |
| --text-muted: 100 116 139; /* secondary */ | |
| --text-faint: 148 163 184; /* tertiary */ | |
| } | |
| .dark { | |
| --bg-app: 9 11 17; | |
| --bg-surface: 17 20 27; | |
| --bg-muted: 22 26 34; | |
| --bg-strong: 28 33 43; | |
| --line: 39 45 58; | |
| --line-soft: 30 36 48; | |
| --text-strong: 248 250 252; | |
| --text: 226 232 240; | |
| --text-muted: 148 163 184; | |
| --text-faint: 100 116 139; | |
| } | |
| @layer base { | |
| html, | |
| body, | |
| #root { | |
| @apply h-full; | |
| } | |
| html { | |
| /* Inter ships with stylistic alternates β `cv11` gives a single-storey | |
| `a`, `ss01` evens out punctuation. Both are part of why Linear, Vercel, | |
| Stripe, Resend etc. all look the same: it's the Inter feature stack. */ | |
| font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11', 'ss01'; | |
| -webkit-font-smoothing: antialiased; | |
| -moz-osx-font-smoothing: grayscale; | |
| text-rendering: optimizeLegibility; | |
| } | |
| body { | |
| @apply font-sans antialiased; | |
| color: rgb(var(--text)); | |
| background-color: rgb(var(--bg-app)); | |
| } | |
| /* Headings get tighter tracking + a hair more weight than the body. */ | |
| h1, h2, h3, h4, h5, h6 { | |
| @apply font-display tracking-tight; | |
| color: rgb(var(--text-strong)); | |
| } | |
| /* Numbers should align in stat tiles, table cells, durations, etc. */ | |
| .tabular, | |
| [data-slot='number'] { | |
| font-variant-numeric: tabular-nums; | |
| } | |
| /* Selection uses the brand color at low alpha. */ | |
| ::selection { | |
| background-color: rgb(var(--brand-500) / 0.18); | |
| color: rgb(var(--text-strong)); | |
| } | |
| /* App scrollbars: thin, near-invisible, lifts on hover. */ | |
| ::-webkit-scrollbar { | |
| width: 10px; | |
| height: 10px; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background-color: rgb(var(--line)); | |
| border: 2px solid transparent; | |
| background-clip: padding-box; | |
| border-radius: 999px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background-color: rgb(var(--text-faint)); | |
| background-clip: padding-box; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: transparent; | |
| } | |
| } | |
| @layer components { | |
| /* βββ Surfaces ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| `.glass` is kept as the primary card surface for backwards compat with | |
| every page that already uses the name. The new look is: white surface, | |
| 1px hairline, almost-invisible shadow. No glassmorphism β just a quiet, | |
| flat card. */ | |
| .surface { | |
| background-color: rgb(var(--bg-surface)); | |
| border: 1px solid rgb(var(--line)); | |
| @apply rounded-xl; | |
| } | |
| .glass { | |
| @apply surface shadow-glass; | |
| } | |
| .glass-strong { | |
| @apply surface shadow-glass-lg; | |
| } | |
| .card { @apply glass p-6; } | |
| .card-sm { @apply glass p-4; } | |
| .divider { | |
| height: 1px; | |
| background-color: rgb(var(--line-soft)); | |
| } | |
| /* βββ Typography helpers βββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .eyebrow { | |
| @apply inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-[0.14em]; | |
| color: rgb(var(--text-muted)); | |
| } | |
| .h-page { | |
| @apply font-display text-2xl font-semibold tracking-tight md:text-3xl; | |
| color: rgb(var(--text-strong)); | |
| } | |
| .h-section { | |
| @apply font-display text-base font-semibold tracking-tight; | |
| color: rgb(var(--text-strong)); | |
| } | |
| .text-muted { color: rgb(var(--text-muted)); } | |
| .text-faint { color: rgb(var(--text-faint)); } | |
| /* βββ Buttons βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| A single `.btn` base, then variants. Every variant has a visible focus | |
| ring (a11y), a subtle hover, and an `active:` press state. */ | |
| .btn { | |
| @apply relative inline-flex select-none items-center justify-center gap-2 | |
| rounded-lg px-3.5 py-2 text-sm font-medium leading-none | |
| transition duration-150 ease-out | |
| disabled:cursor-not-allowed disabled:opacity-50 | |
| focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-400/60 | |
| focus-visible:ring-offset-2 focus-visible:ring-offset-[rgb(var(--bg-app))]; | |
| } | |
| .btn-primary { | |
| @apply btn bg-brand-600 text-white shadow-sm | |
| hover:bg-brand-500 | |
| active:bg-brand-700; | |
| } | |
| .btn-secondary { | |
| @apply btn text-slate-800 dark:text-slate-100; | |
| background-color: rgb(var(--bg-surface)); | |
| border: 1px solid rgb(var(--line)); | |
| } | |
| .btn-secondary:hover { | |
| background-color: rgb(var(--bg-muted)); | |
| } | |
| .btn-secondary:active { | |
| background-color: rgb(var(--bg-strong)); | |
| } | |
| .btn-ghost { | |
| @apply btn bg-transparent; | |
| color: rgb(var(--text-muted)); | |
| } | |
| .btn-ghost:hover { | |
| color: rgb(var(--text-strong)); | |
| background-color: rgb(var(--bg-muted)); | |
| } | |
| .btn-danger { | |
| @apply btn bg-rose-600 text-white shadow-sm | |
| hover:bg-rose-500 | |
| active:bg-rose-700 | |
| focus-visible:ring-rose-400/60; | |
| } | |
| .btn-sm { @apply px-2.5 py-1.5 text-xs; } | |
| .btn-lg { @apply px-5 py-2.5 text-[15px]; } | |
| /* βββ Form controls βββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .input, | |
| .textarea, | |
| .select { | |
| @apply w-full rounded-lg px-3.5 py-2.5 text-sm leading-snug | |
| transition-colors duration-150 | |
| placeholder:text-[rgb(var(--text-faint))] | |
| focus:outline-none; | |
| background-color: rgb(var(--bg-surface)); | |
| border: 1px solid rgb(var(--line)); | |
| color: rgb(var(--text-strong)); | |
| } | |
| .input:hover, | |
| .textarea:hover, | |
| .select:hover { | |
| border-color: rgb(var(--text-faint) / 0.7); | |
| } | |
| .input:focus, | |
| .textarea:focus, | |
| .select:focus { | |
| @apply ring-2 ring-brand-400/40; | |
| border-color: rgb(var(--brand-500)); | |
| } | |
| .label { | |
| @apply mb-1.5 block text-[11px] font-semibold uppercase tracking-[0.12em]; | |
| color: rgb(var(--text-muted)); | |
| } | |
| /* βββ Badges / pills βββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .badge { | |
| @apply inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[11px] font-medium leading-none; | |
| border: 1px solid rgb(var(--line)); | |
| background-color: rgb(var(--bg-muted)); | |
| color: rgb(var(--text-muted)); | |
| height: 22px; | |
| } | |
| .badge-success { @apply badge border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-500/30 dark:bg-emerald-500/10 dark:text-emerald-300; } | |
| .badge-running { @apply badge border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-500/30 dark:bg-sky-500/10 dark:text-sky-300; } | |
| .badge-error { @apply badge border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-500/30 dark:bg-rose-500/10 dark:text-rose-300; } | |
| .badge-warning { @apply badge border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-200; } | |
| .badge-info { @apply badge border-indigo-200 bg-indigo-50 text-indigo-700 dark:border-indigo-500/30 dark:bg-indigo-500/10 dark:text-indigo-300; } | |
| .badge-neutral { @apply badge; } | |
| /* βββ Misc ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .kbd { | |
| @apply inline-flex h-5 items-center rounded border px-1.5 font-mono text-[10px] font-medium; | |
| background-color: rgb(var(--bg-strong)); | |
| border-color: rgb(var(--line)); | |
| color: rgb(var(--text-muted)); | |
| } | |
| /* Subtle dotted background used behind hero / empty states. */ | |
| .dot-grid { | |
| background-image: radial-gradient(rgb(var(--line)) 1px, transparent 1px); | |
| background-size: 22px 22px; | |
| background-position: -1px -1px; | |
| } | |
| } | |
| @layer utilities { | |
| /* Centered max-width container used by list / dashboard pages. */ | |
| .container-page { | |
| @apply mx-auto w-full max-w-6xl; | |
| } | |
| /* Narrower container for single-task / form-driven pages | |
| (wizards, settings, single-form tools). */ | |
| .container-form { | |
| @apply mx-auto w-full max-w-5xl; | |
| } | |
| /* Sticky action bar used at the bottom of long forms / wizards. | |
| Sits flush with the viewport bottom, gets a hairline top border | |
| and a soft surface tint so it reads above the form content. */ | |
| .sticky-action-bar { | |
| @apply sticky bottom-0 z-10 -mx-4 mt-4 flex flex-wrap items-center gap-3 | |
| border-t px-4 py-3 backdrop-blur md:-mx-10 md:px-10; | |
| border-color: rgb(var(--line)); | |
| background-color: rgb(var(--bg-app) / 0.85); | |
| } | |
| /* Subtle entrance animation for the main column. */ | |
| @keyframes app-fade-in { | |
| from { opacity: 0; transform: translateY(2px); } | |
| to { opacity: 1; transform: none; } | |
| } | |
| .animate-app-fade-in { | |
| animation: app-fade-in 240ms ease-out both; | |
| } | |
| /* Shimmer highlight used by the progress bar while a run is working. */ | |
| @keyframes shimmer { | |
| 0% { transform: translateX(-100%); } | |
| 100% { transform: translateX(250%); } | |
| } | |
| } | |