/* ===========================================================================
   ParIndex - design tokens and layout.

   Colour values are the validated data-viz reference palette: the eight
   categorical hues are assigned in fixed slot order and never cycled, and both
   modes are stepped for their own surface rather than one being a flip of the
   other. In light mode the aqua series colour sits below 3:1 against the
   surface, so every chart ships a legend with direct labels and a table view -
   identity is never carried by colour alone.
   =========================================================================== */

:root {
    color-scheme: light;

    --page:            #f9f9f7;
    --surface:         #fcfcfb;
    --surface-raised:  #ffffff;
    --surface-sunken:  #f2f1ee;
    --ink:             #0b0b0b;
    --ink-2:           #52514e;
    --muted:           #898781;
    --grid:            #e1e0d9;
    --axis:            #c3c2b7;
    --border:          rgba(11, 11, 11, 0.10);
    --border-strong:   rgba(11, 11, 11, 0.18);
    --shadow:          0 1px 2px rgba(11, 11, 11, 0.04), 0 8px 24px rgba(11, 11, 11, 0.06);

    --series-1: #2a78d6;
    --series-2: #eb6834;
    --series-3: #1baf7a;
    --series-4: #eda100;
    --series-5: #e87ba4;
    --series-6: #008300;
    --series-7: #4a3aa7;
    --series-8: #e34948;

    --accent:       #2a78d6;
    --accent-ink:   #ffffff;
    --accent-wash:  rgba(42, 120, 214, 0.10);
    --accent-quiet: #cde2fb;
    /* Last year, in a paired comparison: neutral, so the coloured bar is this year. */
    --prior:        #d4d3cc;

    /* Status colours are reserved: never reused as a series hue. */
    --good:     #006300;
    --good-bg:  rgba(12, 163, 12, 0.12);
    --warning:  #fab219;
    --critical: #d03b3b;
    --critical-bg: rgba(208, 59, 59, 0.10);

    --radius:    10px;
    --radius-sm: 6px;
    --topbar-h:  60px;
    --sidenav-w: 216px;

    --font: system-ui, -apple-system, "Segoe UI", sans-serif;
}

@media (prefers-color-scheme: dark) {
    :root:where(:not([data-theme="light"])) {
        color-scheme: dark;

        --page:           #0d0d0d;
        --surface:        #1a1a19;
        --surface-raised: #212120;
        --surface-sunken: #141413;
        --ink:            #ffffff;
        --ink-2:          #c3c2b7;
        --muted:          #898781;
        --grid:           #2c2c2a;
        --axis:           #383835;
        --border:         rgba(255, 255, 255, 0.10);
        --border-strong:  rgba(255, 255, 255, 0.18);
        --shadow:         0 1px 2px rgba(0, 0, 0, 0.4), 0 10px 30px rgba(0, 0, 0, 0.35);

        --series-1: #3987e5;
        --series-2: #d95926;
        --series-3: #199e70;
        --series-4: #c98500;
        --series-5: #d55181;
        --series-6: #008300;
        --series-7: #9085e9;
        --series-8: #e66767;

        --accent:       #3987e5;
        --accent-wash:  rgba(57, 135, 229, 0.16);
        --accent-quiet: #184f95;
        --prior:        #49483f;

        --good:     #0ca30c;
        --good-bg:  rgba(12, 163, 12, 0.16);
        --critical: #e66767;
        --critical-bg: rgba(208, 59, 59, 0.18);
    }
}

/* The theme toggle stamps data-theme on the root and must win in both
   directions, so the dark values are declared again under that scope. */
:root[data-theme="dark"] {
    color-scheme: dark;

    --page:           #0d0d0d;
    --surface:        #1a1a19;
    --surface-raised: #212120;
    --surface-sunken: #141413;
    --ink:            #ffffff;
    --ink-2:          #c3c2b7;
    --muted:          #898781;
    --grid:           #2c2c2a;
    --axis:           #383835;
    --border:         rgba(255, 255, 255, 0.10);
    --border-strong:  rgba(255, 255, 255, 0.18);
    --shadow:         0 1px 2px rgba(0, 0, 0, 0.4), 0 10px 30px rgba(0, 0, 0, 0.35);

    --series-1: #3987e5;
    --series-2: #d95926;
    --series-3: #199e70;
    --series-4: #c98500;
    --series-5: #d55181;
    --series-6: #008300;
    --series-7: #9085e9;
    --series-8: #e66767;

    --accent:       #3987e5;
    --accent-wash:  rgba(57, 135, 229, 0.16);
    --accent-quiet: #184f95;
    --prior:        #49483f;

    --good:     #0ca30c;
    --good-bg:  rgba(12, 163, 12, 0.16);
    --critical: #e66767;
    --critical-bg: rgba(208, 59, 59, 0.18);
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    font-family: var(--font);
    font-size: 14px;
    line-height: 1.5;
    color: var(--ink);
    background: var(--page);
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -0.01em; }
h1 { font-size: 22px; }
h2 { font-size: 16px; }
h3 { font-size: 14px; }
p { margin: 0; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

button, input, select { font: inherit; color: inherit; }

svg { fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
    position: absolute; left: -999px; top: 8px; z-index: 100;
    background: var(--accent); color: var(--accent-ink);
    padding: 8px 14px; border-radius: var(--radius-sm);
}
.skip-link:focus { left: 8px; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --------------------------------------------------------------- top bar --- */

.topbar {
    position: sticky; top: 0; z-index: 40;
    display: flex; align-items: center; gap: 14px;
    height: var(--topbar-h); padding: 0 16px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}

.brand { display: flex; align-items: center; gap: 9px; color: var(--ink); flex: none; }
.brand:hover { text-decoration: none; }
.brand-mark {
    display: grid; place-items: center;
    width: 30px; height: 30px; border-radius: 8px;
    background: var(--accent); color: var(--accent-ink);
    font-size: 12px; font-weight: 700; letter-spacing: 0.02em;
}
.brand-text { font-size: 15.5px; font-weight: 500; letter-spacing: -0.01em; }
.brand-text strong { font-weight: 700; }

.search { position: relative; flex: 1 1 auto; max-width: 520px; margin: 0 auto; }
.search-icon {
    position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
    width: 16px; height: 16px; color: var(--muted); pointer-events: none;
}
.search input {
    width: 100%; height: 36px; padding: 0 12px 0 34px;
    background: var(--surface-sunken);
    border: 1px solid transparent; border-radius: 8px;
}
.search input:focus { background: var(--surface); border-color: var(--border-strong); outline: none; }
.search input::placeholder { color: var(--muted); }

.search-results {
    position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 50;
    max-height: 420px; overflow-y: auto;
    background: var(--surface-raised);
    border: 1px solid var(--border); border-radius: var(--radius);
    box-shadow: var(--shadow); padding: 6px;
}
.search-group-label {
    padding: 8px 10px 4px; font-size: 11px; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted);
}
.search-item {
    display: flex; align-items: baseline; gap: 8px;
    padding: 7px 10px; border-radius: var(--radius-sm);
    cursor: pointer; color: var(--ink);
}
.search-item:hover, .search-item.is-active { background: var(--accent-wash); text-decoration: none; }
.search-item .si-label { font-weight: 500; }
.search-item .si-sub { font-size: 12px; color: var(--muted); margin-left: auto; text-align: right; }

.topbar-actions { display: flex; align-items: center; gap: 8px; flex: none; }

.year-select select {
    height: 36px; padding: 0 8px;
    background: var(--surface-sunken); border: 1px solid transparent;
    border-radius: 8px; font-weight: 500; cursor: pointer;
}

.icon-btn {
    display: grid; place-items: center;
    width: 36px; height: 36px; flex: none;
    background: transparent; border: 1px solid transparent;
    border-radius: 8px; color: var(--ink-2); cursor: pointer;
}
.icon-btn:hover { background: var(--surface-sunken); color: var(--ink); }
.icon-btn svg { width: 18px; height: 18px; }

.icon-moon { display: none; }
:root[data-theme="dark"] .icon-sun { display: none; }
:root[data-theme="dark"] .icon-moon { display: block; }
@media (prefers-color-scheme: dark) {
    :root:where(:not([data-theme="light"])) .icon-sun { display: none; }
    :root:where(:not([data-theme="light"])) .icon-moon { display: block; }
}

.nav-toggle { display: none; }

.account { position: relative; }
.avatar {
    display: grid; place-items: center;
    width: 32px; height: 32px; border-radius: 50%;
    background: var(--accent-wash); color: var(--accent);
    border: 1px solid var(--border); cursor: pointer;
    font-size: 12px; font-weight: 600;
}
.account-menu {
    position: absolute; right: 0; top: calc(100% + 8px); z-index: 50;
    min-width: 236px; padding: 6px;
    background: var(--surface-raised); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: var(--shadow);
}
.account-menu .am-head { padding: 10px; border-bottom: 1px solid var(--border); margin-bottom: 4px; }
.account-menu .am-email { font-weight: 500; word-break: break-all; }
.account-menu button, .account-menu a {
    display: block; width: 100%; text-align: left;
    padding: 8px 10px; border: 0; border-radius: var(--radius-sm);
    background: none; color: var(--ink); cursor: pointer;
}
.account-menu button:hover, .account-menu a:hover { background: var(--surface-sunken); text-decoration: none; }

/* ---------------------------------------------------------------- layout --- */

.shell { display: flex; align-items: flex-start; }

.sidenav {
    position: sticky; top: var(--topbar-h);
    width: var(--sidenav-w); flex: none;
    height: calc(100vh - var(--topbar-h));
    padding: 14px 10px; overflow-y: auto;
    border-right: 1px solid var(--border);
    display: flex; flex-direction: column;
}
.sidenav ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.sidenav a {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px; border-radius: var(--radius-sm);
    color: var(--ink-2); font-weight: 500;
}
.sidenav a:hover { background: var(--surface-sunken); color: var(--ink); text-decoration: none; }
.sidenav a.is-active { background: var(--accent-wash); color: var(--accent); }
.sidenav a svg { width: 17px; height: 17px; flex: none; }

.sidenav-foot { margin-top: auto; padding: 12px 10px 0; border-top: 1px solid var(--border); }
.coverage { font-size: 11.5px; color: var(--muted); line-height: 1.6; }
.coverage strong { color: var(--ink-2); font-weight: 600; }

.content { flex: 1 1 auto; min-width: 0; padding: 22px 24px 64px; }

.page-head {
    display: flex; align-items: flex-start; gap: 16px;
    flex-wrap: wrap; margin-bottom: 18px;
}
.page-head .ph-main { min-width: 0; }
.page-head .ph-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.eyebrow {
    font-size: 11px; font-weight: 600; text-transform: uppercase;
    letter-spacing: 0.07em; color: var(--muted); margin-bottom: 4px;
}
.subtitle { color: var(--ink-2); margin-top: 5px; }

/* ----------------------------------------------------------------- cards --- */

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px;
}
.card + .card { margin-top: 16px; }
.card-head {
    display: flex; align-items: baseline; gap: 12px;
    margin-bottom: 14px; flex-wrap: wrap;
}
.card-head .ch-actions { margin-left: auto; display: flex; gap: 6px; align-items: center; }
.card-note { font-size: 12px; color: var(--muted); margin-top: 3px; }

.grid { display: grid; gap: 16px; }
.grid-kpi { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

/* Stat tile: label, value, delta vs a named period, optional sparkline. */
.stat {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 14px 16px;
    display: flex; flex-direction: column; gap: 2px; min-width: 0;
}
.stat-label { font-size: 12px; color: var(--ink-2); }
.stat-value { font-size: 26px; font-weight: 600; letter-spacing: -0.02em; }
.stat-value.is-hero { font-size: 48px; line-height: 1.1; }
.stat-foot { display: flex; align-items: center; gap: 8px; margin-top: 4px; flex-wrap: wrap; }
.stat-sub { font-size: 12px; color: var(--muted); }
.stat-spark { margin-top: 8px; height: 34px; }

.delta {
    display: inline-flex; align-items: center; gap: 3px;
    padding: 1px 6px 1px 4px; border-radius: 99px;
    font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.delta svg { width: 12px; height: 12px; stroke-width: 2.4; }
.delta.is-up { color: var(--good); background: var(--good-bg); }
.delta.is-down { color: var(--critical); background: var(--critical-bg); }
.delta.is-flat { color: var(--muted); background: var(--surface-sunken); }

/* ---------------------------------------------------------------- tables --- */

.table-wrap { overflow-x: auto; margin: 0 -16px -16px; padding: 0 16px 16px; }

table.data {
    width: 100%; border-collapse: collapse;
    font-variant-numeric: tabular-nums;
}
/* Tight. Ten columns of generous padding is most of a table's width spent on nothing, and the
   rules between rows already do the separating that the space was being asked to do. */
table.data th, table.data td { padding: 7px 8px; text-align: left; }
table.data thead th {
    position: sticky; top: 0; z-index: 1;
    background: var(--surface);
    font-size: 11.5px; font-weight: 600; color: var(--muted);
    text-transform: uppercase; letter-spacing: 0.05em;
    border-bottom: 1px solid var(--border);
    /* Headings wrap. A column has no business being as wide as the words naming it when the
       figures underneath are half that -- "Room revenue" was setting a 128px column to hold
       figures that needed 70. */
    white-space: normal;
    vertical-align: bottom;
}
/* The leaderboard's track, behind the row. A column of figures tells you Hotel Vin is at
   $299.48 and Super 8 at $46.25; it does not show you that one is six times the other until
   you have read both and divided. The bar says it before you read anything.

   A background image rather than an element, so no markup changes and nothing to keep in step
   with the cells. The width rides on --bar, set per row; rows without one get 0% and look
   exactly as they did. */
table.data tbody tr {
    border-bottom: 1px solid var(--grid);
    background-image: linear-gradient(to right,
        var(--accent-wash) 0 var(--bar, 0%), transparent var(--bar, 0%));
    background-repeat: no-repeat;
}
table.data tbody tr:last-child { border-bottom: 0; }
/* background-color, not the shorthand: the shorthand would wipe out the track on hover. */
table.data tbody tr:hover { background-color: var(--surface-sunken); }
/* Figures never wrap: a number broken over two lines stops being a number. Text does, which
   is what lets a long name take two lines instead of a wide column. */
table.data td.num, table.data th.num { text-align: right; white-space: nowrap; }
table.data .primary { font-weight: 500; white-space: normal; min-width: 170px; }
/* The brand, on the identity line. Muted like the address it follows, because it is context
   for the name rather than a column in its own right. */
.hc-brand { color: var(--muted); }
.hc-brand:hover { color: var(--accent); }
table.data .secondary { font-size: 12px; color: var(--muted); }
/* The property whose page this is, inside its own comp set. Marked rather than removed:
   where it sits among its neighbours is the question the card answers, and taking it out
   left the reader comparing its RevPAR against a list it was missing from. */
table.data tbody tr.is-subject { background-color: var(--accent-wash); }
table.data tbody tr.is-subject td.primary { font-weight: 700; }
table.data tbody tr.is-subject:hover { background-color: var(--accent-wash); }
table.data .rank { color: var(--muted); width: 30px; font-size: 12.5px; }

/* --- wide tables on a narrow screen --------------------------------------
   Past five columns a table cannot be read across a phone, and scrolling it sideways is worse
   than it sounds: the column identifying the row is the first one, so reaching a figure
   scrolls the hotel's name off the screen and leaves the reader holding a number with nothing
   attached to it.

   So each row becomes a card. The name keeps the full width at the top, and every figure below
   it is a label-and-value pair -- which is what the header row was for, and it has to come back
   as something once the header is gone.

   Done in CSS on the same markup rather than by rendering a second component: one table, one
   sort, one set of handlers, and nothing that can disagree with itself about what a row is. */
@media (max-width: 640px) {
    .table-wrap:has(> .is-stacked) { overflow-x: visible; }

    table.data.is-stacked,
    table.data.is-stacked tbody,
    table.data.is-stacked tr,
    table.data.is-stacked td { display: block; width: auto; }

    /* The header row is the one thing that genuinely cannot carry over -- its labels are now
       on the cells. Hidden rather than removed so sorting still has something to click after
       the viewport widens again. */
    table.data.is-stacked thead { display: none; }

    table.data.is-stacked tbody tr {
        position: relative;
        border: 1px solid var(--border);
        border-radius: var(--radius);
        padding: 10px 12px;
        margin-bottom: 8px;
        background: var(--surface);
    }
    table.data.is-stacked tbody tr:last-child { margin-bottom: 0; }

    table.data.is-stacked td {
        display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
        padding: 3px 0;
        text-align: right;
        white-space: normal;
    }
    /* The heading, back as a word beside its figure. */
    table.data.is-stacked td[data-label]::before {
        content: attr(data-label);
        flex: 0 0 auto;
        font-size: 11.5px; font-weight: 600; color: var(--muted);
        text-transform: uppercase; letter-spacing: 0.05em;
        text-align: left;
    }
    table.data.is-stacked td.num { text-align: right; }

    /* The name is the card's title: full width, no label, and room for the address under it. */
    table.data.is-stacked td.primary {
        display: block; text-align: left;
        min-width: 0;
        font-size: 15px;
        padding: 0 34px 6px 0;
        margin-bottom: 4px;
        border-bottom: 1px solid var(--grid);
    }
    table.data.is-stacked td.primary::before { content: none; }

    /* Rank reads as a position in a list, so it sits above the name rather than in the figures. */
    table.data.is-stacked td.rank {
        display: block; width: auto; text-align: left;
        font-size: 11.5px; font-weight: 600; color: var(--muted);
        padding: 0 0 2px;
    }
    table.data.is-stacked td.rank::before { content: '#'; margin-right: 1px; }
    table.data.is-stacked td.rank:empty { display: none; }

    /* A cell with nothing in it is a row of label and blank, which is noise on a card even
       though it is a necessary gap in a grid. */
    table.data.is-stacked td:empty { display: none; }

    /* The star has no heading and belongs against the title, not in the figures. */
    table.data.is-stacked td:has(> .star-btn) {
        position: absolute; top: 8px; right: 8px;
        padding: 0; display: block;
    }
    table.data.is-stacked td:has(> .star-btn)::before { content: none; }

    table.data.is-stacked tbody tr:hover { background: var(--surface); }
}

th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--ink); }
th.sortable .sort-caret { opacity: 0; margin-left: 3px; }
th.sortable.is-sorted { color: var(--accent); }
th.sortable.is-sorted .sort-caret { opacity: 1; }

.star-btn {
    display: grid; place-items: center;
    width: 26px; height: 26px; padding: 0;
    background: none; border: 0; border-radius: var(--radius-sm);
    color: var(--muted); cursor: pointer;
}
.star-btn:hover { background: var(--surface-sunken); color: var(--ink); }
.star-btn svg { width: 15px; height: 15px; }
.star-btn.is-on { color: var(--warning); }
.star-btn.is-on svg { fill: var(--warning); }

.pager {
    display: flex; align-items: center; gap: 10px;
    margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border);
    font-size: 13px; color: var(--ink-2); flex-wrap: wrap;
}
.pager .pager-info { margin-right: auto; }

/* --------------------------------------------------------------- controls --- */

.btn {
    display: inline-flex; align-items: center; gap: 6px;
    height: 34px; padding: 0 13px;
    background: var(--surface); color: var(--ink);
    border: 1px solid var(--border-strong); border-radius: 8px;
    font-weight: 500; cursor: pointer; white-space: nowrap;
}
.btn:hover:not(:disabled) { background: var(--surface-sunken); text-decoration: none; }
.btn:disabled { opacity: 0.45; cursor: default; }
.btn svg { width: 15px; height: 15px; }
.btn-primary { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
.btn-primary:hover:not(:disabled) { filter: brightness(1.07); background: var(--accent); }
.btn-sm { height: 28px; padding: 0 9px; font-size: 12.5px; }
.btn-ghost { border-color: transparent; background: transparent; color: var(--ink-2); }
.btn-ghost:hover:not(:disabled) { background: var(--surface-sunken); }

.segmented {
    display: inline-flex; padding: 2px; gap: 2px;
    background: var(--surface-sunken); border-radius: 8px;
}
.segmented button {
    height: 28px; padding: 0 11px; border: 0; border-radius: 6px;
    background: transparent; color: var(--ink-2);
    font-size: 12.5px; font-weight: 500; cursor: pointer; white-space: nowrap;
}
.segmented button:hover { color: var(--ink); }
.segmented button.is-on { background: var(--surface); color: var(--ink); box-shadow: var(--shadow); }

.filters {
    display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end;
    margin-bottom: 16px;
}
.field { display: grid; gap: 4px; min-width: 0; }
.field > span { font-size: 11.5px; font-weight: 600; color: var(--muted); }
.field select, .field input {
    height: 34px; padding: 0 9px; min-width: 130px;
    background: var(--surface); border: 1px solid var(--border-strong);
    border-radius: 8px;
}
.chip {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 2px 4px 2px 9px; border-radius: 99px;
    background: var(--accent-wash); color: var(--accent);
    font-size: 12.5px; font-weight: 500;
}
.chip button {
    display: grid; place-items: center; width: 18px; height: 18px;
    padding: 0; border: 0; border-radius: 50%; background: none;
    color: inherit; cursor: pointer;
}
.chip button:hover { background: rgba(0, 0, 0, 0.1); }
.chip button svg { width: 11px; height: 11px; stroke-width: 2.6; }

.tag {
    display: inline-block; padding: 1px 7px; border-radius: 5px;
    background: var(--surface-sunken); color: var(--ink-2);
    font-size: 11.5px; font-weight: 500;
}
.tag-partial { background: rgba(250, 178, 25, 0.16); color: #8a5c00; }
:root[data-theme="dark"] .tag-partial { color: var(--warning); }
@media (prefers-color-scheme: dark) {
    :root:where(:not([data-theme="light"])) .tag-partial { color: var(--warning); }
}

/* ---------------------------------------------------------------- charts --- */

.chart { width: 100%; position: relative; }
.chart svg { display: block; width: 100%; overflow: visible; }

.chart .grid-line { stroke: var(--grid); stroke-width: 1; }
/* Zero on a chart that crosses it. Growth above and below the line is the thing being read,
   and at the weight of every other gridline the reader has to find it by checking labels. */
.chart .grid-line.is-zero { stroke: var(--border); stroke-width: 1.5; }
.chart .axis-line { stroke: var(--axis); stroke-width: 1; }
.chart .tick-text { fill: var(--muted); stroke: none; font-size: 11px; }
.chart .series-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart .series-area { stroke: none; opacity: 0.10; }
/* End markers carry a 2px ring in the surface colour so they stay readable
   where they cross a line or another marker. */
.chart .series-dot { stroke: var(--surface); stroke-width: 2; }
.chart .bar { stroke: none; }
.chart .value-label { fill: var(--ink); stroke: none; font-size: 11.5px; font-weight: 600; }
.chart .crosshair { stroke: var(--axis); stroke-width: 1; }
.chart .hover-target { fill: transparent; stroke: none; }

.legend {
    display: flex; flex-wrap: wrap; gap: 4px 16px;
    margin-top: 10px; font-size: 12px; color: var(--ink-2);
}
.legend-item { display: inline-flex; align-items: center; gap: 6px; }
.legend-key { width: 12px; height: 3px; border-radius: 2px; flex: none; }
.legend-key.is-dot { width: 9px; height: 9px; border-radius: 50%; }

.chart-tooltip {
    position: fixed; z-index: 60; pointer-events: none;
    min-width: 150px; max-width: 280px; padding: 8px 10px;
    background: var(--surface-raised); color: var(--ink);
    border: 1px solid var(--border-strong); border-radius: 8px;
    box-shadow: var(--shadow); font-size: 12.5px;
}
.chart-tooltip .tt-title { font-weight: 600; margin-bottom: 5px; }
.chart-tooltip .tt-row { display: flex; align-items: center; gap: 8px; }
.chart-tooltip .tt-row .tt-key { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.chart-tooltip .tt-row .tt-name { color: var(--ink-2); }
.chart-tooltip .tt-row .tt-val { margin-left: auto; font-weight: 600; font-variant-numeric: tabular-nums; }

/* Leaderboard: a horizontal bar list, the readable form for ranked magnitude. */
.barlist { display: grid; gap: 2px; position: relative; }
.barlist-row {
    position: relative; display: flex; align-items: center; gap: 10px;
    padding: 7px 10px; border-radius: var(--radius-sm);
    color: var(--ink); overflow: hidden;
}
.barlist-row:hover { text-decoration: none; }
.barlist-row:hover .bl-track { filter: brightness(1.1); }
/* --- the bars' own region ------------------------------------------------
   Stops short of the columns at the right, so a bar at full length ends before the figure and
   the change rather than running underneath them. It also means the bar's percentage and the
   inlaid figure's percentage are measured against the same width by construction, instead of
   being kept in step by two separate calculations.

   How much to reserve depends on which columns the list is carrying, so it is composed from
   the row padding out. --value-ch is set on the list by barList, from the longest figure in
   it. */
.barlist-row { --reserve: 10px; }
.barlist.has-delta .barlist-row { --reserve: 88px; }
.barlist:not(.is-inlaid) .barlist-row {
    --reserve: calc(20px + var(--value-ch, 6) * 1ch);
}
.barlist:not(.is-inlaid).has-delta .barlist-row {
    --reserve: calc(98px + var(--value-ch, 6) * 1ch);
}
.bl-bars {
    position: absolute; left: 0; top: 3px; bottom: 3px; right: var(--reserve);
}

.bl-track {
    position: absolute; left: 0; top: 0; bottom: 0;
    background: var(--accent-wash); border-radius: 4px;
}
/* A part-filed year, set back so it does not read as a year that simply did worse. */
.bl-track.is-muted { background: var(--surface-sunken); }
/* Where a part-filed year is on course to finish: an outline carrying on from the filled
   part, never more solid bar, so the estimate cannot be mistaken for something filed. */
.bl-ahead {
    position: absolute; top: 0; bottom: 0;
    border: 1px dashed var(--accent-quiet); border-radius: 4px;
}
.bl-projected { font-style: italic; color: var(--muted); font-variant-numeric: tabular-nums; }

/* The credit, across the middle, placed exactly as sign() places it on the drawn charts.
   Under the rows it would come off with a crop of the last row, which is the one thing it has
   to survive. Over them it cannot be removed without removing the data it lies on.

   pointer-events: none, because it covers every row and would otherwise swallow the hover on
   all of them. */
.barlist-mark {
    position: absolute; inset: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 2px;
    color: var(--ink); opacity: 0.11;
    pointer-events: none; user-select: none;
    white-space: nowrap;
    /* On the mark, not on each line: both parts are the same type, and declaring it once is
       what stops them drifting apart again. */
    font-size: 19px; font-weight: 500; letter-spacing: 0.08em;
}
/* The same face, size and strength as the line above it. The two are one mark in two parts,
   not a heading with a footnote under it, and setting the address back made it read as an
   afterthought rather than as part of the credit.

   Ellipsised rather than left to overflow: an email has no length worth relying on, and the
   mark is centred, so a long one would hang off both sides of the card. */
.barlist-mark .wm-account {
    max-width: 94%;
    overflow: hidden;
    text-overflow: ellipsis;
}
@media (max-width: 420px) {
    /* Both lines together, since they share their type. */
    .barlist-mark { font-size: 14px; letter-spacing: 0.05em; }
}
/* A column, sized from the longest figure in the list. ch units work here because the
   figures are tabular -- every digit the same width -- so the count of characters is a
   fair measure of how much room they need. */
.bl-value {
    position: relative; margin-left: auto;
    min-width: calc(var(--value-ch, 6) * 1ch);
    text-align: right;
    font-weight: 600; font-variant-numeric: tabular-nums;
}
/* A column, not content. The widest pill measures 67px (an arrow plus +56.5%), so 68 holds
   every case -- including the flat pills, which are narrower and were nudging their rows
   out of line on the leaderboards too. Reserved on every row once any row has one, and not
   reserved at all on a list that has none. */
.barlist.has-delta .bl-delta {
    position: relative;
    flex: 0 0 68px;
    display: flex; justify-content: flex-end; align-items: center;
}
/* --- the figure, set inside its own bar ----------------------------------
   Right-aligned at the bar's end, so the length and the number are read in one place. On a
   series whose years sit close together the lengths alone separate them poorly, and this is
   what gives the eye something to land on.

   The floor is what keeps it honest on a short bar: without it a bar near zero would put its
   figure at the row's left edge, on top of the label. max() pushes the figure just past the
   end of a short bar instead -- outside it rather than over the label -- with no measuring
   and nothing that can mistime. */
.bl-inlay {
    position: absolute;
    /* One limit now. The region already ends before the change column, so nothing has to
       stop the figure running under the pill; all that remains is the floor, which keeps a bar
       near zero from putting its figure at the row's left edge on top of the label. A short
       bar's figure lands just past its end instead. */
    right: calc(100.2% - max(var(--bar-end, 100%), 155px));
    top: 50%; transform: translateY(-50%);
    font-weight: 600; font-size: 13px;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
    white-space: nowrap;
}
.bl-delta { position: relative; }
/* With the figure inside the bar it is out of the flow, and it was the figure's auto margin
   that pushed the pair to the right. Without it the change sat immediately after the label,
   at the far left of the row, so the margin moves here. */
.barlist.is-inlaid .bl-delta { margin-left: auto; }


/* --------------------------------------------------------------- messages --- */

.loading { display: grid; place-items: center; gap: 12px; padding: 80px 0; color: var(--muted); }
.spinner {
    width: 22px; height: 22px; border-radius: 50%;
    border: 2px solid var(--grid); border-top-color: var(--accent);
    animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2s; } }

.empty { padding: 40px 16px; text-align: center; color: var(--muted); }
.empty h3 { color: var(--ink); margin-bottom: 5px; }

.notice {
    display: flex; gap: 10px; align-items: flex-start;
    padding: 11px 13px; border-radius: var(--radius);
    background: var(--accent-wash); color: var(--ink);
    border: 1px solid var(--border); margin-bottom: 16px;
}
.notice svg { width: 17px; height: 17px; flex: none; margin-top: 1px; color: var(--accent); }
.notice.is-warn { background: rgba(250, 178, 25, 0.12); }
.notice.is-warn svg { color: var(--warning); }
.notice .n-title { font-weight: 600; }
.notice .n-body { color: var(--ink-2); font-size: 13px; }

.toast-stack {
    position: fixed; bottom: 18px; left: 50%; transform: translateX(-50%);
    z-index: 80; display: grid; gap: 8px; width: min(420px, calc(100vw - 32px));
}
.toast {
    padding: 11px 14px; border-radius: var(--radius);
    background: var(--surface-raised); border: 1px solid var(--border-strong);
    box-shadow: var(--shadow);
}
.toast.is-error { border-color: var(--critical); }

/* ------------------------------------------------------------------ auth --- */

.auth-page {
    min-height: calc(100vh - var(--topbar-h));
    display: grid; place-items: center; padding: 40px 20px;
}
.auth-card {
    width: 100%; max-width: 400px; padding: 26px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 14px; box-shadow: var(--shadow);
}
.auth-card h1 { font-size: 20px; margin-bottom: 6px; }
.auth-card .subtitle { margin-bottom: 20px; }
.auth-form { display: grid; gap: 13px; }
.auth-form label { display: grid; gap: 5px; font-size: 12.5px; font-weight: 500; color: var(--ink-2); }
.auth-form input {
    height: 38px; padding: 0 11px;
    background: var(--surface); border: 1px solid var(--border-strong); border-radius: 8px;
}
.auth-form .btn { height: 40px; justify-content: center; margin-top: 4px; }
.auth-error {
    padding: 9px 11px; border-radius: 8px;
    background: var(--critical-bg); color: var(--critical); font-size: 13px;
}
.auth-alt { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border);
    font-size: 13px; color: var(--ink-2); text-align: center; }

.plan-pitch { margin-top: 18px; display: grid; gap: 7px; font-size: 13px; color: var(--ink-2); }
.plan-pitch li { display: flex; gap: 8px; align-items: flex-start; }
.plan-pitch ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.plan-pitch svg { width: 15px; height: 15px; color: var(--good); flex: none; margin-top: 2px; }

/* --------------------------------------------------------------- specifics --- */

.hotel-hero { display: flex; gap: 20px; flex-wrap: wrap; align-items: flex-start; }
.hotel-hero .hh-main { min-width: 260px; flex: 1 1 320px; }
.hotel-meta { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-top: 10px; font-size: 13px; color: var(--ink-2); }
.hotel-meta span { display: inline-flex; align-items: center; gap: 5px; }
.hotel-meta svg { width: 14px; height: 14px; color: var(--muted); }

.bench-grid { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.bench {
    padding: 12px 14px; border: 1px solid var(--border);
    border-radius: var(--radius); background: var(--surface);
}
.bench-label { font-size: 12px; color: var(--ink-2); }
.bench-rank { font-size: 20px; font-weight: 600; margin-top: 2px; }
.bench-rank small { font-size: 13px; font-weight: 400; color: var(--muted); }
.bench-index { font-size: 12px; color: var(--muted); margin-top: 3px; }

.name-history { display: grid; gap: 8px; }
.name-history li { display: flex; gap: 10px; align-items: baseline; }
.name-history ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.nh-period { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ------------------------------------------------------------ responsive --- */

@media (max-width: 1000px) {
    .nav-toggle { display: grid; }
    /* The drawer is closed by transform AND visibility. Transform alone is not enough:
       it is animated, and a transition only settles once the page is actually painting,
       so on a page whose animation clock is stalled (a background tab, a hidden pane) the
       drawer would sit at translateX(0) over the content. visibility is not animated, so
       the closed state holds immediately and the drawer cannot be clicked through either. */
    .sidenav {
        position: fixed; top: var(--topbar-h); left: 0; z-index: 35;
        background: var(--surface);
        transform: translateX(-100%);
        visibility: hidden;
        transition: transform 0.18s ease;
    }
    .sidenav.is-open { transform: none; visibility: visible; box-shadow: var(--shadow); }
    .content { padding: 18px 16px 56px; }
}

@media (max-width: 760px) {
    .brand-text { display: none; }
    .search { max-width: none; }
    .content { padding: 14px 12px 48px; }
    .card { padding: 13px; }
    .table-wrap { margin: 0 -13px -13px; padding: 0 13px 13px; }
    .stat-value { font-size: 22px; }
    .stat-value.is-hero { font-size: 34px; }
    h1 { font-size: 19px; }
}

@media (prefers-reduced-motion: reduce) {
    * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

@media print {
    .topbar, .sidenav, .pager, .ph-actions, .ch-actions { display: none !important; }
    .content { padding: 0; }
    .card { break-inside: avoid; border-color: #ccc; }
}

/* ------------------------------------------------------------------ plans --- */

.plan-grid { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); align-items: start; }

.plan-card {
    position: relative; padding: 22px 20px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 14px; display: flex; flex-direction: column; gap: 10px;
}
.plan-card.is-featured { border-color: var(--accent); box-shadow: var(--shadow); }
.plan-card.is-current { border-color: var(--good); }
.plan-badge {
    position: absolute; top: -10px; left: 20px;
    padding: 2px 9px; border-radius: 99px;
    background: var(--accent); color: var(--accent-ink);
    font-size: 11px; font-weight: 600; letter-spacing: 0.02em;
}
.plan-tagline { color: var(--ink-2); }
.plan-price { display: flex; align-items: baseline; gap: 6px; margin: 4px 0 2px; }
.plan-amount { font-size: 30px; font-weight: 600; letter-spacing: -0.02em; }
.plan-period { font-size: 13px; color: var(--muted); }
.plan-features { list-style: none; margin: 4px 0 14px; padding: 0; display: grid; gap: 8px; }
.plan-features li { display: flex; gap: 8px; align-items: flex-start; font-size: 13px; }
.plan-features svg { width: 15px; height: 15px; color: var(--good); flex: none; margin-top: 2px; }
.plan-card .btn { margin-top: auto; justify-content: center; }
.plan-current {
    margin-top: auto; text-align: center; padding: 8px;
    border-radius: 8px; background: var(--good-bg); color: var(--good);
    font-size: 13px; font-weight: 600;
}
.plan-explainer { display: grid; gap: 10px; color: var(--ink-2); font-size: 13.5px; max-width: 76ch; }
.plan-explainer code {
    background: var(--surface-sunken); padding: 1px 5px; border-radius: 4px; font-size: 12.5px;
}

/* Where a gated feature would have been. */
.upgrade-panel {
    display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
    background: linear-gradient(180deg, var(--accent-wash), transparent);
    border-style: dashed;
}
.upgrade-mark {
    display: grid; place-items: center; width: 40px; height: 40px; flex: none;
    border-radius: 10px; background: var(--surface); border: 1px solid var(--border);
    color: var(--accent);
}
.upgrade-mark svg { width: 19px; height: 19px; }
.upgrade-body { flex: 1 1 260px; min-width: 0; }
.upgrade-body h2 { font-size: 15px; margin-bottom: 3px; }
.upgrade-body p { color: var(--ink-2); font-size: 13.5px; }
.upgrade-panel .btn { flex: none; }

/* -------------------------------------------------------------- narrative --- */

.narrative-headline {
    font-size: 17px; font-weight: 500; line-height: 1.45;
    letter-spacing: -0.01em; margin-bottom: 14px; max-width: 70ch;
}
.narrative-body { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.narrative-section h3 {
    font-size: 11.5px; font-weight: 600; text-transform: uppercase;
    letter-spacing: 0.06em; color: var(--muted); margin-bottom: 4px;
}
.narrative-section p { color: var(--ink-2); font-size: 13.5px; line-height: 1.55; }
.narrative-cautions {
    list-style: none; margin: 16px 0 0; padding: 12px 0 0;
    border-top: 1px solid var(--border); display: grid; gap: 6px;
}
.narrative-cautions li {
    position: relative; padding-left: 18px; font-size: 12.5px; color: var(--muted);
}
.narrative-cautions li::before {
    content: '!'; position: absolute; left: 0; top: 0;
    display: grid; place-items: center; width: 13px; height: 13px; margin-top: 2px;
    border-radius: 50%; background: var(--surface-sunken);
    font-size: 9px; font-weight: 700; color: var(--warning);
}

/* --------------------------------------------------------------- forecast --- */

.forecast-summary {
    display: flex; gap: 22px; flex-wrap: wrap; align-items: flex-start;
    padding: 12px 14px; margin-bottom: 14px;
    background: var(--surface-sunken); border-radius: var(--radius);
}
.forecast-figure { flex: none; }
.forecast-accuracy { flex: 1 1 260px; min-width: 0; }
.forecast-accuracy p { color: var(--ink-2); font-size: 13px; margin-top: 4px; max-width: 62ch; }
.forecast-caveats {
    list-style: none; margin: 14px 0 0; padding: 12px 0 0;
    border-top: 1px solid var(--border); display: grid; gap: 6px;
}
.forecast-caveats li {
    position: relative; padding-left: 16px; font-size: 12.5px; color: var(--muted);
}
.forecast-caveats li::before {
    content: ''; position: absolute; left: 3px; top: 7px;
    width: 5px; height: 5px; border-radius: 50%; background: var(--axis);
}

/* ----------------------------------------------------------------- modal --- */

.modal-backdrop {
    position: fixed; inset: 0; z-index: 70;
    display: grid; place-items: center; padding: 20px;
    background: rgba(11, 11, 11, 0.45);
}
.modal {
    width: 100%; max-width: 480px; max-height: calc(100vh - 40px); overflow-y: auto;
    padding: 20px; background: var(--surface-raised);
    border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow);
}
.modal-head { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.modal-head h2 { font-size: 17px; margin-right: auto; }
.modal-intro { color: var(--ink-2); font-size: 13px; margin-bottom: 16px; }
.modal .field-row { display: grid; gap: 10px; grid-template-columns: 100px 1fr; }
.modal textarea, .modal select {
    padding: 9px 11px; background: var(--surface);
    border: 1px solid var(--border-strong); border-radius: 8px;
    font: inherit; color: inherit; resize: vertical;
}
.modal select { height: 38px; padding: 0 9px; }
.modal input[readonly] { background: var(--surface-sunken); color: var(--muted); }

/* ------------------------------------------------------------ admin queue --- */

.review-actions { display: flex; gap: 6px; }
.issue-message {
    white-space: normal; max-width: 320px; display: -webkit-box;
    -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}

/* A compact filter that sits inside a card header rather than above the card. */
.field-inline { flex-direction: row; align-items: center; gap: 7px; }
.field-inline > span { font-size: 11.5px; white-space: nowrap; }
.field-inline select { height: 28px; min-width: 0; padding: 0 6px; font-size: 12.5px; }

/* --- compare -------------------------------------------------------------
   The controls sit on one row that wraps: on a phone each select takes a full
   line rather than shrinking to something untappable. */
.cmp-controls {
    display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
}
.cmp-controls .cmp-add { flex: 1 1 260px; min-width: 200px; margin: 0; max-width: none; }
.cmp-controls .cmp-input {
    width: 100%; height: 36px; padding: 0 12px;
    background: var(--surface-sunken); border: 1px solid transparent; border-radius: 8px;
}
.cmp-controls .cmp-input:focus {
    background: var(--surface); border-color: var(--border-strong); outline: none;
}
.cmp-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.cmp-empty { color: var(--muted); font-size: 13px; }
.cmp-chip {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 5px 8px 5px 10px; border-radius: 999px;
    background: var(--surface-sunken); border: 1px solid var(--border);
    font-size: 13px;
}
/* The dot is the line colour, so a chip and its series are matched by eye rather
   than by reading the legend. */
.cmp-chip-dot {
    width: 9px; height: 9px; border-radius: 50%; background: var(--chip); flex: none;
}
.cmp-chip-x {
    border: 0; background: none; cursor: pointer; color: var(--muted);
    font-size: 15px; line-height: 1; padding: 0 2px;
}
.cmp-chip-x:hover { color: var(--ink); }
.cmp-swatch {
    display: inline-block; width: 9px; height: 9px; border-radius: 50%;
    background: var(--chip); margin-right: 8px;
}
.cmp-results { top: 40px; }

/* A table cell carrying a level and its change. The delta sits under the number
   at a smaller size so a column still scans as figures, not as two columns
   crammed into one. */
/* --- a period against the one before, as paired columns -------------------
   One group per metric, two columns in each: this period and the same period a year earlier.
   Side by side they are compared by looking, which a table of ten figures never is.

   Every group has its own scale. A dollar total, a room count and a percentage share have no
   axis in common, so one scale across the row would draw revenue as the whole chart and leave
   the rest flat on the floor. Inside a group the two columns share a unit, so they share a
   scale, measured from zero. */
.cmpv { margin: 14px 0 2px; }
.cmpv + .cmpv { margin-top: 26px; padding-top: 22px; border-top: 1px solid var(--border); }
/* What the pair of columns in this panel are, where the columns' own heads cannot say it
   -- a twelve-month window will not fit over a sixty-pixel bar. */
.cmpv-caption {
    font-size: 11.5px; color: var(--muted); margin-bottom: 10px;
}

/* Wraps to two or three groups a row on a phone rather than squeezing five. */
.cmpv-groups {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 260px));
    justify-content: start; gap: 18px 10px;
}
.cmpv-group { margin: 0; min-width: 0; }
/* A row of heads over a row of wells, as one grid across both columns -- so a head that
   wraps to two lines pushes both wells down together rather than leaving one bar's baseline
   above the other's. The well is a fixed height because the bars are drawn as a percentage
   of it, and because the reference line measures itself up it. */
.cmpv-plot {
    --well: 92px;
    --col: 76px;
    position: relative; width: fit-content; margin: 0 auto;
    display: grid;
    grid-template-columns: repeat(2, var(--col));
    grid-template-rows: auto var(--well);
    column-gap: 10px;
    /* content-box, so the baseline rule sits outside the well rather than eating a pixel
       of it and leaving the reference line a pixel clear of the bar it marks. */
    box-sizing: content-box;
    border-bottom: 1px solid var(--axis);
}
/* The period this column is, over the column itself. */
.cmpv-head {
    align-self: end; text-align: center; padding-bottom: 5px;
    font-size: 10.5px; line-height: 1.25; color: var(--muted);
}
/* Last year's top, carried across this year's column so a small change is still a visible
   one. Measured up the well rather than the whole plot, because that is what the bars are. */
.cmpv-ref {
    position: absolute; left: 0; right: 0; height: 0;
    bottom: calc(var(--level) * var(--well) / 100 - 1px);
    border-top: 1px dashed var(--axis); pointer-events: none;
}
.cmpv-well { display: flex; align-items: flex-end; width: 100%; min-width: 0; }
.cmpv-bar {
    position: relative;
    width: 100%; border-radius: 3px 3px 0 0; min-height: 2px;
    display: flex; align-items: flex-end; justify-content: center;
}
.cmpv-bar.is-now { background: var(--accent); }
.cmpv-bar.is-then { background: var(--prior); }
/* The figure, written into the bar it belongs to. A number beside a bar has to be matched to
   it; a number inside one is already matched.

   At the foot of the bar, not the head of it: the reference line marks the top of last year's
   column, so a figure sitting at a bar's top is exactly where that line crosses -- and a
   dashed rule struck through a dollar amount is worse than either on its own. */
.cmpv-fig {
    padding-bottom: 4px; font-size: 10.5px; line-height: 1.2;
    font-variant-numeric: tabular-nums; white-space: nowrap;
}
.cmpv-bar.is-now .cmpv-fig { color: var(--accent-ink); }
.cmpv-bar.is-then .cmpv-fig { color: var(--ink); }
/* Too short to hold it. Lifted just clear of the bar rather than clipped: losing a figure to
   keep a layout is the wrong way round. */
.cmpv-bar.is-short .cmpv-fig {
    position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%);
    padding: 0 0 3px; color: var(--ink-2) !important;
}
.cmpv-group figcaption { padding-top: 8px; text-align: center; }
.cmpv-label { font-size: 12px; line-height: 1.25; color: var(--ink); }
.cmpv-move {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
    gap: 4px 6px; margin-top: 5px;
}
.cmpv-diff {
    font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.cmpv-also { margin: 9px 0 0; display: grid; gap: 3px; font-size: 11px; }
.cmpv-also > div {
    display: grid; grid-template-columns: 1fr auto auto; gap: 0 10px; align-items: baseline;
}
.cmpv-also dt { text-align: left; color: var(--muted); }
.cmpv-also dd {
    margin: 0; text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* Toned like the bars above them, so which column is which needs no second legend. */
.cmpv-also dd.is-then { color: var(--muted); }
.cmpv-also dd.is-now { color: var(--ink); }

@media (max-width: 560px) {
    /* Two to a row on a phone. One per row reads no better and doubles the scroll. */
    .cmpv-groups { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px 8px; }
    .cmpv-plot { --well: 74px; --col: 64px; column-gap: 7px; }
    .cmpv-fig { font-size: 10px; }
    .cmpv-head { font-size: 10px; }
    .cmpv-also { font-size: 10px; }
    .cmpv-also > div { gap: 0 6px; }
}

.cmp-cell { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.cmp-cell-delta { font-size: 11px; line-height: 1.2; }

/* --- sign-in ------------------------------------------------------------
   One button, so it carries the page. Google's own mark is used unaltered:
   their branding terms require the official colours and it is what people
   are looking for. */
.btn-google {
    display: flex; align-items: center; justify-content: center; gap: 10px;
    width: 100%; height: 44px; margin-top: 18px;
    background: var(--surface-raised); color: var(--ink);
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
    font-weight: 500;
}
.btn-google:hover { background: var(--surface-sunken); text-decoration: none; }
.btn-google .g-mark { width: 18px; height: 18px; flex: none; stroke: none; }
.auth-note > * { margin-top: 14px; }

/* --- sign-in and refusal pages ------------------------------------------
   Centred in the viewport rather than sitting at the top of a content column,
   because with the app frame hidden there is no column for it to sit in. Wider
   than the old card: the refusal state carries real explanation, and prose set
   to 400px turns into a ladder. */

.auth-wrap {
    min-height: calc(100vh - var(--topbar-h));
    display: grid; place-items: center; padding: 40px 20px 80px;
}
.auth-panel {
    width: 100%; max-width: 520px; padding: 34px 34px 30px;
    background: var(--surface-raised); border: 1px solid var(--border);
    border-radius: 14px; box-shadow: var(--shadow);
}
.auth-panel h1 { font-size: 24px; line-height: 1.2; letter-spacing: -0.02em; margin: 0 0 10px; }
.auth-body { margin: 0; color: var(--ink-2); line-height: 1.6; }

.auth-brand {
    display: flex; align-items: center; gap: 9px;
    font-size: 15px; font-weight: 500; margin-bottom: 20px;
}

/* Informational, not critical and not success. Being turned away is the system working, so red
   would tell someone they had broken something they had not; green would congratulate them on
   being refused. The accent says "read this" without claiming either. */
.auth-badge {
    display: grid; place-items: center; width: 42px; height: 42px; margin-bottom: 18px;
    background: var(--accent-wash); border-radius: 11px; color: var(--accent);
}
.auth-badge svg {
    width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
}

/* The address that was refused. Given its own line and weight because it is the single most
   useful fact on the page -- it is what tells someone they used the wrong account. */
.auth-identity {
    margin: 0 0 12px; padding: 10px 13px;
    background: var(--surface-sunken); border-radius: 8px;
    font-size: 14px; color: var(--ink-2); word-break: break-all;
}
.auth-identity strong { color: var(--ink); font-weight: 600; }

.auth-recorded {
    display: flex; gap: 11px; margin-top: 18px; padding: 14px 16px;
    background: var(--accent-wash); border-radius: 10px;
    font-size: 13.5px; line-height: 1.6; color: var(--ink-2);
}
.auth-recorded svg {
    width: 17px; height: 17px; flex: none; margin-top: 2px; color: var(--accent);
    fill: none; stroke: currentColor; stroke-width: 2.2;
    stroke-linecap: round; stroke-linejoin: round;
}

.auth-note-box {
    display: flex; gap: 11px; margin-top: 20px; padding: 14px 16px;
    background: var(--surface-sunken); border-radius: 10px;
    font-size: 13.5px; line-height: 1.6; color: var(--ink-2);
}
.auth-note-box svg {
    width: 17px; height: 17px; flex: none; margin-top: 2px; color: var(--muted);
    fill: none; stroke: currentColor; stroke-width: 1.8;
}

.auth-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 24px; }
/* This button sizes to its text here; on the old card it filled the card. */
.auth-actions .btn-google { width: auto; margin-top: 0; height: 42px; padding: 0 18px; }
.auth-actions .btn-ghost { height: 42px; padding: 0 16px; }

/* Follow-up under a refusal. Quieter than the panel above it: that says what happened, this
   only suggests what to try, and it must not read as a second error. */
.auth-aside {
    margin: 20px 0 0; padding-top: 18px; border-top: 1px solid var(--border);
    font-size: 13px; line-height: 1.6; color: var(--muted);
}

@media (max-width: 560px) {
    .auth-panel { padding: 26px 22px 24px; }
    .auth-actions .btn { width: 100%; justify-content: center; }
}

/* ===========================================================================
   Landing page.

   The signed-out front page runs full-bleed inside .content and opts out of the
   app frame entirely: no sidebar, no search, no year picker. A sales page
   wrapped in empty application chrome reads as a product that failed to load.

   It stays on the same tokens as the app. The temptation with a marketing page
   is a second palette and a display face, which then looks like a different
   product from the one behind the sign-in button.
   =========================================================================== */

/* Both signed-out pages drop the application frame. On the landing page a sidebar is noise; on
   the sign-in and refusal pages every one of those links comes straight back to sign-in, so
   showing them implies there is something behind the door that merely failed to open. */
body[data-chrome="landing"] .sidenav,
body[data-chrome="landing"] .search,
body[data-chrome="landing"] .year-select,
body[data-chrome="landing"] .nav-toggle,
body[data-chrome="auth"] .sidenav,
body[data-chrome="auth"] .search,
body[data-chrome="auth"] .year-select,
body[data-chrome="auth"] .nav-toggle { display: none; }

body[data-chrome="landing"] .content { padding: 0; }
body[data-chrome="landing"] .topbar { border-bottom-color: transparent; }
/* The brand pushes left and the actions right once the search bar is gone. */
body[data-chrome="landing"] .topbar-actions,
body[data-chrome="auth"] .topbar-actions { margin-left: auto; }
/* The sign-in button in the top bar is redundant on a page that is entirely about signing in. */
body[data-chrome="auth"] #account { display: none; }

.landing { --lp-max: 1080px; }
.landing h2 {
    font-size: clamp(22px, 2.4vw, 30px); line-height: 1.2;
    letter-spacing: -0.015em; margin: 0 0 8px;
}

/* --- hero --------------------------------------------------------------- */

.lp-hero {
    position: relative; overflow: hidden;
    padding: clamp(56px, 9vw, 108px) 24px clamp(48px, 7vw, 84px);
    text-align: center;
    /* A wash rather than a saturated band: the chart below is the colour on this
       page, and a loud hero would compete with the one thing that proves the
       product works. */
    background:
        radial-gradient(ellipse 80% 60% at 50% -10%, var(--accent-wash), transparent 70%),
        linear-gradient(180deg, var(--surface-sunken), transparent 60%);
}
.lp-hero::after {
    /* Hairline that fades at both ends, so the hero resolves into the page
       instead of stopping at a hard rule. */
    content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
    background: linear-gradient(90deg, transparent, var(--border-strong) 30%,
                var(--border-strong) 70%, transparent);
}
.lp-hero-inner { max-width: 760px; margin: 0 auto; }

.lp-eyebrow {
    display: inline-block; margin-bottom: 18px; padding: 5px 12px;
    background: var(--surface-raised); border: 1px solid var(--border);
    border-radius: 999px; box-shadow: var(--shadow);
    font-size: 11.5px; font-weight: 600; letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--ink-2);
}
.lp-hero h1 {
    margin: 0 0 18px;
    font-size: clamp(34px, 5.6vw, 60px); line-height: 1.06;
    letter-spacing: -0.03em; font-weight: 700;
}
.lp-hl { color: var(--accent); }
.lp-lede {
    max-width: 620px; margin: 0 auto;
    font-size: clamp(15px, 1.5vw, 18px); line-height: 1.6; color: var(--ink-2);
}
.lp-cta {
    display: flex; gap: 12px; justify-content: center; flex-wrap: wrap;
    margin-top: 30px;
}
.btn-lg { height: 46px; padding: 0 22px; font-size: 15px; border-radius: 9px; }
/* The hero button sizes to its text; the one on the sign-in card fills its card. */
.lp-cta .btn-google { width: auto; margin-top: 0; }
.lp-hero-note { margin-top: 16px; font-size: 13px; color: var(--muted); }

/* --- stat strip --------------------------------------------------------- */

/* Lifted into the hero's lower edge so the first thing below the headline is
   evidence, not another paragraph. The 1px grid gap is the card background
   showing through, which avoids doubled borders between cells. */
.lp-stats {
    display: grid; gap: 1px; max-width: var(--lp-max);
    margin: 0 auto; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    position: relative; transform: translateY(-30px);
    background: var(--border); border: 1px solid var(--border);
    border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow);
}
.lp-stat { background: var(--surface-raised); padding: 22px 20px; text-align: center; }
.lp-stat-value {
    font-size: clamp(24px, 3vw, 32px); font-weight: 700;
    letter-spacing: -0.02em; line-height: 1.1;
    font-variant-numeric: tabular-nums;
}
.lp-stat-label { margin-top: 6px; font-size: 13px; font-weight: 600; color: var(--ink-2); }
.lp-stat-note { margin-top: 3px; font-size: 12px; color: var(--muted); }

/* --- the proof chart ---------------------------------------------------- */

.lp-proof {
    max-width: var(--lp-max); margin: 0 auto;
    background: var(--surface-raised); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: var(--shadow);
    padding: 26px 26px 20px;
}
.lp-proof-sub { margin: 6px 0 0; color: var(--ink-2); max-width: 640px; }
.lp-proof-chart { margin-top: 20px; }
.lp-proof-foot { margin: 10px 0 0; font-size: 13px; color: var(--muted); }

/* --- features and capabilities ------------------------------------------ */

.lp-features, .lp-capabilities, .lp-plans {
    max-width: var(--lp-max); margin: 0 auto; padding: clamp(48px, 6vw, 76px) 0 0;
}
.lp-section-sub { margin: 0 0 4px; color: var(--ink-2); max-width: 620px; }

.lp-feature-grid {
    display: grid; gap: 18px; margin-top: 26px;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
.lp-feature {
    padding: 22px; background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius);
}
.lp-feature-icon {
    display: grid; place-items: center; width: 38px; height: 38px; margin-bottom: 14px;
    background: var(--accent-wash); border-radius: 9px; color: var(--accent);
}
.lp-feature-icon svg {
    width: 20px; height: 20px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}
.lp-feature h3 { margin: 0 0 6px; font-size: 15.5px; }
.lp-feature p { margin: 0; font-size: 14px; line-height: 1.6; color: var(--ink-2); }

.lp-cap-grid {
    display: grid; gap: 0 24px; margin-top: 22px;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.lp-cap { padding: 18px 0 20px; border-top: 2px solid var(--accent-quiet); }
.lp-cap h3 { margin: 0 0 6px; font-size: 15px; }
.lp-cap p { margin: 0; font-size: 14px; line-height: 1.6; color: var(--ink-2); }

/* --- plans -------------------------------------------------------------- */

.lp-plan-grid {
    display: grid; gap: 16px; margin-top: 26px; align-items: start;
    grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
}
.lp-plan {
    position: relative; padding: 24px 20px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius);
}
/* The featured tier is raised rather than recoloured -- one accent border, no
   fill, so the price list still reads as one set of options. */
.lp-plan.is-featured {
    background: var(--surface-raised); border-color: var(--accent);
    box-shadow: var(--shadow);
}
.lp-plan-flag {
    position: absolute; top: -10px; left: 20px; padding: 3px 10px;
    background: var(--accent); color: var(--accent-ink);
    border-radius: 999px; font-size: 11px; font-weight: 600; letter-spacing: 0.03em;
}
.lp-plan-name {
    font-size: 12px; font-weight: 600; text-transform: uppercase;
    letter-spacing: 0.07em; color: var(--muted);
}
.lp-plan-price {
    margin-top: 8px; font-size: 30px; font-weight: 700; letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
}
.lp-plan-period { font-size: 13px; font-weight: 500; color: var(--muted); letter-spacing: 0; }
.lp-plan-tagline { margin-top: 6px; font-size: 13.5px; color: var(--ink-2); }
.lp-plan-features {
    margin: 16px 0 0; padding: 16px 0 0; list-style: none;
    border-top: 1px solid var(--border);
    display: grid; gap: 9px; font-size: 13.5px; line-height: 1.5; color: var(--ink-2);
}
.lp-plan-features li { position: relative; padding-left: 20px; }
.lp-plan-features li::before {
    content: ""; position: absolute; left: 2px; top: 6px;
    width: 9px; height: 5px; border-left: 1.8px solid var(--accent);
    border-bottom: 1.8px solid var(--accent); transform: rotate(-45deg);
}

/* --- close and footer --------------------------------------------------- */

.lp-close {
    max-width: var(--lp-max); margin: clamp(56px, 7vw, 88px) auto 0;
    padding: clamp(40px, 5vw, 56px) 24px; text-align: center;
    background: var(--surface-sunken); border-radius: var(--radius);
}
.lp-close p { max-width: 480px; margin: 0 auto 22px; color: var(--ink-2); }

.lp-foot {
    max-width: var(--lp-max); margin: clamp(48px, 6vw, 72px) auto 0;
    padding: 40px 0 56px; border-top: 1px solid var(--border);
}
.lp-foot-brand {
    display: flex; align-items: center; gap: 9px;
    font-size: 15px; font-weight: 500; margin-bottom: 10px;
}
.lp-foot p { margin: 0; max-width: 640px; font-size: 12.5px; line-height: 1.6; color: var(--muted); }

/* The landing sections share one gutter, applied here so each section's own
   padding stays about vertical rhythm only. */
.lp-stats, .lp-proof, .lp-features, .lp-capabilities,
.lp-plans, .lp-close, .lp-foot { margin-left: 24px; margin-right: 24px; }

@media (min-width: 1128px) {
    .lp-stats, .lp-proof, .lp-features, .lp-capabilities,
    .lp-plans, .lp-close, .lp-foot { margin-left: auto; margin-right: auto; }
}

@media (max-width: 640px) {
    .lp-stats { transform: none; margin-top: 24px; }
    .lp-cta .btn-lg { width: 100%; justify-content: center; }
}

/* --- rebuild progress ---------------------------------------------------
   A rebuild rewrites every table the site reads and takes minutes. Showing the step it is on,
   and the row counts as they land, is the difference between "working" and "stuck" -- which
   from the outside look identical. */
.rollup-card .rollup-body { display: grid; gap: 14px; }
.rollup-bar {
    height: 6px; border-radius: 999px; overflow: hidden;
    background: var(--surface-sunken);
}
.rollup-bar span {
    display: block; height: 100%; background: var(--accent);
    /* Steps land in bursts, so the width is eased rather than jumping. */
    transition: width 400ms ease;
}
.rollup-counts {
    display: grid; gap: 1px; background: var(--border);
    border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.rollup-counts > div {
    display: flex; justify-content: space-between; gap: 12px; align-items: baseline;
    padding: 9px 12px; background: var(--surface); font-size: 13px;
}
.rollup-counts span { color: var(--ink-2); }
.rollup-counts strong { font-variant-numeric: tabular-nums; }
.is-bad { color: var(--critical); }

/* Sits after the percentage, small and quiet: it qualifies the number rather than
   competing with it. */
.yoy-partial { margin-left: 5px; font-size: 11px; vertical-align: middle; }

/* --- the credit line on every chart ---------------------------------------
   Quiet enough that it never competes with the data, legible enough to survive a screenshot
   and be typed back in. It is a credit, not a seal: anyone can crop it, and the filings behind
   these charts are public record. What it carries is attribution for the work on top of them. */
/* Charts were given more room around them than inside them: the card's own padding, then the
   host's, then the chart's margins, three gaps doing one job. */
.chart-host { margin-top: 2px; }
.card-head + .chart-host { margin-top: 6px; }

/* The change column on a sideways bar chart. SVG text, so it cannot reuse the .delta pill --
   same three colours, no background, because a filled pill per row would out-shout the bars. */
.chart-delta { font-size: 11px; font-weight: 600; }
.chart-delta.is-up { fill: var(--good); }
.chart-delta.is-down { fill: var(--critical); }
.chart-delta.is-flat { fill: var(--muted); }

.chart-watermark {
    /* Not bold. A heavy face at low opacity reads as a smudge rather than as type -- the
       strokes merge before they are faint enough to ignore. A lighter weight stays legible at
       the same opacity and looks deliberate instead of like a printing fault. */
    font-weight: 500;
    letter-spacing: 0.08em;
    /* --ink, not a fixed colour: the mark has to read on a pale surface and a dark one, and it
       is the one element that sits on top of whatever the chart happens to be. */
    fill: var(--ink);
    opacity: 0.11;
    /* Never selectable or hoverable: it lies across the whole plot, so without this it would
       swallow the tooltip targets underneath and be caught by every drag. */
    user-select: none;
    pointer-events: none;
}
/* The address is the smaller of the two lines, so it needs the lighter weight most: at half
   the size anything with body to it closes up into a single grey bar. 300 resolves to the Light
   cut of the system face -- Segoe UI Light on Windows, which is what both the page and the
   exported image render with -- rather than being synthesised from the regular. Set back
   further again, because it identifies the copy rather than the source and does not need equal
   billing with it. */
/* Nothing of its own: it takes the weight, tracking and opacity of the mark it belongs to,
   so the address reads as part of the credit rather than as a footnote beneath it. Its size is
   set per chart in sign(), which has the width to fit it against. */
.chart-save svg { width: 14px; height: 14px; }

/* --- auto-renew ----------------------------------------------------------
   The switch and its consequence on one line. What people are afraid of here is losing access
   the moment they flick it off, so the date they are paid up to is never more than a glance
   away from the control. */
.auto-renew {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px;
    padding: 12px 14px; margin-bottom: 12px;
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface-sunken);
}
.ar-switch { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-weight: 600; }
.ar-switch input { margin: 0; cursor: pointer; }
.ar-detail { font-size: 13px; color: var(--ink-2); }

/* An offer on an invitation reads as a thing granted, not as a warning. */
.tag-offer { background: var(--accent-wash); color: var(--accent); border-color: var(--accent-quiet); }

/* --- the period a card covers -------------------------------------------
   On every card, not once at the top of the page. These cards get screenshotted and pasted
   into decks one at a time, and a revenue figure separated from the months it covers is not
   a weaker claim -- it is a different one. Quiet enough to ignore while reading the page,
   present enough to survive the crop. */
.period-tag {
    display: inline-block; margin-left: 9px; vertical-align: middle;
    padding: 2px 8px; border-radius: 999px;
    background: var(--surface-sunken); border: 1px solid var(--border);
    font-size: 11px; font-weight: 600; letter-spacing: 0.01em;
    color: var(--ink-2); text-transform: none; white-space: nowrap;
}
.period-caption {
    margin: -4px 0 10px; font-size: 12px; font-weight: 600; color: var(--ink-2);
}
@media (max-width: 640px) {
    /* Its own line rather than squeezing the title to nothing. */
    .period-tag { display: block; margin: 5px 0 0; width: fit-content; }
}

/* --- saved lists ---------------------------------------------------------
   Tabs rather than a dropdown: a comp set and a shortlist are looked at in turn, and a
   dropdown hides how many sets there are behind a click. The counts sit in the tab because
   "which of these is the comp set" is usually answered by its size. */
.list-tabs {
    display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
    margin: -4px 0 18px; padding-bottom: 12px;
    border-bottom: 1px solid var(--border);
}
.list-tab {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 6px 12px; border-radius: 999px; border: 1px solid transparent;
    font-size: 13px; font-weight: 600; color: var(--ink-2); text-decoration: none;
    background: none; cursor: pointer; font-family: inherit;
}
.list-tab:hover { background: var(--surface-sunken); color: var(--ink); }
.list-tab.is-active {
    background: var(--accent-wash); border-color: var(--accent-quiet); color: var(--accent);
}
.lt-count {
    font-weight: 500; font-variant-numeric: tabular-nums; opacity: 0.75; font-size: 12px;
}
.lt-add { color: var(--ink-2); font-weight: 500; }

.lp-actions { display: flex; gap: 8px; }

/* --- the star's list picker ----------------------------------------------
   Fixed rather than absolute, so one opened from a row inside a scrolling table is not
   clipped by it. Ticks send immediately; there is nothing to cancel and nothing to save. */
.list-picker {
    position: fixed; z-index: 60; width: 250px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: 0 12px 32px rgba(0, 0, 0, 0.16);
    overflow: hidden;
}
.lp-head {
    padding: 10px 12px 8px; font-size: 12px; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-2);
}
.lp-body { max-height: 240px; overflow-y: auto; }
.lp-row {
    display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: center;
    padding: 8px 12px; font-size: 13px; cursor: pointer;
}
.lp-row:hover { background: var(--surface-sunken); }
.lp-row input { margin: 0; cursor: pointer; }
.lp-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lp-count { color: var(--ink-2); font-variant-numeric: tabular-nums; font-size: 12px; }
.lp-empty, .lp-loading {
    padding: 4px 12px 12px; font-size: 12px; color: var(--ink-2); line-height: 1.45;
}
.lp-new {
    display: flex; gap: 6px; padding: 10px 12px;
    border-top: 1px solid var(--border); background: var(--surface-sunken);
}
.lp-new input {
    flex: 1; min-width: 0; padding: 5px 8px; font: inherit; font-size: 13px;
    border: 1px solid var(--border); border-radius: 7px;
    background: var(--surface); color: var(--ink);
}
/* Last, quiet, and its own row: it throws away the filing as well as the star. */
.lp-unsave {
    display: block; width: 100%; padding: 9px 12px; border: 0;
    border-top: 1px solid var(--border); background: var(--surface);
    font: inherit; font-size: 12px; color: var(--ink-2); text-align: left; cursor: pointer;
}
.lp-unsave:hover { background: var(--surface-sunken); color: var(--critical); }

/* --- monthly ingest ------------------------------------------------------
   The list is tick boxes rather than a "read new files" button because reading a file twice
   inserts a second copy of every row in it. Files already read stay in the list, dimmed and
   dated: hiding them would make a re-read look like a fresh read, which is the mistake worth
   designing against.

   The live line underneath matters as much. A single file takes minutes against a network
   database, and a card that only updates between files is indistinguishable from a hung one. */
.ingest-progress:not(:empty) { display: grid; gap: 12px; margin-bottom: 14px; }
.ingest-live { display: grid; gap: 7px; }
.ingest-live-head {
    display: flex; justify-content: space-between; gap: 12px;
    font-size: 13px; font-weight: 600;
}
.ingest-live-head .muted { font-weight: 400; color: var(--ink-2); }
.ingest-bar {
    height: 6px; border-radius: 999px; overflow: hidden; background: var(--surface-sunken);
}
.ingest-bar-fill {
    height: 100%; background: var(--accent); transition: width 400ms ease;
}
.ingest-live-detail {
    font-size: 13px; color: var(--ink-2); font-variant-numeric: tabular-nums;
}
.ingest-warning { font-size: 13px; line-height: 1.45; margin-bottom: 10px; }
.ingest-list {
    display: grid; gap: 1px; background: var(--border);
    border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden;
}
.ingest-row {
    display: grid; grid-template-columns: auto 1fr auto auto; gap: 12px; align-items: center;
    padding: 9px 12px; background: var(--surface); font-size: 13px; cursor: pointer;
}
.ingest-row:hover { background: var(--surface-sunken); }
/* Dimmed, not hidden: it has to be possible to re-read a file, and obvious that you are. */
.ingest-row.is-read .ingest-name { color: var(--ink-2); }
.ingest-row input { cursor: pointer; margin: 0; }
.ingest-row input:disabled { cursor: default; }
.ingest-name { font-weight: 600; }
.ingest-row.is-read .ingest-name { font-weight: 400; }
.ingest-state { color: var(--ink-2); }
.ingest-rows { color: var(--ink-2); font-variant-numeric: tabular-nums; min-width: 70px; text-align: right; }
@media (max-width: 560px) {
    .ingest-row { grid-template-columns: auto 1fr auto; }
    .ingest-rows { display: none; }
}

/* --- projected remainder of a part-finished year ------------------------
   Drawn as an outline sitting on top of the filed bar, never as more solid bar. The whole
   point of the chart is what was filed; the projection is the one mark on it that nobody
   reported, so it has to read as an estimate at a glance rather than on inspection. */
.chart .bar-projected {
    fill-opacity: 0.10;
    stroke-width: 1.5;
    stroke-dasharray: 4 3;
}
.chart .value-label.is-projected { fill: var(--muted); font-style: italic; }
/* Where a projection sits above the bar, the filed figure moves inside it. */
.chart .value-label.is-inside { fill: var(--surface); font-weight: 600; }

/* --- email preferences --------------------------------------------------- */
.pref-list { display: grid; }
.pref {
    display: flex; gap: 12px; align-items: flex-start;
    padding: 16px 0; border-top: 1px solid var(--border); cursor: pointer;
}
.pref:first-child { border-top: 0; }
.pref input { margin-top: 3px; width: 16px; height: 16px; flex: none; accent-color: var(--accent); }
.pref-text { display: grid; gap: 3px; }
.pref-title { font-weight: 500; }
.pref-body { font-size: 13px; line-height: 1.6; color: var(--ink-2); }
/* The account menu mixes links and buttons; they have to look identical. */
.account-menu a[data-action] {
    display: block; padding: 8px 12px; color: var(--ink); font: inherit; text-align: left;
}
.account-menu a[data-action]:hover { background: var(--surface-sunken); text-decoration: none; }

.billing-actions { display: grid; gap: 10px; justify-items: start; padding-top: 4px; }
.billing-actions .secondary { font-size: 13px; line-height: 1.6; color: var(--ink-2); }

/* --- owner configuration ------------------------------------------------- */
.setting-list { display: grid; }
.setting {
    display: flex; gap: 20px; align-items: flex-start; justify-content: space-between;
    padding: 16px 0; border-top: 1px solid var(--border);
}
.setting:first-child { border-top: 0; }
.setting-text { min-width: 0; }
.setting-label { font-weight: 500; }
.setting-help { margin-top: 3px; font-size: 13px; line-height: 1.6; color: var(--ink-2); }
.setting-origin { margin-top: 4px; font-size: 12px; color: var(--muted); }
.setting-origin code { font-size: 11.5px; }
.setting-control { flex: none; display: flex; align-items: center; }
.setting-control input[type="text"] {
    width: 260px; height: 34px; padding: 0 11px;
    background: var(--surface); border: 1px solid var(--border-strong); border-radius: 8px;
}
.setting-control select {
    height: 34px; padding: 0 9px;
    background: var(--surface); border: 1px solid var(--border-strong); border-radius: 8px;
}
.switch { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
.switch input { width: 16px; height: 16px; accent-color: var(--accent); }
@media (max-width: 620px) {
    .setting { flex-direction: column; gap: 10px; }
    .setting-control input[type="text"] { width: 100%; }
}

/* The name exactly as submitted to the state, under the readable one. Quiet: it is provenance,
   not the title, and it only appears when the two actually differ. */
.filed-as { margin-top: 4px; font-size: 12.5px; color: var(--muted); }
.filed-as strong { color: var(--ink-2); font-weight: 500; }
