/*
 * quiet-corners dashboard styles.
 *
 * Loaded only by themes/headlessmode/layouts/_default/quiet-corners-dashboard.html,
 * from a <link> sitting exactly where the <style> block these rules used to live
 * in sat. Two reasons it is a sheet of its own and not an append to style.css:
 *
 *   1. Cascade. These rules restyle Leaflet at equal specificity and without
 *      !important -- .leaflet-container, .leaflet-popup-content-wrapper and
 *      .leaflet-popup-tip all collide with vendor/leaflet.css (a #ddd map and
 *      white popups). head.html loads style.css BEFORE vendor/leaflet.css, so
 *      folding these in there would hand those three rules to Leaflet.
 *   2. Weight. ~600 lines that one page uses would otherwise download on all
 *      53 pages of the site.
 *
 * The sheet is unlayered, as the <style> block was, so it still outranks
 * everything Tailwind emits (@layer base/components/theme/utilities) regardless
 * of specificity. Keep it that way: an @layer wrapper here would silently hand
 * the page back to the utility classes.
 */
/* The page has two voices, and F19 was what happens without them: every label,
   heading, sentence and control was set in the same dense 0.65-0.7rem monospace as
   the data, so nothing told a reader which text was a number to compare and which
   was a word to read. Data keeps monospace -- seven numeric columns only line up
   because the digits are the same width. Everything a reader READS is the UI face.
   Named here so a control added later inherits the readable one by default instead
   of picking monospace because the rule beside it did. */
:root {
    --qc-font-ui: var(--font-sans, ui-sans-serif, system-ui, sans-serif);
}

/* Remove padding from outer layout context usually set by Hugo theme */
.max-w-6xl.mx-auto.px-6.py-12 {
    max-width: none !important;
    padding: 0 !important;
    margin: 0 !important;
    min-height: 100vh;
}

/* Document/Body overrides for edge-to-edge feel — no overflow:hidden here:
   the page must still scroll to the footer and reflow at 400% zoom. */
body {
    margin: 0;
}

.qc-app-nav {
    display: flex;
    align-items: center;
    gap: 1rem;
}

/* The tool page's one header (audit A10). It carries the product identity that
   a separate product-sheet band used to carry above it — chip, name, one line —
   because the two stacked headers pushed the map 593px down a 390px phone, and
   a map tool that opens with almost no map on screen is not doing its job. The
   type is smaller than the band's for the same reason: this is an app frame, not
   a printed cover. */
.qc-app-identity {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    min-width: 0;
}

.qc-app-titlerow {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.6rem;
}

.qc-app-name {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0;
    color: #10b981;
    font-family: ui-monospace, monospace;
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.15;
}

.qc-app-line {
    margin: 0;
    max-width: 46rem;
    color: #d1d5db;
    font-size: 0.85rem;
    line-height: 1.5;
}

.qc-site-return {
    color: #d1d5db;
    font-family: ui-monospace, monospace;
    font-size: 0.75rem;
    text-decoration: none;
    white-space: nowrap;
}

.qc-site-return:hover {
    color: #34d399;
}

.themed-prose {
    --tw-prose-body: var(--text-secondary);
    --tw-prose-headings: var(--text-primary);
    --tw-prose-links: var(--accent-500);
    --tw-prose-bold: var(--text-primary);
    --tw-prose-code: var(--accent-500);
    --tw-prose-quotes: var(--text-muted);
}

/* Modal Backdrop */
dialog::backdrop {
    background: rgba(0, 0, 0, 0.8);
    backdrop-filter: blur(4px);
}

/* Tooltips */
.qc-tooltip {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    cursor: help;
    border-bottom: 1px dotted rgba(255, 255, 255, 0.3);
}
/* At 0.85em of an already-small label this marker rendered under 11px, the
   smallest glyph on the page and the one that says "there is an explanation
   here". It sits at the label's own size (F19). */
.qc-tooltip-icon {
    color: #6ee7b7;
    font-size: 1em;
}
/* The tooltip is absolutely positioned and 260px wide, so one attached to a
   control near the right edge hangs past the viewport. `visibility: hidden` does
   NOT take an element out of the scrollable overflow area, so all four of them
   widened the DOCUMENT: /quiet-corners/ measured scrollWidth 1530 against a 1440
   viewport and 459 against 390, and the whole page scrolled sideways while every
   tooltip was invisible. Capping the width to the viewport is the fix; the clip
   on the dashboard below is the backstop for any future absolute decoration. */
.qc-tooltip .qc-tooltip-text {
    visibility: hidden;
    width: max-content;
    max-width: min(260px, calc(100vw - 2rem));
    background-color: var(--bg-elevated, #1e1e2e);
    color: var(--text-primary);
    text-align: left;
    border-radius: 0.375rem;
    padding: 0.5rem 0.75rem;
    position: absolute;
    z-index: 1000;
    bottom: 125%;
    left: 50%;
    transform: translateX(-50%);
    opacity: 0;
    transition: opacity 0.2s;
    font-size: 0.75rem;
    font-family: var(--font-sans);
    line-height: 1.4;
    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.6);
    border: 1px solid var(--border-default, #333);
    pointer-events: none;
    white-space: normal;
    text-transform: none;
    letter-spacing: normal;
    font-weight: normal;
}
.qc-tooltip .qc-tooltip-text::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 50%;
    margin-left: -5px;
    border-width: 5px;
    border-style: solid;
    border-color: var(--border-default, #333) transparent transparent transparent;
}
.qc-tooltip:hover .qc-tooltip-text,
.qc-tooltip:focus .qc-tooltip-text,
.qc-tooltip:focus-within .qc-tooltip-text {
    visibility: visible;
    opacity: 1;
}

/* Floating control panel.
   These rules described the horizontal control bar this panel replaced in
   #174 — a wrapping row of `align-items:center` groups. The markup is now a
   vertical stack of two-column grids, so the old `row` direction put every
   label on the same line as its slider and blew past the 207px column: the
   Remoteness readout landed on top of the Stability label, and the Stability
   and Affordability readouts landed outside the panel, on the map.
   The panel owns its own vertical rhythm here rather than via `space-y-3`,
   so there is one spacing mechanism instead of two that add up. */
.qc-controls {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.75rem;
    margin-bottom: 0.75rem;
    font-family: var(--qc-font-ui);
    font-size: 0.8125rem;
    padding: 0.5rem;
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: 0.375rem;
}

.filter-group {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.35rem;
}

.filter-label {
    color: var(--text-muted);
    font-size: 0.8125rem;
    white-space: nowrap;
}

.filter-select {
    padding: 0.25rem 0.5rem;
    background: var(--bg-primary);
    border: 1px solid var(--border-default);
    border-radius: 0.25rem;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 0.8125rem;
    font-family: var(--qc-font-ui);
}

.filter-select:hover {
    border-color: var(--accent-500);
    color: var(--accent-400);
}

/* No fixed width. The markup gives the input `flex-1`, but a range input's
   default `min-width: auto` resolves to its intrinsic size, so a fixed
   100px could not compress into the grid column and overflowed instead. */
.qc-slider {
    width: auto;
    min-width: 0;
    flex: 1 1 auto;
    accent-color: var(--accent-500);
    cursor: pointer;
}

/* Wide enough for the longest readout the JS writes: "250+" and "<=50". */
.qc-slider-val {
    color: var(--accent-400);
    font-size: 0.75rem;
    min-width: 2.75rem;
}

/* Map */
.qc-map-container {
    align-self: flex-start;
    /* The floor, stated once. Nothing in the two-column row may drive the map
       below a width where a county and its surroundings still fit together;
       scripts/qcm/layout.test.mjs measures this number in a real browser. */
    min-width: min(480px, 100%);
    height: 75vh;
    min-height: 640px;
    position: sticky;
    top: 0;
    margin-bottom: 0.75rem;
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: 0.375rem;
    overflow: hidden;
}

#qc-map {
    width: 100%;
    height: 100%;
    background: #0d0d1a;
    /* Above the container's own background, below the panels that sit on it. */
    z-index: 10;
}

/* Override Leaflet for dark theme */
.leaflet-container {
    background: #0d0d1a;
    font-family: ui-monospace, monospace;
}

.leaflet-popup-content-wrapper {
    background: var(--bg-elevated, #1e1e2e);
    color: var(--text-secondary, #a0a0b0);
    border-radius: 0.375rem;
    font-family: ui-monospace, monospace;
    font-size: 0.75rem;
    box-shadow: 0 4px 12px rgba(0,0,0,0.5);
}

.leaflet-popup-tip {
    background: var(--bg-elevated, #1e1e2e);
}

.leaflet-popup-content {
    margin: 0.75rem;
    line-height: 1.5;
}

.leaflet-popup-content strong {
    color: var(--accent-500, #10b981);
    font-size: 0.85rem;
}

.leaflet-popup-content .qc-popup-row {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.15rem 0;
}

.leaflet-popup-content .qc-popup-label {
    color: var(--text-muted, #71717a);
}

.leaflet-popup-content .qc-popup-val {
    color: var(--text-secondary, #a0a0b0);
    font-weight: 600;
}

/* The line that says how many towns matched out of how many exist, and — when the
   answer is none — the way back that keeps the area the reader chose (audit F06). */
.qc-candidates-status {
    margin: 0 0 0.75rem;
    color: var(--text-muted, #71717a);
    line-height: 1.5;
}
.qc-candidates-status strong { color: var(--text-secondary, #a0a0b0); }
.qc-reset-scores {
    display: inline-block;
    margin-top: 0.4rem;
    padding: 0.3rem 0.6rem;
    background: var(--bg-card, #18181b);
    border: 1px solid var(--accent-500, #10b981);
    border-radius: 4px;
    color: var(--accent-500, #10b981);
    font: inherit;
    cursor: pointer;
}
.qc-reset-scores:hover, .qc-reset-scores:focus-visible {
    background: var(--accent-500, #10b981);
    color: #052e16;
}

/* The one line in a town popup that qualifies the two numbers above it, rather
   than adding a third. Separated by a rule and set quieter than the values, so it
   reads as a note about them (audit F17). */
.leaflet-popup-content .qc-popup-note {
    margin: 0.5rem 0 0;
    padding-top: 0.4rem;
    border-top: 1px solid var(--border, #3f3f46);
    color: var(--text-muted, #71717a);
    font-size: 0.7rem;
    line-height: 1.45;
}

.leaflet-control-zoom a {
    background: var(--bg-card, #18181b) !important;
    color: var(--text-muted, #71717a) !important;
    border-color: var(--border-default, #333) !important;
}

.leaflet-control-zoom a:hover {
    background: var(--bg-elevated, #1e1e2e) !important;
    color: var(--accent-500, #10b981) !important;
}

.leaflet-control-zoom a {
    background: #14141e !important;
    color: #c9c9d6 !important;
    border-color: rgba(255,255,255,0.15) !important;
}


.leaflet-control-attribution {
    /* Opaque: axe can't composite alpha over the map canvas, so translucent
       backgrounds read as indeterminate contrast in the pa11y gate. */
    background: #101018 !important;
    color: #c9c9d6 !important;
    font-size: 0.6rem !important;
}

.leaflet-control-attribution a {
    color: #c9c9d6 !important;
}

/* Load-failure banner */
.qc-error {
    position: absolute;
    top: 1rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 60;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0.75rem;
    background: rgba(127, 29, 29, 0.92);
    border: 1px solid #ef4444;
    border-radius: 0.375rem;
    color: #fecaca;
    font-family: ui-monospace, monospace;
    font-size: 0.75rem;
}

.qc-error button {
    padding: 0.2rem 0.6rem;
    background: rgba(0,0,0,0.3);
    border: 1px solid #ef4444;
    border-radius: 0.25rem;
    color: #fff;
    cursor: pointer;
    font-family: inherit;
    font-size: inherit;
}

/* Degraded-map notice (TopoJSON unavailable) */
.qc-map-notice {
    max-width: 230px;
    padding: 0.4rem 0.6rem;
    background: rgba(127, 29, 29, 0.85) !important;
    border: 1px solid #ef4444;
    border-radius: 0.375rem;
    color: #fecaca;
    font-family: ui-monospace, monospace;
    font-size: 0.65rem;
    line-height: 1.4;
}

.qc-legend-note {
    margin-top: 0.25rem;
    color: #f87171;
    font-style: italic;
}

/* Map legend */
.qc-legend {
    background: #14141e !important;
    border: 1px solid rgba(255,255,255,0.1) !important;
    border-radius: 0.375rem;
    padding: 0.6rem 0.75rem;
    font-family: var(--qc-font-ui);
    font-size: 0.75rem;
    color: #b6b6c4;
    line-height: 1.6;
    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.6) !important;
}

.qc-legend-title {
    color: var(--text-secondary, #a0a0b0);
    font-weight: 600;
    margin-bottom: 0.25rem;
    font-size: 0.75rem;
}

.qc-legend-item {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.qc-legend-dot {
    display: inline-block;
    border-radius: 50%;
    flex-shrink: 0;
}

.qc-legend-swatch {
    display: inline-block;
    width: 14px;
    height: 10px;
    border-radius: 2px;
    flex-shrink: 0;
}

/* Table Area within Panel */
.qc-table-wrapper {
    margin-bottom: 0.5rem;
    background: rgba(0,0,0,0.2);
    border: 1px solid rgba(255,255,255,0.05);
    border-radius: 0.5rem;
    padding: 0.5rem;
}

.qc-log-header {
    display: grid;
    grid-template-columns: 1.6fr 0.3fr 0.5fr 0.5fr 0.5fr 0.5fr 0.5fr;
    gap: 0.3rem;
    padding: 0.5rem 0.25rem;
    border-bottom: 1px solid rgba(255,255,255,0.1);
    font-weight: 600;
    font-size: 0.75rem;
    color: var(--text-muted);
    /* Was uppercase with 0.05em tracking. All-caps costs about a sixth of the
       width for no information, and at the readable size that overflowed the
       last heading off the right edge of the results column. Sentence case at
       12px is narrower than caps at 11px AND easier to read (F19). */
    letter-spacing: 0.01em;
    font-family: var(--qc-font-ui);
}

.qc-log-header.sprawl-visible {
    grid-template-columns: 1.6fr 0.3fr 0.5fr 0.5fr 0.5fr 0.5fr 0.5fr 0.5fr;
}

.qc-log-header span {
    cursor: pointer;
    transition: color 0.2s;
    user-select: none;
}

.qc-log-header span:hover {
    color: var(--accent-400);
}

/* Not colour alone: on a muted-grey uppercase row the accent is the only thing
   separating the sorted column from six identical ones, and a reader who cannot
   separate the two had nothing (audit F19). The underline and the caret beside
   it carry the same fact without colour. */
.qc-log-header span.sort-active,
.qc-candidates-header span.sort-active {
    color: var(--accent-500);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.qc-sort-caret {
    margin-left: 0.15em;
    font-size: 0.9em;
}

/* Tonight's sky: five independent facts in a label/value grid, in the results column.
   It takes no width from the map (layout.test.mjs holds that 480px floor) and on a phone
   it sits below the map with the rest of the column. Five short rows cannot grow into the
   height problem the two <details> panels needed 60vh/50vh caps for, and the card carries
   no control at all, so the 44px touch-target and 16px input-font rules have nothing here
   to apply to -- and nothing to inherit wrongly from a selector keyed on another panel. */
.qc-tonight {
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 0.5rem;
    padding: 0.75rem;
}

.qc-tonight-head {
    margin: 0 0 0.5rem;
    color: var(--accent-500);
    font-size: 0.95rem;
    font-weight: 600;
}

.qc-tonight-rows {
    display: grid;
    grid-template-columns: minmax(0, auto) minmax(0, 1fr);
    gap: 0.15rem 0.6rem;
    margin: 0;
    font-family: var(--qc-font-ui);
    font-size: 0.75rem;
}

.qc-tonight-label { color: var(--text-muted); }
.qc-tonight-value { margin: 0; color: var(--text-secondary); }

.qc-tonight-note,
.qc-tonight-zone {
    margin: 0.5rem 0 0;
    color: var(--text-muted);
    font-size: 0.7rem;
    line-height: 1.45;
}

.qc-sort-note {
    margin: 0 0 0.25rem;
    color: var(--text-muted);
    font-size: 0.75rem;
    font-family: var(--qc-font-ui);
}

.qc-candidates-header span[data-sort] {
    cursor: pointer;
    user-select: none;
}

.qc-candidates-header span[data-sort]:hover {
    color: var(--accent-400);
}

.qc-row {
    display: grid;
    grid-template-columns: 1.6fr 0.3fr 0.5fr 0.5fr 0.5fr 0.5fr 0.5fr;
    gap: 0.3rem;
    padding: 0.3rem 0.25rem;
    border-bottom: 1px solid rgba(255,255,255,0.03);
    align-items: center;
    font-family: ui-monospace, monospace;
    font-size: 0.7rem;
    cursor: pointer;
    transition: background 0.15s;
    border-radius: 0.25rem;
}

.qc-row.sprawl-visible {
    grid-template-columns: 1.6fr 0.3fr 0.5fr 0.5fr 0.5fr 0.5fr 0.5fr 0.5fr;
}

.qc-row:hover {
    background: rgba(255,255,255,0.03);
}

.qc-row:last-child {
    border-bottom: none;
}

/* The row's subject, set apart from the six figures that qualify it: the UI face
   at the readable size, in the primary text colour. A place name is a word to read,
   not a column to line up, so it is the one cell in a monospace row that isn't. */
.qc-row .qc-name,
.qc-candidate-row .qc-name {
    color: var(--text-primary);
    font-family: var(--qc-font-ui);
    font-size: 0.8125rem;
    font-weight: 500;
    /* An fr column still floors at min-content, so a long name in a wider face
       could push the results grid out and take the map's width. It shrinks and
       ellipsises instead; scripts/qcm/layout.test.mjs measures the consequence. */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.qc-row .qc-state-col {
    color: var(--text-muted);
}

.qc-row .qc-pop {
    color: var(--text-muted);
    text-align: right;
}

/* No fill behind a score, in a table for the same reason there is none on a card:
   a county row carries five of them and the table paints ten rows, so a filled chip
   behind every figure emphasises nothing. The number is the content and stays bold;
   the band still arrives as the prefix glyph and the colour.
   scripts/qcm/type-scale.test.mjs measures what a score paints. */
.qc-score {
    font-size: 0.75rem;
    font-weight: 600;
    text-align: center;
}

/* Band is conveyed by the prefix glyph, not color alone (WCAG 1.4.1). */
.qc-score.high {
    color: #4ade80;
}
.qc-score.high::before {
    content: "\25B2 ";
}

.qc-score.medium {
    color: #fbbf24;
}
.qc-score.medium::before {
    content: "\25CF ";
}

.qc-score.low {
    color: #f87171;
}
.qc-score.low::before {
    content: "\25BC ";
}

.qc-metro {
    color: var(--text-subtle);
    font-size: 0.65rem;
}

/* Zero-result message (renderTable/renderCandidates empty state) */
.qc-empty {
    padding: 1rem 0.5rem;
    color: var(--text-muted);
    font-family: ui-monospace, monospace;
    font-size: 0.7rem;
    text-align: center;
}

/* Pagination */
.qc-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    padding: 0.5rem 0;
    font-family: var(--qc-font-ui);
    font-size: 0.8125rem;
    color: var(--text-muted);
}

.qc-pagination button {
    padding: 0.3rem 0.6rem;
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: 0.25rem;
    color: var(--text-muted);
    cursor: pointer;
    font-family: var(--qc-font-ui);
    font-size: 0.75rem;
}

.qc-pagination button:hover:not(:disabled) {
    border-color: var(--accent-500);
    color: var(--accent-400);
}

.qc-pagination button:disabled {
    opacity: 0.3;
    cursor: default;
}

/* Candidates Panel */
.qc-candidates-section {
    background: rgba(0,0,0,0.2);
    border: 1px solid rgba(255,255,255,0.05);
    border-radius: 0.5rem;
    padding: 0.5rem;
}

.qc-candidates-header {
    display: grid;
    grid-template-columns: 1.7fr 0.3fr 0.5fr 1.0fr 0.5fr 0.5fr 0.5fr;
    gap: 0.3rem;
    padding: 0.5rem 0.25rem;
    border-bottom: 1px solid rgba(255,255,255,0.1);
    font-weight: 600;
    font-size: 0.75rem;
    color: var(--text-muted);
    /* Was uppercase with 0.05em tracking. All-caps costs about a sixth of the
       width for no information, and at the readable size that overflowed the
       last heading off the right edge of the results column. Sentence case at
       12px is narrower than caps at 11px AND easier to read (F19). */
    letter-spacing: 0.01em;
    font-family: var(--qc-font-ui);
}

.qc-candidate-row {
    display: grid;
    grid-template-columns: 1.7fr 0.3fr 0.5fr 1.0fr 0.5fr 0.5fr 0.5fr;
    gap: 0.3rem;
    padding: 0.3rem 0.25rem;
    border-bottom: 1px solid rgba(255,255,255,0.03);
    align-items: center;
    font-family: ui-monospace, monospace;
    font-size: 0.7rem;
    cursor: pointer;
    transition: background 0.15s;
    border-radius: 0.25rem;
}

.qc-candidate-row:hover {
    background: rgba(255,255,255,0.03);
}

/* Trust Panel (now in modal) */
.qc-trust-panel-inner {
    padding: 1rem;
    background: rgba(255,255,255,0.02);
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: 0.5rem;
}

.qc-trust-row {
    display: flex;
    justify-content: space-between;
    padding: 0.25rem 0;
    border-bottom: 1px solid var(--border-subtle);
}

.qc-trust-row:last-child {
    border-bottom: none;
}

/* Single-column override: phone AND tablet.
   The threshold moved from 768px to 1023px with the two-column layout itself.
   Between the two the map measured 209px wide (audit F02) — narrower than the
   filter panel that overlays it — so that band belongs to the stacked layout. */
@media (max-width: 1023px) {
    .qc-app-header {
        align-items: flex-start;
        flex-direction: column;
        gap: 0.75rem;
    }
    .qc-app-nav {
        justify-content: space-between;
        width: 100%;
    }
    #qc-controls.glass-panel {
        position: relative;
        margin: 0;
        border-radius: 0;
        border-left: none;
        border-right: none;
    }
    /* The page owns vertical scrolling. The map still needs a resolved mobile
       height, and it carries that height itself now (below) rather than taking
       it from a fixed-height container. Viewport-relative, so it stays usable
       in landscape. */
    #qc-map-container {
        align-self: stretch;
        flex: 0 0 auto;
        height: auto;
        min-height: 0;
        position: relative;
        top: auto;
    }
    /* The map stops being an overlay here and becomes an ordinary block, so the
       print panel that shares this container flows BELOW it instead of under it.
       While the map was `absolute inset-0` at z-index 10 and the container had a
       fixed height, that panel was laid out inside the map's own box and painted
       beneath the Leaflet canvas: measurable, and impossible to tap. The container
       grows to hold both, and the map keeps its viewport-relative height here. */
    #qc-map {
        position: static;
        height: 60vh;
        min-height: 18rem;
    }
    /* A 260px tooltip centred on a control in the right-hand grid column hangs
       past the screen — the State tooltip reached 423px in a 390px viewport. Anchor
       every filter tooltip to its trigger's right edge, the same treatment the table
       headers already get, so no bubble can leave the screen whatever the column. */
    .qc-controls .qc-tooltip .qc-tooltip-text {
        left: auto;
        right: 0;
        transform: none;
        max-width: calc(100vw - 2.5rem);
    }
    .qc-controls .qc-tooltip .qc-tooltip-text::after {
        left: auto;
        right: 0.75rem;
        margin-left: 0;
    }
    /* Each row becomes a comparison card here, and no column is dropped.
       These tables used to hide columns 6+ (affordability, remoteness, sprawl)
       and candidate columns 4 and 6+ on a narrow screen — so a reader could set
       the affordability filter on a phone and never see the affordability
       figure. The measure you asked about was the one off screen (audit F19).
       Each cell carries its heading in `data-label`, written by labelCells() in
       quiet-corners.js from the table's own header row, so the label on a card
       cannot drift from the column it names. */
    .qc-log-header,
    .qc-candidates-header {
        display: none;
    }

    .qc-row,
    .qc-row.sprawl-visible,
    .qc-candidate-row {
        grid-template-columns: 1fr 1fr;
        gap: 0.2rem 0.75rem;
        padding: 0.5rem 0.6rem;
        margin-bottom: 0.4rem;
        border: 1px solid rgba(255,255,255,0.08);
        border-radius: 0.375rem;
    }

    /* The card's subject, on its own line above the measures that qualify it. */
    .qc-row .qc-name,
    .qc-candidate-row .qc-name {
        grid-column: 1 / -1;
        font-size: 0.9375rem;
        margin-bottom: 0.15rem;
    }

    /* Label left, value right, on one line per measure. `space-between` handles
       the two-item cells; the band glyph on a score is a ::before, so it takes
       an auto margin to travel to the right with the number it belongs to. */
    .qc-row > *:not(.qc-name),
    .qc-candidate-row > *:not(.qc-name) {
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        gap: 0.5rem;
        text-align: right;
    }

    .qc-row > *:not(.qc-name)::after,
    .qc-candidate-row > *:not(.qc-name)::after {
        content: attr(data-label);
        order: -1;
        flex: 0 0 auto;
        color: var(--text-muted);
        font-family: var(--qc-font-ui);
        font-size: 0.75rem;
        /* A score cell is weight 600 and its ::after inherits that, which made the
           label as loud as the number it names. */
        font-weight: 400;
        text-align: left;
    }

    .qc-row .qc-score::before,
    .qc-candidate-row .qc-score::before {
        margin-left: auto;
    }
}

/* Column-header tooltips open to the LEFT of their trigger, not centred on it.
   A results table is as wide as its container, so its last two column headers sit
   at the right edge and a 260px tooltip centred on them hangs 90px past the
   viewport. Right-aligning every header tooltip keeps all of them inside the
   table without a per-column rule, which a new column would have invalidated. */
.qc-log-header .qc-tooltip .qc-tooltip-text,
.qc-candidates-header .qc-tooltip .qc-tooltip-text {
    left: auto;
    right: 0;
    transform: none;
}
.qc-log-header .qc-tooltip .qc-tooltip-text::after,
.qc-candidates-header .qc-tooltip .qc-tooltip-text::after {
    left: auto;
    right: 0.75rem;
    margin-left: 0;
}

/* Horizontal overflow backstop.
   `clip`, not `hidden`: hidden would make this a scroll container, which breaks
   `position: sticky` inside it and lets a stray element scroll silently instead
   of being caught. clip creates no scroll container, so the page simply cannot
   scroll sideways — and preview-shots.sh now measures this page, so a regression
   is caught rather than discovered months later. Vertical scrolling is untouched
   (`overflow-y: visible` is implied by clip on one axis only). */
#dashboard {
    overflow-x: clip;
}

/* Place labels (audit F05). The dot is the position; the name sits to its right so
   the two never fight for the same pixel. Non-interactive by construction — these
   are context, and a name that swallows a click on the county under it is worse
   than no name. The dark halo is what keeps a light name legible where it crosses
   a county fill; there is no basemap under it below zoom 9 to do that job. */
.qc-place-label-wrap { background: none; border: 0; }

.qc-place-label {
    position: absolute;
    left: 5px;
    top: -0.55rem;
    white-space: nowrap;
    pointer-events: none;
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: 0.6875rem;
    line-height: 1.2;
    color: #dbe4f0;
    text-shadow:
        0 0 3px #0d0d1a, 0 0 3px #0d0d1a,
        1px 1px 0 #0d0d1a, -1px 1px 0 #0d0d1a,
        1px -1px 0 #0d0d1a, -1px -1px 0 #0d0d1a;
}

/* Orientation names (S4c). A large place is not one more town on the list -- it is
   what tells you WHERE the towns are -- so it reads a step brighter and wider than
   the names it frames, without becoming a second headline. */
.qc-place-label--nav {
    color: #f8fafc;
    letter-spacing: 0.04em;
    font-weight: 600;
}

/* Find a town, and get back to the country (audit F20).

   The map's own controls get a lane down the left edge. Every corner of this map
   is already an overlay panel, so the zoom buttons were measuring 30x30 at the top
   left and sitting UNDER the print panel -- visible by geometry, unclickable in
   fact. The panel is shifted clear of the lane in quiet-corners-maps.css. */
.qc-reset-wrap { background: none; }
.qc-reset-view {
    display: block; width: 30px; height: 30px; line-height: 30px;
    padding: 0; border: 0; cursor: pointer;
    background: #12121e; color: #dbe4f0;
    font-size: 1.05rem; text-align: center;
}
.qc-reset-view:hover { background: #1c1c2e; }
.qc-finder-toggle {
    display: block; width: 30px; height: 30px; line-height: 30px;
    padding: 0; border: 0; cursor: pointer;
    background: #12121e; color: #dbe4f0; font-size: 1.15rem; text-align: center;
}
.qc-finder-toggle:hover, .qc-finder-toggle[aria-expanded="true"] { background: #1c1c2e; }
/* The words the button carries are for a touch screen; on a desktop the control
   lane is 30px wide and the glyph plus a title is enough. */
.qc-finder-label { display: none; }
/* Opens over the map rather than inside the 30px lane, and above the panels, so a
   search that is open is a search you can type into. */
.qc-finder-box {
    position: absolute; width: 15rem; max-width: 60vw;
    /* Above the overlay panels (20), which is only possible because the JS
       re-homes this element outside #qc-map. */
    z-index: 30;
}
#qc-place-search {
    width: 100%; box-sizing: border-box;
    padding: 0.3rem 0.5rem; border: 1px solid #2a2a42; border-radius: 2px;
    background: #12121e; color: #dbe4f0;
    font-family: var(--font-mono, ui-monospace, monospace); font-size: 0.75rem;
}
#qc-place-search::placeholder { color: #7b8794; }
.qc-finder-results {
    margin-top: 2px; max-height: 13rem; overflow-y: auto;
    background: #12121e; border: 1px solid #2a2a42;
}
.qc-finder-hit {
    display: block; width: 100%; padding: 0.28rem 0.5rem;
    border: 0; background: none; cursor: pointer; text-align: left;
    color: #dbe4f0;
    font-family: var(--font-mono, ui-monospace, monospace); font-size: 0.72rem;
}
.qc-finder-hit:hover, .qc-finder-hit:focus-visible,
/* The arrow keys leave focus in the input, so the active row needs a highlight
   of its own -- there is no :focus on it to borrow. */
.qc-finder-hit.is-active { background: #23233a; }
/* Muted, and not a button: it is an answer, not something to press. */
/* The announcement region carries text for a screen reader and nothing for the
   eye -- the same sentence is already on screen in .qc-finder-empty. Clipped
   rather than display:none or visibility:hidden, both of which take an element
   out of the accessibility tree and would make the region announce nothing. */
.qc-finder-status {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

.qc-finder-empty {
    margin: 0; padding: 0.4rem 0.5rem;
    color: var(--text-muted, #8b93a7);
    font-family: var(--font-mono, ui-monospace, monospace); font-size: 0.72rem;
}

/* The finder on a touch screen (audit A09).

   Below 1024px the stylesheet said nothing about this control, so a phone got the
   desktop lane as it stands: a 30x30 icon button whose only visible content is the
   glyph, opening a box whose text is 12px. Searching the map meant spotting a
   symbol and hitting it. This block has to come after the desktop rules above
   rather than joining the earlier phone block, because these selectors are no more
   specific than the ones they override and source order is what decides.

   44px is this product's touch-target goal. It is not the WCAG AA minimum — that
   criterion asks for 24px, and 44px is the AAA figure. */
@media (max-width: 1023px) {
    .qc-finder-toggle {
        display: flex;
        align-items: center;
        gap: 0.4rem;
        width: auto;
        height: 44px;
        line-height: 1;
        padding: 0 0.75rem;
    }
    .qc-finder-label {
        display: inline;
        font-family: var(--qc-font-ui);
        font-size: 0.875rem;
        white-space: nowrap;
    }
    /* Wider than the desktop popover, because there is more room across a phone
       than beside a button, and a town plus its state has to fit on one line. */
    .qc-finder-box {
        width: 17rem;
        max-width: calc(100vw - 3rem);
    }
    /* 16px, and this is not a style choice: mobile Safari zooms the whole page when
       a focused input's text is under 16px, which throws the reader out of the map
       the search exists to move around. */
    #qc-place-search {
        padding: 0.6rem 0.7rem;
        font-size: 1rem;
    }
    .qc-finder-results { max-height: 15rem; }
    /* A suggestion row is a thumb target too — picking the wrong town is the same
       kind of failure as failing to open the search at all. */
    .qc-finder-hit {
        display: flex;
        align-items: center;
        min-height: 44px;
        padding: 0.5rem 0.7rem;
        font-size: 0.875rem;
    }
    .qc-finder-empty {
        padding: 0.7rem;
        font-size: 0.875rem;
    }
}

/* The filters, the pagers and the map's own buttons, as a thumb meets them
   (audit F06).

   Same 44px goal, and the same reason this block is last: these selectors are
   no more specific than the desktop rules above, so source order decides.

   A range input keeps drawing the thin track it always drew — it is the BOX
   that grows, and Chromium centres the track in it — so a 16px slider becomes
   a 44px target without becoming a fat bar on screen. A select given a
   min-height does not change its font either; the font-size below is the one
   that does, and it is there because mobile Safari zooms the page whenever a
   focused field's text is under 16px. */
@media (max-width: 1023px) {
    .qc-controls select,
    .qc-controls input[type="number"] {
        min-height: 44px;
        font-size: 1rem;
    }
    .qc-slider { min-height: 44px; }
    .qc-pagination button {
        min-width: 44px;
        min-height: 44px;
    }
    /* Leaflet's own `.leaflet-touch .leaflet-bar a` is (0,2,1) and fixes these at
       30x30, so `!important` is what reaches them — the same treatment the colour
       overrides earlier in this file already needed. 44px is the width the finder
       button in this lane has used since #470, so the lane does not change. */
    .leaflet-control-zoom a {
        width: 44px !important;
        height: 44px !important;
        line-height: 44px !important;
    }
    .qc-reset-view {
        width: 44px;
        height: 44px;
        line-height: 44px;
    }

    /* The per-table sort picker and its Reverse button. They sit beside the sort
       note rather than inside `.qc-controls`, so the rule at the top of this block
       does not reach them -- and a reader who cannot tap the control that re-ranks
       the table is back where audit F01 found them. Matched on the data attributes
       the JS already binds to, so the hook is the same one the behaviour uses. */
    [data-sort-control],
    [data-sort-dir] {
        min-height: 44px;
        font-size: 1rem;
    }
}
