/* ---------------------------------------------------------------------------
   Layout that survives a narrow screen.

   Every panel used to carry its geometry inline — `padding: 0 30px` on the row
   and `minWidth: 400px` on the card — and an inline style cannot be overridden
   by a media query. On a 360px phone that arithmetic guarantees a page wider
   than the viewport, which is what produced the sideways drift while scrolling:
   the body scrolled horizontally under the finger as well as vertically, and
   panels that had already wrapped appeared to jump.

   Geometry therefore lives here, where it can change with the viewport, and
   the Python keeps only the things that do not depend on screen size.
--------------------------------------------------------------------------- */

html, body {
    margin: 0;
    /* The page is now built never to exceed the viewport. This is the belt to
       that braces: a single overwide element can no longer drag the whole
       document sideways. */
    max-width: 100%;
    overflow-x: hidden;
}

*, *::before, *::after { box-sizing: border-box; }

.row {
    display: flex;
    gap: 20px;
    padding: 0 30px;
    margin-bottom: 25px;
    flex-wrap: wrap;
    align-items: stretch;
}

.kpi-row { gap: 15px; justify-content: center; }

.card { min-width: 0; }          /* a flex child may shrink below its content */

/* Every two-column row splits exactly in half, whatever its content.

   Flex could not be made to do this. The map card's header — a title beside a
   three-way toggle — has an 861px minimum content width, so on a 1700px row it
   took 861 and left its neighbour 819: the seam between the columns stepped
   21px sideways on that row alone, and a seam that moves from row to row is
   exactly what reads as panels not lining up. `flex: 1 1 0` with
   `min-width: 0` on both children still resolved to 861/819.

   Grid is declarative about it instead of negotiating: two tracks, each
   `minmax(0, 1fr)`, so neither can be pushed wider by what is inside it.
   A row holding a single card spans both tracks. Below the breakpoint there is
   one track, which is the stacking behaviour the flex version had. */
.row:not(.kpi-row) {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 901px) {
    .row:not(.kpi-row) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .row:not(.kpi-row) > :only-child { grid-column: 1 / -1; }
}

/* With the cards free to narrow, a header whose control no longer fits beside
   its title drops the control onto its own line rather than overflowing. */
.card-header { flex-wrap: wrap; }

/* Grid stretches both cards in a row to the taller one's height. Without
   this the shorter card keeps its chart at a fixed size and pads the
   difference with blank space — the map sat above a 171px empty band, because
   the two stacked cards beside it are taller than one map. Letting the chart
   grow into that space uses it instead, and the g-* height below becomes a
   floor rather than a fixed size. */
.row:not(.kpi-row) > .card { display: flex; flex-direction: column; }
.card > .graph { flex: 1 1 auto; }

.graph { width: 100%; }
.g-map { height: 550px; }
.g-xl  { height: 400px; }
.g-lg  { height: 350px; }
.g-md  { height: 320px; }
.g-sm  { height: 250px; }

/* Plotly sizes itself to its container on first paint and then stops. These
   let it fill a container that has since been resized by a media query. */
.graph .js-plotly-plot,
.graph .plot-container { width: 100% !important; }

/* --- Tablet ------------------------------------------------------------- */
@media (max-width: 1024px) {
    .row { gap: 16px; padding: 0 20px; margin-bottom: 18px; }
    .g-map { height: 460px; }
}

/* --- Phone -------------------------------------------------------------- */
@media (max-width: 720px) {
    .row { gap: 12px; padding: 0 12px; margin-bottom: 14px; }

    /* One panel per row. Anything narrower than this and axis labels, legends
       and value labels start colliding, which reads worse than scrolling. */
    .row > * { flex: 1 1 100% !important; min-width: 0 !important; }

    .card { padding: 14px !important; }

    /* Charts get shorter, not narrower: a 550px map on a phone is most of the
       screen and pushes everything below it out of reach. */
    .g-map { height: 380px; }
    .g-xl  { height: 340px; }
    .g-lg  { height: 300px; }
    .g-md  { height: 280px; }
    .g-sm  { height: 230px; }

    /* Four KPI tiles wide does not fit; two do, and stay legible. */
    .kpi-row > * {
        flex: 1 1 calc(50% - 8px) !important;
        min-width: calc(50% - 8px) !important;
        padding: 14px 10px !important;
    }
    .kpi-value { font-size: 1.5rem !important; }
    .kpi-label { font-size: 0.65rem !important; margin-top: 6px !important; }
    .kpi-detail { font-size: 0.72rem !important; }

    .page-header { padding: 18px 12px 14px 12px !important; }
    .page-header h1 { font-size: 1.6rem !important; }
    .page-header p  { font-size: 0.9rem !important; }

    /* The company/brand switch sat beside its title and squeezed it to a few
       characters; below this width it gets its own line. */
    .card-header { flex-wrap: wrap; gap: 8px; }
    .card-header > *:first-child { flex: 1 1 100% !important; }
}

@media (max-width: 420px) {
    .kpi-row > * { flex: 1 1 100% !important; min-width: 100% !important; }
}

/* --- Wide screens ------------------------------------------------------- */
/* Without a ceiling the panels stretch to whatever the monitor is, and a chart
   two thousand pixels wide is mostly whitespace between the bar and its label. */
@media (min-width: 1800px) {
    .row { max-width: 1760px; margin-left: auto; margin-right: auto; }
}

/* Tables (builder focus, event feed) scroll inside their own box rather than
   widening the page. */
.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.scroll-y { overflow-y: auto; -webkit-overflow-scrolling: touch; }

/* dcc.Graph(responsive=True) writes `height: 100%` inline on its own div, and
   an inline style beats any rule here. So the height belongs to a wrapper the
   stylesheet owns, and the graph fills it — which is also what lets the media
   queries above change chart heights at all. */
.graph > .dash-graph { height: 100%; }

/* --- Tab bar ------------------------------------------------------------ */
/* dcc.Tabs gives every tab `flex: 1`, so two tabs sit at opposite ends of a
   1400px screen and read as two unrelated links rather than one control. */
.tab-container, .dash-tabs {
    justify-content: center !important;
    padding: 0 30px;
}
.tab-container .tab, .dash-tab {
    flex: 0 0 auto !important;
    width: auto !important;
}
@media (max-width: 720px) {
    .tab-container, .dash-tabs { padding: 0 12px; }
    .tab-container .tab, .dash-tab { padding: 9px 14px !important; font-size: 0.78rem !important; }
}

/* --- Focus tab ---------------------------------------------------------- */
/* The builder dropdown renders its menu inside a card that later panels
   overlap; without this the options list is drawn behind the KPI row. */
.card .Select-menu-outer, .card .VirtualizedSelectFocusedOption { z-index: 30; }

/* Download buttons pick up the page font and a hover state, so they read as
   controls rather than as unstyled browser chrome. */
button:hover { border-color: #0369a1; color: #0369a1; }

/* --- "By Builder Over Time" controls ------------------------------------ */
/* Three controls sit in this header and two of them do the same kind of job:
   the metric picker and the builder picker both choose *what* is plotted. The
   Company/Brand switch does not — it changes the level the data is aggregated
   at, which changes what the other two mean, so drawing it as a third
   identical row of pills invited it to be read as one more filter. It is a
   joined segmented switch with a caption instead: one object, clearly a mode.

   The selected fill has to live here rather than in a labelStyle, because an
   inline style cannot express `:checked`. */
.level-switch {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 3px 3px 3px 10px;
    border-radius: 999px;
    background: #eef2f6;
    border: 1px solid #e5e7eb;
}
.level-switch__caption {
    font-size: 0.63rem;
    font-weight: 700;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: #6b7280;
    white-space: nowrap;
}
.level-switch__options { display: flex; gap: 2px; }
.level-switch__options label {
    display: inline-flex;
    align-items: center;
    padding: 4px 12px;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    color: #6b7280;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color 120ms ease, color 120ms ease;
}
.level-switch__options label:hover { color: #0369a1; }

/* The segments only become a switch once the radio dot is gone, and the dot
   can only go if the selected state can be drawn some other way. Both depend
   on :has(), so both are gated on it — without the guard, an older engine
   would hide the dot and leave no indication of which side is active. */
@supports selector(label:has(input:checked)) {
    .level-switch__options input[type="radio"] { display: none; }
    .level-switch__options label:has(input:checked) {
        background: #0369a1;
        color: #ffffff;
        box-shadow: 0 1px 2px rgba(3, 105, 161, 0.28);
    }
    .level-switch__options label:has(input:checked):hover { color: #ffffff; }
}
.level-switch__options input[type="radio"] { margin-right: 5px; }

/* The builder picker is a filter, so it keeps the filters' square-ish shape
   rather than the switch's pill. It needs a floor and a ceiling: unconstrained
   it collapses to nothing beside the flexed title, and with many builders
   chosen it would otherwise push the switch off the row. */
.builder-picker-holder { min-width: 240px; max-width: 380px; flex: 1 1 240px; }
.builder-picker .Select-control {
    border-radius: 4px;
    border-color: #e5e7eb;
    font-size: 0.75rem;
}
.builder-picker .Select-value {
    background-color: #e0f2fe !important;
    border-color: #7dd3fc !important;
    color: #0c4a6e !important;
}

@media (max-width: 720px) {
    /* Stacked, the controls read top-to-bottom; the switch keeps its own row
       so it stays visibly separate from the filters above it. */
    .trend-controls { width: 100%; justify-content: flex-start !important; }
    .builder-picker-holder { min-width: 100%; max-width: none; }
}

/* --- Commentary verdict cards ------------------------------------------- */
/* The square marker inherits the list's text colour by default, which puts a
   full-strength dark square against 0.79rem text and makes the bullet louder
   than the sentence. ::marker lets it carry the card's accent at a weight that
   sits under the words instead of competing with them. */
.verdict-card .verdict-points li::marker { font-size: 0.85em; }
.verdict-card--up   .verdict-points li::marker { color: #047857; }
.verdict-card--down .verdict-points li::marker { color: #b91c1c; }
.verdict-card .verdict-points li { padding-left: 4px; }
.verdict-card .verdict-points li:last-child { margin-bottom: 0; }

/* Long builder names must not push the pair out of alignment. */
.verdict-card { min-width: 0; overflow-wrap: break-word; }

@media (max-width: 720px) {
    .verdict-card { padding: 13px 14px 12px !important; }
}

/* --- Question and passphrase inputs -------------------------------------- */
/* An <input> does not get a line box the way a block element does: the text
   is laid out against the element's content height, and with no line-height
   set the browser derives that from the font's own metrics. On this stack
   that came out tight enough to clip the descenders — the tails of q, p, g
   and y were cut off along the bottom edge.

   Fixing it needs both halves. `line-height` gives the glyphs a box tall
   enough to sit in, and `height: auto` stops that box being squeezed back to
   whatever the browser had decided; a bare line-height on an input whose
   height is otherwise pinned changes nothing. Padding then sits outside the
   text rather than fighting it. */
.chat-input {
    line-height: 1.5;
    height: auto;
    padding: 9px 12px 10px;
    font-size: 0.85rem;
    border-radius: 6px;
    box-sizing: border-box;
}
.chat-input::placeholder { line-height: 1.5; }
.chat-input:focus {
    outline: none;
    border-color: #0369a1;
    box-shadow: 0 0 0 3px rgba(3, 105, 161, 0.10);
}

/* The question box's answer is markdown: keep its blocks tight inside the
   card, and let a small table scroll rather than widen the page. */
.chat-answer-text p { margin: 0 0 8px; }
.chat-answer-text p:last-child { margin-bottom: 0; }
.chat-answer-text ul, .chat-answer-text ol { margin: 0 0 8px; padding-left: 20px; }
.chat-answer-text table { border-collapse: collapse; margin: 6px 0 10px; font-size: 0.82rem; display: block; max-width: 100%; overflow-x: auto; }
.chat-answer-text th, .chat-answer-text td { border: 1px solid #e5e7eb; padding: 3px 8px; text-align: left; }
.chat-answer-text th { background: #f8f9fa; }
.chat-answer-text code { font-size: 0.8rem; }
