@import url("https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap");

/* Theme variables. Same names as before so every var(--x) reference in
   dash_app.py keeps working; only the values changed. --pill-active,
   --up and --down are new. --security-text keeps its old tuned blue
   (not part of the redesign) -- the rest of the theme moved to a
   near-black/off-white palette, but the first column of every table
   keeps a blue "pop of color" accent, kept here even though the new
   design's own --security-text just matches --text. */
:root {
    /* Tells the browser which native-control chrome to use (autofill
       panels, form-control icons like the input clear/reveal buttons,
       default scrollbars) -- without it, some of those default to light
       regardless of our own dark styling. */
    color-scheme: dark;
    --bg: #0f0f0e;
    --text: #ecebe6;
    --header: #161615;          /* nav pill track (background) */
    --header-text: #ecebe6;
    --body-text: #9a9890;
    --security-text: #5DADE2;
    --card-bg: #161615;
    --card-bg-2: #1c1c1a;
    --border: #262624;
    --table-header-bg: #161615;
    --pill-active: #2a2a27;
    --up: #4cc38a;
    --down: #e5675a;
    --accent: #4cc38a;
}
:root[data-theme="light"] {
    color-scheme: light;
    --bg: #f7f7f4;
    --text: #1b1b19;
    --header: #ecebe6;
    --header-text: #1b1b19;
    --body-text: #5f5e58;
    --security-text: #2874a6;
    --card-bg: #ffffff;
    --card-bg-2: #f1f0ec;
    --border: #e2e1db;
    --table-header-bg: #ffffff;
    --pill-active: #ffffff;
    --up: #1f8a5a;
    --down: #c2463a;
    --accent: #1f8a5a;
}

html, body {
    background-color: var(--bg);
    color: var(--text);
    font-family: "Instrument Sans", system-ui, -apple-system, sans-serif;
    -webkit-font-smoothing: antialiased;
    margin: 0;
}
a { color: var(--accent); }
a:hover { color: var(--text); }

/* App shell: fixed-width sidebar + flexible main column. Split out of
   dash_app.py's inline styles (app.layout / _build_sidebar) specifically
   so the mobile media query below can override them -- an inline style
   always wins over an external stylesheet rule, media query or not,
   unless that rule also lives here. Desktop values below are unchanged
   from before this split. */
#app-shell {
    display: grid;
    grid-template-columns: 232px minmax(0, 1fr);
    min-height: 100vh;
    background-color: var(--bg);
}
#app-sidebar {
    border-right: 1px solid var(--border);
    padding: 20px 14px;
    display: flex;
    flex-direction: column;
    gap: 24px;
    position: sticky;
    top: 0;
    height: 100vh;
    box-sizing: border-box;
    background-color: var(--bg);
}

/* Mobile: stack the sidebar above the content instead of beside it --
   below this width the fixed 232px sidebar was eating well over half the
   viewport, squeezing every table/card/chart into a sliver. Only these
   two rules change; nothing above this query (or in dash_app.py's other
   inline styles) is touched, so desktop is unaffected. */
@media (max-width: 768px) {
    #app-shell {
        /* minmax(0, 1fr), not bare 1fr -- a grid track (like a flex
           track) defaults its items' min-width to auto, so a plain 1fr
           track still won't shrink a child below ITS content's own
           intrinsic width. Without the 0 floor here, anything inside the
           sidebar that itself resists shrinking (the search input, see
           its own min-width fix) blows out the whole shell's width
           instead of wrapping/shrinking to the viewport. */
        grid-template-columns: minmax(0, 1fr);
    }
    #app-sidebar {
        /* relative, not static: anchors #theme-toggle-btn below, which
           is absolutely positioned into this row on mobile -- relative
           with no top/left offset of its own behaves exactly like static
           for #app-sidebar's own layout, so this doesn't otherwise
           change anything about how it stacks in the page. */
        position: relative;
        height: auto;
        border-right: none;
        border-bottom: 1px solid var(--border);
    }

    /* Dark-mode toggle: sits in its own full-width row (under the
       sidebar's Trackers list) on desktop, which on a phone screen is a
       whole row spent on one small button -- moved up into the logo
       row's top-right corner instead, and shrunk down to match. Several
       properties need !important since the button's own Python style
       sets them inline (width/padding/font-size) for its desktop,
       full-width appearance. */
    #theme-toggle-btn {
        position: absolute;
        top: 16px;
        right: 14px;
        width: auto !important;
        padding: 4px 10px !important;
        font-size: 11px !important;
    }

    /* Sidebar reorder: Trackers above the search box instead of below it
       -- #app-sidebar is already a flex column (same on desktop), so
       plain order does it, no display:contents trickery needed since
       these are already direct siblings. Search box and footer are left
       at their default order (0), which (ties broken by original DOM
       position) keeps search right after Trackers and the footer last,
       same relative order as before between those two. */
    #sidebar-logo-row {
        order: -2;
    }
    #sidebar-trackers-section {
        order: -1;
    }

    /* Sidebar TRACKERS nav: one horizontal row of 3 tabs instead of 3
       stacked rows -- #sidebar-trackers-list's own flex-direction is set
       inline from Python (column, for desktop's list), so !important is
       needed to flip it here. Each row's status dot and meta badge
       (13F/PTR) are dropped (not just squeezed) to leave enough width for
       the labels alone at phone width; [id^="nav-"] catches all 3 nav
       rows (nav-managers/nav-congress/nav-companies) without needing a
       shared class, since nothing else in the app uses that id prefix.
       The accent-colored border (matching the logo square) + rounded
       corners turn the row into a single pill-strip bar, the same visual
       language as the range picker / compare toggle elsewhere in the
       app (see _NAV_CONTAINER_STYLE), rather than 3 bare rows floating
       on the page. */
    #sidebar-trackers-list {
        flex-direction: row !important;
        gap: 6px;
        border: 1px solid var(--accent);
        border-radius: 9px;
        padding: 4px;
    }
    #sidebar-trackers-list [id^="nav-"] {
        flex: 1;
        justify-content: center;
        padding: 8px 4px;
    }
    #sidebar-trackers-list [id^="nav-"] > span:nth-child(2) {
        flex: initial !important;
    }
    #sidebar-trackers-list [id$="-dot"],
    #sidebar-trackers-list [id^="nav-"] > span:last-child {
        display: none;
    }

    /* dcc.Tabs (Dash 4) has its own internal responsive breakpoint that
       collapses a too-narrow pill row into a tall vertical stack instead
       of actually wrapping -- squeezed next to "High $x / Low $x" on a
       phone-width chart card, that ate most of the chart's height. Giving
       it flex-basis: 100% forces it onto its own full-width row below
       High/Low (the row's own flexWrap: wrap, already set for desktop,
       does the rest), so it renders as a normal wrapping pill row again
       instead of Dash's own collapsed layout. */
    #range-tabs {
        flex-basis: 100%;
    }

    /* Politician Tracker's trade rows (see _build_trade_row) use a
       5-column CSS grid sized for desktop (member/ticker/pill/range-bar/
       date side by side, ~450px minimum) -- inline Python styles, so
       !important is needed to override them here. [id*="trade-row"]
       matches the pattern-matching id's literal {"type":"trade-row",...}
       JSON, which is unique to these rows -- _build_leaderboard_row's
       simpler 3-column grid already fits phone widths and stays as-is.
       Switching to a wrapping flex row lets each piece reflow across a
       couple of lines instead of being clipped at the card's edge. */
    [id*="trade-row"] {
        display: flex !important;
        flex-wrap: wrap !important;
        row-gap: 4px !important;
    }
    [id*="trade-row"] > * {
        flex: 1 1 auto;
    }

    /* Stock price chart card: break out of the page's own 16px side
       padding (_APP_CONTENT_STYLE) to use the full viewport width --
       already narrow on a phone, that inset was eating a meaningful
       fraction of it. padding-left/right need !important: the card's
       Python style sets "padding" as one shorthand value, and an inline
       style (shorthand or not) always beats a plain external rule. The
       price display and header row (High/Low + range picker) get their
       own smaller padding back so their text isn't flush against the
       screen edge; the chart itself stays edge-to-edge. */
    #stock-chart-card {
        margin-left: -16px;
        margin-right: -16px;
        border-left: none;
        border-right: none;
        border-radius: 0;
        padding-left: 0 !important;
        padding-right: 0 !important;
    }

    /* Plotly's own drag-capture rect over the price chart's plot area --
       there to catch pan/zoom/select drags -- suppresses hover redraws
       for as long as it's pressed, regardless of dragmode or fixedrange.
       These mobile figures don't want that rect handling presses at all
       (see the price-chart scrub listeners in custom.js, which drive
       hover manually instead): removing it from the hit-testing lets a
       press pass straight through to those listeners instead of being
       swallowed here first. !important: Plotly sets pointer-events:all
       inline on this rect itself, which otherwise wins regardless of
       this rule's specificity. Desktop keeps it untouched -- dragmode
       stays "zoom" there and this rect is exactly what makes that work. */
    #price-chart .nsewdrag {
        pointer-events: none !important;
    }
    #stock-header-price-inline,
    #stock-chart-header-row {
        padding-left: 12px;
        padding-right: 12px;
    }

    /* Company Tracker: reorders to toggle -> search box -> company name on
       mobile, right below the sidebar -- flex order, not a DOM change, so
       desktop (which never gets this flex-column treatment) is untouched.
       (Price+change used to live in this header too and share in this
       reorder, but now render in a fixed DOM position right above the
       chart instead -- see stock-header-price-inline in dash_app.py --
       so they're out of this scheme entirely.) #stock-header goes
       display:contents (!important: overriding its own inline
       display:flex), dissolving its own box so its children -- the name
       block and the toggle -- become direct flex items of #company-content
       and can be ordered independently of each other and of the search
       box, which otherwise isn't reachable by plain CSS order (a sibling
       of #stock-header, not one of its children). compare-mode-tabs is a
       dcc.Tabs, which Dash wraps in its own "-parent" div even at the
       root -- display:contents dissolves everything down to that wrapper,
       so the wrapper (not the id'd component itself) is the actual flex
       item that needs the order. */
    #company-content {
        display: flex;
        flex-direction: column;
    }
    #stock-header {
        display: contents !important;
    }
    #compare-mode-tabs-parent {
        order: -2;
        margin-bottom: 12px;
    }
    #ticker-search-row {
        order: -1;
    }
    #stock-header-name-block {
        order: 0;
        margin-top: 16px;
    }

    /* Descriptive tagline under the ticker search -- not worth the
       vertical space it costs on a phone-width screen. */
    #company-tagline {
        display: none;
    }

    /* Sidebar footer data-source line -- same reasoning, freeing up a
       bit more of the sidebar's tight vertical space on mobile. */
    #sidebar-data-source {
        display: none;
    }

    /* DCF assumptions panel: fixed at 180px wide on desktop (shared with
       the Politician Tracker filter panel via _FILTER_PANEL_STYLE, so
       that width itself isn't touched here) left most of a phone's width
       sitting empty next to it. max-width needs !important to beat that
       inline 180px cap. A real 2-column grid (not flex-wrap, which fills
       row by row -- no way to keep two independently-ordered groups of
       content each reading top-to-bottom in their own column) with every
       field/header explicitly placed by id, rather than just wrapping
       dash_app.py's two Python-level groups (DCF Assumptions+Model
       Settings, Market Data+Calculate) side by side: those two groups
       are uneven height (3 sliders' worth of extra tooltip-bubble
       margin vs. plain number inputs), so Base FCF and Projection Years
       are reassigned to the second column here to balance the two
       columns out, independent of which Python group they're actually
       in (kept as-is there for desktop, which never applies this grid).
       [id^=...] throughout catches both the primary and Compare-mode
       ("-2") copies. align-items:start so the shorter of two items
       sharing a row (Projection Years, next to the taller Discount
       Rate/WACC slider) sits at the top of that row instead of being
       stretched to match it. */
    [id^="dcf-assumptions-panel"] {
        max-width: 100% !important;
        display: grid;
        grid-template-columns: 1fr 1fr;
        column-gap: 20px;
        align-items: start;
    }
    [id^="dcf-assumptions-main"],
    [id^="dcf-market-data"] {
        display: contents;
    }
    [id^="dcf-header-assumptions"] { grid-column: 1; grid-row: 1; }
    [id^="dcf-row-growth_rate"] { grid-column: 1; grid-row: 2; }
    [id^="dcf-row-terminal_growth"] { grid-column: 1; grid-row: 3; }
    [id^="dcf-header-model-settings"] { grid-column: 1; grid-row: 4; }
    [id^="dcf-row-discount_rate"] { grid-column: 1; grid-row: 5; }
    [id^="dcf-row-base_fcf"] { grid-column: 2; grid-row: 1; }
    [id^="dcf-row-net_debt"] { grid-column: 2; grid-row: 2; }
    [id^="dcf-row-shares_out"] { grid-column: 2; grid-row: 3; }
    [id^="dcf-row-current_price"] { grid-column: 2; grid-row: 4; }
    /* Shares row 5 with the Discount Rate/WACC slider opposite it --
       margin-top nudges it down closer to that slider's own bottom edge
       (the slider's tooltip bubble makes it taller than a plain input),
       so the two columns end at roughly the same height. */
    [id^="dcf-row-years"] { grid-column: 2; grid-row: 5; margin-top: 24px; }
    [id^="calculate-dcf-btn"] { grid-column: 2; grid-row: 6; }
    [id^="dcf-header-market-data"] {
        display: none;
    }

    /* iOS Safari (and some Android browsers) auto-zoom the whole page on
       focusing any text input rendered below 16px -- every text/number
       input and dropdown search box here is sized for a mouse-driven
       desktop context (12-13px), well under that. 16px is the documented
       threshold that avoids the zoom, so every one of them gets bumped
       up to it at this breakpoint; desktop's own (unconditional, so it'd
       otherwise win the cascade regardless of media query) font-size
       rules are declared later in this file, hence !important here
       rather than relying on source order. Purely a mobile focus-time
       fix -- doesn't change how any of these look for a mouse. */
    input[type="text"],
    input[type="number"],
    .dash-dropdown-search {
        font-size: 16px !important;
    }
}

h2 { font-size: 28px; font-weight: 600; letter-spacing: -0.02em; margin: 8px 0 4px; }
h3 { font-size: 20px; font-weight: 600; letter-spacing: -0.005em; margin: 28px 0 8px; }
p  { line-height: 1.55; text-wrap: pretty; }

/* Tables: mono tabular figures, first column stays in the UI sans */
.dash-spreadsheet td.dash-cell,
.dash-spreadsheet th.dash-header {
    font-family: "IBM Plex Mono", ui-monospace, monospace;
    font-variant-numeric: tabular-nums;
}
.dash-spreadsheet td.dash-cell.column-0,
.dash-spreadsheet th.dash-header {
    font-family: "Instrument Sans", system-ui, sans-serif;
}
.dash-spreadsheet td.dash-cell.column-0 { font-weight: 500; }
.dash-spreadsheet tr:hover td.dash-cell { background-color: var(--card-bg-2) !important; }
.dash-table-container {
    border: 1px solid var(--border);
    border-radius: 10px;
    overflow: hidden;
}

/* Buttons */
button {
    font-family: inherit;
    font-size: 13px;
    font-weight: 500;
    color: var(--text);
    background: var(--card-bg-2);
    border: 1px solid var(--border);
    border-radius: 7px;
    padding: 7px 12px;
    cursor: pointer;
    transition: border-color .15s, background-color .15s;
}
button:hover:not(:disabled) { border-color: var(--body-text); }
button:disabled { opacity: .45; cursor: default; }
button[id^="calculate-"] {
    background: var(--accent);
    border-color: var(--accent);
    color: #0f0f0e;
    font-weight: 600;
}

/* Text/number inputs. --card-bg is only a hair lighter than --bg in dark
   mode (#161615 vs #0f0f0e) -- fine for a big card surface sitting next to
   plenty of its own whitespace, but an input that size difference reads as
   just "black" against the page. --pill-active is enough lighter to read
   as its own field. */
input[type="text"], input[type="number"] {
    font-family: inherit;
    font-size: 13px;
    color: var(--text);
    background-color: var(--pill-active);
    border: 1px solid var(--border);
    border-radius: 7px;
    padding: 8px 10px;
    outline: none;
}
/* Edge/IE's native "clear field" (x) and "reveal password" icons render
   with their own light-chrome background regardless of our CSS -- without
   this, a typed value shows a plain white/light box past the text on a
   dark input, since nothing here themes them. */
input[type="text"]::-ms-clear, input[type="text"]::-ms-reveal {
    display: none;
}
/* dash-core-components ships .dash-input-container with its own hardcoded
   white background-color and a purple border/focus outline, and every
   dcc.Input renders as that wrapper div around the actual <input> -- the
   input itself is styled above (including its own green focus ring), but
   dcc.Input's own width (e.g. "220px" set inline on this wrapper) is often
   wider than the <input>'s natural rendered width, leaving a strip of that
   default white showing on one side, and the wrapper's own border/outline
   shows as a second, differently-colored ring around the input's own. */
.dash-input-container, .dash-input-container:focus-within {
    background-color: transparent;
    border: none !important;
    outline: none !important;
}
input[type="text"]:focus, input[type="number"]:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 22%, transparent);
}
input[type="range"] { accent-color: var(--accent); }
/* dcc.Slider's always-visible value bubble (tooltip={"always_visible": True}
   -- see the DCF assumption sliders) defaults to a white bubble with text
   color inherited from the page (var(--text), near-white), unreadable
   against its own white background. */
.dash-slider-tooltip {
    background-color: var(--card-bg-2) !important;
    color: var(--text) !important;
    border: 1px solid var(--border) !important;
    border-radius: 6px !important;
}

/* dcc.Dropdown: Dash 4 renders it as its own <button> trigger (.dash-dropdown)
   plus a virtualized listbox popover (.dash-options-list) when open -- not
   the react-select markup older Dash versions used, and unstyled by
   default (a plain white button with unthemed text, unreadable on a dark
   page). */
.dash-dropdown {
    background-color: var(--pill-active) !important;
    border: 1px solid var(--border) !important;
    border-radius: 7px !important;
    color: var(--text) !important;
}
.dash-dropdown-value, .dash-dropdown-value-item, .dash-dropdown-trigger-icon {
    color: var(--text) !important;
}
.dash-options-list, .dash-dropdown-options,
.dash-dropdown-content, .dash-dropdown-search-container,
.dash-dropdown-search-container:focus-within {
    background-color: var(--card-bg) !important;
    border: 1px solid var(--border) !important;
    outline: none !important;
    border-radius: 7px !important;
}
.dash-options-list-option, .dash-dropdown-option {
    color: var(--text) !important;
}
/* The dropdown's own in-popover filter box, searchable=True -- a separate
   element from the .dash-dropdown trigger above, also unstyled by default.
   Its ancestors (.dash-dropdown-content/.dash-dropdown-search-container,
   above) default to white too, showing through around this input's own
   edges the same way .dash-input-container did earlier. */
.dash-dropdown-search {
    background-color: var(--pill-active) !important;
    color: var(--text) !important;
    border: none !important;
    border-bottom: 1px solid var(--border) !important;
}
.dash-options-list-option:hover, .dash-dropdown-option:hover {
    background-color: var(--card-bg-2) !important;
}

details summary { cursor: pointer; color: var(--body-text); font-size: 13px; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 5px; border: 2px solid var(--card-bg); }

/* Ticker search typeahead: show the suggestions dropdown only while focus
   is somewhere inside its wrapper (the input itself, or a suggestion
   button mid-click) — hides on click-away, reappears on refocus, with no
   JS needed. */
.ticker-search-wrap #company-suggestions {
    display: none;
}
.ticker-search-wrap:focus-within #company-suggestions {
    display: block;
}

.manager-search-wrap #manager-suggestions {
    display: none;
}
.manager-search-wrap:focus-within #manager-suggestions {
    display: block;
}

.global-search-wrap #global-search-suggestions {
    display: none;
}
.global-search-wrap:focus-within #global-search-suggestions {
    display: block;
}

/* Politician Tracker's two cross-chamber summary lists (recent trades,
   leaderboard): each row is click-to-select (see select_member_from_summary
   in dash_app.py, and _build_trade_row/_build_leaderboard_row) -- this is
   the only visual affordance for "this is clickable", so it needs to read
   as inviting on its own. */
.pol-row:hover {
    background-color: var(--card-bg-2);
}
