@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;
    }

    /* No physical keyboard on a phone to advertise a Cmd+K shortcut for. */
    #global-search-shortcut-hint {
        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 layout (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 any of this).
       [id^=...] throughout catches both the primary and Compare-mode
       ("-2") copies.
       Left column uses a real grid (row height = tallest occupant,
       which is fine here -- every row only ever has ONE left-column
       item in it). Right column is deliberately NOT placed in that same
       grid, even though it looks like it should be a second column:
       grid row height is shared across BOTH columns (the tallest item
       in a row sets that row's height for every column), and the left
       column's sliders are far taller than the right column's plain
       inputs -- sharing rows produced visibly uneven gaps on the right
       (6px/34px/34px/6px, measured) even with identical margins, since
       rows 2-3 inherited the sliders' extra height. Positioning the
       right column's fields with position:absolute instead removes
       them from the grid entirely, so their spacing only ever depends
       on their own stacked height -- genuinely even, and independent of
       whatever the left column is doing. */
    [id^="dcf-assumptions-panel"] {
        max-width: 100% !important;
        position: relative;
        display: grid;
        grid-template-columns: 1fr 1fr;
        column-gap: 20px;
    }
    [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; }
    /* Calculate sits at the bottom of the left column now (grid-row 6,
       same column as Discount Rate/WACC above it) instead of the right
       one, so the right column can end right where its own last field
       does, with nothing hanging below it. */
    [id^="calculate-dcf-btn"] { grid-column: 1; grid-row: 6; margin-top: 14px; }

    /* Right column: Base FCF, Net Debt, Diluted Shares, Current Price,
       Projection Years. position:absolute (see above) takes each one out
       of the grid, positioned instead against the panel's own box
       (position:relative on it, above) -- right:0/width match where
       grid-column:2 would otherwise sit, so this still lines up with the
       column-gap between the two. top is a plain fixed offset per row --
       see the next rule for why 77px specifically -- giving a uniform
       25px gap between all five, including Projection Years, which no
       longer gets a bigger gap than the others (tighter than the
       original 34px some of them had, even though it's a bit more than
       the absolute minimum, to hit that same next rule's height-match
       goal). !important on margin-bottom cancels _dcf_field_wrapper_
       style's inline 10px (desktop's spacing, untouched) now that
       spacing is controlled by these top offsets instead. */
    [id^="dcf-row-base_fcf"],
    [id^="dcf-row-net_debt"],
    [id^="dcf-row-shares_out"],
    [id^="dcf-row-current_price"],
    [id^="dcf-row-years"] {
        position: absolute;
        right: 0;
        width: calc(50% - 10px);
        margin-bottom: 0 !important;
    }
    /* top:12px, not 0, on the first one -- position:absolute's "top"
       aligns with the panel's padding-box edge, which (0 border-width)
       is the same as its outer edge, so it ignores _FILTER_PANEL_STYLE's
       own 12px padding entirely unless added back by hand here; without
       it Base FCF sits 12px higher than DCF Assumptions' header instead
       of level with it. The 77px step (each field is 52px tall, so a
       25px gap) makes this column's total span match the left column's
       own height (measured: DCF Assumptions header to Calculate's own
       top is 359px) -- Projection Years' bottom edge lands right at
       Calculate's top instead of well above it. */
    [id^="dcf-row-base_fcf"] { top: 12px; }
    [id^="dcf-row-net_debt"] { top: 89px; }
    [id^="dcf-row-shares_out"] { top: 166px; }
    [id^="dcf-row-current_price"] { top: 243px; }
    [id^="dcf-row-years"] { top: 320px; }
    [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);
}
