/*
 * Cryptobi brand theme.
 *
 * The name is a wordplay: "crypto" (blue, the ledger) + "bi" - business
 * intelligence, and Swedish for bee (black + yellow). The site's own
 * favicon already draws a bee; this file is what turns that into an
 * actual, reusable system instead of colors hardcoded per-template.
 *
 * Hard rule: yellow only ever sits on ink (near-black). Never as text or a
 * large fill directly on a light/paper background - that combination is
 * ~1.4:1 contrast and fails WCAG outright. On ink it's ~11:1 (AAA).
 */

:root {
    --brand-ink: #0a0e14;
    --brand-ink-soft: #141a24;
    --brand-line-on-ink: #262d3a;
    --brand-text-muted-on-ink: #9aa3b5;

    --brand-paper: #f6f7fa;
    --brand-paper-soft: #ececf1;
    --brand-line-on-paper: #dcdfe6;
    --brand-text-muted-on-paper: #565d6b;

    --brand-blue: #2563eb;
    --brand-blue-hover: #1d4ed8;
    --brand-blue-soft: #60a5fa;
    --brand-blue-wash: rgba(37, 99, 235, 0.08);

    --brand-yellow: #f4c430;
    --brand-yellow-deep: #b8860b;
    --brand-yellow-ink: #171204;
    --brand-yellow-wash: rgba(244, 196, 48, 0.12);

    /* Pico consumes these directly - keeps every default-styled button,
       link, and focus ring on-brand across every page with zero
       per-template changes. Values match --brand-blue* above.
       !important is required here: Pico's own light/dark palette rules
       are written as e.g. ":root:not([data-theme=dark])", which is MORE
       specific than a plain ":root" block (the :not() argument's
       specificity counts), so a plain override silently loses no matter
       what order the stylesheets load in. */
    --pico-primary: var(--brand-blue) !important;
    --pico-primary-background: var(--brand-blue) !important;
    --pico-primary-border: var(--brand-blue) !important;
    --pico-primary-hover: var(--brand-blue-hover) !important;
    --pico-primary-hover-background: var(--brand-blue-hover) !important;
    --pico-primary-hover-border: var(--brand-blue-hover) !important;
    --pico-primary-focus: rgba(37, 99, 235, 0.5) !important;
    --pico-primary-inverse: #fff !important;

    /* NB: page width lives in --app-max-width at the bottom of this file.
       There is deliberately no --pico-max-width here - Pico v2 does not read
       such a property, so setting it did nothing while looking like it worked. */
    --pico-font-family: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* ---------- Sitewide: every table cell defaults to a single line, truncated
   with an ellipsis rather than wrapping and inflating row height. Applies
   everywhere theme.css reaches (every table except templates/admin_security.html,
   which extends a different base with its own copy of this rule, and the
   standalone report_view.html/report_email.html, which render arbitrary
   user SQL results and intentionally keep wrapping).

   Deliberately NOT !important - genuinely multi-line cells (error
   tracebacks, violation/message details) opt out via .cell-wrap below
   rather than this rule being weakened globally, and any page's own more
   specific selector (inline style, a class, an nth-child exception like
   schedule.html's Next/Last Run columns) already wins on specificity. */
table td, table th {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 320px;
}
.cell-wrap {
    white-space: normal !important;
    overflow: visible !important;
    text-overflow: unset !important;
    max-width: none !important;
}

/* --pico-primary-inverse is the LABEL color on a primary button, and the
   button's background stays --brand-blue (#2563eb) in dark mode - only
   --pico-primary (links/accents) switches to the soft blue. Setting the label
   to near-ink here therefore put #0a0e14 on #2563eb: 3.74:1, which fails WCAG
   AA for normal text, and made the main call-to-action on every dark-mode page
   ("Log In", "Upgrade to Premium", "Back to Home") the least readable thing on
   it. White gives 5.17:1 on that same blue - the identical value light mode
   already used - so the primary button now reads the same in both themes.
   Near-ink would only be correct if the button background were the soft blue. */
html[data-theme="dark"],
body[data-theme="dark"] {
    --pico-primary: var(--brand-blue-soft) !important;
    --pico-primary-background: var(--brand-blue) !important;
    --pico-primary-hover: #7db4fb !important;
    --pico-primary-hover-background: var(--brand-blue-hover) !important;
    --pico-primary-inverse: #fff !important;
    --pico-primary-focus: rgba(96, 165, 250, 0.45) !important;
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --pico-primary: var(--brand-blue-soft) !important;
        --pico-primary-background: var(--brand-blue) !important;
        --pico-primary-hover: #7db4fb !important;
        --pico-primary-hover-background: var(--brand-blue-hover) !important;
        /* See the note above - must match the data-theme block, since dark mode
           is expressed twice (explicit attribute + prefers-color-scheme). */
        --pico-primary-inverse: #fff !important;
        --pico-primary-focus: rgba(96, 165, 250, 0.45) !important;
    }
}

body, table, input, select, textarea, button {
    font-variant-numeric: tabular-nums;
}

/* ---------- Wordmark ---------- */
.brand-mark {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    text-decoration: none;
    flex-shrink: 0;
}
.brand-mark svg,
.brand-mark img {
    width: 24px;
    height: 24px;
    flex: none;
}
/* The real brand mark (static/logo-bee.png, cropped from the site favicon)
   is wider than it is tall - a fixed square box would squash it, unlike
   the old hand-drawn 48x48 SVG it replaced. */
.brand-mark img {
    width: auto;
    height: 28px;
}
.brand-mark .brand-wordmark {
    font-weight: 800;
    font-size: 1.2rem;
    letter-spacing: -0.01em;
    display: inline-flex;
    align-items: baseline;
    gap: 0.35rem;
    line-height: 1;
}
.brand-mark .brand-crypto {
    color: var(--brand-blue-soft);
}
.brand-mark .brand-bi {
    background: var(--brand-yellow);
    color: var(--brand-yellow-ink);
    font-size: 0.85rem;
    font-weight: 800;
    padding: 0.1em 0.4em 0.15em;
    border-radius: 5px;
    line-height: 1;
}

/* ---------- Shared public-site nav (landing, demo, and any other
   logged-out marketing page) - dark brand chrome, single line on desktop,
   matches the logged-in app's own nav bar so the identity is consistent
   from the very first page a visitor sees. Previously duplicated inline
   per-page (and, in demo.html, never actually applied - see the git/task
   history), so the two pages had visibly different headers. ---------- */
.landing-nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.9rem 1rem;
    background: var(--brand-ink);
}

.landing-nav .nav-links {
    display: flex;
    align-items: center;
    gap: 1.5rem;
}

.landing-nav .nav-links a {
    text-decoration: none;
    color: var(--brand-text-muted-on-ink);
    font-weight: 600;
    font-size: 0.92rem;
}
.landing-nav .nav-links a:hover { color: #fff; }
.landing-nav .nav-links a.button {
    background: var(--brand-blue);
    color: #fff;
    padding: 0.45rem 1.1rem;
    border-radius: var(--pico-border-radius, 0.25rem);
}
.landing-nav .nav-links a.button:hover { background: var(--brand-blue-hover); }

/* ---------- Shared logged-in-app nav (templates/_nav.html) - the real site
   header with the hamburger menu (Explore/Reports/Schedule/etc), included
   verbatim by layout.html and any standalone page (e.g. ticker.html) that
   wants byte-identical navigation instead of a hand-copied approximation. ---------- */
nav {
    width: 100%;
    margin-bottom: 1rem;
    position: sticky;
    top: 0;
    z-index: 1000;
    background: var(--brand-ink);
    border-bottom: 1px solid var(--brand-line-on-ink);
    display: flex;
    justify-content: flex-start;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.nav-container {
    width: 100%;
    max-width: var(--app-max-width, 1450px);
    margin: 0 auto;
    padding: 0;
    display: flex;
    align-items: center;
    gap: 0;
    position: relative;
    justify-content: flex-start;
}
.hamburger {
    display: block;
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 0.5rem 0.4rem;
    font-size: 1.5rem;
    color: var(--brand-blue-soft);
    z-index: 1001;
    flex-shrink: 0;
}
.hamburger:hover { opacity: 0.8; }

/* Anonymous auth buttons - always visible, no hamburger required. `margin-left:
   auto` pushes them to the trailing edge past the brand mark. */
.nav-auth {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-left: auto;
    flex-shrink: 0;
    padding-right: 0.25rem;
}
.nav-auth a {
    text-decoration: none;
    white-space: nowrap;
    font-size: 0.9rem;
    font-weight: 600;
    padding: 0.4rem 0.85rem;
    border-radius: 999px;
    line-height: 1.2;
}
/* Light text on the ink navbar; ~9:1. */
.nav-auth-signin {
    color: #e8ecf3;
    border: 1px solid var(--brand-line-on-ink, #2a3341);
}
.nav-auth-signin:hover { border-color: #47536633; background: rgba(255, 255, 255, 0.06); }
/* Yellow-on-ink is the brand's high-contrast pairing (~11.7:1), same as the
   ticker CTA button. */
.nav-auth-register {
    background: var(--brand-yellow);
    color: var(--brand-ink);
    border: 1px solid var(--brand-yellow);
}
.nav-auth-register:hover { filter: brightness(1.06); }
.nav-menu {
    display: none;
    flex-direction: column;
    position: absolute;
    top: 100%;
    left: 0;
    right: auto;
    min-width: 220px;
    max-width: 320px;
    background: var(--brand-ink-soft);
    border: 1px solid var(--brand-line-on-ink);
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.25);
    z-index: 1000;
    padding: 1rem;
    max-height: calc(100vh - 4rem);
    overflow-y: auto;
    margin-top: 0.5rem;
    text-align: left;
    align-items: flex-start;
}
.nav-menu.active { display: flex; }
.nav-menu li {
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--brand-line-on-ink);
    list-style: none;
}
.nav-menu li:last-child { border-bottom: none; }
.nav-menu a {
    display: block;
    padding: 0.5rem;
    text-decoration: none;
    color: var(--brand-text-muted-on-ink);
}
.nav-menu a:hover { background: var(--brand-ink); color: #fff; }

@media (max-width: 640px) {
    .nav-container {
        width: 100%;
        padding-left: 0.5rem;
        padding-right: 0.5rem;
    }
    .brand-mark img { height: 20px; width: auto; }
    .brand-mark .brand-wordmark { font-size: 1.05rem; }
}

/* ---------- Brand chip / accent utilities ---------- */
.brand-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35em;
    background: var(--brand-ink);
    color: var(--brand-yellow);
    font-weight: 700;
    font-size: 0.75rem;
    letter-spacing: 0.02em;
    padding: 0.2em 0.55em;
    border-radius: 5px;
}
html[data-theme="dark"] .brand-chip {
    background: var(--brand-yellow);
    color: var(--brand-yellow-ink);
}
@media (prefers-color-scheme: dark) {
    html:not([data-theme="light"]) .brand-chip {
        background: var(--brand-yellow);
        color: var(--brand-yellow-ink);
    }
}

.btn-accent {
    background: var(--brand-yellow) !important;
    color: var(--brand-yellow-ink) !important;
    border-color: var(--brand-yellow) !important;
}
.btn-accent:hover, .btn-accent:focus {
    background: var(--brand-yellow-deep) !important;
    border-color: var(--brand-yellow-deep) !important;
    color: #fff !important;
}

/* ---------- Upgrade nudges - shown at the moment a free-plan limit is
   actually hit (schedule cap, report branding), not as generic ambient
   pricing chatter. Reuses .btn-accent so every "go premium" CTA on the
   site looks the same. ---------- */
.upgrade-nudge {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.75rem;
    background: var(--brand-yellow-wash, rgba(244, 196, 48, 0.12));
    border: 1px solid var(--brand-yellow);
    border-radius: var(--pico-border-radius, 0.25rem);
    padding: 0.6rem 1rem;
    margin-top: 0.5rem;
}
.upgrade-nudge span {
    font-size: 0.9rem;
}
.upgrade-nudge .btn-accent {
    flex-shrink: 0;
    padding: 0.4rem 1rem;
    margin: 0;
    width: auto;
}

/* ---------- Flash messages: category-differentiated ----------
   Previously every flash message (success/danger/info/warning) rendered
   identically via a plain Pico <mark>. Category-specific styling both
   looks more finished and, deliberately, keeps "warning" visually distinct
   from brand yellow (pale amber pill vs. the solid ink/yellow brand chip). */
.flash {
    display: block;
    border-radius: 8px;
    padding: 0.7rem 1rem;
    margin-bottom: 0.75rem;
    border: 1px solid transparent;
    font-size: 0.92rem;
    transition: opacity 0.4s ease, max-height 0.4s ease, margin 0.3s ease, padding 0.3s ease;
    max-height: 200px;
}
.flash.fade {
    opacity: 0;
    max-height: 0;
    margin: 0;
    padding: 0;
    overflow: hidden;
    border-width: 0;
}
.flash-success { background: #e7f6ec; color: #14532d; border-color: #bbe6c8; }
.flash-danger, .flash-error { background: #fdecec; color: #7a1212; border-color: #f6c6c6; }
.flash-info { background: var(--brand-blue-wash); color: var(--brand-blue-hover); border-color: rgba(37, 99, 235, 0.25); }
.flash-warning { background: #fff7e0; color: #7a5b00; border-color: #f0d98c; }
html[data-theme="dark"] .flash-success { background: #123420; color: #86e0a6; border-color: #1e5c37; }
html[data-theme="dark"] .flash-danger, html[data-theme="dark"] .flash-error { background: #3a1414; color: #f5a3a3; border-color: #5c2020; }
html[data-theme="dark"] .flash-info { background: rgba(37, 99, 235, 0.14); color: var(--brand-blue-soft); border-color: rgba(96, 165, 250, 0.3); }
html[data-theme="dark"] .flash-warning { background: #2a2308; color: #f0d98c; border-color: #4a3e12; }
@media (prefers-color-scheme: dark) {
    html:not([data-theme="light"]) .flash-success { background: #123420; color: #86e0a6; border-color: #1e5c37; }
    html:not([data-theme="light"]) .flash-danger, html:not([data-theme="light"]) .flash-error { background: #3a1414; color: #f5a3a3; border-color: #5c2020; }
    html:not([data-theme="light"]) .flash-info { background: rgba(37, 99, 235, 0.14); color: var(--brand-blue-soft); border-color: rgba(96, 165, 250, 0.3); }
    html:not([data-theme="light"]) .flash-warning { background: #2a2308; color: #f0d98c; border-color: #4a3e12; }
}

/* Persistent notice banner — same visual language and color tokens as
   .flash-warning/.flash-info, but deliberately NOT matched by the
   `.flash` auto-dismiss JS in layout.html (querySelectorAll('.flash')),
   since these are standing configuration reminders that should stay
   visible on every page load, not fade out after 3s. */
.notice {
    display: block;
    border-radius: 8px;
    padding: 0.7rem 1rem;
    margin-bottom: 0.75rem;
    border: 1px solid transparent;
    font-size: 0.92rem;
}
.notice-warning { background: #fff7e0; color: #7a5b00; border-color: #f0d98c; }
html[data-theme="dark"] .notice-warning { background: #2a2308; color: #f0d98c; border-color: #4a3e12; }
@media (prefers-color-scheme: dark) {
    html:not([data-theme="light"]) .notice-warning { background: #2a2308; color: #f0d98c; border-color: #4a3e12; }
}

/* Gain/loss text — same semantic colors as the flash-success/danger text,
   reused here for %-change figures (price tickers, wizard browse list). */
.pct-up { color: #14532d; }
.pct-down { color: #7a1212; }
html[data-theme="dark"] .pct-up { color: #86e0a6; }
html[data-theme="dark"] .pct-down { color: #f5a3a3; }
@media (prefers-color-scheme: dark) {
    html:not([data-theme="light"]) .pct-up { color: #86e0a6; }
    html:not([data-theme="light"]) .pct-down { color: #f5a3a3; }
}

/* Technical-analysis verdict badges + strength scale - see
   _technical_analysis_sentiment() in routes/public.py. "neutral" means
   notable-but-not-directional (volume-without-price, volatility expansion),
   so it borrows the existing amber .notice-warning palette rather than
   red/green. Bullish/bearish reuse the pct-up/pct-down colors above so a
   green badge always means the same thing on this page. */
.analysis-card-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.35rem; }
.analysis-card-head small { margin-bottom: 0; }
.sentiment-badge {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    padding: 0.15rem 0.55rem;
    border-radius: 999px;
    white-space: nowrap;
}
.sentiment-badge.sentiment-bullish { background: rgba(20, 83, 45, 0.12); color: #14532d; }
.sentiment-badge.sentiment-bearish { background: rgba(122, 18, 18, 0.12); color: #7a1212; }
.sentiment-badge.sentiment-neutral { background: #fff7e0; color: #7a5b00; }
html[data-theme="dark"] .sentiment-badge.sentiment-bullish { background: rgba(134, 224, 166, 0.16); color: #86e0a6; }
html[data-theme="dark"] .sentiment-badge.sentiment-bearish { background: rgba(245, 163, 163, 0.16); color: #f5a3a3; }
html[data-theme="dark"] .sentiment-badge.sentiment-neutral { background: #2a2308; color: #f0d98c; }
@media (prefers-color-scheme: dark) {
    html:not([data-theme="light"]) .sentiment-badge.sentiment-bullish { background: rgba(134, 224, 166, 0.16); color: #86e0a6; }
    html:not([data-theme="light"]) .sentiment-badge.sentiment-bearish { background: rgba(245, 163, 163, 0.16); color: #f5a3a3; }
    html:not([data-theme="light"]) .sentiment-badge.sentiment-neutral { background: #2a2308; color: #f0d98c; }
}

/* The card's left accent reflects the verdict instead of always being brand
   blue - a glance down the grid shows which categories are bullish/bearish
   without reading any text. */
.analysis-card.sentiment-bullish { border-left-color: #14532d; }
.analysis-card.sentiment-bearish { border-left-color: #7a1212; }
.analysis-card.sentiment-neutral { border-left-color: #b8860b; }
html[data-theme="dark"] .analysis-card.sentiment-bullish { border-left-color: #86e0a6; }
html[data-theme="dark"] .analysis-card.sentiment-bearish { border-left-color: #f5a3a3; }
html[data-theme="dark"] .analysis-card.sentiment-neutral { border-left-color: #f0d98c; }
@media (prefers-color-scheme: dark) {
    html:not([data-theme="light"]) .analysis-card.sentiment-bullish { border-left-color: #86e0a6; }
    html:not([data-theme="light"]) .analysis-card.sentiment-bearish { border-left-color: #f5a3a3; }
    html:not([data-theme="light"]) .analysis-card.sentiment-neutral { border-left-color: #f0d98c; }
}

/* Compound selectors here (not just .analysis-meaning) so these win over the
   pre-existing ".analysis-card p" font-size rule further up the cascade. */
.analysis-card p.analysis-meaning { margin: 0 0 0.4rem 0; font-size: 0.85rem; font-weight: 600; }
.analysis-card p.analysis-detail { margin: 0; font-size: 0.75rem; }

/* Confidence (0-1 from the signal engine's own threshold math) rendered as a
   filled track - "how far past the trigger threshold" on one visual scale,
   the same idea as a volume meter, instead of a bare number needing its own
   explanation. */
.strength-scale { height: 5px; border-radius: 999px; background: var(--pico-muted-border-color, #e0e0e0); margin: 0.5rem 0; overflow: hidden; }
.strength-scale-fill { height: 100%; border-radius: 999px; }
.strength-scale-fill.sentiment-bullish { background: #14532d; }
.strength-scale-fill.sentiment-bearish { background: #7a1212; }
.strength-scale-fill.sentiment-neutral { background: #b8860b; }
html[data-theme="dark"] .strength-scale-fill.sentiment-bullish { background: #86e0a6; }
html[data-theme="dark"] .strength-scale-fill.sentiment-bearish { background: #f5a3a3; }
html[data-theme="dark"] .strength-scale-fill.sentiment-neutral { background: #f0d98c; }
@media (prefers-color-scheme: dark) {
    html:not([data-theme="light"]) .strength-scale-fill.sentiment-bullish { background: #86e0a6; }
    html:not([data-theme="light"]) .strength-scale-fill.sentiment-bearish { background: #f5a3a3; }
    html:not([data-theme="light"]) .strength-scale-fill.sentiment-neutral { background: #f0d98c; }
}

/* ==========================================================================
   DESKTOP / LARGE SCREENS
   ==========================================================================
   Everything below is inside a min-width query, so phone and tablet layout is
   byte-for-byte unchanged - the whole site was authored mobile-first with only
   max-width queries, and nothing above 640px was ever addressed.

   ---------- 1. Content, nav and footer share one width ----------

   `--pico-max-width`, which this file and layout.html both used to set, does
   NOT exist in Pico v2 - it is a dead variable. Pico hardcodes
   `.container` to 950px at >=1024px, 1200px at >=1280px and 1450px at
   >=1536px, while `.nav-container` and `.footer-inner` read the 1450px
   variable. Result: on every laptop the header and footer were ~250-500px
   wider than the content they framed, so the logo's left edge did not line up
   with the page's own first column, and at 2560px roughly 1100px of the
   viewport was dead gutter.

   `--app-max-width` is a real variable this file owns and applies to all
   three, so they cannot drift apart again. Values step up with the viewport
   rather than jumping to full-bleed: a dashboard wants width for tables and
   charts, but unbounded line length is worse, not better. */
:root { --app-max-width: 1450px; }

.container,
main.container,
.nav-container,
.footer-inner {
    max-width: var(--app-max-width);
}

/* Matching max-width is not enough on its own: Pico zeroes `.container`'s
   horizontal padding above 576px, while .nav-container and .footer-inner carry
   their own, so the logo still sat ~20px to the right of the page heading it is
   supposed to line up with. One shared gutter on all four keeps the left edges
   identical AND stops content sitting flush against the viewport when the
   viewport is narrower than --app-max-width (at a 1280px window the 1400px
   container would otherwise touch both edges).
   The footer is set alongside these in layout.html, whose inline <style> loads
   after this file and would win a shorthand fight. */
@media (min-width: 1024px) {
    .container,
    main.container,
    .nav-container {
        padding-left: 1.25rem;
        padding-right: 1.25rem;
    }
}

@media (min-width: 1024px) { :root { --app-max-width: 1140px; } }
@media (min-width: 1280px) { :root { --app-max-width: 1400px; } }
@media (min-width: 1536px) { :root { --app-max-width: 1600px; } }
@media (min-width: 1920px) { :root { --app-max-width: 1760px; } }

/* ---------- 2. The nav becomes a real horizontal menu ----------

   `.hamburger` was `display: block` unconditionally and `.nav-menu` an
   absolutely-positioned 220-320px dropdown, so the 11 links in _nav.html
   never laid out in a row: at 2560px the header was a hamburger, a logo, and
   ~2400px of nothing, and every navigation took two clicks.

   Threshold is 1280px, not 1024px, deliberately. The worst-case logged-in menu
   is 11 items - Home, Explore, Setup, Reports, Executions, Schedule, Pricing,
   Forum, Settings, Admin, Logout (an admin who has not finished onboarding
   sees every one) - which needs ~1030px of links next to a ~200px wordmark.
   Measured with that full set injected: at a 1200px viewport the row ran 65px
   past the 1140px container and produced a horizontal scrollbar, so the
   breakpoint sits at 1280px, where the container is 1400px. Below that the
   dropdown - which works fine - is kept. */
@media (min-width: 1280px) {
    .hamburger { display: none; }

    /* !important: base rule is `display: none` and nav.js toggles a
       `.nav-menu.active { display: flex }` class. On desktop the menu must be
       visible whether or not JS has ever run or been clicked. */
    .nav-menu {
        display: flex !important;
        flex-direction: row;
        position: static;
        min-width: 0;
        max-width: none;
        max-height: none;
        overflow: visible;
        flex-wrap: nowrap;
        align-items: center;
        flex: 1;
        gap: 0.15rem;
        padding: 0;
        background: transparent;
        border: none;
        box-shadow: none;
    }

    /* Selector is `nav ul.nav-menu`, not `.nav-menu`, to outrank Pico's
       `nav ul:first-of-type { margin-left: calc(...* -1) }` - a NEGATIVE left
       margin meant to let a nav's first list bleed into the container's
       padding. At (0,1,2) that rule beat a plain `.nav-menu` (0,1,0), so the
       first link was dragged left underneath the wordmark. */
    nav ul.nav-menu {
        margin: 0 0 0 1.5rem;
    }

    .nav-menu li {
        border-bottom: none;
        list-style: none;
        /* Pico's `nav li` sets vertical padding, which is what gave the bar
           its height. Zeroing it entirely collapsed the header to the text
           height, so keep the vertical rhythm and drop only the horizontal
           part (the gap + link padding handle spacing now). */
        padding: 0.35rem 0;
    }
    .nav-menu a {
        /* margin:0 is load-bearing. Pico's `nav li :where(a,[role=link])`
           applies negative margins on both axes to visually cancel its own
           link padding; :where() has zero specificity so that rule is only
           (0,0,2) and loses on padding - but it still won on margin, which
           pulled the links into each other and into the logo. */
        margin: 0;
        padding: 0.45rem 0.7rem;
        border-radius: 6px;
        font-size: 0.9rem;
        font-weight: 600;
        white-space: nowrap;
        transition: background 0.15s ease, color 0.15s ease;
    }
    .nav-menu a:hover {
        background: var(--brand-ink-soft);
        color: #fff;
    }
    /* Logout / Register (always the last item, logged in or out) sits hard
       right, away from the browsing links. */
    .nav-menu li:last-child { margin-left: auto; }
    .nav-menu li:last-child a { color: var(--brand-text-muted-on-ink); }

    nav { margin-bottom: 1.5rem; }
    /* Vertical only - the horizontal gutter is set above and shared with
       .container/.footer-inner so the three stay aligned. A `padding`
       shorthand here would silently undo it. */
    .nav-container {
        padding-top: 0.25rem;
        padding-bottom: 0.25rem;
    }
}

/* 1280-1439px is the tight band: the full 11-item menu fits, but with only a
   few px to spare at the roomier spacing above. Compress the links slightly
   here so a long menu cannot spill, and let them breathe from 1440px up. */
@media (min-width: 1280px) and (max-width: 1439px) {
    .nav-menu { gap: 0; }
    .nav-menu a {
        padding: 0.45rem 0.5rem;
        font-size: 0.85rem;
    }
    nav ul.nav-menu { margin-left: 1rem; }
}

/* ---------- 3. Tables stop truncating when there is room ----------

   The sitewide default caps every cell at 320px and ellipsises the overflow.
   That is right on a phone; on a 1600px screen it hides the end of an
   instrument name while whitespace sits beside it. Raised, not removed - the
   single-line/no-wrap behaviour that keeps rows scannable is the point. */
@media (min-width: 1280px) {
    table td, table th { max-width: 480px; }
}
@media (min-width: 1600px) {
    table td, table th { max-width: 620px; }
}

/* Right-align numeric table cells with tabular figures, so columns of numbers
   line up on the decimal and can be compared down the column rather than
   read one at a time. Opt-in per cell via class="numeric". */
td.numeric, th.numeric {
    text-align: right;
    font-variant-numeric: tabular-nums;
}
