/* Cassandra — structural layout: html/body, app shell, header, main grid, * sticky markets bar, scrollbar. */ 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; position: sticky; top: 0; z-index: 50; } .app-header .brand { color: var(--accent); font-weight: 700; text-decoration: none; } .app-header .brand:hover { color: var(--text); } .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; } /* On desktop the mobile-drawer wrapper has no layout effect — its * children (nav, header-right) flow as if it weren't there. On mobile * the @media block at the bottom converts it to a fixed slide-out. */ .mobile-drawer { display: contents; } .app-header .header-right { display: flex; align-items: center; gap: 14px; } /* Hamburger button — only visible at ≤480px (rule in the mobile block). * Three thin bars; uses the same border/muted treatment as the other * header buttons so the visual rhythm matches. */ .drawer-toggle { display: none; background: transparent; border: 1px solid var(--border); cursor: pointer; padding: 6px 8px; width: 36px; height: 32px; flex-direction: column; justify-content: space-between; align-items: stretch; } .drawer-toggle:hover { border-color: var(--accent); } .drawer-toggle__bar { display: block; height: 2px; background: var(--muted); width: 100%; } .drawer-toggle:hover .drawer-toggle__bar { background: var(--accent); } .drawer-backdrop { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.4); z-index: 90; opacity: 0; transition: opacity 120ms ease-out; } body.drawer-open .drawer-backdrop { opacity: 1; } .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: "◐ light"; } [data-theme="dark"] .theme-toggle__label::before { content: "◐ dark"; } /* Tone toggle (segmented control: Novice | Intermediate) */ .tone-toggle { display: inline-flex; border: 1px solid var(--border); font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase; } .tone-toggle button { background: transparent; color: var(--muted); border: 0; padding: 4px 10px; cursor: pointer; font: inherit; letter-spacing: inherit; text-transform: inherit; } .tone-toggle button + button { border-left: 1px solid var(--border); } .tone-toggle button:hover { color: var(--accent); } .tone-toggle[data-tone="NOVICE"] button[data-value="NOVICE"], .tone-toggle[data-tone="INTERMEDIATE"] button[data-value="INTERMEDIATE"] { background: var(--accent); color: var(--bg); } /* Language toggle in the topbar — same visual rhythm as the tone * toggle so the two controls read as a pair. Only EN and IT are * visible here; the WIP languages (ES/FR/DE) live in /settings. */ .lang-toggle { display: inline-flex; border: 1px solid var(--border); font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase; } .lang-toggle button { background: transparent; color: var(--muted); border: 0; padding: 4px 8px; cursor: pointer; font: inherit; letter-spacing: inherit; text-transform: inherit; } .lang-toggle button + button { border-left: 1px solid var(--border); } .lang-toggle button:hover { color: var(--accent); } .lang-toggle[data-lang="en"] button[data-value="en"], .lang-toggle[data-lang="it"] button[data-value="it"] { background: var(--accent); color: var(--bg); } .app-main { padding: 14px; display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); grid-template-rows: auto auto auto auto; grid-template-areas: "header header" "indicators log" "portfolio log" "news news"; gap: 14px; } @media (max-width: 1100px) { .app-main { grid-template-columns: 1fr; grid-template-areas: "header" "indicators" "portfolio" "log" "news"; } } #dash-header-container { grid-area: header; } #indicators-panel { grid-area: indicators; } #portfolio-panel { grid-area: portfolio; } #log-panel { grid-area: log; /* Don't stretch to fill both grid rows; if the log is shorter than the portfolio next to it, the surplus below would render as a big empty white box. Aligning to the start makes the panel shrink to its content and the dashboard background fills any gap. */ align-self: start; } #news-panel { grid-area: news; } /* Sticky bottom markets bar — uses the same .mkt chip styling as the old dashboard header, extended with each market's headline index. */ .markets-bar { position: sticky; bottom: 0; z-index: 50; background: var(--surface); border-top: 1px solid var(--border); } .markets-bar__inner { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1px; background: var(--border); border: 0; } .markets-bar .mkt { border: 0; border-radius: 0; } /* --- 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); } /* --- Mobile (≤480px) -------------------------------------------------- */ @media (max-width: 480px) { /* Tighten the topbar so the brand, tone toggle and hamburger all fit on a 360px phone. Drop the letter-spacing because at 11px tracking eats horizontal space the brand cannot spare. */ .app-header { padding: 8px 12px; gap: 8px; letter-spacing: 0.04em; } .app-header .brand { font-size: 12px; /* Shrink the leading glyph but don't remove it — keeps brand identity. */ } .beta-chip { display: none; } /* Show the hamburger; the rest of the header widgets collapse into the drawer (the .mobile-drawer block below). */ .drawer-toggle { display: flex; margin-left: auto; } /* Keep the tone toggle visible but trim it: just N / I letters so it fits next to brand + hamburger. */ .tone-toggle--header { font-size: 9.5px; } .tone-toggle--header button { padding: 4px 7px; } /* The drawer wrapper: full-height slide-out from the right. The content inside (nav + header-right) becomes a vertical stack with comfortable touch targets. */ .mobile-drawer { display: flex; flex-direction: column; gap: 0; position: fixed; top: 0; right: 0; bottom: 0; width: min(82vw, 320px); background: var(--surface); border-left: 1px solid var(--border); box-shadow: -2px 0 12px rgba(0, 0, 0, 0.18); transform: translateX(100%); transition: transform 180ms ease-out; z-index: 100; overflow-y: auto; padding: 56px 18px 24px; text-transform: none; letter-spacing: 0.02em; } body.drawer-open .mobile-drawer { transform: translateX(0); } /* Vertical nav inside the drawer — links become big-tap rows, no leading margin like the desktop horizontal nav. */ .mobile-drawer nav { display: flex; flex-direction: column; } .mobile-drawer nav a { margin-left: 0; padding: 12px 4px; border-bottom: 1px solid var(--border); font-size: 14px; text-transform: uppercase; letter-spacing: 0.06em; } .mobile-drawer nav a.active { color: var(--accent); border-left: 2px solid var(--accent); padding-left: 10px; } /* header-right widgets vertically stacked inside the drawer. */ .mobile-drawer .header-right { flex-direction: column; align-items: stretch; gap: 14px; margin-top: 20px; } .mobile-drawer .theme-toggle, .mobile-drawer .lang-toggle { width: 100%; justify-content: center; } .mobile-drawer .lang-toggle { display: inline-flex; } .mobile-drawer .lang-toggle button, .mobile-drawer .theme-toggle { padding: 10px; font-size: 11.5px; } /* The user-menu's dropdown becomes redundant inside the drawer — surface its links flat as a list, and hide the chip button. */ .mobile-drawer .user-menu { width: 100%; } .mobile-drawer .user-chip { display: none; } .mobile-drawer .user-menu__panel { display: block !important; /* override the hidden attribute */ position: static; border: 0; padding: 0; margin-top: 4px; } .mobile-drawer .user-menu__panel[hidden] { display: block !important; } .mobile-drawer .user-menu__item { display: block; padding: 10px 4px; border-bottom: 1px solid var(--border); font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; } .mobile-drawer .meta { margin-top: auto; padding-top: 18px; text-align: center; opacity: 0.7; } /* The drawer container itself sits above the topbar in z-stacking; we still want the close button accessible while it's open, so push a close target into the top-right corner of the drawer via a repurposed pseudo-element. (Simpler than adding new markup.) */ .mobile-drawer::before { content: "✕"; position: absolute; top: 14px; right: 18px; font-size: 18px; color: var(--muted); cursor: pointer; pointer-events: none; /* tap handled by the backdrop / hamburger */ } /* Body-level layout: tighten main padding too — saves another 16px of horizontal real estate which the indicator table and chat bubbles all benefit from. */ .app-main { padding: 10px 8px; gap: 10px; } }