mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-27 12:18:59 +00:00
322d337d20
Remove redundant nav and label dots, move desktop masthead status to animated left rails, and let AuthCanvas scroll long preflight content on small viewports.
790 lines
33 KiB
CSS
790 lines
33 KiB
CSS
@import "tailwindcss";
|
|
|
|
@plugin 'tailwindcss-animate';
|
|
|
|
@custom-variant dark (&:is(.dark *));
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
THEME ENGINE
|
|
Theme is an attribute on <html> (data-theme = dim · oled · light);
|
|
accent is a second attribute (data-accent = one of eight hues).
|
|
Surfaces, ink and borders derive from a handful of raw lightness
|
|
vars per theme, so the live knobs (--border-boost, --glow) modulate
|
|
every hairline and the page glow through calc(). The accent sets
|
|
--brand everywhere and tints the ambient glow.
|
|
|
|
:root carries the shared + computed tokens AND the Dim defaults, so
|
|
a document with no data-theme renders Dim (the default theme).
|
|
[data-theme="oled"] and [data-theme="light"] override from there.
|
|
───────────────────────────────────────────────────────────── */
|
|
:root {
|
|
color-scheme: dark;
|
|
|
|
/* Live knobs (overwritten on <html> by the pre-paint script / useTheme) */
|
|
--border-boost: 0; /* signed offset added to every border L */
|
|
--glow: 0.16; /* ambient top-glow alpha */
|
|
--contrast: 0; /* master contrast: spreads page, ink and borders, signed by --ink-dir */
|
|
|
|
/* Accent default: cyan (overridden by [data-accent]) */
|
|
--accent-h: 196;
|
|
--accent-c: 0.130;
|
|
|
|
/* ---- Computed surfaces (flat neutral ladder per theme). The page tone
|
|
spreads away from the cards as --contrast rises; clamp keeps it in gamut
|
|
and always a hair below --card so page/card separation never collapses
|
|
(matters at high contrast in Light, where --card is pure white). ---- */
|
|
--background: oklch(clamp(0, var(--bg) - var(--ink-dir) * var(--contrast) * 0.03, var(--card-l) - 0.012) 0 0);
|
|
--card: oklch(var(--card-l) 0 0);
|
|
--band: oklch(var(--band-l) 0 0);
|
|
--well: oklch(var(--well-l) 0 0);
|
|
|
|
/* ---- Computed popover / floating-panel fill. Mirrors --background's clamp +
|
|
--ink-dir + contrast spread off a per-theme raw lightness + alpha, so every
|
|
floating surface (sheets, dialogs, dropdowns, menus) tones with the active
|
|
theme (incl. OLED true-black) and deepens with --contrast, instead of being
|
|
a flat per-theme literal that stranded OLED at the Dim value. ---- */
|
|
--popover: oklch(clamp(0, var(--pop-l) - var(--ink-dir) * var(--contrast) * 0.03, 1) 0 0 / var(--pop-a));
|
|
|
|
/* Overlay scrim behind modals & sheets (theme-aware; softened in Light). */
|
|
--scrim: oklch(0 0 0 / 0.45);
|
|
|
|
/* ---- Computed borders (opaque grey, directionally lit). --border-boost and
|
|
--contrast both lift every hairline through one expression; clamp keeps
|
|
them in gamut and below pure white so the lit edge never merges into a
|
|
white card in Light at the low end of the knobs. ---- */
|
|
--card-border: oklch(clamp(0, var(--bb) + var(--border-dir) * (var(--border-boost) + var(--contrast) * 0.05), 0.97) 0 0);
|
|
--card-border-top: oklch(clamp(0, var(--bt) + var(--border-dir) * (var(--border-boost) + var(--contrast) * 0.05), 0.97) 0 0);
|
|
--card-border-hover: oklch(clamp(0, var(--bh) + var(--border-dir) * (var(--border-boost) + var(--contrast) * 0.05), 0.97) 0 0);
|
|
--hairline: oklch(clamp(0, var(--hl) + var(--border-dir) * (var(--border-boost) + var(--contrast) * 0.05), 0.97) 0 0);
|
|
--border: var(--card-border);
|
|
--sidebar-border: var(--card-border);
|
|
|
|
/* ---- Computed ink tiers. --contrast pushes each tier toward its extreme so
|
|
secondary text crisps up as it rises; clamp keeps every tier in gamut. ---- */
|
|
--stat-value: oklch(clamp(0, var(--val) + var(--ink-dir) * var(--contrast) * 0.04, 1) 0 0);
|
|
--stat-title: oklch(clamp(0, var(--title) + var(--ink-dir) * var(--contrast) * 0.055, 1) 0 0);
|
|
--stat-subtitle: oklch(clamp(0, var(--sub) + var(--ink-dir) * var(--contrast) * 0.07, 1) 0 0);
|
|
--stat-icon: oklch(clamp(0, var(--icon) + var(--ink-dir) * var(--contrast) * 0.06, 1) 0 0);
|
|
|
|
/* ---- Computed brand (the accent is the one data color) ---- */
|
|
--brand: oklch(var(--accent-l) var(--accent-c) var(--accent-h));
|
|
--brand-foreground: var(--brand-fg);
|
|
--brand-muted: oklch(var(--accent-l) var(--accent-c) var(--accent-h) / 0.16);
|
|
--ring: var(--brand);
|
|
--sidebar-ring: var(--brand);
|
|
|
|
/* Glass surface = the card tone; borders use the opaque hairline */
|
|
--glass: var(--card);
|
|
|
|
/* Ambient glow tracks the accent hue so the whole page tints with it */
|
|
--glow-color: oklch(var(--glow-l) var(--glow-c) var(--accent-h) / var(--glow));
|
|
|
|
/* Charts: cyan leads as the primary data color, amber for peaks, rose for errors */
|
|
--chart-1: var(--brand);
|
|
--chart-2: var(--warning);
|
|
--chart-3: var(--destructive);
|
|
|
|
/* Severity / chart palette (Signature default = today's saturated semantics).
|
|
Colours only; per-palette fill/gradient/stroke live in JS (SecurityCharts).
|
|
[data-chart-style="muted"|"heat"] override these; signature needs no rule. */
|
|
--sev-critical: var(--destructive);
|
|
--sev-high: var(--warning);
|
|
--sev-medium: color-mix(in oklch, var(--warning) 52%, var(--stat-icon));
|
|
--sev-low: color-mix(in oklch, var(--stat-icon) 72%, var(--well));
|
|
--sev-vuln: var(--brand);
|
|
|
|
/* Sparkline / data-line styling (centralized) */
|
|
--data-line-width: 1.25px;
|
|
--data-peak: var(--warning);
|
|
|
|
/* Fleet Action card transformative chip (reuses the purple label hue) */
|
|
--action-transformative: var(--label-purple);
|
|
|
|
/* ─────────────── Dim defaults (the default theme) ─────────────── */
|
|
/* Raw lightness vars */
|
|
--bg: 0.160; --card-l: 0.212; --band-l: 0.190; --well-l: 0.128;
|
|
--bb: 0.320; --bt: 0.400; --bh: 0.460; --hl: 0.300; --border-dir: 1;
|
|
--val: 0.970; --title: 0.800; --sub: 0.605; --icon: 0.480; --ink-dir: 1;
|
|
--accent-l: 0.745; --brand-fg: oklch(0.15 0 0);
|
|
--glow-l: 0.55; --glow-c: 0.085;
|
|
--pop-l: 0.14; --pop-a: 0.82;
|
|
|
|
/* Dark non-derivable tokens (shared by Dim & OLED) */
|
|
--foreground: oklch(0.94 0 0);
|
|
--card-foreground: oklch(0.94 0 0);
|
|
--popover-foreground: oklch(0.94 0 0);
|
|
--primary: oklch(0.98 0 0);
|
|
--primary-foreground: oklch(0.10 0 0);
|
|
--secondary: oklch(var(--card-l) 0 0);
|
|
--secondary-foreground: oklch(0.94 0 0);
|
|
--muted: oklch(var(--card-l) 0 0);
|
|
--muted-foreground: oklch(0.45 0 0);
|
|
--accent: oklch(1 0 0 / 0.06);
|
|
--accent-foreground: oklch(0.94 0 0);
|
|
--destructive: oklch(0.72 0.18 20);
|
|
--destructive-foreground: oklch(0.94 0 0);
|
|
--destructive-muted: oklch(0.72 0.18 20 / 0.15);
|
|
--input: oklch(var(--card-l) 0 0);
|
|
|
|
/* Glass: solid surface system (no blur on static surfaces) */
|
|
--glass-border: var(--card-border);
|
|
--glass-highlight: oklch(1 0 0 / 0.06);
|
|
|
|
/* Semantic status colors (dark set) */
|
|
--success: oklch(0.78 0.16 155);
|
|
--success-foreground: oklch(0.10 0 0);
|
|
--success-muted: oklch(0.78 0.16 155 / 0.15);
|
|
--warning: oklch(0.82 0.14 75);
|
|
--warning-foreground: oklch(0.10 0 0);
|
|
--warning-muted: oklch(0.82 0.14 75 / 0.15);
|
|
/* --info: TOAST USE ONLY. Not part of the §17 five-slot working palette. */
|
|
--info: oklch(0.70 0.14 250);
|
|
--info-foreground: oklch(0.10 0 0);
|
|
--info-muted: oklch(0.70 0.14 250 / 0.15);
|
|
--update: oklch(0.72 0.20 320);
|
|
--update-foreground: oklch(0.10 0 0);
|
|
--update-muted: oklch(0.72 0.20 320 / 0.15);
|
|
|
|
/* Sidebar (tracks the page tone; borders use the shared hairline) */
|
|
--sidebar: oklch(var(--bg) 0 0 / 0.80);
|
|
--sidebar-foreground: oklch(0.94 0 0);
|
|
--sidebar-primary: oklch(0.98 0 0);
|
|
--sidebar-primary-foreground: oklch(0.10 0 0);
|
|
--sidebar-accent: oklch(1 0 0 / 0.07);
|
|
--sidebar-accent-foreground: oklch(0.94 0 0);
|
|
|
|
/* Label palette: ten raw hues preserved for existing assignments (dark) */
|
|
--label-teal: oklch(0.75 0.10 192);
|
|
--label-teal-bg: oklch(0.75 0.10 192 / 0.15);
|
|
--label-blue: oklch(0.72 0.12 250);
|
|
--label-blue-bg: oklch(0.72 0.12 250 / 0.15);
|
|
--label-purple: oklch(0.72 0.12 300);
|
|
--label-purple-bg: oklch(0.72 0.12 300 / 0.15);
|
|
--label-rose: oklch(0.72 0.14 10);
|
|
--label-rose-bg: oklch(0.72 0.14 10 / 0.15);
|
|
--label-amber: oklch(0.78 0.14 60);
|
|
--label-amber-bg: oklch(0.78 0.14 60 / 0.15);
|
|
--label-green: oklch(0.72 0.13 150);
|
|
--label-green-bg: oklch(0.72 0.13 150 / 0.15);
|
|
--label-orange: oklch(0.75 0.14 45);
|
|
--label-orange-bg: oklch(0.75 0.14 45 / 0.15);
|
|
--label-pink: oklch(0.75 0.14 340);
|
|
--label-pink-bg: oklch(0.75 0.14 340 / 0.15);
|
|
--label-cyan: oklch(0.75 0.10 210);
|
|
--label-cyan-bg: oklch(0.75 0.10 210 / 0.15);
|
|
--label-slate: oklch(0.65 0.01 250);
|
|
--label-slate-bg: oklch(0.65 0.01 250 / 0.15);
|
|
|
|
/* Semantic label roles: new labels should consume these */
|
|
--label-env: var(--label-teal);
|
|
--label-env-bg: var(--label-teal-bg);
|
|
--label-role: var(--label-blue);
|
|
--label-role-bg: var(--label-blue-bg);
|
|
--label-owner: var(--label-green);
|
|
--label-owner-bg: var(--label-green-bg);
|
|
--label-risk: var(--label-rose);
|
|
--label-risk-bg: var(--label-rose-bg);
|
|
--label-custom: var(--label-amber);
|
|
--label-custom-bg: var(--label-amber-bg);
|
|
|
|
/* Chart neutrals + grid/axis (dark) */
|
|
--chart-4: oklch(0.55 0 0);
|
|
--chart-5: oklch(0.80 0 0);
|
|
--chart-grid: oklch(1 0 0 / 0.05);
|
|
--chart-tick: oklch(1 0 0 / 0.40);
|
|
|
|
/* Material simulation: inherent depth (dark) */
|
|
--card-bevel: inset 0 1px 0 0 oklch(1 0 0 / 0.05);
|
|
--button-inner-glow: inset 0 1px 0 0 oklch(1 0 0 / 0.06);
|
|
--chrome-top-highlight: inset 0 1px 0 0 oklch(1 0 0 / 0.08);
|
|
|
|
/* Shadows: near-zero, depth comes from surface tone (dark) */
|
|
--shadow-x: 0px;
|
|
--shadow-y: 1px;
|
|
--shadow-blur: 2px;
|
|
--shadow-spread: 0px;
|
|
--shadow-opacity: 0.15;
|
|
--shadow-color: hsl(0 0% 0%);
|
|
--shadow-2xs: 0 0 0 0 transparent;
|
|
--shadow-xs: 0 0 0 0 transparent;
|
|
--shadow-sm: 0px 1px 2px 0px hsl(0 0% 0% / 0.15);
|
|
--shadow: 0px 1px 2px 0px hsl(0 0% 0% / 0.15);
|
|
--shadow-md: 0px 2px 4px 0px hsl(0 0% 0% / 0.15);
|
|
--shadow-lg: 0px 4px 6px -1px hsl(0 0% 0% / 0.15);
|
|
--shadow-xl: 0px 8px 10px -1px hsl(0 0% 0% / 0.20);
|
|
--shadow-2xl: 0px 12px 20px -2px hsl(0 0% 0% / 0.30);
|
|
|
|
/* Terminal (recessed well, tracks the theme's well tone) */
|
|
--terminal-bg: oklch(var(--well-l) 0 0);
|
|
--terminal-fg: oklch(0.85 0 0);
|
|
--terminal-cursor: oklch(1 0 0);
|
|
--terminal-cursor-accent: oklch(0 0 0);
|
|
--terminal-selection: oklch(1 0 0 / 0.3);
|
|
|
|
/* ─────────────── Shared (theme-independent) ─────────────── */
|
|
/* Typography. Display is the locked signature face; the interface (sans) and
|
|
data (mono) faces are user-swappable via --ui-font / --mono-font, and
|
|
--type-scale multiplies the root font-size (see the html rule below). */
|
|
--type-scale: 1;
|
|
--ui-font: 'Geist';
|
|
--mono-font: 'Geist Mono';
|
|
--font-sans: var(--ui-font), system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
--font-mono: var(--mono-font), ui-monospace, monospace;
|
|
--font-serif: 'Instrument Serif', Georgia, serif;
|
|
--font-display: 'Instrument Serif', Georgia, serif;
|
|
|
|
/* Operational heading family. Signature default = the display serif, italic;
|
|
family + style ONLY so migrated headings keep their own weight/tracking and
|
|
stay a true no-op in Signature. [data-headings="clean"] flips to the sans
|
|
face and the Calm weight/tracking lift lands via the .font-heading rule. */
|
|
--font-heading: var(--font-display);
|
|
--heading-style: italic;
|
|
|
|
/* Shape */
|
|
--radius: 0.5rem;
|
|
|
|
/* Height of the mobile bottom tab bar's interactive area (excludes the
|
|
safe-area inset, which is added where needed). Fixed-position overlays
|
|
(toasts, the deploy pill) offset above it on mobile so they never sit
|
|
behind the tab bar. */
|
|
--sn-mobile-tabbar-h: 56px;
|
|
|
|
/* Motion timing */
|
|
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
|
|
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
|
--duration-fast: 150ms;
|
|
--duration-base: 220ms;
|
|
--duration-slow: 350ms;
|
|
|
|
--tracking-normal: 0em;
|
|
--spacing: 0.25rem;
|
|
|
|
/* Density: comfortable defaults. Overridden under .density-compact. */
|
|
--density-scale: 1;
|
|
--density-row-y: 0.75rem;
|
|
--density-row-x: 1.25rem;
|
|
--density-cell-y: 0.5rem;
|
|
--density-tile-y: 1rem;
|
|
--density-gap: 1rem;
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
ACCENTS (hue + chroma only; lightness comes from the theme)
|
|
───────────────────────────────────────────────────────────── */
|
|
/* A tighter, well-spaced wheel: every pair is >=40deg apart and pushed
|
|
chromatic so the hues stay distinct at the accent lightness. Kept clear of
|
|
success(155) / warning(75) / destructive(20). Steel is the near-neutral. */
|
|
[data-accent="cyan"] { --accent-h: 196; --accent-c: 0.130; }
|
|
[data-accent="blue"] { --accent-h: 252; --accent-c: 0.165; }
|
|
[data-accent="violet"] { --accent-h: 298; --accent-c: 0.190; }
|
|
[data-accent="magenta"] { --accent-h: 344; --accent-c: 0.180; }
|
|
[data-accent="orange"] { --accent-h: 42; --accent-c: 0.165; }
|
|
[data-accent="amber"] { --accent-h: 88; --accent-c: 0.165; }
|
|
[data-accent="lime"] { --accent-h: 132; --accent-c: 0.195; }
|
|
[data-accent="steel"] { --accent-h: 250; --accent-c: 0.040; }
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
OLED: true black for power-saving panels. Only the raw surface,
|
|
ink and glow vars change; the dark non-derivable tokens inherit
|
|
from :root, and the opaque-grey borders keep cards from merging
|
|
into the black the way 6%-white hairlines did.
|
|
───────────────────────────────────────────────────────────── */
|
|
[data-theme="oled"] {
|
|
--bg: 0.035; --card-l: 0.105; --band-l: 0.072; --well-l: 0.020;
|
|
--bb: 0.262; --bt: 0.345; --bh: 0.405; --hl: 0.235; --border-dir: 1;
|
|
--val: 0.950; --title: 0.780; --sub: 0.575; --icon: 0.445; --ink-dir: 1;
|
|
--accent-l: 0.745; --brand-fg: oklch(0.11 0 0);
|
|
--glow-l: 0.46; --glow-c: 0.085;
|
|
--pop-l: 0.06; --pop-a: 0.86;
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
LIGHT: the existing light theme. Borders are opaque and
|
|
directionally lit (the lit top edge is lighter, sides darker,
|
|
so --border-dir flips to -1).
|
|
───────────────────────────────────────────────────────────── */
|
|
[data-theme="light"] {
|
|
color-scheme: light;
|
|
|
|
/* Raw lightness vars */
|
|
--bg: 0.965; --card-l: 1.000; --band-l: 0.972; --well-l: 0.945;
|
|
--bb: 0.855; --bt: 0.910; --bh: 0.780; --hl: 0.885; --border-dir: -1;
|
|
--val: 0.190; --title: 0.400; --sub: 0.500; --icon: 0.620; --ink-dir: -1;
|
|
--accent-l: 0.525; --brand-fg: oklch(0.99 0 0);
|
|
--glow-l: 0.90; --glow-c: 0.040;
|
|
--pop-l: 1.0; --pop-a: 0.92;
|
|
--scrim: oklch(0.22 0 0 / 0.32);
|
|
|
|
/* Light non-derivable tokens */
|
|
--foreground: oklch(0.15 0 0);
|
|
--card-foreground: oklch(0.15 0 0);
|
|
--popover-foreground: oklch(0.15 0 0);
|
|
--primary: oklch(0.15 0 0);
|
|
--primary-foreground: oklch(1 0 0);
|
|
--secondary: oklch(0 0 0 / 0.05);
|
|
--secondary-foreground: oklch(0.15 0 0);
|
|
--muted: oklch(0 0 0 / 0.04);
|
|
--muted-foreground: oklch(0.50 0 0);
|
|
--accent: oklch(0 0 0 / 0.06);
|
|
--accent-foreground: oklch(0.15 0 0);
|
|
--destructive: oklch(0.63 0.18 20);
|
|
--destructive-foreground: oklch(1 0 0);
|
|
--destructive-muted: oklch(0.63 0.18 20 / 0.12);
|
|
--input: oklch(0.97 0 0);
|
|
|
|
--glass-border: var(--card-border);
|
|
--glass-highlight: oklch(0 0 0 / 0.06);
|
|
|
|
--success: oklch(0.65 0.16 155);
|
|
--success-foreground: oklch(1 0 0);
|
|
--success-muted: oklch(0.65 0.16 155 / 0.12);
|
|
--warning: oklch(0.75 0.14 75);
|
|
--warning-foreground: oklch(0.20 0 0);
|
|
--warning-muted: oklch(0.75 0.14 75 / 0.12);
|
|
--info: oklch(0.62 0.14 250);
|
|
--info-foreground: oklch(1 0 0);
|
|
--info-muted: oklch(0.62 0.14 250 / 0.12);
|
|
--update: oklch(0.62 0.22 320);
|
|
--update-foreground: oklch(1 0 0);
|
|
--update-muted: oklch(0.62 0.22 320 / 0.12);
|
|
|
|
--sidebar: oklch(0.98 0 0 / 0.80);
|
|
--sidebar-foreground: oklch(0.15 0 0);
|
|
--sidebar-primary: oklch(0.15 0 0);
|
|
--sidebar-primary-foreground: oklch(1 0 0);
|
|
--sidebar-accent: oklch(0 0 0 / 0.06);
|
|
--sidebar-accent-foreground: oklch(0.15 0 0);
|
|
|
|
/* Label palette (light) */
|
|
--label-teal: oklch(0.55 0.12 192);
|
|
--label-teal-bg: oklch(0.55 0.12 192 / 0.12);
|
|
--label-blue: oklch(0.55 0.14 250);
|
|
--label-blue-bg: oklch(0.55 0.14 250 / 0.12);
|
|
--label-purple: oklch(0.55 0.14 300);
|
|
--label-purple-bg: oklch(0.55 0.14 300 / 0.12);
|
|
--label-rose: oklch(0.55 0.16 10);
|
|
--label-rose-bg: oklch(0.55 0.16 10 / 0.12);
|
|
--label-amber: oklch(0.60 0.16 60);
|
|
--label-amber-bg: oklch(0.60 0.16 60 / 0.12);
|
|
--label-green: oklch(0.55 0.15 150);
|
|
--label-green-bg: oklch(0.55 0.15 150 / 0.12);
|
|
--label-orange: oklch(0.58 0.16 45);
|
|
--label-orange-bg: oklch(0.58 0.16 45 / 0.12);
|
|
--label-pink: oklch(0.58 0.16 340);
|
|
--label-pink-bg: oklch(0.58 0.16 340 / 0.12);
|
|
--label-cyan: oklch(0.58 0.12 210);
|
|
--label-cyan-bg: oklch(0.58 0.12 210 / 0.12);
|
|
--label-slate: oklch(0.50 0.01 250);
|
|
--label-slate-bg: oklch(0.50 0.01 250 / 0.12);
|
|
|
|
/* Chart neutrals + grid/axis (light) */
|
|
--chart-4: oklch(0.70 0 0);
|
|
--chart-5: oklch(0.45 0 0);
|
|
--chart-grid: oklch(0 0 0 / 0.06);
|
|
--chart-tick: oklch(0 0 0 / 0.45);
|
|
|
|
/* Material simulation (light) */
|
|
--card-bevel: inset 0 1px 0 0 oklch(1 0 0 / 0.04), 0 1px 2px 0 oklch(0 0 0 / 0.05);
|
|
--button-inner-glow: inset 0 1px 0 0 oklch(1 0 0 / 0.04);
|
|
--chrome-top-highlight: inset 0 1px 0 0 oklch(1 0 0 / 0.45);
|
|
|
|
/* Shadows (light) */
|
|
--shadow-opacity: 0.10;
|
|
--shadow-2xs: 0px 1px 2px 0px hsl(0 0% 0% / 0.05);
|
|
--shadow-xs: 0px 1px 2px 0px hsl(0 0% 0% / 0.05);
|
|
--shadow-sm: 0px 1px 2px 0px hsl(0 0% 0% / 0.08);
|
|
--shadow: 0px 1px 2px 0px hsl(0 0% 0% / 0.08), 0px 1px 2px -1px hsl(0 0% 0% / 0.08);
|
|
--shadow-md: 0px 1px 2px 0px hsl(0 0% 0% / 0.10), 0px 2px 4px -1px hsl(0 0% 0% / 0.10);
|
|
--shadow-lg: 0px 1px 2px 0px hsl(0 0% 0% / 0.10), 0px 4px 6px -1px hsl(0 0% 0% / 0.10);
|
|
--shadow-xl: 0px 1px 2px 0px hsl(0 0% 0% / 0.10), 0px 8px 10px -1px hsl(0 0% 0% / 0.10);
|
|
--shadow-2xl: 0px 1px 2px 0px hsl(0 0% 0% / 0.25);
|
|
|
|
/* Terminal stays a dark well even in the light theme */
|
|
--terminal-bg: oklch(0.12 0 0);
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
CALM / READABILITY REFRESH
|
|
Three appearance attributes on <html> switch the heading family, the
|
|
severity-chart palette, and material effects. Signature (the defaults in
|
|
:root) needs no rule; Calm flips them. Placed after the theme blocks so the
|
|
reduced-effects --card-bevel override wins in every theme.
|
|
───────────────────────────────────────────────────────────── */
|
|
/* Clean headings: the readability fix. Operational headings drop the display
|
|
serif for the upright interface face. */
|
|
[data-headings="clean"] {
|
|
--font-heading: var(--font-sans);
|
|
--heading-style: normal;
|
|
}
|
|
|
|
/* Muted: desaturated severity semantics, no brand/destructive clash. */
|
|
[data-chart-style="muted"] {
|
|
--sev-critical: oklch(0.605 0.105 28);
|
|
--sev-high: oklch(0.715 0.085 70);
|
|
--sev-medium: oklch(0.675 0.045 88);
|
|
--sev-low: oklch(0.565 0.018 250);
|
|
--sev-vuln: oklch(0.605 0.045 235);
|
|
}
|
|
|
|
/* Heat: a single warm sequential ramp with zero complementary vibration. */
|
|
[data-chart-style="heat"] {
|
|
--sev-critical: oklch(0.565 0.135 30);
|
|
--sev-high: oklch(0.675 0.115 48);
|
|
--sev-medium: oklch(0.770 0.080 70);
|
|
--sev-low: oklch(0.825 0.040 82);
|
|
--sev-vuln: oklch(0.660 0.100 45);
|
|
}
|
|
|
|
/* Reduced effects: flatten the material (Calm only). Charts read the reduced
|
|
flag from the store; here we flatten the real card top-bevel token. */
|
|
[data-effects="reduced"] {
|
|
--card-bevel: none;
|
|
}
|
|
|
|
/* Heading utility: every operational title routes through this. Base sets only
|
|
family + style (Signature parity); the Calm-only descendant rule lifts weight
|
|
and tightens tracking for the sans face, beating any single tracking or weight
|
|
utility on the element. */
|
|
.font-heading {
|
|
font-family: var(--font-heading);
|
|
font-style: var(--heading-style);
|
|
}
|
|
[data-headings="clean"] .font-heading {
|
|
font-weight: 600;
|
|
letter-spacing: -0.006em;
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
TAILWIND THEME MAPPING
|
|
───────────────────────────────────────────────────────────── */
|
|
@theme inline {
|
|
--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-border: var(--border);
|
|
--color-input: var(--input);
|
|
--color-ring: var(--ring);
|
|
--color-brand: var(--brand);
|
|
--color-brand-foreground: var(--brand-foreground);
|
|
--color-brand-muted: var(--brand-muted);
|
|
--color-chart-1: var(--chart-1);
|
|
--color-chart-2: var(--chart-2);
|
|
--color-chart-3: var(--chart-3);
|
|
--color-chart-4: var(--chart-4);
|
|
--color-chart-5: var(--chart-5);
|
|
--color-sidebar: var(--sidebar);
|
|
--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-glass: var(--glass);
|
|
--color-glass-border: var(--glass-border);
|
|
--color-glass-highlight: var(--glass-highlight);
|
|
|
|
--color-stat-value: var(--stat-value);
|
|
--color-stat-title: var(--stat-title);
|
|
--color-stat-subtitle: var(--stat-subtitle);
|
|
--color-stat-icon: var(--stat-icon);
|
|
|
|
--color-card-border: var(--card-border);
|
|
--color-card-border-top: var(--card-border-top);
|
|
--color-card-border-hover: var(--card-border-hover);
|
|
|
|
--color-band: var(--band);
|
|
--color-well: var(--well);
|
|
--color-hairline: var(--hairline);
|
|
|
|
--color-chart-grid: var(--chart-grid);
|
|
--color-chart-tick: var(--chart-tick);
|
|
|
|
--color-success: var(--success);
|
|
--color-success-foreground: var(--success-foreground);
|
|
--color-success-muted: var(--success-muted);
|
|
--color-warning: var(--warning);
|
|
--color-warning-foreground: var(--warning-foreground);
|
|
--color-warning-muted: var(--warning-muted);
|
|
--color-info: var(--info);
|
|
--color-info-foreground: var(--info-foreground);
|
|
--color-info-muted: var(--info-muted);
|
|
--color-update: var(--update);
|
|
--color-update-foreground: var(--update-foreground);
|
|
--color-update-muted: var(--update-muted);
|
|
--color-destructive-muted: var(--destructive-muted);
|
|
--color-action-transformative: var(--action-transformative);
|
|
|
|
--font-sans: var(--font-sans);
|
|
--font-mono: var(--font-mono);
|
|
--font-serif: var(--font-serif);
|
|
--font-display: var(--font-display);
|
|
|
|
--radius-sm: calc(var(--radius) - 4px);
|
|
--radius-md: calc(var(--radius) - 2px);
|
|
--radius-lg: var(--radius);
|
|
--radius-xl: calc(var(--radius) + 4px);
|
|
|
|
--shadow-2xs: var(--shadow-2xs);
|
|
--shadow-xs: var(--shadow-xs);
|
|
--shadow-sm: var(--shadow-sm);
|
|
--shadow: var(--shadow);
|
|
--shadow-md: var(--shadow-md);
|
|
--shadow-lg: var(--shadow-lg);
|
|
--shadow-xl: var(--shadow-xl);
|
|
--shadow-2xl: var(--shadow-2xl);
|
|
--shadow-card-bevel: var(--card-bevel);
|
|
--shadow-btn-glow: var(--button-inner-glow);
|
|
--shadow-chrome-top: var(--chrome-top-highlight);
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
BASE STYLES
|
|
───────────────────────────────────────────────────────────── */
|
|
@layer base {
|
|
* {
|
|
@apply border-border outline-ring/50;
|
|
}
|
|
|
|
body {
|
|
@apply text-foreground;
|
|
/* Ambient top-center glow tracks the accent hue and the --glow knob. Shows
|
|
on screens whose root is transparent (e.g. Login). Full-screen app shells
|
|
that paint an opaque --background must use .app-canvas instead, or they
|
|
occlude this. */
|
|
background:
|
|
radial-gradient(
|
|
ellipse 156% 106% at 50% -6%,
|
|
var(--glow-color),
|
|
transparent 70%
|
|
),
|
|
var(--background);
|
|
background-attachment: fixed;
|
|
}
|
|
}
|
|
|
|
/* The page canvas: opaque --background base plus the ambient accent glow. Use on
|
|
full-screen app shells (EditorLayout, the loading wrapper) that would otherwise
|
|
paint a flat bg-background over the body glow and hide it entirely. */
|
|
.app-canvas {
|
|
background:
|
|
radial-gradient(
|
|
ellipse 156% 106% at 50% -6%,
|
|
var(--glow-color),
|
|
transparent 70%
|
|
),
|
|
var(--background);
|
|
background-attachment: fixed;
|
|
}
|
|
|
|
/* Accent wash for floating panels (sheets & large dialogs). Layers the ambient
|
|
accent glow as a background image over the panel's own --popover fill, so a
|
|
portaled surface carries the same accent personality .app-canvas gives the
|
|
page. Image-only (the popover color stays the base) to keep the glass
|
|
translucency and backdrop blur; anchored to the panel top so the translucent
|
|
header bands let it bleed through. */
|
|
.panel-glow {
|
|
background-image: radial-gradient(
|
|
ellipse 130% 42% at 50% 0%,
|
|
var(--glow-color),
|
|
transparent 64%
|
|
);
|
|
}
|
|
|
|
/* Density: compact mode compresses row/cell padding by ~50%. */
|
|
body.density-compact {
|
|
--density-scale: 0.75;
|
|
--density-row-y: 0.375rem;
|
|
--density-row-x: 1rem;
|
|
--density-cell-y: 0.25rem;
|
|
--density-tile-y: 0.625rem;
|
|
--density-gap: 0.75rem;
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
TYPOGRAPHY & RENDERING
|
|
───────────────────────────────────────────────────────────── */
|
|
html,
|
|
body,
|
|
#root {
|
|
height: 100%;
|
|
width: 100%;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* Text-size control: one root multiplier scales the whole rem-based type ramp.
|
|
Layout px is untouched; only rem-sized text grows/shrinks. */
|
|
html {
|
|
font-size: calc(16px * var(--type-scale));
|
|
}
|
|
|
|
body {
|
|
font-family: var(--font-sans), system-ui, -apple-system, BlinkMacSystemFont,
|
|
'Segoe UI', sans-serif;
|
|
line-height: 1.5;
|
|
font-weight: 400;
|
|
font-synthesis: none;
|
|
text-rendering: optimizeLegibility;
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
SCROLLBAR
|
|
───────────────────────────────────────────────────────────── */
|
|
::-webkit-scrollbar {
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
::-webkit-scrollbar-thumb {
|
|
background: oklch(0.40 0 0 / 0.5);
|
|
border-radius: 4px;
|
|
transition: background var(--duration-fast) ease;
|
|
}
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: oklch(0.55 0 0 / 0.7);
|
|
}
|
|
.dark ::-webkit-scrollbar-thumb {
|
|
background: oklch(0.35 0 0 / 0.6);
|
|
}
|
|
.dark ::-webkit-scrollbar-thumb:hover {
|
|
background: oklch(0.50 0 0 / 0.8);
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
GLASS UTILITIES
|
|
───────────────────────────────────────────────────────────── */
|
|
.glass {
|
|
background: var(--glass);
|
|
border: 1px solid var(--glass-border);
|
|
}
|
|
|
|
.glass-strong {
|
|
background: var(--glass);
|
|
border: 1px solid var(--glass-border);
|
|
}
|
|
|
|
.glass-float {
|
|
background: var(--popover);
|
|
backdrop-filter: blur(10px) saturate(1.15);
|
|
-webkit-backdrop-filter: blur(10px) saturate(1.15);
|
|
border: 1px solid var(--glass-border);
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
ANIMATION KEYFRAMES
|
|
───────────────────────────────────────────────────────────── */
|
|
@keyframes fade-in {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
@keyframes fade-up {
|
|
from { opacity: 0; transform: translateY(8px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
@keyframes fade-down {
|
|
from { opacity: 0; transform: translateY(-8px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
@keyframes slide-in-left {
|
|
from { opacity: 0; transform: translateX(-12px); }
|
|
to { opacity: 1; transform: translateX(0); }
|
|
}
|
|
@keyframes scale-in {
|
|
from { opacity: 0; transform: scale(0.95); }
|
|
to { opacity: 1; transform: scale(1); }
|
|
}
|
|
@keyframes shimmer {
|
|
0% { transform: translateY(-100%); }
|
|
100% { transform: translateY(200%); }
|
|
}
|
|
@keyframes masthead-rail-glow {
|
|
0%, 100% { opacity: 0.55; box-shadow: inset 0 0 0 0 transparent; }
|
|
50% { opacity: 1; box-shadow: inset 0 0 10px 0 oklch(1 0 0 / 0.12); }
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
ANIMATION UTILITIES
|
|
───────────────────────────────────────────────────────────── */
|
|
.animate-fade-in {
|
|
animation: fade-in var(--duration-base) var(--ease-out-expo) both;
|
|
}
|
|
.animate-fade-up {
|
|
animation: fade-up var(--duration-base) var(--ease-out-expo) both;
|
|
}
|
|
.animate-fade-down {
|
|
animation: fade-down var(--duration-base) var(--ease-out-expo) both;
|
|
}
|
|
.animate-slide-in-left {
|
|
animation: slide-in-left var(--duration-base) var(--ease-out-expo) both;
|
|
}
|
|
.animate-scale-in {
|
|
animation: scale-in var(--duration-fast) var(--ease-spring) both;
|
|
}
|
|
.animate-shimmer {
|
|
animation: shimmer 4.5s ease-in-out infinite alternate;
|
|
}
|
|
.masthead-rail-shimmer {
|
|
animation: shimmer 11s ease-in-out infinite alternate;
|
|
}
|
|
.masthead-rail-glow {
|
|
animation: masthead-rail-glow 5.5s ease-in-out infinite alternate;
|
|
}
|
|
|
|
/* Stagger delay helpers */
|
|
.animate-delay-50 { animation-delay: 50ms; }
|
|
.animate-delay-100 { animation-delay: 100ms; }
|
|
.animate-delay-150 { animation-delay: 150ms; }
|
|
.animate-delay-200 { animation-delay: 200ms; }
|
|
.animate-delay-300 { animation-delay: 300ms; }
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
PREFERS REDUCED MOTION - zero-cost accessibility
|
|
───────────────────────────────────────────────────────────── */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
}
|
|
}
|
|
|
|
/* The "Reduced motion" appearance setting (data-motion="reduced" on <html>)
|
|
clamps CSS animations/transitions, mirroring the OS preference above. Sonner
|
|
toasts animate via inline transform/opacity (React state), not CSS, so they
|
|
are unaffected. framer-motion is handled separately by MotionConfig. */
|
|
[data-motion="reduced"] *,
|
|
[data-motion="reduced"] *::before,
|
|
[data-motion="reduced"] *::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────────
|
|
XTERM.JS
|
|
───────────────────────────────────────────────────────────── */
|
|
.xterm {
|
|
padding: 0;
|
|
}
|