431 lines
13 KiB
CSS
431 lines
13 KiB
CSS
/* =====================================================================
|
||
Cyclone — design system
|
||
---------------------------------------------------------------------
|
||
A precision instrument for one operator on one machine. The visual
|
||
language borrows from Bloomberg terminals and Linear-grade
|
||
engineering tools: dark, hairline-strict, mono-heavy, with one
|
||
editorial serif accent for the moments that deserve weight.
|
||
---------------------------------------------------------------------
|
||
Type stack:
|
||
- Geist Sans · UI, body, controls
|
||
- Geist Mono · every number, every id, every code-like datum
|
||
- Instrument Serif · editorial display (hero numbers, headlines)
|
||
===================================================================== */
|
||
|
||
@tailwind base;
|
||
@tailwind components;
|
||
@tailwind utilities;
|
||
|
||
@layer base {
|
||
:root {
|
||
/* ---- Core surfaces ----
|
||
A graphite canvas with a whisper of blue. Slightly warm at the
|
||
midtones (--card) to keep the chrome from feeling sterile. */
|
||
--background: 222 18% 5%; /* #0b0c10 — near-black, cool */
|
||
--foreground: 220 14% 96%; /* #f4f5f7 — paper white */
|
||
|
||
--card: 222 16% 8%; /* #11131a */
|
||
--card-foreground: 220 14% 96%;
|
||
|
||
--popover: 222 18% 9%; /* #13151c */
|
||
--popover-foreground: 220 14% 96%;
|
||
|
||
--muted: 222 12% 13%; /* #1f2229 */
|
||
--muted-foreground: 220 8% 64%; /* #9b9ea6 */
|
||
|
||
--secondary: 222 12% 13%;
|
||
--secondary-foreground: 220 14% 96%;
|
||
|
||
/* ---- Accents ----
|
||
Electric blue (primary) for actions and emphasis. Amber (signal)
|
||
is reserved for "needs attention" states and the Inbox terminal
|
||
accents — never decorative. */
|
||
--accent: 212 100% 60%; /* #2e8aff — electric, modern */
|
||
--accent-foreground: 0 0% 100%;
|
||
--signal: 36 92% 56%; /* #f3a531 — warm amber */
|
||
|
||
--primary: 212 100% 60%;
|
||
--primary-foreground: 0 0% 100%;
|
||
|
||
--destructive: 358 82% 60%; /* #ec4458 */
|
||
--destructive-foreground: 0 0% 100%;
|
||
|
||
--success: 152 64% 46%; /* #2cbd76 */
|
||
--success-foreground: 152 70% 8%;
|
||
|
||
--warning: 36 92% 56%; /* #f3a531 */
|
||
--warning-foreground: 26 80% 12%;
|
||
|
||
--border: 222 10% 16%; /* #262932 */
|
||
--input: 222 10% 16%;
|
||
--ring: 212 100% 60%;
|
||
|
||
/* ---- Sidebar ---- */
|
||
--sidebar: 222 20% 4%; /* darker than the page */
|
||
--sidebar-foreground: 220 14% 96%;
|
||
--sidebar-accent: 212 100% 60%;
|
||
--sidebar-border: 222 12% 12%;
|
||
|
||
/* ---- Document surface (drawer detail panels) ----
|
||
A faint warm paper. Keeps detail views distinct from the chrome
|
||
without going full light. Pairs with editorial serif headlines. */
|
||
--surface: 36 18% 96%; /* #f6f3ec */
|
||
--surface-ink: 30 12% 9%; /* #181614 */
|
||
--surface-ink-2: 30 6% 32%; /* #4f4b46 */
|
||
--surface-ink-3: 30 4% 52%; /* #85807a */
|
||
--surface-line: 30 14% 14%; /* #24221f */
|
||
--surface-line-soft: 30 14% 14% / 0.12;
|
||
|
||
/* ---- Ticker-tape terminal (Inbox) ----
|
||
Bloomberg-coded: three accent rails on near-black, with the
|
||
paper-tone ink for legibility. */
|
||
--tt-bg: 220 22% 5%; /* #0a0d12 */
|
||
--tt-bg-elev: 220 18% 9%; /* #13171f */
|
||
--tt-ink: 36 18% 88%; /* #e2ddd0 */
|
||
--tt-ink-dim: 220 6% 58%; /* #8e9098 */
|
||
--tt-amber: 36 88% 56%; /* #efa836 */
|
||
--tt-oxblood: 354 50% 40%; /* #8a2d35 */
|
||
--tt-ink-blue: 212 38% 36%; /* #2e4d77 */
|
||
--tt-muted: 220 4% 38%; /* #5b5d62 */
|
||
|
||
/* ---- Legacy aliases (--m-*)
|
||
Kept as thin shims so any older component still using the
|
||
"warm paper" surface family resolves to the same hue family.
|
||
New components should prefer --surface-* / --destructive / etc. */
|
||
--m-surface: hsl(var(--surface));
|
||
--m-ink-primary: hsl(var(--surface-ink));
|
||
--m-ink-secondary: hsl(var(--surface-ink-2));
|
||
--m-ink-tertiary: hsl(var(--surface-ink-3));
|
||
--m-border-heavy: hsl(var(--surface-line));
|
||
--m-font-mono: var(--font-mono);
|
||
--m-error: hsl(var(--destructive));
|
||
--m-error-bg: hsl(var(--destructive) / 0.08);
|
||
--m-success: hsl(var(--success));
|
||
--m-warning: hsl(var(--warning));
|
||
--m-accent: hsl(var(--accent));
|
||
|
||
--radius: 0.625rem; /* 10px — slightly tighter */
|
||
|
||
/* ---- Type stack ---- */
|
||
--font-sans: "Geist", ui-sans-serif, -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
|
||
--font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
|
||
--font-display: "Instrument Serif", "Iowan Old Style", "Apple Garamond", "Baskerville", "Times New Roman", serif;
|
||
}
|
||
|
||
* {
|
||
@apply border-border;
|
||
}
|
||
|
||
html, body, #root {
|
||
height: 100%;
|
||
}
|
||
|
||
body {
|
||
@apply bg-background text-foreground;
|
||
font-family: var(--font-sans);
|
||
font-feature-settings: "ss01", "cv11", "calt";
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
text-rendering: optimizeLegibility;
|
||
}
|
||
|
||
/* A single, precise light source anchored top-right.
|
||
One light, not two — an instrument, not a wash. */
|
||
body::before {
|
||
content: "";
|
||
position: fixed;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
background: radial-gradient(
|
||
55rem 35rem at 100% -15%,
|
||
hsla(212, 100%, 60%, 0.07),
|
||
transparent 62%
|
||
);
|
||
}
|
||
|
||
/* Hairline grid — the substrate of a precision instrument.
|
||
Sits behind everything, gives the dark surfaces a sense of
|
||
scale and the work a sense of "field". */
|
||
body::after {
|
||
content: "";
|
||
position: fixed;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
opacity: 0.5;
|
||
background-image:
|
||
linear-gradient(to right, hsl(222 10% 16% / 0.18) 1px, transparent 1px),
|
||
linear-gradient(to bottom, hsl(222 10% 16% / 0.18) 1px, transparent 1px);
|
||
background-size: 64px 64px;
|
||
mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, black 30%, transparent 80%);
|
||
-webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, black 30%, transparent 80%);
|
||
}
|
||
|
||
/* Custom scrollbar — minimal, hairline, Apple-style */
|
||
::-webkit-scrollbar {
|
||
width: 10px;
|
||
height: 10px;
|
||
}
|
||
::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
::-webkit-scrollbar-thumb {
|
||
background: hsl(var(--border));
|
||
border-radius: 999px;
|
||
border: 2px solid transparent;
|
||
background-clip: content-box;
|
||
}
|
||
::-webkit-scrollbar-thumb:hover {
|
||
background: hsl(var(--muted-foreground) / 0.4);
|
||
background-clip: content-box;
|
||
}
|
||
|
||
/* Focus ring — accent, 2px, with offset.
|
||
Components override this with Tailwind focus-visible:ring-* utilities
|
||
for a ring+offset combo that matches the rest of the design system. */
|
||
:focus-visible {
|
||
outline: 2px solid hsl(var(--ring));
|
||
outline-offset: 2px;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*, *::before, *::after {
|
||
animation-duration: 0.001ms !important;
|
||
animation-iteration-count: 1 !important;
|
||
transition-duration: 0.001ms !important;
|
||
scroll-behavior: auto !important;
|
||
}
|
||
}
|
||
}
|
||
|
||
@layer components {
|
||
/* Tabular numerals everywhere a number is shown */
|
||
.num {
|
||
font-variant-numeric: tabular-nums;
|
||
font-feature-settings: "tnum", "ss01";
|
||
}
|
||
|
||
/* Display face — Instrument Serif. Used for the moments that need
|
||
editorial weight: hero numbers, page titles in document surfaces,
|
||
and the brand mark. */
|
||
.display {
|
||
font-family: var(--font-display);
|
||
font-weight: 400;
|
||
letter-spacing: -0.015em;
|
||
font-feature-settings: "ss01";
|
||
}
|
||
|
||
/* Data mono — Geist Mono with tabular-nums. The voice of every
|
||
id, code, dollar amount, and timestamp in the app. */
|
||
.mono {
|
||
font-family: var(--font-mono);
|
||
font-variant-numeric: tabular-nums;
|
||
letter-spacing: -0.01em;
|
||
}
|
||
|
||
/* Soft card surface — a 1px hairline + a 1px inner highlight to
|
||
give the surface a sense of depth without a heavy shadow. */
|
||
.surface {
|
||
background-color: hsl(var(--card));
|
||
border: 1px solid hsl(var(--border));
|
||
box-shadow:
|
||
inset 0 1px 0 0 hsl(0 0% 100% / 0.04),
|
||
0 1px 2px 0 hsl(0 0% 0% / 0.4);
|
||
}
|
||
|
||
/* Subtle elevated surface — used for cards that should lift a
|
||
little more than the default. */
|
||
.surface-2 {
|
||
background: linear-gradient(180deg, hsl(222 16% 9%) 0%, hsl(222 16% 7.5%) 100%);
|
||
border: 1px solid hsl(var(--border));
|
||
box-shadow:
|
||
inset 0 1px 0 0 hsl(0 0% 100% / 0.05),
|
||
0 1px 3px 0 hsl(0 0% 0% / 0.5);
|
||
}
|
||
|
||
/* Hairline — a 1px separator that reads as a divider, not a border */
|
||
.hairline {
|
||
border-color: hsl(var(--border) / 0.6);
|
||
}
|
||
|
||
/* Active nav indicator — a thin left-aligned accent line with a
|
||
subtle glow that matches the instrument aesthetic. */
|
||
.nav-active {
|
||
background-color: hsl(var(--muted) / 0.6);
|
||
color: hsl(var(--foreground));
|
||
}
|
||
.nav-active::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: -0.75rem;
|
||
top: 0.5rem;
|
||
bottom: 0.5rem;
|
||
width: 2px;
|
||
border-radius: 2px;
|
||
background-color: hsl(var(--accent));
|
||
box-shadow: 0 0 12px hsl(var(--accent) / 0.5);
|
||
}
|
||
|
||
/* Skip link — invisible until focused. */
|
||
.skip-link {
|
||
position: fixed;
|
||
top: 0.75rem;
|
||
left: 0.75rem;
|
||
z-index: 100;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
height: 2.25rem;
|
||
padding: 0 0.875rem;
|
||
border-radius: 0.625rem;
|
||
background-color: hsl(var(--card));
|
||
color: hsl(var(--foreground));
|
||
border: 1px solid hsl(var(--border));
|
||
box-shadow:
|
||
inset 0 1px 0 0 hsl(0 0% 100% / 0.04),
|
||
0 8px 24px hsl(0 0% 0% / 0.45);
|
||
font-size: 12.5px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.01em;
|
||
transform: translateY(-200%);
|
||
transition: transform 150ms ease-out;
|
||
}
|
||
.skip-link:focus-visible {
|
||
transform: translateY(0);
|
||
outline: none;
|
||
box-shadow:
|
||
inset 0 1px 0 0 hsl(0 0% 100% / 0.04),
|
||
0 0 0 2px hsl(var(--background)),
|
||
0 0 0 4px hsl(var(--ring)),
|
||
0 8px 24px hsl(0 0% 0% / 0.45);
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.skip-link {
|
||
transition: none;
|
||
}
|
||
}
|
||
|
||
/* Keyboard hint chip */
|
||
.kbd {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.15rem;
|
||
height: 1.375rem;
|
||
min-width: 1.375rem;
|
||
padding: 0 0.4rem;
|
||
border-radius: 0.375rem;
|
||
border: 1px solid hsl(var(--border));
|
||
background: hsl(var(--muted) / 0.5);
|
||
color: hsl(var(--muted-foreground));
|
||
font-family: var(--font-mono);
|
||
font-size: 10.5px;
|
||
font-weight: 500;
|
||
letter-spacing: 0.04em;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* Section label — the all-caps eyebrow used everywhere */
|
||
.eyebrow {
|
||
font-size: 10.5px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.18em;
|
||
text-transform: uppercase;
|
||
color: hsl(var(--muted-foreground));
|
||
}
|
||
|
||
/* Editorial serif headline — for hero numbers and document
|
||
surfaces. A single line height that breathes. */
|
||
.editorial {
|
||
font-family: var(--font-display);
|
||
font-weight: 400;
|
||
letter-spacing: -0.02em;
|
||
line-height: 1.05;
|
||
}
|
||
|
||
/* Row hover state — slightly more refined than the default
|
||
table hover. A subtle tint + a left accent strip. */
|
||
.row-hover {
|
||
transition: background-color 150ms ease;
|
||
}
|
||
.row-hover:hover {
|
||
background-color: hsl(var(--muted) / 0.4);
|
||
}
|
||
}
|
||
|
||
@layer utilities {
|
||
.text-balance {
|
||
text-wrap: balance;
|
||
}
|
||
.text-pretty {
|
||
text-wrap: pretty;
|
||
}
|
||
.ring-inset-hairline {
|
||
box-shadow: inset 0 0 0 1px hsl(var(--border) / 0.6);
|
||
}
|
||
}
|
||
|
||
/* ----------------------------------------------------------------------
|
||
Print styles — a clean paper-record rendering of detail drawers.
|
||
---------------------------------------------------------------------- */
|
||
@media print {
|
||
aside, header, [data-print="hide"], button[aria-label^="Close"] {
|
||
display: none !important;
|
||
}
|
||
:root {
|
||
--background: #ffffff;
|
||
--foreground: #000000;
|
||
--card: #ffffff;
|
||
--card-foreground: #000000;
|
||
--muted: #f5f5f5;
|
||
--muted-foreground: #444444;
|
||
--border: #cccccc;
|
||
--accent: #1a1a1a;
|
||
}
|
||
body {
|
||
background: #ffffff !important;
|
||
color: #000000 !important;
|
||
}
|
||
[role="dialog"] {
|
||
position: static !important;
|
||
inset: auto !important;
|
||
width: 100% !important;
|
||
max-width: 100% !important;
|
||
height: auto !important;
|
||
transform: none !important;
|
||
background: #ffffff !important;
|
||
box-shadow: none !important;
|
||
border: none !important;
|
||
}
|
||
[data-state="open"][data-radix-dialog-overlay],
|
||
.animate-fade-in,
|
||
.animate-shimmer,
|
||
.animate-scan {
|
||
animation: none !important;
|
||
transition: none !important;
|
||
}
|
||
[role="dialog"] * {
|
||
overflow: visible !important;
|
||
}
|
||
table { page-break-inside: avoid; }
|
||
section, h1, h2, h3 { page-break-after: avoid; }
|
||
*, *::before, *::after {
|
||
animation-duration: 0s !important;
|
||
}
|
||
}
|
||
|
||
/* Universal drill-down affordance — applied by DrillableCell. */
|
||
.drillable {
|
||
cursor: pointer;
|
||
transition: background-color 120ms ease;
|
||
}
|
||
.drillable:hover {
|
||
background-color: hsl(var(--accent) / 0.08);
|
||
}
|
||
.drillable:hover::after {
|
||
content: "›";
|
||
margin-left: 6px;
|
||
color: hsl(var(--accent));
|
||
font-weight: 600;
|
||
}
|