Files
lib-theme-prism/theme.css
jules 37c7ed3e96
Some checks failed
validate theme / validate (push) Has been cancelled
Derive primary-button gradient from --primary; narrow calm bg to violet family
Two color-coherence fixes driven by me.sky-ai.com's UI review:

- The premium button gradient was a fixed 290→350 sweep that ended in
  hot pink, so CTAs read magenta against violet nav/links — and ignored
  the data-palette accents entirely. It now derives from var(--primary)
  with a subtle ±12° hue sweep (relative color syntax; degrades to the
  solid primary fill on older browsers). Dark mode needs no override —
  it follows dark's --primary. Affects all Prism consumers.

- The calm background variant (consumer surfaces) narrows from the full
  rainbow (violet/pink/cyan/mint corners) to an analogous violet family
  (periwinkle 245 → lavender 280 → violet 295 → pink 330), light + dark,
  including calm-specific aurora drift blobs. The cyan/green corners sat
  opposite the violet brand and made pages read as four brands at once.
  Full-strength drift is untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 22:41:39 +10:00

800 lines
31 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* prism — crystal-clear glass theme.
*
* Iridescent multi-hue palette (lavender → peach → cyan → mint) refracted
* through frosted-glass surfaces. Earns its name twice: once for the
* literal glass refraction, once for the hue spectrum the corner glows
* span. Born in skyai-finance; lifted into a standalone theme when the
* shape stabilised.
*
* Forked from skyrise (lib-theme-skyrise) with four product-driven
* changes that became theme-wide:
*
* 1. Bakes in --warning + --warning-foreground (skyrise had them;
* pristine did not). Any product that displays health/risk needs
* warning semantics in both modes.
* 2. Adds a --text-hero size step (3.5rem) above display, for KPI
* heroes and editorial mastheads.
* 3. Ships a `.num` utility that sets tabular-nums + slashed-zero +
* letter-spacing:0 — non-negotiable on money numbers, useful
* anywhere digits matter.
* 4. Redesigns the dark-mode background. Skyrise's dark blobs sat at
* L≈0.18 with chroma≈0.12, near OKLCH gamut edges, which clipped
* to muddy gray. Here the corner glows are pushed to L≈0.30 with
* lower chroma, paired with a cleaner near-black base — they read
* as soft brand-accent light instead of tinted fog.
*
* Also dropped: the 11 background atmospheres and 4 surface tints
* skyrise shipped for white-label flexibility. Prism picks one brand
* surface (light + dark) and ships it. If alternates return, re-import
* from skyrise.
*
* Fonts loaded by the consuming app via Google Fonts URL @import at the
* very top of its entry CSS — see README. Do NOT @import them here.
*/
:root {
/* ── Colors ──
* Light mode rests on an iridescent lavender→peach→mint→rose gradient
* (defined in @layer base below). Glass surfaces sit at low alpha so the
* gradient reads through; foreground is pushed to L=0.16 for vivid,
* never-faint text against translucent surfaces. */
--background: oklch(0.97 0.015 295);
--foreground: oklch(0.16 0.025 285);
--card: oklch(1 0 0 / 0.58);
--card-foreground: oklch(0.16 0.025 285);
--popover: oklch(1 0 0 / 0.74);
--popover-foreground: oklch(0.16 0.025 285);
--primary: oklch(0.55 0.22 295);
--primary-foreground: oklch(1 0 0);
--secondary: oklch(0.96 0.012 290);
--secondary-foreground: oklch(0.22 0.025 285);
--muted: oklch(0.95 0.012 290);
--muted-foreground: oklch(0.42 0.02 285);
--accent: oklch(0.93 0.04 295);
--accent-foreground: oklch(0.25 0.05 295);
--destructive: oklch(0.6 0.24 25);
--success: oklch(0.5 0.18 150);
--success-foreground: oklch(1 0 0);
--warning: oklch(0.7 0.18 75);
--warning-foreground: oklch(0.18 0.04 75);
--border: oklch(0 0 0 / 0.09);
--input: oklch(0 0 0 / 0.09);
--ring: oklch(0.55 0.22 295 / 0.55);
/* Charts: violet → blue-cyan → amber → rose → emerald. Hues spread ≥60°
* apart and lightness is varied between adjacent indices so adjacent
* series stay perceptually distinct in multi-line / stacked charts. */
--chart-1: oklch(0.55 0.24 295);
--chart-2: oklch(0.62 0.2 230);
--chart-3: oklch(0.82 0.18 80);
--chart-4: oklch(0.72 0.2 350);
--chart-5: oklch(0.5 0.18 150);
--sidebar: oklch(1 0 0 / 0.5);
--sidebar-foreground: oklch(0.16 0.025 285);
--sidebar-primary: oklch(0.55 0.22 295);
--sidebar-primary-foreground: oklch(1 0 0);
--sidebar-accent: oklch(0.93 0.04 295);
--sidebar-accent-foreground: oklch(0.25 0.05 295);
--sidebar-border: oklch(0 0 0 / 0.07);
--sidebar-ring: oklch(0.55 0.22 295 / 0.55);
--syntax-keyword: oklch(0.5 0.22 295);
--syntax-string: oklch(0.5 0.16 150);
--syntax-number: oklch(0.55 0.18 35);
--syntax-comment: oklch(0.52 0.02 285);
--syntax-function: oklch(0.48 0.18 235);
--syntax-type: oklch(0.48 0.14 195);
/* ── Chat surfaces ──
* Contract for lib-agent-chat-ui / lib-agent-dock-ui. User bubble is
* a filled, deep, high-contrast panel with light text; assistant
* bubble is a card-like surface with a hairline border. */
--chat-user-bg: oklch(0.48 0.2 295);
--chat-user-fg: oklch(1 0 0);
--chat-assistant-bg: oklch(1 0 0 / 0.7);
--chat-assistant-fg: oklch(0.16 0.025 285);
--chat-assistant-border: oklch(0 0 0 / 0.09);
/* ── Radius ── Generous, premium feel. */
--radius: 1rem;
/* ── Motion ── Apple-style spring physics. */
--duration-fast: 180ms;
--duration-base: 320ms;
--duration-slow: 450ms;
--duration-slower: 600ms;
--duration-spring: 520ms;
--ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
--ease-decelerate: cubic-bezier(0, 0, 0.2, 1);
--ease-accelerate: cubic-bezier(0.4, 0, 1, 1);
--ease-spring-gentle: linear(0, 0.04 8%, 0.12 15%, 0.23 22%, 0.37 30%, 0.51 38%, 0.63 46%, 0.74 54%, 0.83 62%, 0.89 70%, 0.94 78%, 0.97 86%, 0.99 94%, 1);
--ease-spring-snappy: linear(0, 0.06 6%, 0.18 12%, 0.33 18%, 0.5 25%, 0.65 32%, 0.8 40%, 0.9 48%, 0.98 56%, 1.015 64%, 1.02 72%, 1.015 80%, 1.005 88%, 1);
--ease-spring-bouncy: linear(0, 0.08 8%, 0.22 16%, 0.4 24%, 0.58 32%, 0.75 40%, 0.88 48%, 0.98 56%, 1.04 64%, 1.06 72%, 1.04 80%, 1.01 86%, 0.995 92%, 1);
--ease-emphasized: var(--ease-spring-snappy);
/* ── Typography ── Bumped one notch from typical sizes for readability.
* Body sits at 17px (1.0625rem) — never-faint text. All scales ride
* along proportionally. Hero is the finance KPI step above display. */
--text-hero-size: 3.5rem;
--text-hero-lh: 3.75rem;
--text-display-size: 3rem;
--text-display-lh: 3.25rem;
--text-headline-size: 2rem;
--text-headline-lh: 2.375rem;
--text-title-size: 1.5rem;
--text-title-lh: 1.875rem;
--text-body-size: 1.0625rem;
--text-body-lh: 1.625rem;
--text-label-size: 0.9375rem;
--text-label-lh: 1.25rem;
--text-caption-size: 0.8125rem;
--text-caption-lh: 1.125rem;
/* ── Elevation ── Soft layered shadows in line with glass philosophy. */
--elevation-0: none;
--elevation-1: 0 1px 2px oklch(0 0 0 / 0.06);
--elevation-2: 0 2px 8px oklch(0 0 0 / 0.07), 0 1px 2px oklch(0 0 0 / 0.04);
--elevation-3: 0 4px 16px oklch(0 0 0 / 0.08), 0 2px 4px oklch(0 0 0 / 0.04);
--elevation-4: 0 12px 36px oklch(0.5 0.18 295 / 0.14), 0 4px 12px oklch(0 0 0 / 0.06);
--elevation-5: 0 20px 56px oklch(0.5 0.18 295 / 0.18), 0 8px 20px oklch(0 0 0 / 0.08);
--spacing: 0.25rem;
--border-width-thin: 0.5px;
--border-width-base: 1px;
--border-width-thick: 2px;
--border-width-heavy: 4px;
/* ── Glass vibrancy ── Heavy frost, like the skyrise reference. */
--glass-blur: 32px;
--glass-saturate: 220%;
--glass-brightness: 1.06;
--glass-contrast: 1.02;
/* ── Font stacks ── Loaded by the consuming app. */
--font-sans: "Inter", "Inter Variable", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
--font-heading: "Instrument Sans", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, "Cascadia Mono", Consolas, monospace;
/* Tailwind utility families. Distinct names so a consuming app's
* `@theme inline` can indirect to these (--font-heading/--font-sans collide
* with Tailwind's own font-* namespace). Mirror --font-heading/--font-sans. */
--heading-family: "Instrument Sans", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--body-family: "Inter", "Inter Variable", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
/*
* Dark mode — clean deep-violet base. Foreground stays high-L for the
* vivid, never-faint feel. Corner accents live in the body bg block below.
*/
.dark {
--background: oklch(0.10 0.015 285);
--foreground: oklch(0.97 0.005 280);
--card: oklch(0.22 0.04 290 / 0.55);
--card-foreground: oklch(0.97 0.005 280);
--popover: oklch(0.22 0.04 290 / 0.78);
--popover-foreground: oklch(0.97 0.005 280);
--primary: oklch(0.7 0.2 295);
--primary-foreground: oklch(0.14 0.04 295);
--secondary: oklch(0.26 0.03 290);
--secondary-foreground: oklch(0.95 0.005 280);
--muted: oklch(0.26 0.03 290);
--muted-foreground: oklch(0.78 0.015 285);
--accent: oklch(0.32 0.06 295);
--accent-foreground: oklch(0.95 0.04 295);
--destructive: oklch(0.7 0.22 25);
--success: oklch(0.74 0.16 150);
--success-foreground: oklch(0.12 0.02 150);
--warning: oklch(0.78 0.16 75);
--warning-foreground: oklch(0.16 0.04 75);
--border: oklch(1 0 0 / 0.1);
--input: oklch(1 0 0 / 0.1);
--ring: oklch(0.7 0.2 295 / 0.55);
--chart-1: oklch(0.68 0.24 295);
--chart-2: oklch(0.72 0.2 230);
--chart-3: oklch(0.88 0.18 80);
--chart-4: oklch(0.82 0.2 350);
--chart-5: oklch(0.6 0.18 150);
--sidebar: oklch(0.2 0.04 290 / 0.55);
--sidebar-foreground: oklch(0.97 0.005 280);
--sidebar-primary: oklch(0.7 0.2 295);
--sidebar-primary-foreground: oklch(0.14 0.04 295);
--sidebar-accent: oklch(0.32 0.06 295);
--sidebar-accent-foreground: oklch(0.95 0.04 295);
--sidebar-border: oklch(1 0 0 / 0.08);
--sidebar-ring: oklch(0.7 0.2 295 / 0.55);
--syntax-keyword: oklch(0.78 0.18 295);
--syntax-string: oklch(0.8 0.16 150);
--syntax-number: oklch(0.82 0.16 35);
--syntax-comment: oklch(0.65 0.02 285);
--syntax-function: oklch(0.78 0.16 235);
--syntax-type: oklch(0.78 0.14 195);
/* ── Chat surfaces (dark) ── */
--chat-user-bg: oklch(0.54 0.2 295);
--chat-user-fg: oklch(1 0 0);
--chat-assistant-bg: oklch(0.24 0.04 290 / 0.7);
--chat-assistant-fg: oklch(0.97 0.005 280);
--chat-assistant-border: oklch(1 0 0 / 0.1);
--elevation-1: 0 1px 2px oklch(0 0 0 / 0.4);
--elevation-2: 0 2px 8px oklch(0 0 0 / 0.4), 0 1px 2px oklch(0 0 0 / 0.3);
--elevation-3: 0 4px 16px oklch(0 0 0 / 0.45), 0 2px 4px oklch(0 0 0 / 0.3);
--elevation-4: 0 12px 36px oklch(0.4 0.16 295 / 0.18), 0 4px 12px oklch(0 0 0 / 0.5);
--elevation-5: 0 20px 56px oklch(0.4 0.16 295 / 0.22), 0 8px 20px oklch(0 0 0 / 0.55);
--glass-brightness: 0.92;
--glass-saturate: 200%;
}
/* ── Accent palettes ──────────────────────────────────────────────
* Apply by setting `data-palette="sky|rose|sage"` on `<html>` (or any
* subtree). Pairs with `.dark`. Violet is the default — no attribute
* needed. Calibrated to Prism's L/C scale so the crystal-glass surfaces
* stay coherent; we don't try to match Avalon's flatter palette.
* Consumed by arcadia-personal-cloud-web's accent picker. */
/* Sky — calm blue, the default consumer accent on me.sky-ai.com. */
[data-palette="sky"] {
--primary: oklch(0.55 0.18 235);
--primary-foreground: oklch(1 0 0);
--accent: oklch(0.93 0.04 235);
--accent-foreground: oklch(0.25 0.05 235);
--ring: oklch(0.55 0.18 235 / 0.55);
--chart-1: oklch(0.55 0.2 235);
--sidebar-primary: oklch(0.55 0.18 235);
--sidebar-accent: oklch(0.93 0.04 235);
--sidebar-accent-foreground: oklch(0.25 0.05 235);
--sidebar-ring: oklch(0.55 0.18 235 / 0.55);
--chat-user-bg: oklch(0.48 0.18 235);
--syntax-keyword: oklch(0.5 0.18 235);
}
/* Rose — warm coral-pink. */
[data-palette="rose"] {
--primary: oklch(0.6 0.2 10);
--primary-foreground: oklch(1 0 0);
--accent: oklch(0.93 0.05 10);
--accent-foreground: oklch(0.28 0.08 10);
--ring: oklch(0.6 0.2 10 / 0.55);
--chart-1: oklch(0.6 0.22 10);
--sidebar-primary: oklch(0.6 0.2 10);
--sidebar-accent: oklch(0.93 0.05 10);
--sidebar-accent-foreground: oklch(0.28 0.08 10);
--sidebar-ring: oklch(0.6 0.2 10 / 0.55);
--chat-user-bg: oklch(0.52 0.2 10);
--syntax-keyword: oklch(0.55 0.18 10);
}
/* Sage — calm green, personal and organic. */
[data-palette="sage"] {
--primary: oklch(0.55 0.13 155);
--primary-foreground: oklch(1 0 0);
--accent: oklch(0.92 0.045 150);
--accent-foreground: oklch(0.3 0.08 155);
--ring: oklch(0.55 0.13 155 / 0.55);
--chart-1: oklch(0.55 0.15 155);
--sidebar-primary: oklch(0.55 0.13 155);
--sidebar-accent: oklch(0.92 0.045 150);
--sidebar-accent-foreground: oklch(0.3 0.08 155);
--sidebar-ring: oklch(0.55 0.13 155 / 0.55);
--chat-user-bg: oklch(0.48 0.13 155);
--syntax-keyword: oklch(0.5 0.13 155);
}
.dark[data-palette="sky"],
.dark [data-palette="sky"] {
--primary: oklch(0.72 0.18 235);
--primary-foreground: oklch(0.14 0.04 235);
--accent: oklch(0.32 0.06 235);
--accent-foreground: oklch(0.95 0.04 235);
--ring: oklch(0.72 0.18 235 / 0.55);
--chart-1: oklch(0.7 0.2 235);
--sidebar-primary: oklch(0.72 0.18 235);
--sidebar-primary-foreground: oklch(0.14 0.04 235);
--sidebar-accent: oklch(0.32 0.06 235);
--sidebar-accent-foreground: oklch(0.95 0.04 235);
--sidebar-ring: oklch(0.72 0.18 235 / 0.55);
--chat-user-bg: oklch(0.56 0.18 235);
--syntax-keyword: oklch(0.78 0.16 235);
}
.dark[data-palette="rose"],
.dark [data-palette="rose"] {
--primary: oklch(0.74 0.18 10);
--primary-foreground: oklch(0.16 0.04 10);
--accent: oklch(0.32 0.06 10);
--accent-foreground: oklch(0.95 0.04 10);
--ring: oklch(0.74 0.18 10 / 0.55);
--chart-1: oklch(0.72 0.2 10);
--sidebar-primary: oklch(0.74 0.18 10);
--sidebar-primary-foreground: oklch(0.16 0.04 10);
--sidebar-accent: oklch(0.32 0.06 10);
--sidebar-accent-foreground: oklch(0.95 0.04 10);
--sidebar-ring: oklch(0.74 0.18 10 / 0.55);
--chat-user-bg: oklch(0.58 0.2 10);
--syntax-keyword: oklch(0.8 0.16 10);
}
.dark[data-palette="sage"],
.dark [data-palette="sage"] {
--primary: oklch(0.72 0.13 155);
--primary-foreground: oklch(0.14 0.04 155);
--accent: oklch(0.3 0.05 155);
--accent-foreground: oklch(0.95 0.04 155);
--ring: oklch(0.72 0.13 155 / 0.55);
--chart-1: oklch(0.7 0.15 155);
--sidebar-primary: oklch(0.72 0.13 155);
--sidebar-primary-foreground: oklch(0.14 0.04 155);
--sidebar-accent: oklch(0.3 0.05 155);
--sidebar-accent-foreground: oklch(0.95 0.04 155);
--sidebar-ring: oklch(0.72 0.13 155 / 0.55);
--chat-user-bg: oklch(0.56 0.13 155);
--syntax-keyword: oklch(0.8 0.13 155);
}
@layer base {
body {
background-attachment: fixed;
min-height: 100vh;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
letter-spacing: -0.011em;
font-family: var(--font-sans);
font-size: var(--text-body-size);
line-height: var(--text-body-lh);
}
/* Themed text selection — keeps the violet identity. */
::selection {
background-color: oklch(0.55 0.22 295 / 0.28);
color: var(--foreground);
}
.dark ::selection {
background-color: oklch(0.7 0.2 295 / 0.4);
color: var(--foreground);
}
/* Themed scrollbars (Firefox + Chromium; macOS overlay ignores both). */
* {
scrollbar-width: thin;
scrollbar-color: oklch(0 0 0 / 0.18) transparent;
}
.dark * {
scrollbar-color: oklch(1 0 0 / 0.18) transparent;
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
background-color: oklch(0 0 0 / 0.18);
border-radius: 999px;
border: 2px solid transparent;
background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover {
background-color: oklch(0 0 0 / 0.28);
background-clip: padding-box;
}
.dark ::-webkit-scrollbar-thumb {
background-color: oklch(1 0 0 / 0.18);
background-clip: padding-box;
}
.dark ::-webkit-scrollbar-thumb:hover {
background-color: oklch(1 0 0 / 0.28);
background-clip: padding-box;
}
/* Headings get the display family — Instrument Sans's tighter counters
* against Inter's open body feel. */
h1, h2, h3, h4, h5, h6,
[data-slot="card-title"],
[data-slot="dialog-title"],
[data-slot="alert-dialog-title"],
[data-slot="sheet-title"],
[data-slot="drawer-title"] {
font-family: var(--font-heading);
letter-spacing: -0.02em;
font-weight: 600;
}
/*
* ── Light-mode body ── iridescent drift, the brand surface.
*/
body,
body[data-bg="drift"],
.bg-variant-drift {
background-image:
radial-gradient(ellipse 80% 60% at 12% 8%, oklch(0.92 0.09 295 / 0.7), transparent 60%),
radial-gradient(ellipse 70% 60% at 88% 14%, oklch(0.93 0.1 350 / 0.7), transparent 55%),
radial-gradient(ellipse 80% 60% at 92% 88%, oklch(0.93 0.1 200 / 0.7), transparent 60%),
radial-gradient(ellipse 70% 60% at 8% 92%, oklch(0.94 0.09 145 / 0.65), transparent 55%),
linear-gradient(180deg, oklch(0.985 0.02 295), oklch(0.96 0.025 320));
}
/*
* ── Dark-mode body ── deep clean base + four soft corner glows.
*
* Design: blob L pushed to ~0.30 (vs skyrise's 0.18) so they sit well
* above the L=0.10 base — perceived as *light* rather than coloured
* fog. Chroma dropped to 0.060.09 (vs 0.120.14) to stay inside OKLCH
* dark-gamut sweet-spot and avoid the gray clip. Base gradient is
* near-monochrome (chroma 0.010.02) so the accents do all the colour
* work — same hue palette as light (purple→pink→cyan→mint), but
* articulated through light, not stain.
*/
html.dark body,
html.dark body[data-bg="drift"],
html.dark .bg-variant-drift {
background-image:
radial-gradient(ellipse 70% 55% at 10% 8%, oklch(0.32 0.09 295 / 0.65), transparent 60%),
radial-gradient(ellipse 60% 50% at 90% 14%, oklch(0.30 0.09 340 / 0.60), transparent 55%),
radial-gradient(ellipse 70% 55% at 92% 90%, oklch(0.28 0.07 200 / 0.55), transparent 60%),
radial-gradient(ellipse 60% 50% at 8% 92%, oklch(0.30 0.06 145 / 0.50), transparent 55%),
linear-gradient(180deg, oklch(0.10 0.012 285), oklch(0.08 0.018 295));
}
/*
* Animated drift layer — two large radial-blob fields that translate
* and rotate slowly, driving the chroma the glass refracts.
*/
[data-slot="aurora-field"] { display: none; }
body[data-bg="drift"] [data-slot="aurora-field"],
body:not([data-bg]) [data-slot="aurora-field"] { display: block; }
.aurora-blob {
position: absolute;
inset: -25%;
background-repeat: no-repeat;
will-change: transform;
}
.aurora-blob-1 {
background-image:
radial-gradient(ellipse 44% 36% at 22% 28%, oklch(0.9 0.13 295 / 0.85), transparent 60%),
radial-gradient(ellipse 38% 32% at 78% 22%, oklch(0.9 0.13 350 / 0.8), transparent 55%);
animation: prism-drift-a 32s ease-in-out infinite;
}
.aurora-blob-2 {
background-image:
radial-gradient(ellipse 48% 38% at 82% 74%, oklch(0.9 0.14 200 / 0.8), transparent 60%),
radial-gradient(ellipse 42% 34% at 16% 80%, oklch(0.92 0.12 145 / 0.75), transparent 55%);
animation: prism-drift-b 40s ease-in-out infinite;
}
/* Dark variant of the drifting aurora — same L/chroma discipline as
* the static dark base above. Same hues, tame chroma, well above base. */
html.dark .aurora-blob-1 {
background-image:
radial-gradient(ellipse 44% 36% at 22% 28%, oklch(0.32 0.09 295 / 0.55), transparent 60%),
radial-gradient(ellipse 38% 32% at 78% 22%, oklch(0.30 0.10 340 / 0.50), transparent 55%);
}
html.dark .aurora-blob-2 {
background-image:
radial-gradient(ellipse 48% 38% at 82% 74%, oklch(0.28 0.08 200 / 0.50), transparent 60%),
radial-gradient(ellipse 42% 34% at 16% 80%, oklch(0.30 0.07 145 / 0.45), transparent 55%);
}
/*
* ── Calm variant ── opt-in via body[data-bg="calm"]. Quieter AND
* narrower than drift: corner gradients drop to ~45% alpha, the
* drift layer dims to match, and the hue story tightens from the
* full rainbow (violet/pink/cyan/mint) to an analogous violet
* family (periwinkle 245 → lavender 280 → violet 295 → pink 330).
* The cyan/green corners sat opposite the violet brand on the hue
* wheel and made consumer pages read as four different brands at
* once. For consumer surfaces (me.sky-ai.com) where full-strength
* drift floods short pages and the app reads as a white card
* floating on a poster.
*/
body[data-bg="calm"] {
background-image:
radial-gradient(ellipse 80% 60% at 12% 8%, oklch(0.92 0.09 295 / 0.32), transparent 60%),
radial-gradient(ellipse 70% 60% at 88% 14%, oklch(0.93 0.09 330 / 0.30), transparent 55%),
radial-gradient(ellipse 80% 60% at 92% 88%, oklch(0.93 0.08 245 / 0.30), transparent 60%),
radial-gradient(ellipse 70% 60% at 8% 92%, oklch(0.94 0.07 280 / 0.28), transparent 55%),
linear-gradient(180deg, oklch(0.985 0.012 295), oklch(0.97 0.015 310));
}
html.dark body[data-bg="calm"] {
background-image:
radial-gradient(ellipse 70% 55% at 10% 8%, oklch(0.32 0.09 295 / 0.65), transparent 60%),
radial-gradient(ellipse 60% 50% at 90% 14%, oklch(0.30 0.08 330 / 0.60), transparent 55%),
radial-gradient(ellipse 70% 55% at 92% 90%, oklch(0.28 0.06 245 / 0.55), transparent 60%),
radial-gradient(ellipse 60% 50% at 8% 92%, oklch(0.30 0.05 280 / 0.50), transparent 55%),
linear-gradient(180deg, oklch(0.10 0.012 285), oklch(0.08 0.018 295));
}
body[data-bg="calm"] [data-slot="aurora-field"] { display: block; }
body[data-bg="calm"] .aurora-blob { opacity: 0.4; }
html.dark body[data-bg="calm"] .aurora-blob { opacity: 0.7; }
/* Calm drift blobs ride the same narrowed violet-family hues. */
body[data-bg="calm"] .aurora-blob-1 {
background-image:
radial-gradient(ellipse 44% 36% at 22% 28%, oklch(0.9 0.11 295 / 0.85), transparent 60%),
radial-gradient(ellipse 38% 32% at 78% 22%, oklch(0.9 0.1 330 / 0.8), transparent 55%);
}
body[data-bg="calm"] .aurora-blob-2 {
background-image:
radial-gradient(ellipse 48% 38% at 82% 74%, oklch(0.9 0.1 245 / 0.8), transparent 60%),
radial-gradient(ellipse 42% 34% at 16% 80%, oklch(0.92 0.08 280 / 0.75), transparent 55%);
}
html.dark body[data-bg="calm"] .aurora-blob-1 {
background-image:
radial-gradient(ellipse 44% 36% at 22% 28%, oklch(0.32 0.09 295 / 0.55), transparent 60%),
radial-gradient(ellipse 38% 32% at 78% 22%, oklch(0.30 0.08 330 / 0.50), transparent 55%);
}
html.dark body[data-bg="calm"] .aurora-blob-2 {
background-image:
radial-gradient(ellipse 48% 38% at 82% 74%, oklch(0.28 0.06 245 / 0.50), transparent 60%),
radial-gradient(ellipse 42% 34% at 16% 80%, oklch(0.30 0.05 280 / 0.45), transparent 55%);
}
@keyframes prism-drift-a {
0%, 100% { transform: translate3d(0, 0, 0) rotate(0deg) scale(1); }
33% { transform: translate3d(5%, -3%, 0) rotate(10deg) scale(1.07); }
66% { transform: translate3d(-3%, 4%, 0) rotate(-8deg) scale(0.96); }
}
@keyframes prism-drift-b {
0%, 100% { transform: translate3d(0, 0, 0) rotate(0deg) scale(1); }
33% { transform: translate3d(-5%, 4%, 0) rotate(-12deg) scale(1.09); }
66% { transform: translate3d(4%, -3%, 0) rotate(8deg) scale(0.94); }
}
@media (prefers-reduced-motion: reduce) {
.aurora-blob { animation: none; }
}
/*
* ── Glass surfaces ── Apple-vibrancy stack on every shadcn surface
* that exposes a [data-slot]. blur softens, saturate pumps the
* gradient chroma the glass is sampling, brightness/contrast keep
* edges legible.
*/
[data-slot="card"],
[data-slot="popover-content"],
[data-slot="dropdown-menu-content"],
[data-slot="context-menu-content"],
[data-slot="menubar-content"],
[data-slot="hover-card-content"],
[data-slot="select-content"],
[data-slot="combobox-content"],
[data-slot="command"],
[data-slot="tooltip-content"],
[data-slot="sheet-content"],
[data-slot="dialog-content"],
[data-slot="alert-dialog-content"],
[data-slot="drawer-content"],
[data-slot="sidebar"] {
position: relative;
backdrop-filter:
blur(var(--glass-blur))
saturate(var(--glass-saturate))
brightness(var(--glass-brightness))
contrast(var(--glass-contrast));
-webkit-backdrop-filter:
blur(var(--glass-blur))
saturate(var(--glass-saturate))
brightness(var(--glass-brightness))
contrast(var(--glass-contrast));
transition-timing-function: var(--ease-spring-snappy);
animation-timing-function: var(--ease-spring-snappy);
animation-duration: var(--duration-spring);
}
/* Springy press feel on interactive bits. */
[data-slot="button"],
[data-slot="chip"],
[data-slot="toggle"],
[data-slot="item"] {
transition-timing-function: var(--ease-spring-bouncy);
transition-duration: var(--duration-fast);
}
/*
* Premium primary button — gradient derived from --primary (subtle
* ±12° hue sweep) + top-edge sheen. Deriving rather than hardcoding
* keeps buttons on-brand with whatever --primary resolves to: the
* old fixed 290→350 sweep ended in hot pink, so CTAs read magenta
* against violet nav/links — and ignored the data-palette accents.
*/
[data-slot="button"].bg-primary {
background-image: linear-gradient(
135deg,
oklch(from var(--primary) calc(l + 0.04) c calc(h - 12)) 0%,
var(--primary) 50%,
oklch(from var(--primary) calc(l - 0.02) c calc(h + 14)) 100%
);
position: relative;
isolation: isolate;
}
[data-slot="button"].bg-primary::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
pointer-events: none;
background: linear-gradient(
180deg,
oklch(1 0 0 / 0.35) 0,
oklch(1 0 0 / 0.08) 45%,
oklch(1 0 0 / 0) 60%
);
mix-blend-mode: plus-lighter;
}
/* Dark needs no gradient override — it derives from dark --primary. */
.dark [data-slot="button"].bg-primary::after {
background: linear-gradient(
180deg,
oklch(1 0 0 / 0.18) 0,
oklch(1 0 0 / 0.04) 45%,
oklch(1 0 0 / 0) 60%
);
}
/*
* Premium card padding — roomy 24px vs the lib's compact 16px.
*/
[data-slot="card"],
[data-slot="dialog-content"],
[data-slot="alert-dialog-content"],
[data-slot="sheet-content"],
[data-slot="drawer-content"],
[data-slot="popover-content"] {
--card-padding: calc(var(--spacing) * 6);
gap: calc(var(--spacing) * 5);
}
[data-slot="card-header"],
[data-slot="card-content"],
[data-slot="card-footer"] {
padding-inline: var(--card-padding, calc(var(--spacing) * 6));
}
/*
* AI orb avatar — any avatar fallback marked `data-ai` gets the
* rainbow gradient signature.
*/
[data-slot="avatar-fallback"][data-ai] {
background-image: conic-gradient(
from 210deg at 50% 50%,
oklch(0.6 0.24 295) 0deg,
oklch(0.7 0.22 350) 90deg,
oklch(0.78 0.18 35) 180deg,
oklch(0.7 0.18 200) 270deg,
oklch(0.6 0.24 295) 360deg
);
background-color: transparent;
color: oklch(1 0 0);
font-weight: 600;
position: relative;
}
[data-slot="avatar-fallback"][data-ai]::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
pointer-events: none;
background: linear-gradient(
180deg,
oklch(1 0 0 / 0.3) 0,
oklch(1 0 0 / 0) 50%
);
mix-blend-mode: plus-lighter;
}
.dark [data-slot="avatar-fallback"][data-ai] {
background-image: conic-gradient(
from 210deg at 50% 50%,
oklch(0.7 0.22 295) 0deg,
oklch(0.78 0.2 350) 90deg,
oklch(0.84 0.16 35) 180deg,
oklch(0.78 0.16 200) 270deg,
oklch(0.7 0.22 295) 360deg
);
}
/*
* Inner top highlight — the wet-glass sheen Apple uses on surfaces.
*/
[data-slot="card"]::after,
[data-slot="popover-content"]::after,
[data-slot="dropdown-menu-content"]::after,
[data-slot="context-menu-content"]::after,
[data-slot="menubar-content"]::after,
[data-slot="hover-card-content"]::after,
[data-slot="select-content"]::after,
[data-slot="combobox-content"]::after,
[data-slot="command"]::after,
[data-slot="tooltip-content"]::after,
[data-slot="sheet-content"]::after,
[data-slot="dialog-content"]::after,
[data-slot="alert-dialog-content"]::after,
[data-slot="drawer-content"]::after,
[data-slot="sidebar"]::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
pointer-events: none;
background:
linear-gradient(
180deg,
oklch(1 0 0 / 0.6) 0,
oklch(1 0 0 / 0) 1.5px,
oklch(1 0 0 / 0.14) 1.5px,
oklch(1 0 0 / 0) 35%
);
mix-blend-mode: plus-lighter;
}
.dark [data-slot="card"]::after,
.dark [data-slot="popover-content"]::after,
.dark [data-slot="dropdown-menu-content"]::after,
.dark [data-slot="context-menu-content"]::after,
.dark [data-slot="menubar-content"]::after,
.dark [data-slot="hover-card-content"]::after,
.dark [data-slot="select-content"]::after,
.dark [data-slot="combobox-content"]::after,
.dark [data-slot="command"]::after,
.dark [data-slot="tooltip-content"]::after,
.dark [data-slot="sheet-content"]::after,
.dark [data-slot="dialog-content"]::after,
.dark [data-slot="alert-dialog-content"]::after,
.dark [data-slot="drawer-content"]::after,
.dark [data-slot="sidebar"]::after {
background:
linear-gradient(
180deg,
oklch(1 0 0 / 0.2) 0,
oklch(1 0 0 / 0) 1.5px,
oklch(1 0 0 / 0.05) 1.5px,
oklch(1 0 0 / 0) 35%
);
}
/*
* ── `.num` utility ── Apply to any element that displays money or
* counts. Forces tabular monospaced digits, the unambiguous slashed-
* zero stylistic set Inter ships, and resets the body's negative
* tracking that would otherwise crash digits into each other.
*/
.num {
font-feature-settings: "tnum", "ss01", "cv01";
font-variant-numeric: tabular-nums;
letter-spacing: 0;
}
}
/* ── Root size & font-scale ──
* 18px root by default. Body lands ~19px once the --text-body-size
* 1.0625rem multiplier applies. The data-font-scale hook lets the
* consuming app expose a user-facing size toggle. */
@layer base {
html { font-size: 18px; }
html[data-font-scale="sm"] { font-size: 16px; }
html[data-font-scale="md"] { font-size: 18px; }
html[data-font-scale="lg"] { font-size: 20px; }
html[data-font-scale="xl"] { font-size: 22px; }
}