Files
cyclone/src/index.css
T

431 lines
13 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.
/* =====================================================================
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;
}