Files

3226 lines
83 KiB
CSS
Raw Permalink 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.
/* ============================================================
Trump Alpha — Design System
============================================================ */
:root {
/* Surfaces — warm off-whites */
--bg: oklch(99% 0.004 85);
--bg-sunk: oklch(97.8% 0.006 85);
--bg-elev: #ffffff;
--surface: #ffffff;
--surface-2: oklch(97.2% 0.006 85);
--surface-3: oklch(94% 0.008 85);
/* Text */
--ink: oklch(18% 0.008 85);
--ink-2: oklch(38% 0.008 85);
--ink-3: oklch(55% 0.008 85);
--ink-4: oklch(70% 0.006 85);
/* Borders */
--line: oklch(93% 0.008 85);
--line-2: oklch(88% 0.01 85);
/* Amber accent */
--amber: oklch(78% 0.17 75);
--amber-ink: oklch(45% 0.16 70);
--amber-soft: oklch(96% 0.05 85);
--amber-ring: oklch(88% 0.12 80);
/* Signal colors */
--up: oklch(62% 0.17 148);
--up-soft: oklch(95% 0.05 148);
--down: oklch(58% 0.22 25);
--down-soft: oklch(95% 0.05 25);
--violet: oklch(55% 0.17 280);
--violet-soft: oklch(96% 0.04 280);
/* Radii */
--r-xs: 8px;
--r-sm: 10px;
--r-md: 14px;
--r-lg: 20px;
--r-xl: 28px;
--r-pill: 999px;
/* Shadow */
--shadow-1: 0 1px 3px rgba(20, 18, 14, 0.07), 0 1px 2px rgba(20, 18, 14, 0.04);
--shadow-2: 0 4px 20px rgba(20, 18, 14, 0.09), 0 1px 4px rgba(20, 18, 14, 0.05);
--shadow-3: 0 16px 48px rgba(20, 18, 14, 0.12), 0 2px 6px rgba(20, 18, 14, 0.06);
--sans: 'Geist', ui-sans-serif, system-ui, -apple-system, 'Helvetica Neue', sans-serif;
--mono: 'Geist Mono', ui-monospace, 'SF Mono', Menlo, monospace;
}
html[data-theme="dark"] {
--bg: oklch(15% 0.008 85);
--bg-sunk: oklch(12% 0.008 85);
--bg-elev: oklch(20% 0.008 85);
--surface: oklch(18% 0.008 85);
--surface-2: oklch(21% 0.008 85);
--surface-3: oklch(25% 0.01 85);
--ink: oklch(97% 0.005 85);
--ink-2: oklch(82% 0.006 85);
--ink-3: oklch(62% 0.006 85);
--ink-4: oklch(45% 0.006 85);
--line: oklch(24% 0.008 85);
--line-2: oklch(30% 0.01 85);
--amber: oklch(78% 0.17 75);
--amber-ink: oklch(82% 0.16 75);
--amber-soft: oklch(25% 0.05 75);
--amber-ring: oklch(38% 0.12 75);
--up: oklch(70% 0.18 148);
--up-soft: oklch(24% 0.06 148);
--down: oklch(68% 0.22 25);
--down-soft: oklch(24% 0.08 25);
--violet: oklch(70% 0.17 280);
--violet-soft: oklch(24% 0.06 280);
--shadow-1: 0 1px 3px rgba(0, 0, 0, 0.4);
--shadow-2: 0 4px 20px rgba(0, 0, 0, 0.45);
--shadow-3: 0 16px 48px rgba(0, 0, 0, 0.6);
color-scheme: dark;
}
html[data-theme="dark"] .src-ico.x { background: oklch(92% 0.005 85); color: oklch(15% 0.008 85); }
html[data-theme="dark"] .kpi.accent {
background: linear-gradient(135deg, oklch(28% 0.05 75), oklch(24% 0.07 70));
}
/* Dashboard Performance card uses the same "amber accent" treatment as
.kpi.accent — without a dark-mode override the cream background floats
on the dark grid as a glaring out-of-theme block. Mirror the kpi dark
gradient so the card stays visually grouped with everything else. */
html[data-theme="dark"] .overview-stat-card.accent {
background: var(--surface);
border-color: var(--line);
border-left: 3px solid var(--amber);
}
html[data-theme="dark"] .landing-nav {
background: oklch(15% 0.008 85 / 0.8);
}
html[data-theme="dark"] .src-ico.truth {
background: oklch(28% 0.10 25); color: oklch(82% 0.18 25); font-weight: 700;
}
html[data-theme="dark"] .post-row.selected {
background: oklch(22% 0.03 75);
}
html, body {
margin: 0;
padding: 0;
background: var(--bg);
color: var(--ink);
font-family: var(--sans);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-feature-settings: 'ss01', 'cv11';
transition: background 200ms, color 200ms;
}
* { box-sizing: border-box; }
/* Global focus ring — amber for brand consistency, only on keyboard nav */
:focus-visible {
outline: 2px solid var(--amber);
outline-offset: 2px;
border-radius: 4px;
}
button { font-family: inherit; cursor: pointer; border: 0; background: transparent; color: inherit; }
input, textarea { font-family: inherit; }
a { color: inherit; text-decoration: none; }
.mono { font-family: var(--mono); font-feature-settings: 'tnum', 'zero'; }
.tnum { font-variant-numeric: tabular-nums; }
/* ============================================================
App scaffold
============================================================ */
.app {
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* ---------- Top navbar ---------- */
.nav {
height: 60px;
padding: 0 28px;
display: flex;
align-items: center;
gap: 28px;
background: color-mix(in oklab, var(--bg) 88%, transparent);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-bottom: 1px solid var(--line);
position: sticky;
top: 0;
z-index: 40;
box-shadow: 0 1px 0 color-mix(in oklab, var(--line) 50%, transparent),
0 4px 16px rgba(0,0,0,0.04);
}
.wallet-anchor {
position: relative;
display: flex;
align-items: center;
}
.wallet-connect-error {
position: absolute;
right: 0;
top: calc(100% + 10px);
margin: 0;
width: min(320px, calc(100vw - 28px));
padding: 10px 12px;
border-radius: 10px;
border: 1px solid color-mix(in oklab, var(--down) 24%, var(--line));
background: color-mix(in oklab, var(--down-soft) 72%, var(--surface));
color: var(--down);
box-shadow: var(--shadow-2);
font-size: 12px;
line-height: 1.45;
z-index: 50;
transform-origin: top right;
animation: walletErrorIn 160ms ease-out;
}
.wallet-connect-error::before {
content: '';
position: absolute;
right: 26px;
top: -7px;
width: 12px;
height: 12px;
background: inherit;
border-left: 1px solid color-mix(in oklab, var(--down) 24%, var(--line));
border-top: 1px solid color-mix(in oklab, var(--down) 24%, var(--line));
transform: rotate(45deg);
}
@keyframes walletErrorIn {
from {
opacity: 0;
transform: translateY(-6px) scale(0.98);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.brand {
display: flex;
align-items: center;
gap: 10px;
font-weight: 700;
font-size: 15px;
letter-spacing: -0.02em;
}
.brand-mark {
width: 30px;
height: 30px;
border-radius: 9px;
background: linear-gradient(145deg, oklch(26% 0.015 75) 0%, oklch(13% 0.006 85) 100%);
color: oklch(96% 0.006 85);
display: grid;
place-items: center;
font-family: var(--mono);
font-size: 15px;
font-weight: 600;
position: relative;
box-shadow: 0 2px 8px rgba(0,0,0,0.25), inset 0 1px 0 rgba(255,255,255,0.08);
}
.brand-mark::after {
content: '';
position: absolute;
right: -3px;
bottom: -3px;
width: 10px;
height: 10px;
border-radius: 999px;
background: var(--amber);
border: 2px solid var(--bg);
box-shadow: 0 1px 4px color-mix(in oklab, var(--amber) 50%, transparent);
}
.nav-tabs {
display: flex;
align-items: center;
gap: 3px;
padding: 4px;
background: var(--bg-sunk);
border: 1px solid var(--line);
border-radius: var(--r-pill);
}
.nav-tab {
padding: 7px 15px;
border-radius: var(--r-pill);
font-size: 13px;
font-weight: 500;
color: var(--ink-3);
transition: color 120ms, background 120ms;
}
.nav-tab:hover { color: var(--ink-2); }
.nav-tab.active {
background: color-mix(in oklab, var(--amber) 7%, var(--surface));
color: var(--ink);
box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255,255,255,0.7);
border: 1px solid color-mix(in oklab, var(--amber) 22%, transparent);
font-weight: 600;
}
.nav-spacer { flex: 1; }
.nav-right {
display: flex;
align-items: center;
gap: 10px;
}
.icon-btn {
width: 36px;
height: 36px;
border-radius: var(--r-pill);
background: var(--bg-sunk);
border: 1px solid var(--line);
display: grid;
place-items: center;
color: var(--ink-2);
transition: background 120ms, color 120ms;
}
.icon-btn:hover { background: var(--surface-3); color: var(--ink); }
.connect-btn {
padding: 12px 22px;
border-radius: var(--r-pill);
background: linear-gradient(135deg, oklch(24% 0.012 80) 0%, oklch(14% 0.006 85) 100%);
color: oklch(97% 0.005 85);
font-size: 14px;
font-weight: 600;
letter-spacing: -0.005em;
transition: opacity 120ms, transform 120ms, box-shadow 120ms;
box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255,255,255,0.07);
}
.connect-btn:hover { opacity: 0.88; box-shadow: var(--shadow-2); }
.connect-btn:active { transform: scale(0.98); }
.connect-btn.lg { padding: 13px 26px; font-size: 14px; }
html[data-theme="dark"] .connect-btn {
background: linear-gradient(135deg, var(--amber) 0%, oklch(73% 0.18 70) 100%);
color: oklch(18% 0.04 75);
box-shadow: 0 2px 10px color-mix(in oklab, var(--amber) 40%, transparent),
inset 0 1px 0 rgba(255,255,255,0.25);
}
html[data-theme="dark"] .connect-btn:hover { opacity: 0.9; }
.theme-toggle { width: 40px; height: 40px; }
html[data-theme="dark"] .icon-btn { background: var(--surface-2); border-color: var(--line); }
html[data-theme="dark"] .icon-btn:hover { background: var(--surface-3); }
html[data-theme="dark"] .card {
box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255,255,255,0.04);
}
html[data-theme="dark"] .overview-market-card,
html[data-theme="dark"] .overview-side-card,
html[data-theme="dark"] .overview-stats-bar {
box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255,255,255,0.03);
}
html[data-theme="dark"] .nav-tab.active {
background: color-mix(in oklab, var(--amber) 10%, var(--surface-2));
border-color: color-mix(in oklab, var(--amber) 28%, transparent);
box-shadow: var(--shadow-1);
}
html[data-theme="dark"] .asset-switch button.on { background: var(--surface-2); }
.wallet-chip {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 12px 6px 6px;
border-radius: var(--r-pill);
background: var(--bg-sunk);
border: 1px solid var(--line);
font-size: 13px;
}
.wallet-chip .ava {
width: 24px;
height: 24px;
border-radius: 999px;
background: linear-gradient(135deg, var(--amber), oklch(65% 0.2 35));
}
.wallet-menu {
display: none;
position: absolute;
right: 0;
top: calc(100% + 6px);
min-width: 180px;
padding: 6px;
z-index: 1000;
background: var(--bg-elev);
border: 1px solid var(--line);
border-radius: var(--r-sm);
box-shadow: var(--shadow-2);
}
.wallet-menu.open { display: block; }
.wallet-menu button {
width: 100%;
padding: 8px 10px;
font-size: 13px;
text-align: left;
border-radius: 6px;
color: var(--ink);
}
.wallet-menu button:hover { background: var(--bg-sunk); }
.wallet-menu button.danger { color: var(--down); }
/* ============================================================
Page shell
============================================================ */
.page {
max-width: 1360px;
margin: 0 auto;
padding: 28px 28px 64px;
width: 100%;
}
.page.wide { max-width: 1600px; }
.page-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
margin-bottom: 28px;
gap: 24px;
}
.page-title {
font-size: 32px;
letter-spacing: -0.035em;
font-weight: 800;
line-height: 1.05;
margin: 0;
color: var(--ink);
}
.page-sub {
color: var(--ink-3);
font-size: 14px;
line-height: 1.5;
margin-top: 5px;
margin-bottom: 0;
}
/* PageHint — the prominent "what is this page" tagline directly under
.page-title. Stronger contrast + bigger than .page-sub so it actually
gets read (page-sub was so faint it functioned as decoration).
Use via <PageHint>One sentence about the page.</PageHint>. */
.page-hint {
color: var(--ink-3);
font-size: 14px;
line-height: 1.5;
margin: 6px 0 0;
max-width: 64ch;
}
.page-hint-count {
color: var(--amber-ink);
font-size: 12px;
font-weight: 600;
margin-left: 10px;
padding-left: 10px;
border-left: 1px solid var(--line);
opacity: 0.8;
}
/* SectionHint — always-visible explanation block. Pairs with a section/tab
to tell the user "what does this view actually do" in 13 sentences.
More detailed than PageHint, less detail-y than an InfoTip. Spans the
full content width on purpose — narrow boxes inside a wide page looked
like accidentally-floating callouts. */
.section-hint {
background: color-mix(in oklab, var(--amber) 4%, var(--bg-sunk));
border: 1px solid color-mix(in oklab, var(--amber) 18%, var(--line));
border-left: 3px solid color-mix(in oklab, var(--amber) 60%, transparent);
border-radius: 8px;
padding: 12px 16px;
margin-bottom: 14px;
color: var(--ink-2);
font-size: 14px;
line-height: 1.55;
}
.section-hint strong {
color: var(--ink);
font-weight: 600;
}
/* InfoTip — small "?" pill that reveals a one-line tooltip on hover/focus.
No JS, pure CSS. Pattern mimics the (i) dots in Linear/Hyperliquid:
subtle until needed, never blocks the underlying content. */
.infotip {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
vertical-align: middle;
margin-left: 6px;
cursor: help;
outline: none;
/* The icon should sit on the text baseline without nudging line-height. */
line-height: 1;
}
.infotip-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--bg-sunk);
color: var(--ink-3);
font-size: 10px;
font-weight: 700;
border: 1px solid var(--line);
transition: background 0.12s, color 0.12s, border-color 0.12s;
user-select: none;
}
.infotip:hover .infotip-icon,
.infotip:focus-visible .infotip-icon {
background: var(--ink);
color: var(--bg);
border-color: var(--ink);
}
.infotip-bubble {
position: absolute;
z-index: 50;
width: var(--tip-w, 240px);
padding: 8px 10px;
/* Always-dark bubble — common pattern (Linear/Stripe/GitHub) — but needs
a visible border in dark mode so it doesn't blend into the page bg. */
background: #0f0d0a;
color: #f5f2ea;
border: 1px solid transparent;
border-radius: 6px;
font-size: 12px;
font-weight: 400;
line-height: 1.5;
letter-spacing: 0;
text-transform: none;
text-align: left;
box-shadow: 0 6px 24px rgba(0,0,0,0.25), 0 1px 2px rgba(0,0,0,0.2);
pointer-events: none;
opacity: 0;
transform: translateY(2px);
transition: opacity 0.12s, transform 0.12s;
white-space: normal;
}
/* Dark mode: page bg is ~oklch(15%), bubble is #0f0d0a — visually identical.
Lighten the bubble slightly and add a 1px ring so the shape stays defined. */
html[data-theme="dark"] .infotip-bubble {
background: oklch(28% 0.008 85);
border-color: oklch(38% 0.01 85);
box-shadow: 0 6px 24px rgba(0,0,0,0.6), 0 1px 2px rgba(0,0,0,0.4);
}
/* MacroPanel cell — subtle hover so the grid feels alive without becoming
a giant heat-map of click affordances. Just a tiny background lift. */
.macro-cell:hover {
background: var(--bg-sunk) !important;
}
.macro-panel {
padding: 0;
margin-bottom: 12px;
overflow: hidden;
}
.macro-panel-head {
padding: 20px 22px 18px;
border-bottom: 1px solid var(--line);
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
background: color-mix(in oklab, var(--amber) 3%, var(--surface));
}
.macro-panel-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 15px;
font-weight: 700;
letter-spacing: -0.01em;
}
.macro-panel-dot {
width: 7px;
height: 7px;
border-radius: 999px;
background: var(--up);
display: inline-block;
position: relative;
box-shadow: 0 0 5px color-mix(in oklab, var(--up) 60%, transparent);
animation: macro-dot-pulse 2.4s ease-out infinite;
}
@keyframes macro-dot-pulse {
0%, 60% { box-shadow: 0 0 5px color-mix(in oklab, var(--up) 60%, transparent); }
30% { box-shadow: 0 0 10px color-mix(in oklab, var(--up) 80%, transparent); }
100% { box-shadow: 0 0 5px color-mix(in oklab, var(--up) 60%, transparent); }
}
.macro-panel-subtitle {
margin-top: 6px;
font-size: 12px;
line-height: 1.45;
color: var(--ink-3);
}
.macro-panel-meta {
text-align: right;
font-size: 11px;
line-height: 1.45;
color: var(--ink-4);
flex-shrink: 0;
}
.macro-section {
border-top: 1px solid var(--line);
}
.macro-section-title {
display: flex;
align-items: center;
gap: 10px;
padding: 14px 22px 12px;
/* Was 10px / ink-3 / weight 700 — too quiet, users couldn't see the
section structure. Bumped to 12px / ink-2 / weight 800. Still uppercase
+ tracked so it reads as a label, not a heading that competes with the
metric values. */
font-size: 12px;
color: var(--ink-2);
letter-spacing: 0.10em;
text-transform: uppercase;
font-weight: 800;
background: var(--bg-sunk);
border-bottom: 1px solid var(--line);
}
/* Tiny dot anchor on the left — visual signal that "this is a section
header" without using a heavy colored bar. */
.macro-section-title::before {
content: '';
width: 5px;
height: 5px;
border-radius: 50%;
background: var(--amber);
flex-shrink: 0;
box-shadow: 0 0 5px color-mix(in oklab, var(--amber) 50%, transparent);
opacity: 0.8;
}
.macro-grid {
display: grid;
gap: 14px;
padding: 14px;
}
.macro-grid.one {
grid-template-columns: minmax(0, 1fr);
}
.macro-grid.two {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.macro-metric-card,
.macro-guide-card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: 12px;
padding: 18px;
min-width: 0;
min-height: 248px;
display: flex;
flex-direction: column;
}
.macro-metric-card {
box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255,255,255,0.6);
}
.macro-metric-card.tone-up {
border-color: color-mix(in oklab, var(--up) 28%, var(--line));
}
.macro-metric-card.tone-down {
border-color: color-mix(in oklab, var(--down) 28%, var(--line));
}
/* Stat card layout — label STACKED ABOVE the big number, not side-by-side.
Previous side-by-side layout pushed the title to the far left and the
value to the far right with too much air between them on wide screens.
The eye now reads top→bottom: label → value → summary → chips → button. */
.macro-metric-head {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
}
.macro-metric-title {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
/* Slightly larger + bolder so "AHR999" doesn't disappear next to the
big number below. Treated as a label, not a heading. */
font-size: 15px;
font-weight: 650;
color: var(--ink-2);
}
.macro-rank {
font-size: 11px;
color: var(--ink-4);
letter-spacing: 0.08em;
font-weight: 700;
flex-shrink: 0;
}
.macro-metric-value {
/* Same size as before but sits directly under the label now, so the
eye-distance between "AHR999" and "0.4663" is ~6px instead of the
full card width. */
font-size: 34px;
line-height: 1;
letter-spacing: -0.03em;
font-weight: 800;
color: var(--ink);
text-align: left;
flex-shrink: 0;
}
.macro-guide-title {
font-size: 20px;
line-height: 1.15;
letter-spacing: -0.02em;
font-weight: 700;
color: var(--ink);
}
.macro-summary {
margin-top: 12px;
font-size: 14px;
line-height: 1.55;
color: var(--ink-2);
min-height: 66px;
}
/* Hero variant — for a section with a SINGLE headline card (Valuation/AHR999).
The stacked card layout stretched full-width left a huge dead middle; the
hero lays out horizontally: value block left, summary + chips filling the
middle, chart button right. Wraps back to stacked on narrow screens. */
.macro-metric-card.hero {
flex-direction: row;
align-items: center;
flex-wrap: wrap;
gap: clamp(16px, 3vw, 40px);
min-height: 0;
}
.macro-metric-card.hero .macro-hero-body {
flex: 1 1 300px;
min-width: 0;
}
.macro-metric-card.hero .macro-summary {
margin-top: 0;
min-height: 0;
}
.macro-metric-card.hero .macro-thresholds {
margin-top: 10px;
}
.macro-metric-card.hero .macro-actions {
margin-top: 0;
padding-top: 0;
flex-shrink: 0;
}
.macro-thresholds {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 14px;
}
/* THRESHOLD CHIPS — "you are here" treatment
─────────────────────────────────────────────────────────
Design goal: the chip that the CURRENT value falls into must be the
single most-eye-catching element in the cell. Everything else fades back.
Two states:
base (inactive) — flat, outlined, muted gray. Tone is suppressed
so a "down (red)" inactive chip doesn't visually
outshout an active "neutral" chip. Stripping
tone here is intentional.
.active — filled pill with the tone colour, larger, raised
on shadow, scale 1.05. The eye can't miss it.
Hover the row → all chips bump to 0.8 opacity so you can read the
other bands' rules without losing the active one. */
.macro-threshold-chip {
display: inline-flex;
align-items: center;
min-height: 28px;
padding: 5px 12px;
border-radius: 999px;
border: 1px solid var(--line);
background: transparent;
font-size: 11.5px;
line-height: 1.35;
color: var(--ink-4);
font-weight: 500;
opacity: 0.45;
transition: opacity 160ms, transform 180ms cubic-bezier(.2,.8,.2,1),
box-shadow 180ms, background 180ms, color 180ms;
}
.macro-thresholds:hover .macro-threshold-chip { opacity: 0.8; }
/* When INACTIVE, suppress tone colors. They'd compete with the active chip
for attention. Tone returns the moment the chip becomes .active below. */
.macro-threshold-chip:not(.active) {
color: var(--ink-4) !important;
background: transparent !important;
border-color: var(--line) !important;
}
/* ACTIVE — the band the current value falls into. */
.macro-threshold-chip.active {
opacity: 1;
font-weight: 700;
font-size: 12px;
padding: 6px 14px;
transform: scale(1.06);
letter-spacing: 0.01em;
}
.macro-threshold-chip.up.active {
background: linear-gradient(135deg, var(--up) 0%, oklch(66% 0.19 155) 100%);
color: #fff;
border-color: var(--up);
box-shadow:
0 6px 18px color-mix(in oklab, var(--up) 40%, transparent),
inset 0 1px 0 rgba(255,255,255,0.28);
}
.macro-threshold-chip.down.active {
background: linear-gradient(135deg, var(--down) 0%, oklch(55% 0.24 18) 100%);
color: #fff;
border-color: var(--down);
box-shadow:
0 6px 18px color-mix(in oklab, var(--down) 40%, transparent),
inset 0 1px 0 rgba(255,255,255,0.22);
}
.macro-threshold-chip.neutral.active {
background: linear-gradient(135deg, var(--amber) 0%, oklch(70% 0.19 65) 100%);
color: oklch(20% 0.04 75);
border-color: var(--amber);
box-shadow:
0 6px 18px color-mix(in oklab, var(--amber) 40%, transparent),
inset 0 1px 0 rgba(255,255,255,0.45);
}
/* Dark mode tweaks — the soft white-shimmer inset doesn't read on dark cards,
so swap to a subtle darker outline. */
html[data-theme="dark"] .macro-threshold-chip.up.active,
html[data-theme="dark"] .macro-threshold-chip.down.active,
html[data-theme="dark"] .macro-threshold-chip.neutral.active {
box-shadow:
0 8px 24px color-mix(in oklab, currentColor 30%, transparent),
0 0 0 1px rgba(0,0,0,0.25) inset;
}
/* Section variants — reference cards (rules-only, no live number) get a
muted treatment so users instantly see they're explanatory, not live.
We REPLACE the dot anchor with a 📐 emoji (overriding the .macro-section-
title::before above) and dim the text color. */
.macro-section.reference .macro-section-title {
color: var(--ink-3);
}
.macro-section.reference .macro-section-title::before {
content: '📐';
width: auto;
height: auto;
background: none;
border-radius: 0;
font-size: 13px;
line-height: 1;
filter: grayscale(0.3);
}
.macro-guide-card {
background: color-mix(in oklab, var(--bg-sunk) 60%, var(--surface));
border-style: dashed;
}
.macro-guide-card .macro-guide-title::before {
content: 'RULES ';
font-size: 9px;
font-weight: 800;
letter-spacing: 0.12em;
padding: 2px 6px;
border-radius: 4px;
background: color-mix(in oklab, var(--ink) 8%, transparent);
color: var(--ink-3);
margin-right: 8px;
vertical-align: middle;
}
/* Action row — single small button, aligned to the bottom-right of the card.
Was previously a full-width dual-button group; pinning a compact button to
the corner keeps the card's eye-flow on the value + thresholds, with the
"go look at history" affordance present but quiet. */
.macro-actions {
display: flex;
justify-content: flex-end;
margin-top: auto;
padding-top: 14px;
}
.macro-action-btn {
display: inline-flex;
align-items: center;
gap: 6px;
/* Compact natural width — fits "Open chart CoinGlass ↗" comfortably */
padding: 7px 13px;
border-radius: 9px;
border: 1px solid var(--line);
background: color-mix(in oklab, var(--ink) 4%, var(--surface));
color: var(--ink);
text-decoration: none;
transition: background 120ms, border-color 120ms, transform 120ms, box-shadow 120ms;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.5), var(--shadow-1);
}
.macro-action-btn:hover {
background: color-mix(in oklab, var(--ink) 8%, var(--surface));
border-color: color-mix(in oklab, var(--ink) 14%, var(--line));
transform: translateY(-1px);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.5), var(--shadow-2);
}
.macro-action-btn.chart {
background: color-mix(in oklab, var(--amber) 12%, var(--surface));
border-color: color-mix(in oklab, var(--amber) 22%, var(--line));
}
/* The kicker ("OPEN CHART" uppercase eyebrow) used to sit ABOVE the label
in a 2-row layout. With the new compact button it lives inline next to
the label as a tiny prefix tag — same words, less vertical space. */
.macro-action-copy {
display: inline-flex;
align-items: baseline;
gap: 8px;
min-width: 0;
}
.macro-action-kicker {
font-size: 9.5px;
line-height: 1;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-4);
font-weight: 700;
}
.macro-action-label {
font-size: 13px;
line-height: 1;
color: var(--ink);
font-weight: 600;
white-space: nowrap;
}
.macro-action-arrow {
font-size: 13px;
line-height: 1;
color: var(--ink-3);
flex-shrink: 0;
}
.macro-disclosure {
margin-top: 14px;
border-top: 1px dashed var(--line);
padding-top: 12px;
}
.macro-disclosure summary {
list-style: none;
cursor: pointer;
font-size: 12px;
font-weight: 600;
color: var(--ink-2);
}
.macro-disclosure summary::-webkit-details-marker {
display: none;
}
.macro-disclosure-body {
margin-top: 8px;
font-size: 12px;
line-height: 1.55;
color: var(--ink-3);
}
.macro-composite {
/* Aligned to the indicator-card grid above (.macro-grid uses 14px gutters
and metric cards have 12px radius / 1px border / surface bg). The
composite reads as a "summary card" sitting at the same indent as the
metric cards rather than a full-bleed banner. */
margin: 14px;
padding: 20px 22px 22px;
box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255,255,255,0.5);
background: var(--surface);
border: 1px solid var(--line);
border-radius: 12px;
box-shadow: var(--shadow-1);
}
.macro-composite-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 14px;
}
.macro-composite-label {
font-size: 11px;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--amber-ink);
font-weight: 700;
opacity: 0.85;
}
.macro-composite-right {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
justify-content: flex-end;
}
.macro-composite-value {
font-size: 32px;
line-height: 0.95;
font-weight: 800;
letter-spacing: -0.03em;
}
.macro-regime-pill {
display: inline-flex;
align-items: center;
min-height: 28px;
padding: 0 12px;
border-radius: 999px;
border: 1px solid var(--line);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.07em;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.35);
}
.macro-composite-track {
position: relative;
height: 18px;
/* No `overflow: hidden` — the 24px needle is taller than the track and
gets clipped top/bottom otherwise. The gradient is naturally rounded
by border-radius, so clipping isn't needed. */
border-radius: 999px;
border: 1px solid color-mix(in oklab, var(--line) 78%, transparent);
box-shadow: inset 0 1px 2px rgba(0,0,0,0.08);
margin: 6px 0;
}
.macro-composite-track::after {
content: '';
position: absolute;
top: 2px;
bottom: 2px;
left: 50%;
width: 1px;
background: color-mix(in oklab, var(--ink) 14%, transparent);
transform: translateX(-50%);
}
.macro-composite-needle {
position: absolute;
top: 50%;
width: 24px;
height: 24px;
border-radius: 50%;
border: 3px solid var(--ink);
background: var(--surface);
box-shadow: var(--shadow-2);
transform: translateY(-50%);
transition: left 0.4s cubic-bezier(.2,.8,.2,1);
/* React re-mounts this element via `key={composite_score}` whenever the
score changes, which re-triggers the pulse animation below — a brief
attention cue for users tracking how the macro tilt shifts day to day. */
animation: macro-needle-pulse 0.9s ease-out 1;
}
@keyframes macro-needle-pulse {
0% { transform: translateY(-50%) scale(0.85); box-shadow: 0 0 0 0 currentColor; }
40% { transform: translateY(-50%) scale(1.18); box-shadow: 0 0 0 8px color-mix(in oklab, currentColor 18%, transparent); }
100% { transform: translateY(-50%) scale(1); box-shadow: var(--shadow-2); }
}
.macro-composite-scale {
display: flex;
justify-content: space-between;
gap: 8px;
margin-top: 8px;
font-size: 10px;
letter-spacing: 0.04em;
color: var(--ink-4);
text-transform: uppercase;
}
.infotip:hover .infotip-bubble,
.infotip:focus-visible .infotip-bubble {
opacity: 1;
transform: translateY(0);
}
/* Placement variants (default = top).
--tip-shift is set by InfoTip.tsx on reveal: it nudges a bubble that would
hang past the viewport edge back on-screen (icons near the screen margin). */
.infotip-top .infotip-bubble {
bottom: calc(100% + 8px);
left: 50%; transform: translate(calc(-50% + var(--tip-shift, 0px)), 2px);
}
.infotip-top:hover .infotip-bubble,
.infotip-top:focus-visible .infotip-bubble {
transform: translate(calc(-50% + var(--tip-shift, 0px)), 0);
}
.infotip-bottom .infotip-bubble {
top: calc(100% + 8px);
left: 50%; transform: translate(calc(-50% + var(--tip-shift, 0px)), -2px);
}
.infotip-bottom:hover .infotip-bubble,
.infotip-bottom:focus-visible .infotip-bubble {
transform: translate(calc(-50% + var(--tip-shift, 0px)), 0);
}
.infotip-left .infotip-bubble {
right: calc(100% + 8px);
top: 50%; transform: translate(2px, -50%);
}
.infotip-left:hover .infotip-bubble,
.infotip-left:focus-visible .infotip-bubble {
transform: translate(0, -50%);
}
.infotip-right .infotip-bubble {
left: calc(100% + 8px);
top: 50%; transform: translate(-2px, -50%);
}
.infotip-right:hover .infotip-bubble,
.infotip-right:focus-visible .infotip-bubble {
transform: translate(0, -50%);
}
/* ============================================================
Cards + primitives
============================================================ */
.card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 20px;
box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255,255,255,0.55);
}
.card.flush { padding: 0; }
.card.soft { background: var(--bg-sunk); }
.card.raise { box-shadow: var(--shadow-1); }
.section-title {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;
}
.section-title h2 {
margin: 0;
font-size: 17px;
font-weight: 700;
letter-spacing: -0.02em;
}
.section-title .hint {
font-size: 12px;
color: var(--ink-3);
}
.chip {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
border-radius: var(--r-pill);
background: var(--bg-sunk);
border: 1px solid var(--line);
font-size: 12px;
color: var(--ink-2);
font-weight: 500;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.45);
}
.chip .dot { width: 6px; height: 6px; border-radius: 999px; background: currentColor; }
/* Text darkened to clear WCAG AA 4.5:1 on the soft tint (was 3.4:1). */
.chip.up { color: oklch(33% 0.17 150); background: var(--up-soft); border-color: oklch(84% 0.10 148); font-weight: 600; }
.chip.down { color: oklch(36% 0.20 27); background: var(--down-soft); border-color: oklch(86% 0.10 25); font-weight: 600; }
/* Dark mode: soft tint is dark, so the text must go light to keep contrast. */
html[data-theme="dark"] .chip.up { color: oklch(83% 0.15 150); }
html[data-theme="dark"] .chip.down { color: oklch(80% 0.16 27); }
.chip.amber { color: var(--amber-ink); background: var(--amber-soft); border-color: var(--amber-ring); font-weight: 600; }
.chip.violet { color: oklch(36% 0.17 280); background: var(--violet-soft); border-color: oklch(84% 0.10 280); font-weight: 600; }
.chip.neutral { color: var(--ink-2); }
.live-dot {
width: 7px;
height: 7px;
border-radius: 999px;
background: var(--up);
position: relative;
display: inline-block;
box-shadow: 0 0 6px color-mix(in oklab, var(--up) 60%, transparent);
}
.live-dot::after {
content: '';
position: absolute;
inset: -4px;
border-radius: 999px;
background: var(--up);
opacity: 0.35;
animation: pulse 1.8s ease-out infinite;
}
@keyframes pulse {
0% { transform: scale(0.5); opacity: 0.6; }
100% { transform: scale(2.6); opacity: 0; }
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 10px 16px;
border-radius: var(--r-pill);
font-size: 13px;
font-weight: 500;
transition: background 120ms, transform 120ms;
border: 1px solid transparent;
}
.btn.primary {
background: linear-gradient(135deg, oklch(24% 0.012 80) 0%, oklch(14% 0.006 85) 100%);
color: oklch(97% 0.005 85);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.07);
}
.btn.primary:hover { opacity: 0.88; }
.btn.amber {
background: linear-gradient(135deg, var(--amber) 0%, oklch(73% 0.18 70) 100%);
color: oklch(20% 0.04 75);
font-weight: 700;
box-shadow: 0 2px 8px color-mix(in oklab, var(--amber) 40%, transparent),
inset 0 1px 0 rgba(255,255,255,0.35);
}
.btn.amber:hover {
background: linear-gradient(135deg, oklch(82% 0.17 75) 0%, oklch(76% 0.19 68) 100%);
box-shadow: 0 4px 14px color-mix(in oklab, var(--amber) 45%, transparent),
inset 0 1px 0 rgba(255,255,255,0.35);
}
.btn.ghost { background: var(--bg-sunk); color: var(--ink); border-color: var(--line); }
.btn.ghost:hover { background: var(--surface-3); }
.btn.lg { padding: 14px 22px; font-size: 15px; }
.btn:active { transform: scale(0.98); }
.delta { font-family: var(--mono); font-weight: 600; }
.delta.up { color: oklch(40% 0.18 148); }
.delta.down { color: oklch(44% 0.22 25); }
/* ── iOS-style toggle switch ──────────────────────────────── */
.switch {
position: relative;
display: inline-block;
width: 34px;
height: 20px;
flex-shrink: 0;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.switch-track {
position: absolute;
cursor: pointer;
top: 0; left: 0; right: 0; bottom: 0;
background: var(--line-2);
border-radius: 999px;
transition: background 160ms;
}
.switch-track::before {
content: '';
position: absolute;
height: 16px;
width: 16px;
left: 2px;
top: 2px;
background: #fff;
border-radius: 50%;
transition: transform 160ms;
box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}
.switch input:checked + .switch-track {
background: linear-gradient(135deg, var(--amber) 0%, oklch(73% 0.18 70) 100%);
box-shadow: 0 0 0 2px color-mix(in oklab, var(--amber) 18%, transparent);
}
.switch input:checked + .switch-track::before { transform: translateX(14px); }
.switch.up input:checked + .switch-track {
background: linear-gradient(135deg, var(--up) 0%, oklch(66% 0.19 155) 100%);
box-shadow: 0 0 0 2px color-mix(in oklab, var(--up) 18%, transparent);
}
.switch.down input:checked + .switch-track { background: var(--down); }
/* ── Form row: label | control, strict-aligned ─────────────── */
.form-row {
display: grid;
grid-template-columns: minmax(220px, 260px) minmax(0, 1fr);
align-items: start;
gap: 24px;
padding: 16px 0;
}
.form-row + .form-row { border-top: 1px solid var(--line); }
.form-row-label {
font-size: 14px;
font-weight: 600;
color: var(--ink);
line-height: 1.35;
}
.form-row-label .hint {
display: block;
font-size: 12px;
font-weight: 400;
color: var(--ink-3);
margin-top: 5px;
line-height: 1.5;
max-width: 28ch;
}
.form-row-control {
display: flex;
align-items: flex-start;
gap: 12px;
flex-wrap: wrap;
min-width: 0;
}
/* ── Section divider with label ────────────────────────────── */
.section-head {
display: flex;
align-items: center;
gap: 12px;
margin: 10px 0 2px;
padding-top: 18px;
border-top: 1px solid var(--line);
}
.section-head:first-child { border-top: 0; padding-top: 0; }
.section-head-label {
font-size: 10.5px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--amber-ink);
opacity: 0.75;
}
/* ── Settings legacy classes (removed from HTML 2026-06) ──────────────── */
/* These are kept as empty stubs so any cached HTML from CDN doesn't break. */
.settings-scope-intro,
.settings-control-center {
display: grid;
grid-template-columns: minmax(0, 1.4fr) minmax(320px, 1fr);
gap: 16px;
padding: 18px 20px;
margin-bottom: 16px;
border: 1px solid var(--line);
border-radius: 14px;
background: linear-gradient(135deg, color-mix(in oklab, var(--amber) 7%, var(--surface)) 0%, var(--surface) 48%, color-mix(in oklab, var(--up) 6%, var(--surface)) 100%);
box-shadow: var(--shadow-1);
}
.settings-control-kicker,
.settings-section-kicker {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-4);
margin-bottom: 6px;
}
.settings-control-title,
.settings-section-title {
font-size: 20px;
line-height: 1.2;
font-weight: 700;
color: var(--ink);
}
.settings-control-copy {
margin-top: 8px;
font-size: 13px;
line-height: 1.6;
color: var(--ink-3);
max-width: 58ch;
}
.settings-control-meta {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
}
.settings-meta-card {
padding: 12px 14px;
border: 1px solid var(--line);
border-radius: 12px;
background: color-mix(in oklab, var(--surface) 92%, transparent);
box-shadow: var(--shadow-1);
}
.settings-meta-card strong {
display: block;
font-size: 13px;
line-height: 1.4;
color: var(--ink);
}
.settings-meta-label {
display: block;
margin-bottom: 4px;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ink-4);
}
.settings-scope-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
}
.settings-scope-card {
padding: 16px;
border-radius: 12px;
border: 1px solid var(--line);
background: var(--surface);
box-shadow: var(--shadow-1);
}
.settings-scope-card.trump {
background: color-mix(in oklab, var(--amber) 8%, var(--surface));
}
.settings-scope-card.btc {
background: color-mix(in oklab, var(--up) 8%, var(--surface));
}
.settings-scope-card.global {
background: color-mix(in oklab, var(--ink) 3%, var(--surface));
}
.settings-scope-eyebrow {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-4);
margin-bottom: 8px;
}
.settings-scope-title {
font-size: 15px;
font-weight: 700;
color: var(--ink);
margin-bottom: 6px;
}
.settings-scope-copy {
font-size: 12px;
line-height: 1.6;
color: var(--ink-3);
margin-bottom: 14px;
}
.settings-scope-link {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 7px 12px;
border-radius: 999px;
border: 1px solid var(--line);
background: var(--surface);
font-size: 12px;
font-weight: 700;
color: var(--ink);
box-shadow: var(--shadow-1);
}
.settings-section-shell {
margin-bottom: 18px;
}
.settings-section-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 12px;
margin-bottom: 10px;
}
.settings-section-note {
font-size: 12px;
line-height: 1.5;
color: var(--ink-3);
text-align: right;
}
/* ── Inline $ prefix input ─────────────────────────────────── */
.num-field {
display: inline-flex;
align-items: center;
background: var(--bg-sunk);
border: 1px solid var(--line);
border-radius: var(--r-sm);
padding: 0 10px;
transition: border-color 120ms, background 120ms;
}
.num-field:focus-within { border-color: var(--amber-ring); background: var(--bg); }
.num-field[data-disabled='true'] { opacity: 0.4; pointer-events: none; }
.num-field .prefix, .num-field .suffix {
font-size: 12px;
color: var(--ink-3);
font-family: var(--mono);
}
.num-field input {
border: 0;
background: transparent;
font-family: var(--mono);
font-size: 13px;
padding: 8px 6px;
width: 80px;
outline: none;
color: var(--ink);
}
.num-field input::-webkit-inner-spin-button,
.num-field input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
/* ── Slider: unified look ──────────────────────────────────── */
.slider-field { flex: 1; display: flex; flex-direction: column; gap: 4px; }
.slider-field input[type=range] {
-webkit-appearance: none;
width: 100%;
height: 4px;
background: var(--line-2);
border-radius: 999px;
outline: none;
}
.slider-field input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
background: var(--amber);
border: 2px solid var(--bg);
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0,0,0,0.18);
cursor: pointer;
}
.slider-field .ticks {
display: flex;
justify-content: space-between;
font-size: 10px;
color: var(--ink-4);
font-variant-numeric: tabular-nums;
}
.slider-readout {
font-family: var(--mono);
font-size: 14px;
font-weight: 600;
min-width: 56px;
text-align: right;
color: var(--ink);
font-variant-numeric: tabular-nums;
}
.settings-note {
width: 100%;
padding: 10px 12px;
border-radius: 8px;
background: var(--bg-sunk);
border: 1px solid var(--line);
font-size: 12px;
line-height: 1.55;
color: var(--ink-3);
}
.settings-note.warn {
color: var(--down);
border-color: color-mix(in oklab, var(--down) 18%, var(--line));
background: var(--down-soft);
}
/* ============================================================
Dashboard specific
============================================================ */
.dash-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) 360px;
gap: 20px;
align-items: start;
}
/* Grid items default to min-width:auto, so an intrinsically-wide child (the
lightweight-charts canvas, a long URL) blows the column past the viewport
on phones — the whole page then scrolls sideways. Let columns shrink. */
.dash-grid > * { min-width: 0; }
.hero-value {
font-size: 44px;
font-weight: 600;
letter-spacing: -0.03em;
line-height: 1;
font-family: var(--mono);
font-feature-settings: 'tnum';
}
.hero-value .cents { color: var(--ink-3); font-size: 0.6em; }
.asset-switch {
display: inline-flex;
padding: 4px;
background: var(--bg-sunk);
border: 1px solid var(--line);
border-radius: var(--r-pill);
gap: 2px;
}
.asset-switch button {
padding: 7px 14px;
border-radius: var(--r-pill);
font-size: 13px;
font-weight: 500;
color: var(--ink-3);
display: flex;
align-items: center;
gap: 6px;
}
.asset-switch button.on {
background: color-mix(in oklab, var(--amber) 6%, var(--surface));
color: var(--ink);
box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255,255,255,0.6);
border: 1px solid color-mix(in oklab, var(--amber) 20%, transparent);
}
.asset-dot {
width: 14px;
height: 14px;
border-radius: 999px;
}
.asset-dot.btc { background: linear-gradient(135deg, #f7931a, #f2a93b); }
.asset-dot.eth { background: linear-gradient(135deg, #627eea, #8fa2ff); }
.tf-bar {
display: inline-flex;
gap: 2px;
padding: 3px;
background: var(--bg-sunk);
border: 1px solid var(--line);
border-radius: var(--r-pill);
}
.tf-bar button {
padding: 5px 10px;
border-radius: var(--r-pill);
font-size: 12px;
font-weight: 500;
color: var(--ink-3);
font-family: var(--mono);
}
.tf-bar button.on {
background: linear-gradient(135deg, oklch(24% 0.012 80) 0%, oklch(14% 0.006 85) 100%);
color: oklch(97% 0.005 85);
box-shadow: 0 1px 4px rgba(0,0,0,0.2), inset 0 1px 0 rgba(255,255,255,0.07);
}
.chart-wrap {
margin-top: 8px;
position: relative;
height: 360px;
}
.chart-footnote {
display: flex;
gap: 18px;
font-size: 11.5px;
color: var(--ink-3);
border-top: 1px dashed var(--line);
padding-top: 14px;
margin-top: 4px;
letter-spacing: 0.01em;
}
.chart-footnote .item { display: flex; align-items: center; gap: 6px; }
.legend-dot { width: 8px; height: 8px; border-radius: 3px; box-shadow: 0 1px 3px rgba(0,0,0,0.12); }
/* KPI tiles */
.kpi-row {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
margin-bottom: 20px;
}
.kpi {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 16px 18px;
display: flex;
flex-direction: column;
gap: 6px;
position: relative;
overflow: hidden;
box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255,255,255,0.5);
}
.kpi .label {
font-size: 11px;
color: var(--ink-3);
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
display: flex;
align-items: center;
gap: 6px;
}
.kpi .value {
font-size: 24px;
font-weight: 700;
letter-spacing: -0.02em;
font-family: var(--mono);
}
.kpi .foot {
font-size: 12px;
color: var(--ink-3);
display: flex;
align-items: center;
gap: 6px;
}
.kpi.accent {
background: linear-gradient(135deg, oklch(97% 0.05 82), oklch(95% 0.09 76));
border-color: var(--amber-ring);
box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255,255,255,0.7);
}
.kpi.accent .label { color: var(--amber-ink); }
/* Overview */
.overview-shell {
display: grid;
grid-template-columns: minmax(0, 1fr) 300px;
gap: 14px;
margin-bottom: 20px;
align-items: start;
}
.overview-main {
display: flex;
flex-direction: column;
gap: 12px;
min-width: 0;
}
.overview-side {
display: flex;
flex-direction: column;
gap: 16px;
}
.overview-market-card,
.overview-side-card,
.overview-stat-card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: 16px;
padding: 20px;
box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255,255,255,0.55);
min-width: 0;
}
.overview-market-card {
background:
linear-gradient(135deg,
color-mix(in oklab, var(--amber) 12%, var(--surface)) 0%,
var(--surface) 48%,
color-mix(in oklab, var(--up) 8%, var(--surface)) 100%);
box-shadow: var(--shadow-2), inset 0 1px 0 rgba(255,255,255,0.65);
}
.overview-kicker {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--amber-ink);
margin-bottom: 10px;
opacity: 0.85;
}
.overview-card-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 18px;
}
.overview-headline-row {
display: flex;
align-items: flex-end;
gap: 12px;
flex-wrap: wrap;
}
.overview-market-subtitle {
margin-top: 8px;
font-size: 13px;
line-height: 1.5;
color: var(--ink-3);
}
.overview-controls {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10px;
flex-wrap: wrap;
}
/* ── Macro composite — unified card (band + track + scale together) ── */
/* Outer wrapper so band / track / scale read as ONE component.
Tone-coloured rim hints at the regime without competing with the big
number; it only kicks in for bull / bear so a neutral day stays calm. */
.overview-macro-card {
margin-top: 4px;
padding: 16px 18px 14px;
border-radius: 14px;
border: 1px solid var(--line);
background: color-mix(in oklab, var(--bg-sunk) 72%, var(--surface));
box-shadow: var(--shadow-1);
transition: border-color 200ms ease;
}
.overview-macro-card.bull {
border-color: color-mix(in oklab, var(--up) 30%, var(--line));
background: color-mix(in oklab, var(--up) 5%, color-mix(in oklab, var(--bg-sunk) 72%, var(--surface)));
}
.overview-macro-card.bear {
border-color: color-mix(in oklab, var(--down) 30%, var(--line));
background: color-mix(in oklab, var(--down) 5%, color-mix(in oklab, var(--bg-sunk) 72%, var(--surface)));
}
/* Band = title-row + summary (left) | big score + pill (right).
No own border any more — the card around it is the visual edge. */
.overview-macro-band {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
}
.overview-macro-copy {
min-width: 0;
flex: 1 1 0;
}
.overview-macro-title-row {
display: flex;
align-items: baseline;
gap: 10px;
flex-wrap: wrap;
margin-bottom: 4px;
}
.overview-macro-title {
font-size: 14px;
font-weight: 700;
letter-spacing: 0.02em;
color: var(--ink);
}
/* Small dim caption next to the title — gives the user a quick "what is
this and when did it update" anchor without crowding the headline. */
.overview-macro-stamp {
font-size: 11px;
letter-spacing: 0.04em;
color: var(--ink-4);
text-transform: uppercase;
}
.overview-macro-text,
.overview-side-copy,
.overview-stat-label {
font-size: 13px;
line-height: 1.5;
color: var(--ink-3);
}
.overview-macro-score {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 6px;
flex-shrink: 0;
}
.overview-score-value {
font-family: var(--mono);
font-size: 38px;
line-height: 1;
font-weight: 800;
letter-spacing: -0.03em;
color: var(--ink);
}
.overview-score-value.bull { color: var(--up); }
.overview-score-value.bear { color: var(--down); }
.overview-score-value.neutral { color: var(--ink-2); }
.overview-score-pill {
display: inline-flex;
align-items: center;
height: 24px;
padding: 0 11px;
border-radius: 999px;
border: 1px solid var(--line);
background: var(--surface);
font-size: 10.5px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-3);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.5);
}
.overview-score-pill.bull {
/* Darkened from var(--up) (62% → 3.0:1) to clear AA 4.5:1 on the 12% tint. */
color: oklch(40% 0.17 150);
border-color: color-mix(in oklab, var(--up) 32%, var(--line));
background: color-mix(in oklab, var(--up) 12%, var(--surface));
}
.overview-score-pill.bear {
color: oklch(42% 0.20 27);
border-color: color-mix(in oklab, var(--down) 32%, var(--line));
background: color-mix(in oklab, var(--down) 12%, var(--surface));
}
/* Dark mode: tint background is dark, so revert to the bright tone tokens. */
html[data-theme="dark"] .overview-score-pill.bull { color: var(--up); }
html[data-theme="dark"] .overview-score-pill.bear { color: var(--down); }
/* Gradient track. Removed the .overview-score-fill overlay — the gradient
already encodes "where am I on the spectrum"; layering an opaque fill
only obscured the gradient at scores near 0. */
.overview-score-track {
position: relative;
height: 16px;
margin-top: 14px;
border-radius: 999px;
overflow: visible;
border: 1px solid color-mix(in oklab, var(--line) 78%, transparent);
box-shadow: inset 0 1px 3px rgba(15, 23, 42, 0.1), inset 0 -1px 0 rgba(255,255,255,0.3);
background: linear-gradient(90deg,
color-mix(in oklab, var(--down) 70%, transparent) 0%,
color-mix(in oklab, var(--down) 20%, transparent) 35%,
color-mix(in oklab, var(--bg-sunk) 90%, transparent) 50%,
color-mix(in oklab, var(--up) 20%, transparent) 65%,
color-mix(in oklab, var(--up) 70%, transparent) 100%);
}
/* Vertical centerline tick so 0 is visually obvious. */
.overview-score-track::after {
content: '';
position: absolute;
top: -3px;
bottom: -3px;
left: 50%;
width: 1px;
background: color-mix(in oklab, var(--ink) 18%, transparent);
transform: translateX(-50%);
}
/* SOLID needle (was a hollow ring before — looked like a placeholder).
Tone-coloured fill + white ring so it stands out against any gradient
position. Two-layer shadow for depth on light AND dark themes. */
.overview-score-needle {
position: absolute;
top: 50%;
width: 22px;
height: 22px;
border-radius: 999px;
background: var(--ink-2);
border: 3px solid var(--surface);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25),
0 0 0 1px color-mix(in oklab, var(--ink) 18%, transparent);
transform: translateY(-50%);
transition: left 400ms cubic-bezier(0.4, 0, 0.2, 1);
}
.overview-score-needle.bull { background: var(--up); }
.overview-score-needle.bear { background: var(--down); }
.overview-score-needle.neutral { background: var(--ink-3); }
.overview-score-scale {
display: flex;
justify-content: space-between;
gap: 10px;
margin-top: 8px;
font-size: 10.5px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ink-4);
}
.overview-system-strip {
display: none; /* replaced by .overview-stats-bar */
}
.overview-system-chip {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 2px;
flex: 1;
padding: 10px 16px;
border-right: 1px solid var(--line);
border-radius: 0;
border-top: none;
border-bottom: none;
border-left: none;
background: transparent;
box-shadow: none;
transition: background 100ms;
}
.overview-system-chip:last-child {
border-right: none;
}
.overview-system-chip:hover {
transform: none;
box-shadow: none;
background: color-mix(in oklab, var(--amber) 5%, transparent);
}
.overview-system-chip.passive {
cursor: default;
}
.overview-system-chip.passive:hover {
background: transparent;
}
.overview-system-chip-name {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ink-4);
}
.overview-system-chip strong {
font-family: var(--mono);
font-size: 20px;
line-height: 1.1;
font-weight: 700;
color: var(--ink);
}
/* ── Unified stats bar (replaces system strip + secondary grid) ─── */
.overview-stats-bar {
display: flex;
align-items: stretch;
border: 1px solid var(--line);
border-radius: 14px;
overflow: hidden;
background: var(--surface);
box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255,255,255,0.55);
}
.stats-bar-item {
display: flex;
flex-direction: column;
justify-content: center;
gap: 3px;
flex: 1;
padding: 12px 16px;
border-right: 1px solid var(--line);
background: transparent;
border-top: none;
border-bottom: none;
border-left: none;
transition: background 100ms;
text-decoration: none;
color: inherit;
}
.stats-bar-item:not(.passive):hover {
background: color-mix(in oklab, var(--amber) 5%, transparent);
}
.stats-bar-item.passive {
cursor: default;
}
/* Thin vertical separator between signal counts and personal stats */
.stats-bar-divider {
width: 1px;
background: var(--line);
flex-shrink: 0;
align-self: stretch;
margin: 10px 0;
}
.stats-bar-label {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ink-4);
white-space: nowrap;
}
.stats-bar-value {
font-family: var(--mono);
font-size: 22px;
font-weight: 800;
line-height: 1.1;
letter-spacing: -0.025em;
}
/* Keep old classes so nothing else breaks */
.overview-secondary-grid { display: none; }
.overview-stat-card { display: none; }
.overview-stat-value {
font-family: var(--mono);
font-size: 32px;
line-height: 1;
font-weight: 800;
letter-spacing: -0.03em;
color: var(--ink);
}
.overview-side-card.compact {
min-height: 112px;
}
.overview-account-list {
display: flex;
flex-direction: column;
gap: 0;
}
.overview-account-item {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 16px;
padding: 10px 0;
border-bottom: 1px solid var(--line);
font-size: 13px;
color: var(--ink-3);
}
.overview-account-item:last-child {
border-bottom: 0;
padding-bottom: 0;
}
.overview-account-item strong {
color: var(--ink);
font-size: 14px;
text-align: right;
}
/* Right rail */
.rail { display: flex; flex-direction: column; gap: 20px; }
.confidence-bar {
height: 6px;
background: var(--surface-3);
border-radius: 999px;
overflow: hidden;
position: relative;
margin-top: 6px;
}
.confidence-bar > div {
height: 100%;
background: linear-gradient(90deg, var(--amber) 0%, oklch(68% 0.2 45) 100%);
border-radius: 999px;
transition: width 600ms ease-out;
}
.ai-metric-head {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 12px;
margin-bottom: 8px;
font-size: 13px;
color: var(--ink-3);
}
.ai-metric-head strong {
font-size: 18px;
line-height: 1;
color: var(--ink);
font-family: var(--mono);
font-weight: 600;
letter-spacing: -0.02em;
}
.ai-reasoning-label {
display: inline-flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--amber-ink);
}
.ai-reasoning-label::before {
content: '';
width: 8px;
height: 8px;
border-radius: 999px;
background: linear-gradient(135deg, var(--amber), oklch(68% 0.2 45));
box-shadow: 0 0 0 4px color-mix(in oklab, var(--amber) 18%, transparent);
}
.ai-reasoning-card {
padding: 16px 18px;
background:
linear-gradient(180deg, color-mix(in oklab, var(--amber-soft) 55%, var(--surface)) 0%, var(--bg-sunk) 100%);
border-radius: 18px;
border: 1px solid color-mix(in oklab, var(--amber-ring) 55%, var(--line));
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7), var(--shadow-1);
font-size: 15px;
line-height: 1.78;
color: var(--ink);
}
.ai-reasoning-card.scroll {
max-height: 180px;
overflow-y: auto;
padding-right: 14px;
}
html[data-theme="dark"] .ai-reasoning-card {
background:
linear-gradient(180deg, color-mix(in oklab, var(--amber-soft) 30%, var(--surface-2)) 0%, var(--surface) 100%);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03), var(--shadow-1);
}
/* Post list (stream) */
.post-stream {
display: flex;
flex-direction: column;
gap: 8px;
}
.post-row {
display: flex;
flex-direction: column;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
transition: border-color 150ms, background 150ms, box-shadow 150ms;
cursor: pointer;
overflow: hidden;
box-shadow: var(--shadow-1);
}
.post-row:hover {
border-color: color-mix(in oklab, var(--amber) 25%, var(--line));
box-shadow: var(--shadow-2);
transform: translateY(-1px);
}
.post-row.selected {
border-color: color-mix(in oklab, var(--amber) 55%, var(--line));
background: color-mix(in oklab, var(--amber) 4%, var(--surface));
box-shadow: var(--shadow-2), 0 0 0 1px color-mix(in oklab, var(--amber) 20%, transparent);
}
/* Buy signal: green left accent */
.post-row.signal-buy {
border-left: 4px solid var(--up);
background: linear-gradient(90deg, color-mix(in oklab, var(--up) 5%, var(--surface)) 0%, var(--surface) 160px);
}
/* Short signal: red left accent */
.post-row.signal-short {
border-left: 4px solid var(--down);
background: linear-gradient(90deg, color-mix(in oklab, var(--down) 5%, var(--surface)) 0%, var(--surface) 160px);
}
html[data-theme="dark"] .post-row.signal-buy {
background: linear-gradient(90deg, color-mix(in oklab, var(--up) 10%, var(--surface)) 0%, var(--surface) 160px);
}
html[data-theme="dark"] .post-row.signal-short {
background: linear-gradient(90deg, color-mix(in oklab, var(--down) 10%, var(--surface)) 0%, var(--surface) 160px);
}
/* Off-topic / noise posts: compact, recessed, low-emphasis. They're the bulk
of the Trump feed but carry no signal — make them read as skippable filler
so the eye lands on the real signals. */
.post-row.noise {
background: var(--bg-sunk);
border-color: transparent;
opacity: 0.82;
}
.post-row.noise:hover {
opacity: 1;
}
.post-row.noise:hover { border-color: var(--line); }
.post-row.noise .post-row-main {
grid-template-columns: 22px 1fr; /* drop the aside column */
gap: 10px;
padding: 8px 14px;
align-items: center;
}
.post-row.noise .src-ico {
width: 22px; height: 22px;
font-size: 11px; border-radius: 6px;
opacity: 0.45;
align-self: center;
}
.post-row.noise .post-body .meta { margin-bottom: 2px; }
.post-row.noise .post-body .text { margin: 0; font-size: 13px; }
.post-row-main {
display: grid;
grid-template-columns: 36px 1fr auto;
gap: 14px;
padding: 14px 16px;
align-items: flex-start;
}
.post-row-detail {
border-top: 1px solid var(--line);
padding: 14px 16px;
display: flex;
flex-direction: column;
gap: 14px;
}
.src-ico {
width: 36px;
height: 36px;
border-radius: 10px;
display: grid;
place-items: center;
font-family: var(--mono);
font-weight: 600;
font-size: 15px;
flex-shrink: 0;
}
.src-ico.x { background: #111; color: #fff; }
.src-ico.truth { background: oklch(92% 0.08 28); color: oklch(40% 0.22 25); font-weight: 700; }
/* Non-Trump sources — distinct hues so the feed reads at a glance. */
.src-ico.breakout { background: oklch(94% 0.08 200); color: oklch(40% 0.15 220); }
.src-ico.reversal { background: oklch(94% 0.08 300); color: oklch(40% 0.15 300); }
.src-ico.whale { background: oklch(94% 0.08 150); color: oklch(40% 0.15 150); font-size: 13px; }
.src-ico.manual { background: oklch(94% 0.05 60); color: oklch(45% 0.15 60); font-size: 13px; }
.src-ico.external { background: var(--bg-sunk); color: var(--ink-2); }
/* min-width:0 — without it a long unbreakable URL in the text widens the 1fr
grid column past the card and the right edge (meta tag included) is clipped. */
.post-body { min-width: 0; }
.post-body .meta {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 6px;
font-size: 12px;
color: var(--ink-3);
}
.post-body .text {
font-size: 14px;
line-height: 1.5;
color: var(--ink);
margin: 0 0 10px;
overflow-wrap: anywhere;
}
.post-aside {
display: flex;
flex-direction: column;
gap: 8px;
align-items: flex-end;
min-width: 120px;
}
/* Signal pill */
.sig {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
border-radius: var(--r-pill);
font-size: 11px;
font-weight: 800;
letter-spacing: 0.06em;
}
.sig.buy {
background: linear-gradient(135deg, var(--up) 0%, oklch(66% 0.19 155) 100%);
color: #fff;
box-shadow: 0 2px 8px color-mix(in oklab, var(--up) 45%, transparent),
inset 0 1px 0 rgba(255,255,255,0.2);
}
.sig.sell, .sig.short {
background: linear-gradient(135deg, var(--down) 0%, oklch(55% 0.24 18) 100%);
color: #fff;
box-shadow: 0 2px 8px color-mix(in oklab, var(--down) 45%, transparent),
inset 0 1px 0 rgba(255,255,255,0.15);
}
.sig.hold {
background: var(--bg-sunk);
color: var(--ink-3);
border: 1px solid var(--line);
font-weight: 600;
}
.impact-mini {
display: flex;
gap: 8px;
align-items: center;
font-family: var(--mono);
font-size: 12px;
}
.impact-mini .tf { color: var(--ink-4); font-size: 11px; }
/* Trades table */
.table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
font-size: 13px;
}
.table th {
text-align: left;
font-weight: 700;
color: var(--ink-3);
padding: 12px 18px;
border-bottom: 1px solid var(--line);
font-size: 11px;
letter-spacing: 0.06em;
text-transform: uppercase;
background: var(--bg-sunk);
}
.table th:first-child { border-top-left-radius: var(--r-md); }
.table th:last-child { border-top-right-radius: var(--r-md); text-align: right; }
.table td {
padding: 14px 18px;
border-bottom: 1px solid var(--line);
}
.table td:last-child { text-align: right; }
.table tr:last-child td { border-bottom: 0; }
.table tr:hover td { background: var(--bg-sunk); }
.side-pill {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 3px 9px;
border-radius: var(--r-pill);
font-size: 12px;
font-weight: 600;
}
.side-pill.long { background: var(--up-soft); color: oklch(38% 0.16 148); }
.side-pill.short { background: var(--down-soft); color: oklch(42% 0.2 25); }
/* Settings */
.settings-grid {
display: grid;
grid-template-columns: 260px 1fr;
gap: 32px;
align-items: start;
}
.settings-side {
position: sticky;
top: 84px;
display: flex;
flex-direction: column;
gap: 2px;
}
.settings-side button {
text-align: left;
padding: 10px 14px;
border-radius: var(--r-sm);
font-size: 13px;
color: var(--ink-2);
font-weight: 500;
}
.settings-side button.on { background: var(--bg-sunk); color: var(--ink); }
.settings-side button:hover { background: var(--bg-sunk); color: var(--ink); }
.field {
display: flex;
flex-direction: column;
gap: 6px;
margin-bottom: 20px;
}
.field label {
font-size: 13px;
font-weight: 500;
}
.field .hint {
font-size: 12px;
color: var(--ink-3);
}
.field input, .field select, .field textarea {
border: 1px solid var(--line);
background: var(--surface);
border-radius: var(--r-sm);
padding: 11px 14px;
font-size: 14px;
width: 100%;
outline: none;
transition: border-color 120ms, box-shadow 120ms;
color: var(--ink);
}
.field input:focus, .field textarea:focus {
border-color: var(--amber-ring);
box-shadow: 0 0 0 3px oklch(88% 0.12 80 / 0.4);
}
.settings-switch {
--w: 40px;
width: var(--w);
height: 24px;
border-radius: 999px;
background: var(--surface-3);
position: relative;
cursor: pointer;
transition: background 150ms;
border: 1px solid var(--line);
flex-shrink: 0;
}
.settings-switch::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 18px;
height: 18px;
border-radius: 999px;
background: var(--surface);
box-shadow: var(--shadow-1);
transition: transform 180ms;
}
.settings-switch.on {
background: linear-gradient(135deg, var(--amber) 0%, oklch(73% 0.18 70) 100%);
border-color: var(--amber);
box-shadow: 0 0 0 2px color-mix(in oklab, var(--amber) 20%, transparent);
}
.settings-switch.on::after { transform: translateX(16px); }
.setting-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 24px;
padding: 16px 0;
border-bottom: 1px solid var(--line);
}
.setting-row:last-child { border-bottom: 0; }
.setting-row .desc { font-size: 12px; color: var(--ink-3); margin-top: 4px; max-width: 440px; line-height: 1.5; }
/* Analytics */
.metric-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
@media (max-width: 1180px) {
.overview-shell {
grid-template-columns: 1fr;
}
.overview-side {
order: -1;
}
/* Stats bar: 2 columns on tablet/narrow desktop */
.overview-stats-bar {
flex-wrap: wrap;
}
.stats-bar-item {
flex: 0 0 calc(50% - 1px);
border-right: 1px solid var(--line);
border-bottom: 1px solid var(--line);
}
.stats-bar-item:nth-child(2n) {
border-right: none;
}
.stats-bar-item:nth-last-child(-n+2) {
border-bottom: none;
}
.stats-bar-divider { display: none; }
.dash-grid {
grid-template-columns: 1fr;
}
.rail {
order: -1;
}
}
@media (max-width: 900px) {
.nav {
height: auto;
min-height: 64px;
padding: 12px 18px;
flex-wrap: wrap;
gap: 12px;
}
.brand {
flex: 1;
}
.nav-tabs {
order: 3;
width: 100%;
overflow-x: auto;
justify-content: flex-start;
}
.nav-right {
margin-left: auto;
}
.page {
padding: 24px 18px 64px;
}
.page-head {
align-items: flex-start;
flex-direction: column;
gap: 12px;
}
.page-title {
font-size: 28px;
}
.kpi-row,
.metric-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.overview-secondary-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.settings-grid,
.form-row {
grid-template-columns: 1fr;
gap: 10px;
}
.settings-side {
position: static;
}
.chart-footnote {
flex-wrap: wrap;
}
.chart-footnote > div:last-child {
margin-left: 0 !important;
width: 100%;
}
.table {
min-width: 760px;
}
.card.flush {
overflow-x: auto !important;
}
}
@media (max-width: 900px) {
.nav-tabs {
/* Edge-to-edge horizontal scroll with snap on mobile */
margin: 0 -18px;
padding: 0 18px;
gap: 4px;
scroll-snap-type: x proximity;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.nav-tabs::-webkit-scrollbar { display: none; }
.nav-tab {
flex: 0 0 auto;
scroll-snap-align: start;
padding: 8px 12px;
font-size: 13px;
}
}
@media (max-width: 640px) {
.nav {
padding: 10px 14px;
}
.nav-tabs {
margin: 0 -14px;
padding: 0 14px;
}
.brand span:last-child {
display: none;
}
.connect-btn.lg {
padding: 9px 12px;
font-size: 13px;
}
.wallet-chip {
padding: 6px 10px !important;
font-size: 12px;
}
.page {
padding: 20px 14px 56px;
}
.kpi-row,
.metric-grid {
grid-template-columns: 1fr;
}
.overview-card-head,
.overview-controls,
.overview-macro-band,
.overview-account-item {
flex-direction: column;
align-items: flex-start;
}
.overview-score-value {
font-size: 34px;
}
.overview-account-item strong {
text-align: left;
}
.overview-secondary-grid {
grid-template-columns: 1fr;
}
.overview-system-strip {
flex-wrap: wrap;
}
/* Guest onboarding strip: 3-col → 1-col on mobile */
.guest-strip { grid-template-columns: 1fr !important; }
.macro-panel-head,
.macro-composite-head {
flex-direction: column;
align-items: flex-start;
}
.macro-panel-meta {
text-align: left;
}
.macro-grid.two {
grid-template-columns: 1fr;
}
.macro-grid {
padding: 12px;
gap: 12px;
}
.macro-metric-card,
.macro-guide-card {
padding: 16px;
min-height: 0;
}
.macro-metric-head {
/* Desktop is now also flex-direction: column. Just override the gap
for mobile to keep label-to-value spacing comfortable on small fonts. */
gap: 8px;
}
.macro-metric-value {
font-size: 30px;
}
.macro-guide-title {
font-size: 18px;
}
.macro-summary {
font-size: 13px;
line-height: 1.5;
min-height: 0;
}
.macro-threshold-chip {
font-size: 11.5px;
/* Bump tap target on mobile — iOS HIG asks for ≥44pt, this is the
inline-flex bubble so we go a bit smaller but still comfortable. */
min-height: 36px;
padding: 7px 12px;
}
.macro-threshold-chip.active {
font-size: 12px;
padding: 8px 14px;
/* Stronger scale on mobile — small screens lose subtle differences. */
transform: scale(1.08);
}
.macro-action-btn {
/* Stays compact on mobile too — bottom-right corner pill, not full-width. */
padding: 8px 12px;
}
.settings-scope-grid {
grid-template-columns: 1fr;
}
.settings-control-center {
grid-template-columns: 1fr;
}
.settings-section-head {
flex-direction: column;
align-items: flex-start;
}
.settings-section-note {
text-align: left;
}
/* Composite scale labels — "BEAR / NEUTRAL / BULL" letters crowd at narrow
widths. Shorten them. */
.macro-composite-scale {
font-size: 9.5px;
letter-spacing: 0.02em;
}
/* InfoTip — boost tap area and clamp bubble inside viewport */
.infotip {
/* Pad outside the visible icon to enlarge the hit zone without making
the icon itself look bloated. */
padding: 8px;
margin: -8px -2px -8px 4px;
}
.infotip-icon {
width: 18px;
height: 18px;
font-size: 11px;
}
.infotip-bubble {
/* On small viewports a 280px-wide bubble can hang off the edge when
the tip sits near the right margin. Clamp to viewport width minus
a 16px breathing room each side. */
max-width: calc(100vw - 32px) !important;
}
/* Touch-only browsers fire :active on tap. Combined with :hover, this
ensures bubble shows on first tap (not the doubled-tap that some
CSS-hover-only tooltips require). */
.infotip:active .infotip-bubble {
opacity: 1;
}
/* Keep the viewport-clamp shift on tap — `transform: none` here used to
discard it and re-centre the bubble off-screen. */
.infotip-top:active .infotip-bubble,
.infotip-bottom:active .infotip-bubble {
transform: translate(calc(-50% + var(--tip-shift, 0px)), 0);
}
.infotip-left:active .infotip-bubble { transform: translate(0, -50%); }
.infotip-right:active .infotip-bubble { transform: translate(0, -50%); }
.macro-action-label {
font-size: 12px;
white-space: normal;
}
.macro-composite {
padding: 18px 16px 20px;
}
.macro-composite-right {
justify-content: flex-start;
}
.row.between {
align-items: flex-start;
}
.post-row-main {
grid-template-columns: 32px 1fr;
}
.post-aside {
grid-column: 2;
align-items: flex-start;
min-width: 0;
}
.src-ico {
width: 32px;
height: 32px;
}
.chart-wrap,
.chart-wrap > div {
height: 300px !important;
}
.hero-value {
font-size: 34px;
}
.asset-switch,
.tf-bar {
max-width: 100%;
overflow-x: auto;
}
footer {
flex-wrap: wrap;
padding: 18px 14px !important;
}
.card[style*='grid-template-columns: 1fr auto'] {
grid-template-columns: 1fr !important;
}
/* Tables: horizontal-scroll with subtle hint rather than page overflow */
.card.flush { border-radius: 10px; }
.table { font-size: 12px; }
.table th, .table td { padding: 10px 12px !important; }
/* Modals / dialogs on small screens */
.modal, .dialog { width: calc(100vw - 24px) !important; max-width: calc(100vw - 24px) !important; }
}
/* Ultra-narrow screens — drop the regime descriptive words on the composite
scale so it doesn't wrap. Keep just the numeric anchors. The .word spans
inside each label are set in MacroPanel.tsx so this hide works cleanly. */
@media (max-width: 420px) {
.macro-composite-scale .word { display: none; }
}
@media (max-width: 380px) {
.nav { padding: 8px 12px; }
.page { padding: 16px 12px 48px; }
.page-title { font-size: 24px; }
.hero-value { font-size: 28px; }
.connect-btn.lg { padding: 8px 10px; font-size: 12px; }
}
/* Global safety: no page-level horizontal scroll.
Only set overflow-x on html (the real viewport scroll container).
Setting it on body would make body a secondary scroll container and
silently break `position: sticky` on the navbar. */
html { max-width: 100%; overflow-x: hidden; }
body { max-width: 100%; }
/* Misc utilities */
.stack { display: flex; flex-direction: column; }
.row { display: flex; align-items: center; }
.between { justify-content: space-between; }
.gap-s { gap: 8px; }
.gap-m { gap: 14px; }
.gap-l { gap: 20px; }
.grow { flex: 1; }
.mono-num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.muted { color: var(--ink-3); }
.tiny { font-size: 11px; text-transform: uppercase; letter-spacing: 0.09em; color: var(--ink-3); font-weight: 600; }
/* Scrollbar */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 999px; border: 2px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: color-mix(in oklab, var(--amber) 25%, var(--line-2)); }
/* ── Skeleton / shimmer loading ─────────────────────────────────────────────
Use .skeleton on any block element to get a shimmer placeholder.
Combine with explicit width/height or min-height to shape the placeholder.
.skeleton-card wraps a card-shaped block. */
@keyframes shimmer {
0% { background-position: -600px 0; }
100% { background-position: 600px 0; }
}
.skeleton {
border-radius: 6px;
background: linear-gradient(
90deg,
var(--bg-sunk) 25%,
color-mix(in srgb, var(--bg-sunk) 60%, var(--line)) 50%,
var(--bg-sunk) 75%
);
background-size: 1200px 100%;
animation: shimmer 1.4s ease-in-out infinite;
}
.skeleton-card {
border-radius: var(--r-md);
border: 1px solid var(--line);
padding: 20px;
background: var(--surface);
box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255,255,255,0.5);
display: flex;
flex-direction: column;
gap: 10px;
}
/* Convenience shorthands */
.sk-line { height: 14px; }
.sk-line-sm { height: 11px; }
.sk-title { height: 20px; }
.sk-w-full { width: 100%; }
.sk-w-3q { width: 75%; }
.sk-w-half { width: 50%; }
.sk-w-q { width: 25%; }
/* ============================================================
Live "terminal" motion layer (A-tier + ticker)
All effects degrade to static under prefers-reduced-motion
(see the media query at the very end of this block).
============================================================ */
/* ── Tick flash: a value that changed pulses green/red briefly ─────────────── */
@keyframes tick-flash-up {
0% { color: var(--up); text-shadow: 0 0 10px color-mix(in oklab, var(--up) 55%, transparent); }
100% { color: inherit; text-shadow: none; }
}
@keyframes tick-flash-down {
0% { color: var(--down); text-shadow: 0 0 10px color-mix(in oklab, var(--down) 55%, transparent); }
100% { color: inherit; text-shadow: none; }
}
.tick-num { transition: color 120ms; }
.tick-flash-up { animation: tick-flash-up 0.6s ease-out 1; }
.tick-flash-down { animation: tick-flash-down 0.6s ease-out 1; }
/* ── New signal row: slide in from the top + an amber sweep across it ──────── */
@keyframes signal-slide-in {
0% { opacity: 0; transform: translateY(-10px); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes signal-sweep {
0% { background-position: -150% 0; }
100% { background-position: 250% 0; }
}
.signal-enter { animation: signal-slide-in 0.32s cubic-bezier(.2,.8,.2,1) 1; }
.signal-enter > * { position: relative; }
.signal-enter > *::after {
content: '';
position: absolute;
inset: 0;
border-radius: var(--r-md);
pointer-events: none;
background: linear-gradient(
100deg,
transparent 0%,
color-mix(in oklab, var(--amber) 26%, transparent) 50%,
transparent 100%
);
background-size: 60% 100%;
background-repeat: no-repeat;
animation: signal-sweep 1.1s ease-out 1;
}
/* ── Card / chip hover glow — subtle amber ring lift on interactive cards ──── */
.overview-system-chip,
.overview-stat-card {
transition: border-color 160ms, box-shadow 220ms, transform 160ms;
}
.overview-system-chip:hover,
.overview-stat-card:hover {
box-shadow: 0 0 0 1px var(--amber-ring),
0 0 18px color-mix(in oklab, var(--amber) 22%, transparent);
transform: translateY(-1px);
}
/* ── Macro needle afterglow — a soft trailing pulse when it slides ─────────── */
.overview-score-needle {
box-shadow: 0 0 0 0 color-mix(in oklab, currentColor 45%, transparent);
transition: left 0.4s cubic-bezier(.2,.8,.2,1), box-shadow 0.6s ease-out;
}
.overview-score-needle.bull,
.overview-score-needle.bear { box-shadow: 0 0 14px 2px color-mix(in oklab, currentColor 40%, transparent); }
/* ============================================================
Ticker tape (B-#6)
============================================================ */
@keyframes ticker-marquee {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
.ticker {
position: sticky;
top: 0;
z-index: 90;
overflow: hidden;
border-bottom: 1px solid var(--line);
background: color-mix(in oklab, var(--amber) 2%, var(--bg-sunk));
/* fade the two edges so cells appear/vanish instead of hard-cutting */
-webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.ticker-track {
display: inline-flex;
align-items: center;
white-space: nowrap;
will-change: transform;
animation: ticker-marquee 40s linear infinite;
}
.ticker:hover .ticker-track { animation-play-state: paused; }
.ticker-cell {
display: inline-flex;
align-items: baseline;
gap: 7px;
padding: 7px 22px;
font-size: 12px;
font-family: var(--mono);
font-variant-numeric: tabular-nums;
border-right: 1px solid var(--line);
}
.ticker-sym { color: var(--amber-ink); font-weight: 700; letter-spacing: 0.05em; opacity: 0.8; }
.ticker-px { color: var(--ink); font-weight: 500; transition: color 120ms; }
.ticker-arrow { font-size: 9px; width: 9px; display: inline-block; }
.ticker-arrow.up { color: var(--up); }
.ticker-arrow.down { color: var(--down); }
/* reuse the tick-flash keyframes on a whole ticker cell */
.ticker-cell.tick-flash-up .ticker-px { animation: tick-flash-up 0.6s ease-out 1; }
.ticker-cell.tick-flash-down .ticker-px { animation: tick-flash-down 0.6s ease-out 1; }
html[data-theme="dark"] .ticker {
background: color-mix(in oklab, var(--amber) 4%, var(--bg-sunk));
}
/* ============================================================
Mobile wallet sheet
============================================================ */
@keyframes sheet-slide-up {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
.mobile-sheet-enter {
animation: sheet-slide-up 0.28s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
/* ── Accessibility: honour reduced-motion. Kill all the looping/entrance
motion above; keep static, readable end-states. ───────────────────────── */
@media (prefers-reduced-motion: reduce) {
/* Catch-all: neutralise every animation/transition (incl. infinite ones —
macro-dot-pulse, live-dot pulse, shimmer, needle pulse — and any added
later). 0.01ms instead of 0 so JS onAnimationEnd handlers still fire. */
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
/* Layout side-effects the duration reset alone can't undo: */
.ticker-track { transform: none; white-space: normal; flex-wrap: wrap; }
.signal-enter > *::after { display: none; }
.overview-system-chip:hover,
.overview-stat-card:hover { transform: none; }
}
/* ============================================================
Ticker tape — mobile overrides
============================================================ */
@media (max-width: 900px) {
.ticker {
/* On tablet/mobile the mask edges would eat too much of the narrow strip.
Tighten to 4% so content is visible closer to the edges. */
-webkit-mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
}
.ticker-cell {
padding: 6px 14px;
gap: 5px;
}
.ticker-sym { font-size: 11px; }
.ticker-px { font-size: 11px; }
}
@media (max-width: 640px) {
/* Slow the tape a touch on mobile so it's readable while scrolling */
.ticker-track { animation-duration: 55s; }
.ticker-cell {
padding: 5px 12px;
font-size: 11px;
}
/* On very small screens, hide the direction arrow to save space */
.ticker-arrow { display: none; }
/* Overview hero price — keep it from bumping into the chip on mobile */
.overview-headline-row {
flex-wrap: wrap;
gap: 6px;
}
/* Prevent the overview market card controls from stacking awkwardly */
.overview-controls {
width: 100%;
align-items: flex-start;
}
/* Chart: reduce height on phones so it doesn't eat the full viewport */
.chart-wrap,
.chart-wrap > div {
height: 240px !important;
}
/* overview-side cards are low priority on mobile — compress them */
.overview-side-card.compact {
display: none;
}
/* system strip: wrap to 2-per-row on small phones */
.overview-system-strip {
flex-wrap: wrap;
}
.overview-system-chip {
flex: 0 0 calc(50% - 1px);
}
/* Post row: remove the aside column on very small screens */
.post-row-main {
grid-template-columns: 28px 1fr;
gap: 10px;
padding: 12px 14px;
}
}
@media (max-width: 420px) {
/* Ultra-narrow: hide ticker entirely — the price display in the overview
card covers the same information without crowding the header */
.ticker { display: none; }
}