read.markets/app/static/css/cassandra.css
Giorgio Gilestro 8a155ef157 phase B (2/2): CSV upload endpoint + drag-drop UI
Completes Phase B. The full alternative-onboarding flow is now end-to-end:
drop a T212 pie CSV → parser → InstrumentMap resolver → PortfolioSnapshot
+ Position rows, all without ever asking the user for broker credentials.

- persist_pie() in app/services/csv_import.py: takes a ParsedPie, resolves
  each Slice via InstrumentMap, writes Portfolio + Snapshot + Position
  rows. Unmapped slices are still persisted using their CSV values and
  surfaced in the response for the UI to warn about.
- POST /api/portfolios/upload: multipart endpoint accepting CSV file +
  optional portfolio_name + currency. 2 MiB cap. Returns import summary.
- /upload page with drag-drop dropzone, file input fallback, and inline
  result panel showing invested/value/result + unmapped-slice warnings.
- New "Import" link in the header nav.

Verified end-to-end against the real T212 export: all 13 positions land
with correct T212 tickers (incl. FPp_EQ for the Paris TotalEnergies
listing the heuristic resolver picks), zero unmapped slices, totals
reconcile to the penny.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 11:00:42 +01:00

869 lines
24 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.

/* Cassandra — geopolitical-terminal aesthetic with two themes.
* Mono for data, headers, terminal feel; sans for prose surfaces (log + chat). */
:root {
/* Dark theme (default) */
--bg: #0a0e14;
--surface: #11151c;
--surface-2: #161b25;
--border: #2a3142;
--text: #d4dae8; /* lifted from #c0caf5 for readability */
--muted: #8189a1; /* lifted from #565f89 — was unreadably dim */
--dim: #565f89;
--accent: #00d9ff;
--positive: #50fa7b;
--negative: #ff5b5b;
--alert: #ff8a4a;
--warning: #f1fa8c;
--user-bubble-bg: rgba(0, 217, 255, 0.08);
}
[data-theme="light"] {
--bg: #f5f3ec; /* warm off-white, easier on the eyes than pure white */
--surface: #ffffff;
--surface-2: #efece3;
--border: #d6d3cb;
--text: #1c1f25;
--muted: #545b69;
--dim: #8a8f9a;
--accent: #0e7490; /* deep teal — still terminal-feel on light */
--positive: #166534;
--negative: #b91c1c;
--alert: #c2410c;
--warning: #a16207;
--user-bubble-bg: rgba(14, 116, 144, 0.07);
}
/* Font stacks. Mono for terminal feel; sans for reading. */
:root {
--font-mono: 'JetBrains Mono', 'IBM Plex Mono', 'Fira Code', ui-monospace, monospace;
--font-sans: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', Roboto,
'Helvetica Neue', system-ui, sans-serif;
}
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
background: var(--bg);
color: var(--text);
font-family: var(--font-mono);
font-size: 13px;
line-height: 1.5;
font-variant-numeric: tabular-nums;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
/* --- Layout ---------------------------------------------------------- */
.app {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
.app-header {
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid var(--border);
padding: 10px 18px;
background: var(--surface);
letter-spacing: 0.08em;
text-transform: uppercase;
}
.app-header .brand {
color: var(--accent);
font-weight: 700;
}
.app-header .brand::before { content: "▰ "; opacity: 0.6; }
.app-header nav a {
margin-left: 18px;
color: var(--muted);
}
.app-header nav a.active { color: var(--text); }
.app-header .meta { color: var(--muted); font-size: 11px; }
.app-header .header-right { display: flex; align-items: center; gap: 14px; }
.theme-toggle {
background: transparent;
border: 1px solid var(--border);
color: var(--muted);
padding: 3px 8px;
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.08em;
cursor: pointer;
text-transform: lowercase;
}
.theme-toggle:hover { color: var(--accent); border-color: var(--accent); }
.theme-toggle__label::before { content: "◐ dark"; }
[data-theme="light"] .theme-toggle__label::before { content: "◐ light"; }
.app-main {
padding: 14px;
display: grid;
grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
grid-template-rows: auto auto auto;
grid-template-areas:
"indicators log"
"portfolio log"
"news news";
gap: 14px;
}
@media (max-width: 1100px) {
.app-main {
grid-template-columns: 1fr;
grid-template-areas: "indicators" "portfolio" "log" "news";
}
}
#indicators-panel { grid-area: indicators; }
#portfolio-panel { grid-area: portfolio; }
#log-panel { grid-area: log; }
#news-panel { grid-area: news; }
.app-footer {
border-top: 1px solid var(--border);
padding: 8px 18px;
background: var(--surface);
font-size: 11px;
color: var(--muted);
display: flex;
gap: 16px;
flex-wrap: wrap;
}
/* --- Panels ----------------------------------------------------------- */
.panel {
background: var(--surface);
border: 1px solid var(--border);
position: relative;
}
.panel-header {
border-bottom: 1px solid var(--border);
padding: 8px 12px;
display: flex;
align-items: center;
justify-content: space-between;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--muted);
font-size: 11px;
background: linear-gradient(180deg, var(--surface-2), var(--surface));
}
.panel-header .title { color: var(--text); font-weight: 700; }
.panel-header .title::before { content: "■ "; color: var(--accent); }
.panel-header .meta { color: var(--dim); }
.panel-body { padding: 6px 0; }
.panel-body--scroll { max-height: 70vh; overflow-y: auto; }
/* --- Tables ----------------------------------------------------------- */
table.dense {
width: 100%;
border-collapse: collapse;
}
table.dense th, table.dense td {
padding: 4px 12px;
font-size: 12px;
border-bottom: 1px solid var(--surface-2);
white-space: nowrap;
}
table.dense th {
text-align: left;
color: var(--muted);
font-weight: 400;
text-transform: uppercase;
letter-spacing: 0.06em;
font-size: 10px;
background: var(--surface-2);
}
table.dense th.num,
table.dense td.num { text-align: right; }
table.dense td.label { color: var(--text); }
table.dense td.label.has-tip,
table.dense td[title] {
cursor: help;
border-bottom: 1px dotted color-mix(in srgb, var(--accent) 40%, transparent);
border-bottom-width: 1px;
}
table.dense tr:hover td { background: color-mix(in srgb, var(--accent) 5%, transparent); }
.pos { color: var(--positive); }
.neg { color: var(--negative); }
.neu { color: var(--muted); }
.note { color: var(--dim); font-size: 11px; }
/* Stale indicator rows — last observation > 90 days old */
table.dense tr.row-stale td { color: var(--dim); }
.stale-tag {
display: inline-block;
font-size: 8.5px;
letter-spacing: 0.08em;
color: var(--alert);
border: 1px solid var(--alert);
padding: 0 4px;
margin-left: 4px;
vertical-align: middle;
text-transform: uppercase;
cursor: help;
}
/* --- Status LEDs ------------------------------------------------------ */
.led { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 4px; vertical-align: middle; }
.led.ok { background: var(--positive); box-shadow: 0 0 6px var(--positive); }
.led.warn { background: var(--warning); box-shadow: 0 0 6px var(--warning); }
.led.err { background: var(--negative); box-shadow: 0 0 6px var(--negative); }
.led.idle { background: var(--dim); }
/* --- Dashboard top header (markets + aggregate read) ----------------- */
.dash-header {
display: grid;
grid-template-columns: 1fr;
gap: 12px;
margin-bottom: 0;
}
.dash-header__markets {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 1px;
background: var(--border);
border: 1px solid var(--border);
}
.mkt {
background: var(--surface);
padding: 6px 10px;
font-family: var(--font-mono);
font-size: 11px;
display: grid;
grid-template-columns: auto 1fr auto;
grid-template-rows: auto auto;
align-items: center;
gap: 2px 6px;
}
.mkt__dot {
width: 8px; height: 8px; border-radius: 50%;
grid-row: 1; grid-column: 1;
}
.mkt--open .mkt__dot { background: var(--positive); box-shadow: 0 0 6px var(--positive); }
.mkt--closed .mkt__dot { background: var(--dim); }
.mkt__name {
grid-row: 1; grid-column: 2;
color: var(--text); font-weight: 700;
text-transform: uppercase; letter-spacing: 0.08em;
}
.mkt__state {
grid-row: 1; grid-column: 3;
font-size: 9.5px; letter-spacing: 0.08em;
}
.mkt--open .mkt__state { color: var(--positive); }
.mkt--closed .mkt__state { color: var(--dim); }
.mkt__when {
grid-row: 2; grid-column: 2 / -1;
color: var(--muted); font-size: 10px;
font-variant-numeric: tabular-nums;
}
.mkt__when-label { color: var(--dim); }
.dash-header__read {
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
background: color-mix(in srgb, var(--accent) 4%, transparent);
padding: 10px 14px;
}
.dash-header__read-meta {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 4px;
}
.dash-header__read-body {
margin: 0;
font-family: var(--font-sans);
font-size: 14px;
line-height: 1.55;
color: var(--text);
}
.dash-header__read--pending { color: var(--dim); font-style: italic; }
.dash-header__read--pending .dash-header__read-body { color: var(--dim); font-size: 12px; }
/* --- Indicator group summary (above the table) ----------------------- */
.ind-summary {
font-family: var(--font-sans);
padding: 10px 16px;
border-bottom: 1px solid var(--surface-2);
border-left: 3px solid var(--accent);
background: color-mix(in srgb, var(--accent) 4%, transparent);
}
.ind-summary__head {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 4px;
}
.ind-summary__label {
font-family: var(--font-mono);
font-size: 10px;
color: var(--accent);
text-transform: uppercase;
letter-spacing: 0.1em;
font-weight: 700;
}
.ind-summary__label::before { content: "▸ "; }
.ind-summary__when {
font-family: var(--font-mono);
font-size: 10px;
color: var(--dim);
font-variant-numeric: tabular-nums;
}
.ind-summary__body {
margin: 0;
font-size: 13.5px;
line-height: 1.55;
color: var(--text);
}
.ind-summary--pending { color: var(--dim); font-style: italic; }
.ind-summary--pending .ind-summary__body { color: var(--dim); font-size: 12px; }
/* --- Group tabs ------------------------------------------------------- */
.group-tabs {
display: flex;
border-bottom: 1px solid var(--border);
overflow-x: auto;
}
.group-tabs button {
background: transparent;
border: 0;
border-right: 1px solid var(--border);
color: var(--muted);
font-family: inherit;
font-size: 11px;
padding: 6px 12px;
text-transform: uppercase;
letter-spacing: 0.06em;
cursor: pointer;
}
.group-tabs button:hover { color: var(--text); }
.group-tabs button.active {
color: var(--accent);
background: var(--bg);
box-shadow: inset 0 -2px 0 var(--accent);
}
/* --- Portfolio overall ----------------------------------------------- */
.pf-overall {
border-bottom: 1px solid var(--border);
padding: 10px 14px 12px;
background: linear-gradient(180deg, var(--surface-2), var(--surface));
}
.pf-overall__head {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 8px;
}
.pf-name {
color: var(--accent);
text-transform: uppercase;
letter-spacing: 0.1em;
font-weight: 700;
font-size: 11px;
}
.pf-name::before { content: "◆ "; opacity: 0.6; }
.pf-as-of { color: var(--dim); font-size: 11px; }
.pf-overall__grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 6px 24px;
}
@media (max-width: 640px) {
.pf-overall__grid { grid-template-columns: repeat(2, 1fr); }
}
.pf-stat-label {
font-size: 10px;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.08em;
}
.pf-stat-value {
font-size: 16px;
color: var(--text);
font-variant-numeric: tabular-nums;
margin-top: 2px;
}
.pf-stat-value.pos { color: var(--positive); }
.pf-stat-value.neg { color: var(--negative); }
.pf-stat-value.neu { color: var(--muted); }
.pf-ccy { color: var(--dim); font-size: 11px; margin-left: 2px; }
.pf-pct { color: var(--dim); font-size: 11px; margin-left: 4px; }
/* --- Log panel -------------------------------------------------------- */
.log-content {
font-family: var(--font-sans);
padding: 28px clamp(20px, 4vw, 56px) 32px;
font-size: 15.5px;
line-height: 1.72;
color: var(--text);
max-width: 76ch;
margin: 0 auto;
max-height: calc(100vh - 240px);
overflow-y: auto;
}
.log-content p { margin: 0 0 1.1em; }
.log-content h1, .log-content h2, .log-content h3, .log-content h4 {
font-family: var(--font-mono);
color: var(--accent);
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 12px;
margin-top: 1.8em;
margin-bottom: 0.5em;
font-weight: 700;
}
.log-content h1:first-child,
.log-content h2:first-child,
.log-content h3:first-child { margin-top: 0; }
/* TL;DR callout — model is instructed to put it first, so style the first
* heading + paragraph block as a callout. */
.log-content h3:first-of-type {
font-size: 11px;
color: var(--accent);
border-left: 3px solid var(--accent);
padding-left: 10px;
margin-bottom: 0;
}
.log-content h3:first-of-type + p {
font-size: 16.5px;
line-height: 1.6;
color: var(--text);
border-left: 3px solid var(--accent);
padding: 4px 14px 12px;
margin: 0 0 1.8em;
background: color-mix(in srgb, var(--accent) 5%, transparent);
font-weight: 500;
}
.log-content strong { color: var(--text); font-weight: 700; }
.log-content em { color: var(--muted); font-style: italic; }
.log-content ul, .log-content ol { padding-left: 1.4em; margin: 0 0 1.1em; }
.log-content li { margin-bottom: 0.4em; }
.log-content hr {
border: 0;
border-top: 1px solid var(--border);
margin: 1.6em 0;
}
/* --- Log page (calendar + log + chat sidebar) ------------------------- */
.log-page__body {
display: grid;
grid-template-columns: 220px 1fr 320px;
gap: 1px;
background: var(--border);
}
@media (max-width: 1100px) {
.log-page__body { grid-template-columns: 1fr; }
}
.log-page__cal, .log-page__content, .log-page__chat { background: var(--surface); }
.log-page__cal { padding: 10px; }
.log-page__content { min-height: 60vh; }
.log-page__chat { padding: 8px; min-height: 60vh; display: flex; flex-direction: column; }
/* --- Calendar widget --------------------------------------------------- */
.cal__nav {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.cal__title { color: var(--accent); font-weight: 700; }
.cal__btn {
background: transparent;
color: var(--muted);
border: 1px solid var(--border);
padding: 2px 8px;
cursor: pointer;
font-family: inherit;
font-size: 13px;
}
.cal__btn:hover { color: var(--accent); border-color: var(--accent); }
.cal__grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 1px;
background: var(--border);
border: 1px solid var(--border);
}
.cal__h {
text-align: center;
font-size: 9px;
color: var(--dim);
background: var(--surface-2);
padding: 3px 0;
text-transform: uppercase;
}
.cal__d {
background: var(--surface);
border: 0;
color: var(--muted);
font-family: inherit;
font-size: 11px;
padding: 6px 0;
text-align: center;
cursor: not-allowed;
}
.cal__d--empty { background: var(--bg); cursor: default; }
.cal__d--has-log {
color: var(--text);
cursor: pointer;
position: relative;
}
.cal__d--has-log::after {
content: "";
position: absolute;
bottom: 3px;
left: 50%;
transform: translateX(-50%);
width: 3px; height: 3px;
border-radius: 50%;
background: var(--accent);
}
.cal__d--has-log:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.cal__d--today { color: var(--warning); }
.cal__d--selected {
background: var(--accent);
color: var(--bg);
font-weight: 700;
}
.cal__d--selected::after { background: var(--bg); }
/* --- Badges (tone / analysis indicators) ------------------------------ */
.badge {
display: inline-block;
font-family: var(--font-mono);
font-size: 9.5px;
letter-spacing: 0.06em;
text-transform: uppercase;
padding: 1px 6px;
border: 1px solid currentColor;
margin-right: 4px;
background: transparent;
vertical-align: middle;
}
/* Tone axis — green→accent→amber as audience density rises */
.badge--tone-novice { color: var(--positive); }
.badge--tone-intermediate { color: var(--accent); }
.badge--tone-pro { color: var(--alert); }
/* Analysis axis — dry is muted, speculative is accent */
.badge--analysis-dry { color: var(--muted); }
.badge--analysis-speculative { color: var(--accent); }
.badge--ver { color: var(--dim); }
.meta__hint { color: var(--dim); font-size: 10px; margin-right: 4px; }
/* --- Log metadata footer ---------------------------------------------- */
.log-meta {
padding: 8px clamp(20px, 4vw, 56px) 16px;
max-width: 76ch;
margin: 0 auto;
border-top: 1px dashed var(--border);
}
.log-meta__row { display: flex; flex-wrap: wrap; align-items: center; gap: 0; margin-top: 6px; }
.log-meta__row--dim { color: var(--dim); font-size: 10.5px; }
/* --- Upload page (drag-drop CSV) ------------------------------------- */
.dz {
border: 2px dashed var(--border);
background: var(--surface-2);
padding: 36px 20px;
text-align: center;
cursor: pointer;
transition: border-color 0.15s, background 0.15s;
}
.dz:hover, .dz--over {
border-color: var(--accent);
background: color-mix(in srgb, var(--accent) 6%, var(--surface-2));
}
.dz__icon {
font-family: var(--font-mono);
font-size: 28px;
color: var(--accent);
letter-spacing: -2px;
margin-bottom: 6px;
}
.dz__label {
font-family: var(--font-mono);
font-size: 13px;
color: var(--text);
text-transform: uppercase;
letter-spacing: 0.08em;
}
.dz__hint { color: var(--muted); font-size: 11.5px; margin-top: 4px; }
.dz__hint a { color: var(--accent); }
.dz__filename { margin-top: 10px; color: var(--accent); font-size: 12px; font-family: var(--font-mono); min-height: 1em; }
.form-row { display: grid; grid-template-columns: 180px 1fr; align-items: center; gap: 12px; padding: 6px 0; }
.form-row label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; }
.form-row input[type="text"], .form-row select {
background: var(--bg);
border: 1px solid var(--border);
color: var(--text);
font-family: var(--font-mono);
font-size: 12px;
padding: 6px 8px;
outline: none;
}
.form-row input[type="text"]:focus, .form-row select:focus { border-color: var(--accent); }
#submit-btn {
margin-top: 14px;
background: transparent;
border: 1px solid var(--accent);
color: var(--accent);
font-family: var(--font-mono);
font-size: 11px;
padding: 8px 18px;
text-transform: uppercase;
letter-spacing: 0.1em;
cursor: pointer;
}
#submit-btn:hover:not(:disabled) { background: var(--accent); color: var(--bg); }
#submit-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.result {
margin-top: 20px;
padding: 14px;
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
background: color-mix(in srgb, var(--accent) 4%, transparent);
font-family: var(--font-sans);
font-size: 13px;
}
.result--err { border-left-color: var(--negative); background: color-mix(in srgb, var(--negative) 5%, transparent); }
.result__head {
font-family: var(--font-mono);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--accent);
margin-bottom: 10px;
}
.result--err .result__head { color: var(--negative); }
.result__tag {
display: inline-block;
margin-left: 6px;
font-size: 9px;
padding: 1px 5px;
border: 1px solid var(--accent);
color: var(--accent);
}
.result__grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px 18px;
margin-bottom: 10px;
}
.result__grid .k {
font-family: var(--font-mono);
font-size: 9.5px;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.08em;
}
.result__grid .v { font-size: 17px; color: var(--text); font-variant-numeric: tabular-nums; margin-top: 2px; }
.result__grid .v.pos { color: var(--positive); }
.result__grid .v.neg { color: var(--negative); }
.result__row { color: var(--muted); font-size: 12px; margin-top: 6px; }
.result__warn { color: var(--alert); font-size: 12px; margin-top: 4px; }
.result__warn code { background: rgba(0,0,0,0.15); padding: 1px 4px; font-family: var(--font-mono); }
/* --- Chat sidebar ----------------------------------------------------- */
.chat-header {
border-bottom: 1px solid var(--border);
padding: 6px 4px 8px;
margin-bottom: 6px;
display: flex;
flex-direction: column;
}
.chat-title {
color: var(--accent);
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
font-size: 11px;
}
.chat-title::before { content: "▸ "; }
.chat-hint { color: var(--dim); font-size: 10px; margin-top: 2px; }
.chat-thread {
flex: 1 1 auto;
overflow-y: auto;
padding: 4px 2px;
display: flex;
flex-direction: column;
gap: 8px;
min-height: 0;
}
.chat-msg {
font-family: var(--font-sans);
font-size: 13.5px;
padding: 9px 11px;
border: 1px solid var(--border);
line-height: 1.6;
word-wrap: break-word;
}
.chat-msg--system {
color: var(--muted);
font-size: 12px;
background: transparent;
border-style: dashed;
font-family: var(--font-mono);
}
.chat-msg--user {
background: var(--user-bubble-bg);
border-color: var(--accent);
color: var(--text);
align-self: flex-end;
max-width: 92%;
white-space: pre-wrap;
}
.chat-msg--user::before {
content: "you ";
font-family: var(--font-mono);
color: var(--accent);
opacity: 0.7;
font-size: 10px;
}
.chat-msg--assistant { background: var(--surface-2); color: var(--text); }
.chat-msg--assistant::before {
content: "cassandra ";
font-family: var(--font-mono);
color: var(--accent);
opacity: 0.7;
font-size: 10px;
}
.chat-msg--pending { color: var(--dim); font-style: italic; }
.chat-msg--error { color: var(--negative); border-color: var(--negative); }
.chat-msg p { margin: 0.4em 0; }
.chat-msg p:first-child { margin-top: 0; }
.chat-msg p:last-child { margin-bottom: 0; }
.chat-msg h2, .chat-msg h3, .chat-msg h4 {
font-family: var(--font-mono);
color: var(--accent);
font-size: 11px;
margin: 0.8em 0 0.3em;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.chat-msg strong { color: var(--text); font-weight: 700; }
.chat-msg em { color: var(--muted); font-style: italic; }
.chat-form {
border-top: 1px solid var(--border);
padding-top: 6px;
display: flex;
gap: 6px;
align-items: flex-end;
}
.chat-form textarea {
flex: 1;
background: var(--bg);
border: 1px solid var(--border);
color: var(--text);
font-family: inherit;
font-size: 12px;
padding: 6px 8px;
resize: vertical;
min-height: 36px;
outline: none;
}
.chat-form textarea:focus { border-color: var(--accent); }
.chat-form button {
background: transparent;
border: 1px solid var(--accent);
color: var(--accent);
font-family: inherit;
font-size: 11px;
padding: 6px 12px;
text-transform: uppercase;
letter-spacing: 0.08em;
cursor: pointer;
}
.chat-form button:hover:not(:disabled) { background: var(--accent); color: var(--bg); }
.chat-form button:disabled { opacity: 0.4; cursor: not-allowed; }
/* --- News ------------------------------------------------------------- */
.news-row {
padding: 4px 12px;
display: grid;
grid-template-columns: 50px 130px 1fr 110px;
gap: 12px;
font-size: 12px;
border-bottom: 1px solid var(--surface-2);
align-items: baseline;
}
@media (max-width: 720px) {
.news-row { grid-template-columns: 50px 100px 1fr; }
.news-row .local { display: none; }
}
.news-row:hover { background: color-mix(in srgb, var(--accent) 5%, transparent); }
.news-row .age { color: var(--dim); text-align: right; }
.news-row .source { color: var(--muted); font-size: 11px; }
.news-row .title { color: var(--text); }
.news-row .title:hover { color: var(--accent); }
.news-row .local {
color: var(--muted);
font-size: 11px;
text-align: right;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
/* --- Empty / loading state ------------------------------------------- */
.empty {
padding: 24px;
text-align: center;
color: var(--muted);
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.htmx-indicator {
display: inline-block;
color: var(--dim);
opacity: 0;
transition: opacity 0.2s;
}
.htmx-request .htmx-indicator { opacity: 1; }
/* --- Scrollbar -------------------------------------------------------- */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--dim); border-radius: 0; }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }