/* ============================================
   barrievotes.ca — data tables

   Loaded only by pages under /data/. Not yet used by any page — the tables
   arrive with the content — but the rules the data layer has to follow are
   set here so they are consistent from the first table onward:

     · Every table scrolls inside its own container. The page body must never
       scroll sideways on a phone.
     · Numbers are tabular so columns of digits align vertically.
     · Every table carries a <caption> and a source line. A number on this
       site without a visible provenance is a bug.
   ============================================ */

.table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    margin: 0 0 .75rem;
}

/* Mobile: tables keep their 32rem minimum and scroll sideways inside the
   wrapper. Below the width where that minimum must overflow, say so above
   the table instead of hoping the affordance gets discovered. Pure CSS by
   viewport width; nav.js refines it per table when it runs, removing the
   hint from tables that do fit. */
@media (max-width: 36rem) {
    .table-wrap::before {
        content: "scroll sideways to see the full table \2192";
        display: block;
        /* Sticky, so the strip rides along during horizontal scroll. A plain
           block is only as wide as the visible area and scrolls away with
           the content, leaving a blank gap above the header. */
        position: sticky;
        left: 0;
        padding: .4rem .6rem;
        font-size: .75rem;
        color: var(--ink-3);
        background: var(--bg-2);
        border-bottom: 1px solid var(--line);
    }
    .table-wrap.fits::before { content: none; }
}

table.data {
    width: 100%;
    border-collapse: collapse;
    font-size: .9375rem;
    min-width: 32rem;   /* below this, scrolling beats crushing the columns */
}

table.data caption {
    text-align: left;
    padding: .85rem 1rem;
    font-weight: 650;
    border-bottom: 1px solid var(--line);
    background: var(--bg-2);
}

table.data th,
table.data td {
    padding: .55rem 1rem;
    text-align: right;
    border-bottom: 1px solid var(--line);
    white-space: nowrap;
}

table.data th:first-child,
table.data td:first-child {
    text-align: left;
    position: sticky;
    left: 0;
    background: var(--bg);
    white-space: normal;
    min-width: 9rem;
    /* Forces its own compositing layer. Without it, a sticky first cell
       whose content wraps to more lines than usual (a long location name at
       the mobile 11rem cap, not just a short year) can paint on first load
       with the cells beside it left unpainted until the next scroll or
       repaint — a Chromium sticky-in-table quirk, not a layout bug: the
       DOM and geometry are already correct at that point. */
    will-change: transform;
}

table.data thead th {
    background: var(--bg-2);
    font-size: .75rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--ink-3);
    position: sticky;
    top: 0;
}
table.data thead th:first-child { background: var(--bg-2); z-index: 1; }

table.data tbody tr:hover td { background: var(--bg-2); }
table.data tbody tr:hover td:first-child { background: var(--bg-3); }
table.data tbody tr:last-child td { border-bottom: 0; }

/* A figure we could not verify is shown as unavailable, never as zero and
   never silently dropped. */
table.data td.na { color: var(--ink-3); font-style: italic; }

/* Charts. Always a visual repeat of a table on the same page — the table is
   the record, the chart is the shape of it. Caveats render in the
   figcaption, on the figure itself, never in a distant footnote. */
figure.chart { margin: 0 0 1rem; }
figure.chart svg { display: block; }

/* Every chart ships two orientations: vertical bars for wide screens, and a
   horizontal variant with a narrower viewBox for phones — a 640-unit SVG
   scaled to a 375px screen shrinks its labels below legibility, which is
   the actual mobile failure. CSS picks one; both are in the markup. */
.chart-h { display: none; }
@media (max-width: 40rem) {
    .chart-v { display: none; }
    .chart-h { display: block; }
}
figure.chart figcaption {
    font-size: .8125rem;
    color: var(--ink-3);
    margin-top: .35rem;
    max-width: var(--measure);
}

/* Colour-box legend above a stacked chart. */
.chart-legend {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem 1.25rem;
    margin: 0 0 .4rem;
    font-size: .8125rem;
    color: var(--ink-2);
}
.chart-legend .chart-key { white-space: nowrap; }
.chart-legend .swatch, .bucket-badge .swatch {
    display: inline-block;
    width: .75em;
    height: .75em;
    border-radius: 2px;
    margin-right: .4em;
}

/* Spending-type bucket badge — same swatch shape/colour as the chart legend
   above it, in an itemized-expense table's "Bucket" column. */
.bucket-badge { white-space: nowrap; }

/* Source line. Required under every table. */
/* Two related charts sharing a row on wide screens, stacking on small ones —
   built for the water/wastewater pair, reusable for any pair. */
/* The Ex-[Role] pill: formerly elected, holds no seat now. Muted relative
   to the incumbent pill — a record, not a status. */
.pill-ex { border: 1px solid var(--line-strong); color: var(--ink-2); background: var(--bg-2); }

.chart-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 0 1.25rem; }
@media (max-width: 44rem) { .chart-pair { grid-template-columns: 1fr; } }

.table-source {
    font-size: .8125rem;
    color: var(--ink-3);
    margin: 0 0 2rem;
    max-width: var(--measure);
}
.table-source strong { color: var(--ink-2); font-weight: 600; }

/* Flags a series that spans a definitional or boundary change — stated on
   the table itself rather than buried in a footnote. */
.series-break {
    display: inline-block;
    font-size: .75rem;
    font-weight: 650;
    color: var(--st-waiting);
    background: var(--st-waiting-bg);
    padding: .1rem .4rem;
    border-radius: 3px;
}

/* ── Results tables ──────────────────────────────────────────────────────── */
/* Text columns. Declared here rather than in styles.css because this sheet
   loads second — alignment should not depend on winning a specificity race
   against the base `table.data td { text-align: right }` rule above. */
table.data td.cell-left,
table.data th.cell-left { text-align: left; white-space: normal; }

.row-elected th, .row-elected td { font-weight: 650; }
.row-elected th[scope="row"] { box-shadow: inset 3px 0 0 var(--accent); }
.pill-elected { color: var(--st-live); background: var(--st-live-bg); margin-left: .4rem; }

/* An acclamation is a result, not a missing number. */
.acclaimed-cell { color: var(--st-waiting); font-weight: 650; }

/* Row-level status washes. Two states colour the WHOLE row, because they
   change how every figure in it should be read: an acclaimed seat (amber —
   the acclamation hue used sitewide) had no vote behind any of its numbers,
   and a race not yet run (the in-progress blue) has no numbers at all yet.
   Declared after the sticky-first-column and zebra rules so the wash wins
   the equal-specificity tie. Not used where every row would carry it (the
   acclamations page) — a table fully highlighted highlights nothing. */
table.data .row-acclaimed th,
table.data .row-acclaimed td { background: var(--st-waiting-bg); }

/* Comparison tables: the subject of the comparison, clustered and tinted so
   it reads apart from the comparator rows above it, and a computed summary
   row (an average) that belongs to neither group. The summary row's top
   border draws the boundary between the comparators and the cluster. */
table.data .row-summary th,
table.data .row-summary td { background: var(--bg-2); font-style: italic;
                             border-top: 2px solid var(--line); }
table.data .row-subject th,
table.data .row-subject td { background: var(--accent-soft); }
table.data .row-subject th[scope="row"] { box-shadow: inset 3px 0 0 var(--accent); }
table.data .row-pending th,
table.data .row-pending td   { background: var(--st-progress-bg); }

/* Vote-share bars. A visual repeat of the Share column — same number, same
   denominator, nothing the digits don't already say (the span is
   aria-hidden). Neutral grey for the field; the accent marks only the row
   already marked Elected, so no colour codes partisan. */
td.share-cell { min-width: 7.5rem; }
.share-bar {
    display: block;
    height: 3px;
    margin-top: .3rem;
    background: var(--bg-3);
    border-radius: 2px;
    overflow: hidden;
}
.share-bar > span {
    display: block;
    height: 100%;
    background: var(--ink-3);
    border-radius: 2px;
}
.row-elected .share-bar > span { background: var(--accent); }

/* Marks a figure that covers only part of the relevant area. */
.partial-mark {
    display: inline-block;
    white-space: nowrap;
    font-size: .6875rem; text-transform: uppercase; letter-spacing: .03em;
    color: var(--st-waiting); background: var(--st-waiting-bg);
    padding: .05rem .3rem; border-radius: 3px; margin-left: .3rem;
}

table.data td .na, .na { color: var(--ink-3); font-style: italic; font-size: .8125rem; }
.row-past th, .row-past td { opacity: .5; }

.who {
    display: inline-block;
    white-space: nowrap;
    font-size: .6875rem; text-transform: uppercase; letter-spacing: .03em;
    padding: .05rem .3rem; border-radius: 3px; margin-left: .4rem;
    background: var(--bg-3); color: var(--ink-3);
}
.who-voters { color: var(--st-live); background: var(--st-live-bg); }

/* The row for the person whose page this is. Marked without colour alone —
   the profile's own name is also the only unlinked name in the table. */
.row-highlight th, .row-highlight td { background: var(--accent-soft); }
.row-highlight th[scope="row"] { background: var(--accent-soft); font-weight: 700; }

/* Shown when a document spelled the person's name differently from the one we
   publish — "Maria C. Hardie" on the 2014 certificate, "Maria Hardie" since. */
.as-recorded {
    display: block;
    font-size: .75rem;
    color: var(--ink-3);
    font-style: italic;
}

/* ── Evidence layer: money and indicator tables ──────────────────────────────
   Used by includes/finance.php and includes/indicators.php. */

/* Footnote markers, and the list that defines them. Mapping notes on a levy
   table and comparability notes on a series both use this: the note has to
   stay attached to the row it qualifies without crowding the column. */
.mark {
    font-size: .7em;
    font-weight: 650;
    color: var(--accent);
    margin-left: .15em;
}
.note-list {
    margin: 0 0 1rem;
    padding-left: 1.1rem;
    max-width: var(--measure);
    font-size: .8125rem;
    color: var(--ink-3);
}
.note-list li { margin-bottom: .3rem; }

/* What a figure is measured against, printed with the figure rather than
   under the table. A "typical home" dollar amount and a capital estimate are
   both unreadable without their basis, and a reader scanning a column of
   numbers will not go looking for a footnote. */
.figure-basis {
    display: block;
    font-size: .75rem;
    font-weight: 400;
    color: var(--ink-3);
    white-space: normal;
}

/* The measurement note, above its table. Every count of people undercounts;
   this is where the page says so, before it says a number. */
.measure-note {
    border-left: 3px solid var(--line);
    padding: .1rem 0 .1rem .9rem;
    margin: 0 0 1rem;
    max-width: var(--measure);
    font-size: .875rem;
    color: var(--ink-2);
}
.measure-note p { margin: 0; }

/* A series row where the measure changed under the series. The rule is the
   break; the .series-break badge beside it says what happened. */
.row-break > * { border-top: 2px solid var(--st-waiting) !important; }

/* Cost variance, rendered beside the numbers it belongs to. A bare delta
   implies mismanagement; the reason may be a scope decision council took
   deliberately. Both go in the same cell, explanation last and unmuted. */
.data-capital td { vertical-align: top; }
.variance-delta {
    margin: 0 0 .25rem;
    font-weight: 650;
    font-variant-numeric: tabular-nums;
}
.variance-why {
    margin: 0;
    font-size: .8125rem;
    color: var(--ink-2);
    white-space: normal;
}
/* Not knowing why is itself a finding, and reads as one. */
.variance-unknown { color: var(--ink-3); font-style: italic; }
/* Two figures on different bases are not a variance. */
.variance-warn {
    margin: 0 0 .25rem;
    font-size: .8125rem;
    color: var(--st-waiting);
    white-space: normal;
}

/* ── The source index (/about/sources/) ──────────────────────────────────────
   A table of documents rather than of figures, so the cells are text and the
   whole table reads left-aligned and wraps. */

.data-sources td,
.data-sources th[scope="row"] { vertical-align: top; white-space: normal; }
.data-sources th[scope="row"] { font-weight: 600; }

/* Jump links to each group. */
.source-jump { margin: 0 0 2rem; }
.source-jump ul {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem .5rem;
    list-style: none;
    margin: 0;
    padding: 0;
}
.source-jump a {
    display: inline-block;
    padding: .3rem .6rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    font-size: .875rem;
    text-decoration: none;
    color: var(--ink-2);
}
.source-jump a:hover { background: var(--bg-2); color: var(--ink); }
.jump-count { color: var(--ink-3); font-variant-numeric: tabular-nums; }

/* Marks a document that survives only in the Internet Archive. Not a warning —
   an archived URL is a legitimate citation — but the reader is owed the fact
   that the publisher no longer hosts it. */
.pill-archived {
    color: var(--st-waiting);
    background: var(--st-waiting-bg);
    margin-left: .4rem;
}

/* The one source that is not what the rest of the table implies. Louder than a
   note, because the 2003 figures are the single exception to the sourcing rule
   and this index would otherwise present them as equivalent to a certificate. */
/* Both drop back to the body face. The row header is monospaced so document
   titles line up as identifiers; a paragraph of prose set in that same face
   reads as code and is markedly harder to scan. */
.source-caution,
.source-note {
    display: block;
    margin-top: .3rem;
    font-family: var(--font-text);
    font-size: .8125rem;
    line-height: 1.45;
}
.source-caution { font-weight: 600; color: var(--st-waiting); }
.source-note    { font-weight: 400; color: var(--ink-3); }

/* Which pages a source holds up. Page names stay on one line — auto table
   layout otherwise squeezes this column to its narrowest word and breaks
   "Find your ward" over three rows. The wrapper scrolls if that costs width. */
.source-uses {
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: .875rem;
}
.source-uses li { margin-bottom: .15rem; white-space: nowrap; }

/* ── Mobile table density ────────────────────────────────────────────────────
   Phone-width tables live inside a scroll wrapper anyway, so generous cell
   padding and the 9rem sticky-column floor only waste the little width there
   is. Short first columns (a year, a ward number) shrink to their content;
   name columns still wrap. Declared LAST: the base padding and min-width
   rules above would otherwise win the equal-specificity tie. */
@media (max-width: 36rem) {
    table.data th,
    table.data td { padding: .5rem .6rem; }
    table.data th:first-child,
    table.data td:first-child { min-width: 0; }
}

/* A chart that renders one orientation at every width. */
.chart-full { display: block; }

/* The chart tooltip (assets/charts.js). Fixed palette both themes via vars. */
.bv-tip {
    position: absolute;
    z-index: 50;
    max-width: 20rem;
    padding: .35rem .6rem;
    font-size: .8125rem;
    line-height: 1.35;
    color: var(--bg);
    background: var(--ink-2);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    pointer-events: none;
}
.chart [data-tip] { cursor: default; }
.chart [data-tip]:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Phones: let candidate names wrap instead of forcing a wide first column,
   so the vote and share columns stay on screen.

   max-width alone does not work here: table.data uses the default auto
   layout, where a cell's column still sizes to fit its longest unbroken
   content regardless of max-width — a known table-rendering quirk, not a
   typo. table-layout: fixed makes the browser honour an explicit width
   instead, but fixed layout takes its column widths from the FIRST row
   (the header), so the width has to be set on :first-child generally, not
   only on tbody's th[scope="row"], or the header row leaves the column
   unconstrained and every wrap rule below is silently ignored. */
@media (max-width: 34rem) {
    table.data { table-layout: fixed; }
    table.data th:first-child,
    table.data td:first-child {
        width: 11rem;
        max-width: 11rem;
        white-space: normal;
        overflow-wrap: anywhere;
        hyphens: auto;
    }
    table.data td.cell-left { white-space: normal; overflow-wrap: anywhere; hyphens: auto; }
    table.data { font-size: .8125rem; }
    table.data th, table.data td { padding-left: .45rem; padding-right: .45rem; }
    /* The caption is the table's own label, and at the 32rem table
       min-width it was exactly as wide as the table — reading it meant
       scrolling sideways before seeing a single row. Pinned and wrapped
       like the "scroll sideways" hint above it, so the title is legible
       from the moment the table scrolls into view. */
    table.data caption {
        position: sticky;
        left: 0;
        width: calc(100vw - 2.5rem);
        white-space: normal;
        overflow-wrap: anywhere;
    }
}

/* ── Career timeline lists (/candidates/directory/) ─────────────────────────
   A two-column grid: name, then the compact timeline strip. The axis strip
   at the top of each list is sticky — it rides just under the site header
   once scrolling passes it — and shares the rows' column template, so its
   ticks align with the gridlines below by construction. Name and strip
   cells stretch to the same row height and both carry the bottom border,
   so the rules line up across the two columns. */
.tl-list {
    --tl-cols: minmax(9em, 14em) 1fr;
    display: grid;
    grid-template-columns: var(--tl-cols);
    column-gap: 0.75rem;
    margin: 1rem 0;
}
.tl-name, .tl-strip {
    display: flex;
    align-items: center;
    min-width: 0;
    padding: 1px 0;
    border-bottom: 1px solid var(--line);
}
.tl-name {
    justify-content: flex-end;
    text-align: right;
    font-size: 0.85rem;
    font-weight: 600;
    overflow-wrap: anywhere;
}
.tl-axis-wrap {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: var(--tl-cols);
    column-gap: 0.75rem;
    position: sticky;
    top: calc(4rem + 1px);   /* just under the sticky site header */
    z-index: 5;
    background: var(--bg);
    padding: 0.2rem 0 0.05rem;
}
@media (max-width: 640px) {
    .tl-list, .tl-axis-wrap { --tl-cols: 1fr; }
    .tl-axis-wrap > span { display: none; }
    .tl-name { justify-content: flex-start; text-align: left; padding-top: 0.4rem; border-bottom: none; }
}
