Files
lib-theme-prism/theme.css
jules 8ba09b5db3 feat: initial — fork skyrise for finance product
- Bakes in --warning / --warning-foreground (light + dark).
- Adds --text-hero size step (3.5rem) above display, for KPI heroes.
- Ships a .num utility (tabular-nums + slashed-zero + zero tracking).
- Redesigned dark-mode background: corner glows pushed to L≈0.30 with
  lower chroma over a clean near-black base — reads as soft brand-accent
  light instead of skyrise's muddy gamut-fringe blobs.
- Drops 11 background atmospheres + 4 surface tints — focused product theme.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 22:02:38 +10:00

608 lines
22 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.

/*
* skyai-finance — finance-focused premium AI glass theme.
* Forked from skyrise (lib-theme-skyrise) with four product-driven changes:
*
* 1. Bakes in --warning + --warning-foreground (skyrise had them; pristine
* did not — finance KPIs need warning semantics out of the box).
* 2. Adds a --text-hero size step (3.5rem) above display, for KPI heroes.
* 3. Ships a `.num` utility that sets tabular-nums + slashed-zero +
* letter-spacing:0 — non-negotiable on money numbers.
* 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. A focused product picks one brand
* surface and ships it; if those 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);
/* ── 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;
}
/*
* 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);
--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%;
}
@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: skyfin-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: skyfin-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%);
}
@keyframes skyfin-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 skyfin-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 — violet→fuchsia gradient + top-edge sheen.
*/
[data-slot="button"].bg-primary {
background-image: linear-gradient(
135deg,
oklch(0.56 0.24 290) 0%,
oklch(0.6 0.26 320) 50%,
oklch(0.58 0.24 350) 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 [data-slot="button"].bg-primary {
background-image: linear-gradient(
135deg,
oklch(0.66 0.22 290) 0%,
oklch(0.7 0.24 320) 50%,
oklch(0.66 0.22 350) 100%
);
}
.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; }
}