/* ============================================================
   AssemblySite — Unified Stylesheet
   Merged from: site.css + dc.css + Blazor defaults
   Dark theme native | Mobile-first
   ============================================================ */

/* ── CSS Custom Properties (VDC theme — SP Red+Green) ── */
:root {
    /*  Corporate light. Same variable NAMES as the dark campaign palette this
        replaced, so the ~2,900 var() call sites did not have to change.  */
    --bg: #ffffff; --bg2: #161920; --bg3: #1f232c; --bg4: #232833;
    --border: #232833; --border2: #333b49;
    --text: #f5f7fa; --muted: #7c8494; --muted2: #aeb6c2;

    /*  Semantic hues are darkened against the dark-theme originals: #4caf50 on
        white is 2.8:1 and fails AA, #15803d is 4.6:1 and passes. Every value
        below clears 4.5:1 on --bg.  */
    --green: #15803d; --green-dim: rgba(21,128,61,0.10);
    --red: #b91c1c; --red-dim: rgba(185,28,28,0.10);
    --amber: #b45309; --amber-dim: rgba(180,83,9,0.10);
    --blue: #1d4ed8; --blue-dim: rgba(29,78,216,0.10);
    --cyan: #0e7490;
    --purple: #6d28d9; --purple-dim: rgba(109,40,217,0.10);

    /*  One accent across all seven sites: the product reads as one system, and
        users move between assembly/teams/booth on the same credentials.  */
    --primary: #1e40af;
    --accent: #2563eb; --accent-dim: rgba(37,99,235,0.10);
    --on-accent: #ffffff;

    --shadow-sm: 0 1px 2px rgba(15,23,42,0.06);
    --shadow: 0 1px 3px rgba(15,23,42,0.10), 0 1px 2px rgba(15,23,42,0.06);
    --shadow-lg: 0 10px 24px rgba(15,23,42,0.10);

    --mono: 'JetBrains Mono', ui-monospace, monospace;
    --sans: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --radius: 8px}

/* ── Reset & Base ── */
* { box-sizing: border-box}
html, body { margin:0; padding:0; background:#161920; color:var(--text); font-family:var(--sans); font-size:16px; line-height:1.4}
h1, h2, h3 { margin:0 0 8px}
h2 { font-size:20px}
h3 { font-size:16px}
a { color:var(--cyan); text-decoration:none !important}
a:hover { text-decoration:none !important}

/* ── Blazor framework styles ── */
.content { padding-top:1.1rem}
h1:focus { outline:none}
.valid.modified:not([type=checkbox]) { outline:1px solid #15803d}
.invalid { outline:1px solid #b91c1c}
.validation-message { color:#ff5c77}
.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem; color: white}
.blazor-error-boundary::after { content: "An error has occurred."}
.darker-border-checkbox.form-check-input { border-color: #333b49}
.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder { color: var(--bs-secondary-color, var(--muted)); text-align: end}
.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder { text-align: start}

/* ── Topbar ── */
.topbar { display:flex; justify-content:space-between; align-items:center; padding:10px 16px; color:var(--text); background:linear-gradient(90deg,#161920,#161920); border-bottom:1px solid rgba(180,83,9,.25); position:sticky; top:0; z-index:50; box-shadow:0 2px 12px rgba(0,0,0,.35)}
.topbar .brand { font-family:var(--mono); font-size:13px; font-weight:800; letter-spacing:.18em; color:#fbbf24; text-transform:uppercase; text-shadow:0 0 14px rgba(180,83,9,.4)}
.topbar a, .topbar span { color:var(--muted2); font-size:13px}
.topbar a { text-decoration:none}
.topbar .logout-btn { display:inline-block; padding:7px 16px; background:rgba(255,255,255,0.072); color:#f5f7fa; border:1px solid rgba(255,255,255,0.216); border-radius:8px; font-size:13px; font-weight:600; text-decoration:none; transition:all .15s; cursor:pointer}
.topbar .logout-btn:hover { background:rgba(180,83,9,.85); border-color:rgba(180,83,9,.95); transform:translateY(-1px)}


/* ── Top-bar actions: layout switcher + Logout ──
   The two share a row and must not push the brand off the bar on a 360px phone.
   The select is styled rather than left native because the default control renders
   light-on-light inside a dark WebView on Android and is effectively invisible. */
.topbar-actions { display:flex; align-items:center; gap:8px; flex-shrink:0}
.topbar-actions form { margin:0; display:flex}
.topbar .view-switch {
    appearance:none; -webkit-appearance:none;
    padding:7px 26px 7px 10px;
    background-color:rgba(255,255,255,0.072);
    /* Chevron drawn in the background so no extra element is needed; it is a data
       URI because the app must work with no network in the field. */
    background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5' stroke='%23cbd5e1' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat:no-repeat; background-position:right 8px center; background-size:12px;
    color:#f5f7fa; border:1px solid rgba(255,255,255,0.216); border-radius:8px;
    font-family:inherit; font-size:13px; font-weight:600; line-height:1.2;
    cursor:pointer; transition:all .15s}
.topbar .view-switch:hover { border-color:rgba(180,83,9,.7)}
.topbar .view-switch:focus-visible { outline:2px solid rgba(180,83,9,.9); outline-offset:2px}
/* The popup list is native and inherits the page, not the control, so the option
   colours are set explicitly or they come out unreadable on a dark theme. */
.topbar .view-switch option { background:#161920; color:#f5f7fa}

@media (max-width:480px) {
    .topbar-actions { gap:6px}
    .topbar .view-switch { padding:5px 22px 5px 8px; font-size:12px; background-position:right 6px center}
}
@media (max-width:360px) {
    .topbar .view-switch { padding:5px 20px 5px 6px; font-size:11px}
}

/* Layer-specific brand colors */
/* The separator is dimmed so "AC 190" and "Booth 10" read as two facts rather
   than one run-on string. Letter-spacing tightens below 380px: the bar now carries
   roughly twice the characters it used to and has to share the row with Logout. */
.topbar .brand .brand-sep { opacity:.45; margin:0 2px}
@media (max-width:380px) {
    .topbar .brand { letter-spacing:.08em; font-size:12px}
}
.topbar.layer-state         .brand { color:#fbbf24; text-shadow:0 0 16px rgba(161,98,7,.35)}
.topbar.layer-district      .brand { color:#c4b5fd; text-shadow:0 0 16px rgba(109,40,217,.35)}
.topbar.layer-parliamentary .brand { color:#60a5fa; text-shadow:0 0 16px rgba(29,78,216,.35)}
.topbar.layer-assembly      .brand { color:#ff5c77; text-shadow:0 0 16px rgba(185,28,28,.35)}
.topbar.layer-sector        .brand { color:#3fd37e; text-shadow:0 0 16px rgba(21,128,61,.35)}
.topbar.layer-booth         .brand { color:#fbbf24; text-shadow:0 0 16px rgba(180,83,9,.35)}

@media (max-width: 480px) {
    .topbar { padding:10px 12px}
    .topbar .brand { font-size:12px; letter-spacing:.10em}
    .topbar .logout-btn { padding:5px 12px; font-size:12px}
}

/* ── Main content ── */
.main { padding:16px; padding-bottom:60px; max-width:700px; margin:0 auto}

/* ── Bottom navigation ──────────────────────────────────────────────────────
   Four tabs plus a raised centre action.

   Notes:
     * Every rule is scoped to .bb-tab, never to `.bottombar a`. The centre
       button is an <a> too, so a bare `.bottombar a.active { background:accent }`
       painted an amber rectangle behind its circle.
     * Active state is the accent colour plus a 2px top indicator, not a filled
       block: at 375px a filled tab reads as an error state next to red alerts.
     * padding-bottom carries env(safe-area-inset-bottom) for the MAUI WebView on
       notched phones, so the bar is not overlapped by the home indicator.
     * the centre button is a fixed in-bar circle, sized to the bar height — it
       does not overflow, so the bar needs no overflow:visible.
   ───────────────────────────────────────────────────────────────────────────*/
.bottombar {
    position: fixed; bottom: 0; left: 0; right: 0; z-index: 10;
    display: flex; align-items: stretch;
    background: rgba(255,255,255,.96);
    -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
    border-top: 1px solid var(--accent-dim);
    padding-bottom: env(safe-area-inset-bottom, 0)}

.bb-tab {
    position: relative;
    flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    padding: 9px 2px 8px;
    text-decoration: none; color: var(--muted);
    transition: color .15s ease;
    -webkit-tap-highlight-color: transparent}
.bb-tab svg { width: 22px; height: 22px; display: block}
.bb-lbl {
    font-family: var(--mono); font-size: 9.5px; font-weight: 700;
    letter-spacing: .08em; text-transform: uppercase;
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap}
.bb-tab.active { color: var(--accent)}
.bb-tab.active::before {
    content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
    width: 26px; height: 2px; border-radius: 0 0 3px 3px; background: var(--accent)}
.bb-tab:active { color: var(--accent); opacity: .7}

/* Centre action — a FIXED round button inside the bar, not a floating action
   button. It occupies the middle of five slots and is sized to fit the bar
   height, so nothing overflows. */
.bb-fab {
    flex: 1 1 0; min-width: 0;
    display: flex; align-items: center; justify-content: center;
    padding: 6px 2px; text-decoration: none;
    -webkit-tap-highlight-color: transparent}
.bb-fab-ring {
    width: 42px; height: 42px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: linear-gradient(150deg, var(--amber) 0%, var(--accent) 55%, var(--green) 100%);
    border: 1px solid var(--border2);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .4);
    transition: transform .15s ease, box-shadow .15s ease}
.bb-fab:active .bb-fab-ring { transform: scale(.92)}
.bb-fab.active .bb-fab-ring { box-shadow: 0 0 0 2px var(--accent), 0 2px 10px var(--amber-dim)}

/* "AI" wordmark rather than a glyph — at this size two letters read instantly
   where a spark or star reads as decoration. Dark ink, not white: this app's
   ring is amber-led and is a light surface, unlike the green-led sibling's. */
.bb-fab-txt {
    font-family: var(--mono); font-size: 14px; font-weight: 800;
    letter-spacing: .06em; color: #0a120a; line-height: 1}

/* ── Page header ── */
.page-header { display:flex; align-items:flex-start; justify-content:space-between; margin-bottom:24px; gap:16px}
.page-title { font-family:var(--mono); font-size:17px; font-weight:700; color:var(--text)}
.page-subtitle { font-size:13px; color:var(--muted); margin-top:3px}
.page-actions { display:flex; gap:8px; align-items:center; flex-shrink:0; flex-wrap:wrap}

/* ── Page header decoration ──
   An accent bar keys the page family (green /voters, cyan /booth, purple AI) and
   the icon repeats the tile that linked here, so a page is recognisable before
   its title is read. The bar is a ::before rather than a border-left because
   .page-header is a flex row and a border would shift the actions to the right. */
.page-header { position:relative; padding-left:14px}
.page-header::before {
    content:""; position:absolute; left:0; top:2px; bottom:2px; width:3px;
    border-radius:2px; background:var(--ph-accent, var(--green))}
.page-header.ph-booth  { --ph-accent:var(--cyan)}
.page-header.ph-ai     { --ph-accent:var(--purple)}
.page-header.ph-voters { --ph-accent:var(--green)}
.page-hd { display:flex; align-items:flex-start; gap:11px; min-width:0}
/* The hub tiles draw these paths with stroke="var(--accent)", so the icon takes
   the page family colour rather than shipping its own. Sized to sit with the
   22px title without becoming the loudest thing in the header. */
.page-icon-svg { width:26px; height:26px; flex-shrink:0; margin-top:3px;
    --accent:var(--cyan); opacity:.9}
@media(max-width:560px){ .page-icon-svg { width:22px; height:22px} }
.page-icon { width:30px; height:30px; flex-shrink:0; margin-top:1px; object-fit:contain; opacity:.92}
@media(max-width:560px){ .page-icon{ width:24px; height:24px} .page-header{ padding-left:11px} }

/* ── Section header ── */
.section-hdr { font-family:var(--mono); font-size:9px; letter-spacing:.18em; color:var(--muted); text-transform:uppercase; border-bottom:1px solid var(--border); padding-bottom:7px; margin-bottom:14px}

/* ── Stat cards ── */
.cards-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(155px,1fr)); gap:10px; margin-bottom:24px}
.col-span-2 { grid-column:span 2}
.cards-grid-8 { display:grid; grid-template-columns:repeat(8,minmax(0,1fr)); gap:10px; margin-bottom:24px}
.cards-grid-7 { display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); gap:10px; margin-bottom:24px}
@media(max-width:700px){ .cards-grid-8,.cards-grid-7{grid-template-columns:repeat(2,minmax(0,1fr))} }

.stat-card { background:var(--bg2); border:1px solid var(--border); border-radius:var(--radius); padding:14px 16px; position:relative; overflow:hidden}
.stat-card.c-green  { border-top:2px solid var(--green)}
.stat-card.c-red    { border-top:2px solid var(--red)}
.stat-card.c-amber  { border-top:2px solid var(--amber)}
.stat-card.c-blue   { border-top:2px solid var(--blue)}
.stat-card.c-cyan   { border-top:2px solid var(--cyan)}
.stat-card.c-purple { border-top:2px solid var(--purple)}
.stat-card.c-gray   { border-top:2px solid #333b49}

.card-label { font-family:var(--mono); font-size:9px; letter-spacing:.12em; color:var(--muted); text-transform:uppercase; margin-bottom:8px}
.card-value { font-family:var(--mono); font-size:24px; font-weight:700; line-height:1; margin-bottom:5px}
.c-green  .card-value { color:var(--green)}
.c-red    .card-value { color:var(--red)}
.c-amber  .card-value { color:var(--amber)}
.c-blue   .card-value { color:var(--blue)}
.c-cyan   .card-value { color:var(--cyan)}
.c-purple .card-value { color:var(--purple)}
.c-gray   .card-value { color:#aeb6c2}
.card-sub { font-family:var(--mono); font-size:10px; color:var(--muted)}

/* ── Progress bar ── */
.progress-box { background:var(--bg2); border:1px solid var(--border); border-radius:var(--radius); padding:18px 22px; margin-bottom:24px}
.progress-top { display:flex; justify-content:space-between; align-items:baseline; margin-bottom:10px}
.progress-pct { font-family:var(--mono); font-size:18px; font-weight:700; color:var(--green)}
.progress-meta { font-family:var(--mono); font-size:10px; color:var(--muted)}
.prog-track { height:8px; background:var(--bg4); border-radius:4px; overflow:hidden; display:flex; gap:1px}
.prog-seg { height:100%; border-radius:3px; transition:width .5s ease}
.seg-done { background:var(--green)} .seg-fail { background:var(--red)} .seg-nf { background:var(--border2)} .seg-ip { background:var(--amber)}
.prog-legend { display:flex; flex-wrap:wrap; gap:14px; margin-top:8px}
.leg { display:flex; align-items:center; gap:5px; font-family:var(--mono); font-size:10px; color:var(--muted)}
.leg-dot { width:7px; height:7px; border-radius:2px; flex-shrink:0}

/* ── Two-column layout ── */
.two-col { display:grid; grid-template-columns:1fr 1fr; gap:18px; margin-bottom:24px}
@media(max-width:1050px){ .two-col{grid-template-columns:1fr} }

/* ── Tables ── */
.table-wrap { background:var(--bg2); border:1px solid var(--border); border-radius:var(--radius); overflow:hidden; margin-bottom:24px}
.data-table { width:100%; border-collapse:collapse; font-size:12px}
.data-table th { background:var(--bg3); color:var(--muted); font-family:var(--mono); font-size:9px; letter-spacing:.1em; text-transform:uppercase; padding:8px 12px; text-align:left; border-bottom:1px solid var(--border); white-space:nowrap}
.data-table td { padding:8px 12px; border-bottom:1px solid var(--border); color:var(--muted2); font-family:var(--mono); font-size:11px}
.data-table tr:last-child td { border-bottom:none}
.data-table tr:hover td { background:var(--bg3)}
.data-table a { color:var(--cyan); text-decoration:none}
.data-table a:hover { text-decoration:underline}
.data-table th.sortable { cursor:pointer}
.data-table th.sortable:hover { color:var(--text)}

/* ── Bar chart (CSS only) ── */
.bar-chart { display:flex; flex-direction:column; gap:6px}
.bar-row { display:flex; align-items:center; gap:10px; font-family:var(--mono); font-size:10px}
.bar-label { width:80px; color:var(--muted); text-align:right; flex-shrink:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.bar-track { flex:1; height:18px; background:var(--bg4); border-radius:3px; overflow:hidden; position:relative}
.bar-fill { height:100%; border-radius:3px; transition:width .4s ease; display:flex; align-items:center; justify-content:flex-end; padding-right:6px; font-size:9px; font-weight:700}
.bar-value { width:60px; text-align:right; flex-shrink:0; color:var(--muted2)}
.bar { background:var(--green)}

/* ── PDA-G palette ── */
.pdag-p { color:var(--amber)} .pdag-d { color:var(--blue)} .pdag-a { color:var(--green)} .pdag-g { color:var(--purple)}
.bg-pdag-p { background:var(--amber)} .bg-pdag-d { background:var(--blue)} .bg-pdag-a { background:var(--green)} .bg-pdag-g { background:var(--purple)}
.pdag-tile { display:flex; flex-direction:column; align-items:center; justify-content:center}
.pdag-letter { font-size:40px; font-weight:800; font-family:var(--sans); line-height:1; margin-bottom:4px}
/* Count under a PDA-G tile. Muted rather than coloured: the letter is
   already carrying the colour, and two coloured things in one tile fight. */
.pdag-tile .pdag-n { display:block; margin-top:2px; font-family:var(--mono);
    font-size:12px; color:var(--muted)}
.pdag-p .pdag-letter { color:var(--amber)}
.pdag-d .pdag-letter { color:var(--blue)}
.pdag-a .pdag-letter { color:var(--green)}
.pdag-g .pdag-letter { color:var(--purple)}

/* ── Badges ── */
.badge { display:inline-block; font-family:var(--mono); font-size:8px; font-weight:700; letter-spacing:.06em; padding:2px 5px; border-radius:3px}
.badge-done    { background:var(--green-dim); color:var(--green)}
.badge-failed  { background:var(--red-dim); color:var(--red)}
.badge-ip      { background:var(--amber-dim); color:var(--amber)}
.badge-pending { background:var(--bg4); color:var(--muted2)}

/* ── Buttons ── */
.btn { display:inline-block; padding:10px 20px; background:var(--bg3); border:1px solid var(--border2); color:var(--text); font-family:var(--mono); font-size:11px; border-radius:var(--radius); text-decoration:none; cursor:pointer}
.btn:active, .btn:hover { background:var(--bg4)}
.page-actions .btn { font-size:10px; padding:5px 12px; white-space:nowrap}
.page-actions .btn-green { background:var(--green-dim); color:var(--green); border-color:var(--green)}
.pill { background:var(--green-dim); color:var(--green)}

/* Bootstrap overrides for dark theme */
.btn-primary { background-color:var(--green); border-color:var(--green); color:var(--on-accent)}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { box-shadow:0 0 0 0.1rem rgba(15,23,42,.1), 0 0 0 0.25rem rgba(21,128,61,.4)}

/* ── Cards ── */
.card { background:var(--bg2); border:1px solid var(--border); color:var(--text); border-radius:8px; padding:16px; margin-bottom:12px; box-shadow:0 1px 4px rgba(0,0,0,.18)}

/* ── Forms ── */
input[type=text], input[type=password], input[type=email], input[type=number], textarea, select {
    width:100%; box-sizing:border-box; padding:10px 12px; font-size:14px; font-family:var(--sans);
    background:var(--bg3); color:var(--text); border:1px solid var(--border2); border-radius:var(--radius); outline:none; transition:border-color .15s, box-shadow .15s}
input:focus, textarea:focus, select:focus { border-color:var(--cyan); box-shadow:0 0 0 2px rgba(14,116,144,.15)}
textarea { min-height:60px; resize:vertical}
select { cursor:pointer; appearance:none; -webkit-appearance:none;
    background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14'><path d='M3 5 L7 9 L11 5' stroke='%2394a3b8' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
    background-repeat:no-repeat; background-position:right 12px center; padding-right:34px}
.form-row { margin-bottom:14px}
.form-row label { display:block; font-family:var(--mono); font-size:11px; font-weight:600; color:var(--muted2); letter-spacing:.06em; text-transform:uppercase; margin-bottom:5px}

.edit-actions { display:flex; align-items:center; gap:10px; flex-wrap:wrap}
.btn-save { background:var(--green)!important; color:var(--on-accent)!important; border-color:var(--green)!important; font-weight:700; padding:10px 28px!important; font-size:13px!important}
.btn-save:hover { opacity:.9}
.btn-cancel { background:var(--bg3)!important; color:var(--muted2)!important; border:1px solid var(--border2)!important; padding:10px 22px!important; font-size:13px!important}
.btn-cancel:hover { background:var(--bg4)!important; color:var(--text)!important}

/* ── Status messages ── */
.err { color:var(--red); font-weight:600}
.ok  { color:var(--green); font-weight:600}
.err-text { color:var(--red)!important; font-size:10px}

/* ── Voter cards ── */
/* .voter-card rules live in the "Voter list card" block near the end of this
   file. Two superseded sets stood here: the original, plus its unused
   .vc-layout / .vc-srno / .vc-content / .vc-row1 / .vc-en two-column layout
   that no component references; and a later slate-palette override
   (#1a1f2e / #94a3b8 / #4a9eff / #ff6b9d) belonging to no part of this app,
   which won on order and is why the list rendered flat and off-theme. */
.vc-edit:hover { background:rgba(180,83,9,.20); border-color:var(--amber); box-shadow:0 0 8px rgba(180,83,9,.2)}

.vc-row2 { display:flex; align-items:center; gap:8px; margin-bottom:5px; font-size:12px}
.vc-sur { color:var(--muted2); font-weight:500}
.vc-vid { font-family:var(--mono); font-size:10px; color:var(--green); letter-spacing:.03em; padding:2px 7px; background:rgba(21,128,61,.10); border:1px solid rgba(21,128,61,.15); border-radius:4px; font-weight:600}

.vc-row3 { display:flex; flex-wrap:wrap; gap:0; font-family:var(--mono); font-size:11px; color:var(--muted2); line-height:1.3}
.vc-row3 span { white-space:nowrap}
.vc-row3 span::after { content:"\00b7"; margin:0 5px; color:var(--muted); opacity:.4}
.vc-row3 span:last-child::after { display:none}
.vc-row3 .vc-ph { color:var(--green)}
.vc-row3 .vc-pty { color:var(--blue); font-weight:700}

/* ── Tile grid ── */
.tile-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:14px; padding:16px}
.tile { background:var(--bg2); border:1px solid var(--border); border-radius:12px; padding:16px; text-align:center; text-decoration:none; color:var(--text); box-shadow:0 2px 8px rgba(0,0,0,.25); transition:transform .15s,box-shadow .15s,border-color .15s}
.tile:active { transform:scale(0.97)}
.tile:hover { box-shadow:0 4px 20px rgba(0,0,0,.35); border-color:var(--border2)}
.tile img { width:80px; height:80px; object-fit:contain}
/* Booth-hub tiles carry a live count, so their icon is smaller than the 80px
   the voter-list tiles use and sits above the label rather than dominating. */
.tile .tile-ico { width:44px; height:44px; margin-bottom:6px}

.tile h5 { margin:8px 0 0; font-size:14px; font-weight:600; color:var(--muted2)}

/* ── Picker bar ── */
.pdc-picker { display:grid; grid-template-columns:auto 1fr; gap:10px; align-items:center; width:100%; margin:14px 0 22px}
@media(max-width:600px){ .pdc-picker{grid-template-columns:1fr} }
.pdc-picker-lbl { font-family:var(--mono); font-size:11px; color:var(--muted2); text-transform:uppercase; letter-spacing:.14em; font-weight:700}
.pdc-picker-sel {
    appearance:none; -webkit-appearance:none; cursor:pointer; width:100%; max-width:420px;
    background-color:var(--bg3);
    background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14'><path d='M3 5 L7 9 L11 5' stroke='%2394a3b8' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
    background-repeat:no-repeat; background-position:right 12px center;
    color:var(--text); border:1px solid var(--border2); border-radius:8px;
    padding:9px 34px 9px 12px; font-family:var(--mono); font-size:13px; font-weight:500}
.pdc-picker-sel:focus { outline:none; border-color:#0e7490; box-shadow:0 0 0 2px rgba(14,116,144,.18)}
.pdc-picker-sel option { background:var(--bg2); color:var(--text)}

/* ── Geo bar ── */
.geo-bar { display:flex; flex-wrap:wrap; gap:10px; align-items:center; margin:14px 0 22px}
.geo-bar select { appearance:none; -webkit-appearance:none; background:var(--bg3); color:var(--text); border:1px solid var(--border2); border-radius:8px; padding:9px 12px; font-family:var(--mono); font-size:13px; font-weight:500}

/* ── Booth Picker ── */
.bp-bar { display:flex; align-items:center; gap:10px; padding:10px 14px; margin:0 0 12px; background:linear-gradient(135deg,#161920,#161920); border:1px solid rgba(21,128,61,0.10); border-radius:var(--radius); flex-wrap:wrap}
.bp-lbl { font-family:var(--mono); font-size:10px; font-weight:600; color:var(--muted); text-transform:uppercase; letter-spacing:.08em; white-space:nowrap}
.bp-sel { flex:1; min-width:140px; padding:8px 30px 8px 10px; font-family:var(--mono); font-size:12px; background:var(--bg3); color:var(--text); border:1px solid var(--border2); border-radius:var(--radius); outline:none; appearance:none; -webkit-appearance:none; cursor:pointer; background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14'><path d='M3 5 L7 9 L11 5' stroke='%2394a3b8' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>"); background-repeat:no-repeat; background-position:right 10px center}
.bp-sel:focus { border-color:var(--cyan); box-shadow:0 0 0 2px rgba(14,116,144,.15)}

/* ── Search box ── */
.search-box { background:var(--bg3); border:1px solid var(--border2); color:var(--text); font-family:var(--mono); font-size:12px; padding:7px 12px; border-radius:var(--radius); outline:none; width:260px}
.search-box:focus { border-color:var(--cyan)}

/* ── Filter bar ── */
.filter-header { font-family:var(--mono); font-size:15px; font-weight:700; color:var(--text); padding:14px 0 4px; letter-spacing:.02em}
.filter-bar { display:flex; align-items:center; gap:10px; padding:10px 14px; margin:6px 0 10px; background:linear-gradient(135deg,#161920,#161920); border:1px solid rgba(21,128,61,0.10); border-radius:var(--radius); flex-wrap:wrap; box-shadow:0 2px 10px rgba(0,0,0,.3)}
.filter-bar label { font-family:var(--mono); font-size:10px; font-weight:600; color:var(--muted); text-transform:uppercase; letter-spacing:.08em; white-space:nowrap}
.filter-bar select, .filter-bar input[type=text] { flex:1; min-width:120px; padding:9px 12px; font-family:var(--mono); font-size:13px; background:var(--bg3); color:var(--text); border:1px solid var(--border2); border-radius:var(--radius); outline:none; transition:border-color .15s,box-shadow .15s; appearance:none; -webkit-appearance:none}
.filter-bar select { background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14'><path d='M3 5 L7 9 L11 5' stroke='%2394a3b8' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>"); background-repeat:no-repeat; background-position:right 10px center; padding-right:30px; cursor:pointer}
.filter-bar select:focus, .filter-bar input[type=text]:focus { border-color:var(--cyan); box-shadow:0 0 0 2px rgba(14,116,144,.15)}
.filter-bar .btn { background:var(--green); color:var(--on-accent); border:none; padding:9px 18px; font-family:var(--mono); font-size:11px; font-weight:700; border-radius:var(--radius); cursor:pointer; letter-spacing:.04em; text-transform:uppercase; transition:opacity .15s}
.filter-bar .btn:hover { opacity:.85}
.filter-results-count { font-family:var(--mono); font-size:12px; color:var(--muted2); padding:4px 0 8px; letter-spacing:.02em}

/* ── Letter picker ── */
.letter-picker { display:flex; flex-wrap:wrap; gap:4px; padding:8px 16px}
.letter-picker a { padding:4px 8px; background:var(--bg3); color:var(--muted2); border:1px solid var(--border); text-decoration:none; border-radius:4px; font-size:12px}
.letter-picker a.active { background:#b45309; color:#f5f7fa; border-color:#b45309; font-weight:700}

/* ── Surname Cloud ── */
.surname-cloud { display:flex; flex-wrap:wrap; gap:6px 10px; align-items:baseline; padding:14px; margin:8px 0 12px; background:linear-gradient(135deg,#161920,#161920); border:1px solid rgba(21,128,61,0.10); border-radius:var(--radius); line-height:1.6; box-shadow:0 2px 10px rgba(0,0,0,.3)}
.sc-tag { text-decoration:none; color:var(--muted2); font-family:var(--sans); transition:color .15s; white-space:nowrap}
.sc-tag:hover { color:var(--accent)}
.sc-tag.sc-active { color:var(--accent); font-weight:700}
.sc-tag .sc-cnt { font-size:inherit; color:var(--muted); font-family:var(--mono); font-weight:400}

/* ── Pager ── */
.pager { display:flex; flex-wrap:wrap; gap:4px; justify-content:center; padding:16px 0; margin:12px 0}
.pager-btn { display:inline-flex; align-items:center; justify-content:center; min-width:36px; height:36px; padding:0 10px; border-radius:var(--radius); font-size:13px; font-weight:600; color:var(--text); background:var(--bg2); border:1px solid var(--border); text-decoration:none; cursor:pointer; transition:background .15s,border-color .15s}
.pager-btn:hover { background:var(--bg3); border-color:var(--border2)}
.pager-btn.active { background:var(--accent); color:var(--on-accent); border-color:var(--accent)}
.pager-btn.disabled { opacity:.35; cursor:default; pointer-events:none}
.pager-dots { display:inline-flex; align-items:center; padding:0 4px; color:var(--muted)}

/* ── Back link ── */
.back-link { display:inline-block; margin-top:16px; font-family:var(--mono); font-size:12px; color:var(--cyan); text-decoration:none; padding:6px 0}
.back-link:hover { text-decoration:underline}

/* ── Text helpers ── */
.text-muted  { color:var(--muted)}
.text-muted2 { color:var(--muted2)}
.text-green  { color:var(--green)}
.text-red    { color:var(--red)}
.text-amber  { color:var(--amber)}
.text-blue   { color:var(--blue)}
.text-cyan   { color:var(--cyan)}
.text-purple { color:var(--purple)}
.mono { font-family:var(--mono)}
.mb24 { margin-bottom:24px}

/* ── Stat grid ── */
.stat-grid > div { color:var(--muted2)}

/* ── Login page ── */

/* Booth layer theme */
body.layer-booth { --layer-primary: #1d4ed8; --layer-secondary: #1d4ed8}

/* Voter Card */
/* Pager */
.pager{display:flex;align-items:center;justify-content:center;gap:12px;padding:12px}
.pager button{background:var(--bg2,#161920);border:1px solid rgba(255,255,255,0.12);color:#f5f7fa;padding:6px 16px;border-radius:6px;cursor:pointer}
.pager button:disabled{opacity:.4;cursor:default}
.pager .pager-info{font-size:13px;color:#aeb6c2}
/* Booth Picker */
.booth-picker select{width:100%;padding:8px 12px;background:var(--bg2,#161920);border:1px solid rgba(255,255,255,0.12);border-radius:8px;color:#f5f7fa;font-size:14px}


/* ── Account login card (/Account/Login) ──
   Phone-first: these apps render inside a MAUI WebView, so the card stays
   narrow, tap targets stay >= 44px and the inputs stay at 16px so iOS does
   not zoom the viewport on focus. Accent is set per app on .login-card. */
/*  Centred both ways. This was align-items:flex-start, which pinned the card
    to the top of a full-height flex container - horizontally centred, but
    sitting under the topbar with the rest of the viewport empty below it.
    The six projects that style .login-wrap inline in Login.razor already
    centre it this way; these six were the odd ones out.

    min-height uses svh where supported so the card does not jump when a
    mobile browser's address bar collapses; vh is the fallback.            */
.login-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 80vh;
    min-height: 80svh;
    padding: 24px 16px}
.login-card { --login-accent:var(--accent); --login-ink:var(--on-accent); width:100%; max-width:360px; background:var(--bg2); border:1px solid var(--border2); border-radius:14px; padding:24px 20px; box-shadow:0 12px 32px rgba(0,0,0,.45)}
.login-header { text-align:center; margin-bottom:20px}
.login-logo { font-size:40px; line-height:1; margin-bottom:8px}
.login-title { font-family:var(--mono); font-size:20px; font-weight:800; letter-spacing:.10em; text-transform:uppercase; color:var(--login-accent, var(--accent)); margin:0 0 4px}
.login-sub { color:var(--muted); font-size:12px; margin:0}
.login-field { margin-bottom:14px}
.login-label { display:block; font-size:11px; font-weight:700; letter-spacing:.14em; text-transform:uppercase; color:var(--muted); margin-bottom:6px}
.login-input { width:100%; min-height:46px; padding:12px 14px; background:var(--bg); border:1px solid var(--border2); border-radius:10px; color:var(--text); font-size:16px; outline:none; transition:border-color .15s, box-shadow .15s}
.login-input:focus { border-color:var(--login-accent, var(--accent)); box-shadow:0 0 0 3px rgba(15,23,42,.07)}
.login-input::placeholder { color:var(--muted); opacity:.7}
.login-remember { margin-bottom:18px; color:var(--muted2); font-size:13px}
.login-remember label { display:flex; align-items:center; gap:10px; min-height:44px; margin:0; cursor:pointer}
.login-btn { width:100%; min-height:48px; padding:13px 20px; background:var(--login-accent, var(--accent)); border:none; border-radius:10px; color:var(--login-ink, var(--on-accent)); font-family:var(--mono); font-size:15px; font-weight:800; letter-spacing:.08em; text-transform:uppercase; cursor:pointer; transition:filter .15s, transform .1s}
.login-btn:hover { filter:brightness(1.08)}
.login-btn:active { transform:translateY(1px)}

/* ── Voter list card ──────────────────────────────────────────────────────
   One voter per card, because this app runs at ~375px in a MAUI WebView where
   the sibling apps' ten-column table is unreadable. Reading order is: rank and
   EPIC, then the name, then relation and house, then the tags.

   Sizes were raised across the board — the name carries the card, so it leads
   at 18px with the Devanagari line right under it at 16px, and the supporting
   rows sit at 13px rather than the 11.5px they started at. On a phone held at
   arm's length the old scale was legible but not comfortable. */

.voter-card {
    position: relative;
    background: linear-gradient(140deg, #161920 0%, #161920 100%);
    border: 1px solid var(--border);
    border-left: 4px solid var(--green);
    border-radius: 14px;
    padding: 14px 16px 15px;
    margin-bottom: 11px;
    box-shadow: 0 2px 12px rgba(0, 0, 0, .38);
    transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
    overflow: hidden}
/* A soft wash behind the header, so the card reads as a record and not a slab. */
.voter-card::before {
    content: ""; position: absolute; inset: 0 0 auto 0; height: 56px;
    background: linear-gradient(180deg, rgba(255,255,255,0.042), transparent);
    pointer-events: none}
.voter-card:hover { transform: translateY(-2px); box-shadow: 0 8px 26px rgba(0, 0, 0, .55)}

/* The card you came back to after editing. Scrolling to it silently still leaves
   you hunting for which of the ~9 cards on screen was yours, so it says so for a
   moment and then stops shouting. */
.voter-card.vc-returned {
    animation: vc-return-glow 2.4s ease-out}
@keyframes vc-return-glow {
    0%, 55% { box-shadow: 0 0 0 2px var(--accent), 0 8px 26px rgba(0, 0, 0, .55)}
    100%    { box-shadow: 0 0 0 0 rgba(180,83,9,0), 0 8px 26px rgba(0, 0, 0, 0)}
}
@media (prefers-reduced-motion: reduce) {
    .voter-card.vc-returned { animation: none; box-shadow: 0 0 0 2px var(--accent)}
}
.voter-card:active { transform: scale(.995)}
.voter-card.vc-m { border-left-color: var(--blue)}
.voter-card.vc-f { border-left-color: #be185d}

.vc-head {
    position: relative;
    display: flex; align-items: center; gap: 10px;
    font-family: var(--mono); font-size: 12px; color: var(--muted);
    margin-bottom: 7px}
.vc-sr {
    font-size: 14px; font-weight: 800; color: var(--green);
    letter-spacing: .02em}
.voter-card.vc-m .vc-sr { color: var(--blue)}
.voter-card.vc-f .vc-sr { color: #f472b6}
.vc-id {
    flex: 1 1 auto; min-width: 0; text-align: center; color: var(--muted2);
    font-size: 12px; letter-spacing: .08em;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap}
.vc-age {
    flex: 0 0 auto; font-size: 12.5px; font-weight: 700; color: var(--text);
    background: rgba(255,255,255,0.072); border: 1px solid var(--border2);
    border-radius: 999px; padding: 3px 11px}
.voter-card.vc-m .vc-age { color: var(--blue); border-color: rgba(29,78,216,.4); background: rgba(29,78,216,.10)}
.voter-card.vc-f .vc-age { color: #f472b6; border-color: rgba(190,24,93,.4); background: rgba(190,24,93,.10)}

/* English and Devanagari side by side. baseline alignment, not center: the two
   scripts have very different cap heights and centring them makes the smaller one
   look like it is floating. */
.vc-names { position: relative; display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 9px}
.vc-name  { font-size: 18px; font-weight: 800; color: #f5f7fa; line-height: 1.25; letter-spacing: -.01em}
.vc-hname { font-size: 15px; color: var(--muted2); line-height: 1.35}

.vc-detail {
    display: flex; flex-wrap: wrap; gap: 5px 16px;
    font-size: 13px; color: var(--muted2); margin-top: 9px;
    padding-top: 9px; border-top: 1px solid rgba(255,255,255,0.06)}

/* The corrections band. Bleeds to the card's edges (margins negate its
   14px 16px 15px padding) so it reads as a distinct strip rather than another
   paragraph; .voter-card already clips it with overflow:hidden, so it needs no
   radius of its own. Wraps across as many rows as the content needs. */
.vc-corr {
    margin: 11px -16px -15px;
    padding: 8px 16px 10px;
    background: rgba(21,128,61,.07);
    border-top: 1px solid rgba(21,128,61,.22);
    font-size: 13px}
/* Three across. minmax(0,1fr) rather than 1fr: a grid column's default min-width
   is auto, so a long Devanagari value would widen its column past the card. */
.vc-corr-grid {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 7px 12px; margin-top: 6px}
.vc-corr-grid .vc-c { display: flex; flex-direction: column; gap: 0; min-width: 0}
.vc-c-v { color: #f5f7fa; word-break: break-word; line-height: 1.3}
@media (max-width: 380px) { .vc-corr-grid { grid-template-columns: repeat(2, minmax(0, 1fr))} }
.vc-corr-hdr {
    font-family: var(--mono); font-size: 10.5px; letter-spacing: .09em;
    text-transform: uppercase; color: var(--green); opacity: .85}
.vc-c { color: #f5f7fa}
.vc-c-k { color: var(--muted); font-size: 10px; letter-spacing: .06em; text-transform: uppercase}

.vc-tag {
    display: inline-block; font-family: var(--mono); font-size: 12px;
    background: rgba(255,255,255,0.06); border: 1px solid var(--border2);
    border-radius: 999px; padding: 4px 11px; margin: 10px 6px 0 0;
    color: var(--muted2); white-space: nowrap}
.vc-edit {
    display: inline-block; float: right;
    font-family: var(--mono); font-size: 12px; font-weight: 700;
    letter-spacing: .05em;
    background: var(--green-dim); border: 1px solid var(--green);
    color: var(--green); border-radius: 999px;
    padding: 4px 15px; margin-top: 10px; text-decoration: none;
    transition: background .15s ease, color .15s ease}
.vc-edit:hover { background: var(--green); color: var(--on-accent)}

/* ── Voter record (/voters/edit) ─────────────────────────────────────────────
   The record used to be three label/value <table>s with every label in
   .text-muted at 11px, which washed out to near-invisible on the dark green
   ground — "Booth", "Section", "Serial No." were unreadable. Labels now sit at
   --muted2 with letter-spacing, values at full --text.

   Section colour comes from --vr-accent, set inline per block, so one rule set
   serves all five sections. Roll entry leads on --accent (this app's amber):
   it is the booth layer, and those are the booth's own fields.
   ───────────────────────────────────────────────────────────────────────────*/
.vr-hero {
    background: linear-gradient(150deg, var(--bg3) 0%, var(--bg2) 60%);
    border: 1px solid var(--border2); border-left: 3px solid var(--accent);
    border-radius: 12px; padding: 16px; margin-bottom: 14px}
.vr-hero-top { display: flex; gap: 13px; align-items: center}
.vr-avatar {
    flex: 0 0 46px; width: 46px; height: 46px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: linear-gradient(150deg, var(--accent), #b45309);
    color: #f5f7fa; font-family: var(--mono); font-size: 16px; font-weight: 800}
.vr-name { font-size: 18px; font-weight: 700; color: #f5f7fa; line-height: 1.25}
.vr-name-hi { font-size: 14px; color: var(--muted2); margin-top: 1px}
.vr-epic { font-size: 11px; color: var(--accent); margin-top: 4px; letter-spacing: .06em}

.vr-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 13px}
.vr-chip {
    font-family: var(--mono); font-size: 10.5px; color: var(--muted2);
    background: rgba(255,255,255,0.048); border: 1px solid var(--border2);
    border-radius: 999px; padding: 4px 10px; white-space: nowrap}
.vr-chip b { color: var(--text); font-weight: 700}
.vr-ok   { color: var(--green); border-color: var(--green)}
.vr-warn { color: var(--amber); border-color: var(--amber)}
.vr-pdag { font-weight: 700}
.vr-pdag.pd-p { color: var(--green);  border-color: var(--green)}
.vr-pdag.pd-d { color: var(--red);    border-color: var(--red)}
.vr-pdag.pd-a { color: var(--blue);   border-color: var(--blue)}
.vr-pdag.pd-g { color: var(--purple); border-color: var(--purple)}

.vr-sec {
    background: var(--bg2); border: 1px solid var(--border);
    border-left: 3px solid var(--vr-accent, var(--accent));
    border-radius: 10px; margin-bottom: 12px; overflow: hidden}
.vr-sec-hdr {
    font-family: var(--mono); font-size: 10px; font-weight: 700;
    letter-spacing: .16em; text-transform: uppercase;
    color: var(--vr-accent, var(--accent));
    padding: 11px 14px 9px; border-bottom: 1px solid var(--border)}
.vr-list {
    padding: 2px 0;
    /* Two pairs across. One field per full-width line left most of the row empty,
       which pushed the later sections below the fold for no gain. */
    display: grid;
    grid-template-columns: 1fr 1fr}
/* Vertical rule between the columns, matching the horizontal one in weight. */
.vr-list > .vr-row:nth-child(odd) { border-right: 1px solid rgba(255,255,255,0.048)}
/* Both columns end at the last two children, not just the final one. */
.vr-list > .vr-row:nth-last-child(-n+2) { border-bottom: 0}
/* Narrow screens keep BOTH columns and stack the label over its value instead of
   dropping to one pair per line. A label and a value side by side genuinely do not
   fit twice across at 375px — but stacked they do, and that is the difference
   between Roll entry taking four lines and taking seven. The old rule collapsed to
   a single column, which on the phone this app actually runs on left ~60% of every
   row empty and pushed Classification below the fold. */
@media (max-width: 560px) {
    .vr-row { flex-direction: column; align-items: stretch; gap: 1px; padding: 7px 12px}
    .vr-k { flex: 0 0 auto; font-size: 9px}
    .vr-v { font-size: 13px}
}
/* Below ~330px even a stacked pair is too tight for two across. */
@media (max-width: 330px) {
    .vr-list { grid-template-columns: 1fr}
    .vr-list > .vr-row:nth-child(odd) { border-right: 0}
    .vr-list > .vr-row:nth-last-child(-n+2) { border-bottom: 1px solid rgba(255,255,255,0.048)}
    .vr-list > .vr-row:last-child { border-bottom: 0}
}
.vr-row {
    display: flex; align-items: baseline; gap: 12px;
    padding: 9px 14px; border-bottom: 1px solid rgba(255,255,255,0.048);
    min-width: 0;   /* let a long value shrink rather than widen its grid column */
}
.vr-row:last-child { border-bottom: 0}
.vr-k {
    flex: 0 0 42%; font-family: var(--mono); font-size: 10px;
    letter-spacing: .1em; text-transform: uppercase; color: var(--muted2)}
.vr-v { flex: 1 1 auto; min-width: 0; font-size: 13.5px; color: var(--text); word-break: break-word}
.vr-empty { color: var(--muted); opacity: .55}

/* Survey form. The controls restate background / border rather than inherit the
   global input rule: these sit on --bg2 panels, where the global --bg3 fill
   leaves the field and its panel the same colour. */
.vr-form { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 14px}
.vr-f { display: flex; flex-direction: column; gap: 4px; min-width: 0}
.vr-f.vr-wide { grid-column: 1 / -1}
.vr-f label {
    font-family: var(--mono); font-size: 9.5px; letter-spacing: .1em;
    text-transform: uppercase; color: var(--muted2)}
.vr-f select, .vr-f input, .vr-f textarea {
    width: 100%; background: var(--bg); color: var(--text);
    border: 1px solid var(--border2); border-radius: 7px;
    padding: 8px 10px; font-size: 13px; font-family: var(--sans)}
.vr-f select:focus, .vr-f input:focus, .vr-f textarea:focus {
    outline: none; border-color: var(--accent);
    box-shadow: 0 0 0 2px rgba(180,83,9,.18)}
.vr-hint {
    display: block; margin-top: 4px;
    font-size: 11px; line-height: 1.35; color: var(--muted, #7c8494)}
.vr-f.vr-check { flex-direction: row; align-items: center; gap: 8px}
.vr-f.vr-check input { width: auto}
.vr-f.vr-check label { text-transform: none; font-size: 12px; letter-spacing: 0}

.vr-actions {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 0 14px 14px}

@media (max-width: 420px) {
    .vr-form { grid-template-columns: 1fr}
    .vr-k { flex-basis: 46%}
}

/* ── Booth dashboard / analysis chrome (bv-*) ──
   These lived in a <style> block inside Home.razor, which meant they existed
   ONLY on Home. /analysis uses bv-hero, bv-sec, bv-h2, bv-empty and bv-hero-sub
   and defined none of them, so its headings rendered as bare text -- the
   "02Analysis SectionsNAV" run-together heading. Shared chrome belongs in the
   shared stylesheet. */
/* ── Booth Dashboard ── */
    :root {
        --bv-bg: #161920;
        --bv-card: #161920;
        --bv-card-border: #333b49;
        --bv-text: #f5f7fa;
        --bv-text-dim: #aeb6c2;
        --bv-accent: #b45309;
        --bv-radius: 10px}

    .bv-sec {
        margin-bottom: 1.5rem;
        padding: 1.25rem;
        border-radius: var(--bv-radius);
        border: 1px solid var(--bv-card-border);
        background: var(--bv-card)}

    .bv-hero {
        text-align: center;
        padding: 1.5rem 0 1rem}
    .bv-hero h1 {
        font-size: 1.6rem;
        font-weight: 700;
        color: var(--bv-text);
        margin: 0}
    .bv-hero-sub {
        color: var(--bv-text-dim);
        font-size: 0.9rem;
        margin: 0.3rem 0 0}

    .bv-h2 {
        display: flex;
        align-items: center;
        gap: 0.6rem;
        margin: 0 0 1rem;
        padding-bottom: 0.6rem;
        border-bottom: 1px solid var(--bv-card-border);
        font-size: 1rem;
        font-weight: 600;
        color: var(--bv-text)}
    .bv-h2 .nmb {
        font-size: 0.75rem;
        font-weight: 700;
        color: var(--bv-accent);
        background: rgba(180,83,9,0.15);
        padding: 0.15rem 0.45rem;
        border-radius: 4px}
    .bv-h2 .ttl { flex: 1}
    .bv-h2 .tag {
        font-size: 0.65rem;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: 0.06em;
        color: var(--bv-text-dim);
        background: rgba(255,255,255,0.072);
        padding: 0.15rem 0.5rem;
        border-radius: 4px}

    .bv-kpi {
        display: grid;
        grid-template-columns: repeat(2, 1fr);
        gap: 0.65rem}
    .bv-kpi .card {
        background: rgba(255,255,255,0.036);
        border: 1px solid var(--bv-card-border);
        border-radius: 8px;
        padding: 0.75rem 0.9rem}
    .bv-kpi .card .lbl {
        font-size: 0.7rem;
        text-transform: uppercase;
        letter-spacing: 0.04em;
        color: var(--bv-text-dim);
        margin-bottom: 0.25rem}
    .bv-kpi .card .val {
        font-size: 1.1rem;
        font-weight: 700;
        color: var(--bv-text)}
    .bv-kpi .card .val small {
        font-size: 0.75rem;
        font-weight: 400;
        color: var(--bv-text-dim)}

    .bv-empty {
        text-align: center;
        padding: 2rem;
        color: var(--bv-text-dim)}
    .bv-empty h2 {
        color: var(--bv-text);
        margin-bottom: 0.5rem}

    /* Section theme accents (left border) */
    .t-hero  { border-left: 3px solid #b45309}
    .t-demo  { border-left: 3px solid #6d28d9}
    .t-pdag  { border-left: 3px solid #15803d}
    .t-rel   { border-left: 3px solid #15803d}
    .t-surname { border-left: 3px solid #be185d}
    .t-hh    { border-left: 3px solid #0e7490}
    .t-fam   { border-left: 3px solid #b45309}
    .t-rolls { border-left: 3px solid #1d4ed8}
    .t-elec  { border-left: 3px solid #b91c1c}

    /* Plain CSS, so a single @. The doubled form is Razor escaping and came along
       when this block was lifted out of a <style> block in Home.razor -- which made
       the rule invalid and left the KPI grid stuck at two columns on every width. */
    @media (min-width: 768px) {
        .bv-kpi { grid-template-columns: repeat(4, 1fr)}
    }

/* ── Booth hub: the polling-station card ──
   Its own card, not .progress-box. That wrapper is what this app shows a failed
   query in, and using it for the booth's identity made the most important card on
   the hub read as a notice. Cyan edge to match the Booth tab accent. */
.bh-station {
    background: var(--bg2); border: 1px solid var(--border); border-left: 3px solid var(--cyan);
    border-radius: 0 var(--radius) var(--radius) 0; padding: 16px 18px; margin-bottom: 24px}
.bh-st-top { display: flex; align-items: flex-start; gap: 12px}
.bh-st-ico { width: 30px; height: 30px; flex-shrink: 0; margin-top: 2px}
.bh-st-id { min-width: 0}
.bh-st-name { font-size: 17px; font-weight: 700; color: var(--text); line-height: 1.3; margin-top: 2px}
.bh-st-name-en { font-size: 13px; color: var(--muted2); line-height: 1.35; margin-top: 1px}

.bh-st-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px}
.bh-chip {
    font-family: var(--mono); font-size: 10.5px; color: var(--muted2);
    background: rgba(255,255,255,0.06); border: 1px solid var(--border2);
    border-radius: 999px; padding: 3px 10px}

/* Two across, label over value. .prog-legend is a chart key: it wrapped these six
   into a paragraph where a label sat inline with the previous value. */
.bh-st-grid {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 11px 14px;
    margin-top: 14px; padding-top: 13px; border-top: 1px solid var(--border)}
/* minmax(0,1fr) rather than 1fr: a grid column's default min-width is auto, so a
   long Devanagari value would push its column past the card instead of wrapping. */
.bh-fact { display: flex; flex-direction: column; gap: 1px; min-width: 0}
.bh-fact-k {
    font-family: var(--mono); font-size: 9px; letter-spacing: .12em;
    text-transform: uppercase; color: var(--muted)}
.bh-fact-v { font-size: 13.5px; color: var(--text); word-break: break-word}
.bh-fact-empty { color: var(--muted); opacity: .5}

.bh-st-foot {
    font-family: var(--mono); font-size: 10px; color: var(--muted);
    margin-top: 13px; padding-top: 11px; border-top: 1px solid var(--border)}
@media (max-width: 400px) { .bh-st-grid { grid-template-columns: 1fr 1fr} }
@media (max-width: 300px) { .bh-st-grid { grid-template-columns: 1fr} }

/* ── Two-row family rows ──
   Nine columns overflowed a phone. Each family is a pair of rows now: counts on
   the first, descriptive fields wrapped on the second. The pair is held together
   visually by suppressing the border between them, so the eye reads one record
   rather than two rows. */
.fam-r1 td { border-bottom: 0 !important; padding-bottom: 4px}
.fam-r2 td { padding-top: 0; padding-bottom: 9px}
.fam-r2 .fam-f {
    display: inline-flex; align-items: baseline; gap: 5px;
    margin-right: 14px; white-space: nowrap}
.fam-r2 .fam-k {
    font-family: var(--mono); font-size: 9px; letter-spacing: .1em;
    text-transform: uppercase; color: var(--muted)}
/* The wrapper still scrolls if a caste name is very long, but the table no longer
   demands more width than the screen for every row. */
.table-wrap { overflow-x: auto}

/* ── QuickGrid virtualisation ──
   QuickGrid computes its window from the scroll position of ITS OWN container, not
   the page. Without a constrained height here it never learns which rows are
   visible: the first window renders and scrolling then empties the list. */
.sqlgrid-wrap { height: calc(100vh - 260px); min-height: 320px; overflow-y: auto}
.sqlgrid { width: 100%; border-collapse: collapse}
.sqlgrid thead { display: none}            /* one card column needs no header row */
.sqlgrid td.vg-cell { padding: 0; border: 0}
.sqlgrid-empty { padding: 18px; color: var(--muted)}

/* ── Voter card action row ──
   Circular actions under the tags: call / SMS / WhatsApp, then religion and
   gender as read-only badges, then Open. The three contact actions render
   disabled rather than absent when no number has been collected -- dashed and
   muted, which reads as "not collected yet" rather than "not supported". */
.vc-tags { display: flex; flex-wrap: wrap; gap: 6px}
.vc-acts {
    display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
    margin-top: 10px}
.vc-act {
    width: 32px; height: 32px; flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%; font-size: 15px; line-height: 1; text-decoration: none;
    border: 1px solid transparent; background: rgba(255,255,255,0.06)}
a.vc-act:active { transform: scale(.94)}
a.vc-act:focus-visible { outline: 2px solid var(--green); outline-offset: 2px}

.vc-act--call { background: rgba(21,128,61,.16); border-color: rgba(21,128,61,.45)}
.vc-act--sms  { background: rgba(29,78,216,.16); border-color: rgba(29,78,216,.45)}
.vc-act--wa   { background: rgba(21,128,61,.16); border-color: rgba(21,128,61,.5)}

/* No number on file. Dashed, dimmed, and not a link. */
.vc-act--off {
    border: 1px dashed var(--border2); background: transparent;
    opacity: .38; filter: grayscale(1); cursor: default}

.vc-act--rel, .vc-act--gen { font-size: 14px}
.rel-hindu     { background: rgba(180,83,9,.16); border-color: rgba(180,83,9,.45)}
.rel-muslim    { background: rgba(21,128,61,.16); border-color: rgba(21,128,61,.45)}
.rel-buddhist  { background: rgba(180,83,9,.16); border-color: rgba(180,83,9,.45)}
.rel-sikh      { background: rgba(29,78,216,.16); border-color: rgba(29,78,216,.45)}
.rel-jain      { background: rgba(109,40,217,.16); border-color: rgba(109,40,217,.45)}
.rel-christian { background: rgba(148, 163, 184, .16); border-color: rgba(148, 163, 184, .45)}
.rel-none      { opacity: .34; font-size: 9px}

.gen-m    { background: rgba(29,78,216,.16); border-color: rgba(29,78,216,.45); color: #60a5fa}
.gen-f    { background: rgba(190,24,93,.16); border-color: rgba(190,24,93,.45); color: #f472b6}
.gen-o    { background: rgba(109,40,217,.16); border-color: rgba(109,40,217,.45); color: #60a5fa}
.gen-none { opacity: .34; font-size: 9px}

/* Open sits at the far end of the same row. */
.vc-acts .vc-edit { margin-left: auto}

/* ── Charts (acharts.js) ──────────────────────────────────────────────
   Mobile-first: one chart per row on a phone, two from 560px. The host div is
   the element immediately after the JSON payload script block. This app had the
   renderer wired but none of its layout, so .acard-canvas had no height for an
   SVG to size against. Ported from AssemblyWeb along with the renderer. */
.acharts { display:grid; grid-template-columns:minmax(0,1fr); gap:12px; margin-bottom:24px; align-items:start}
@media(min-width:560px){ .acharts { grid-template-columns:repeat(2,minmax(0,1fr))} }

/* Fallback when no chart data available. */
.acharts:empty::after { content:"No chart data available."; display:block; font-family:var(--mono); font-size:11px; color:var(--muted); background:var(--bg2); border:1px dashed var(--border2); border-radius:var(--radius); padding:20px; text-align:center}

.acard { background:var(--bg2); border:1px solid var(--border); border-radius:var(--radius); padding:12px; min-width:0}
.acard-head { display:flex; align-items:baseline; gap:8px; margin-bottom:8px}
.acard h4 { flex:1; min-width:0; font-family:var(--mono); font-size:10.5px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--muted2); margin:0}
.acard-sub { font-family:var(--mono); font-size:9px; color:var(--muted); white-space:nowrap}
.acard-canvas { position:relative; height:200px}
.acard-canvas svg { display:block; width:100%; height:100%}
.acard--wide { grid-column:1 / -1}
.acard--failed { border-color:var(--red)}

/* ── Icons (bi-*) ──────────────────────────────────────────
   The markup asks for Bootstrap Icons, but the font was never vendored and
   never linked, so every <i class="bi bi-..."> rendered at zero width. These
   apps run in a MAUI WebView that may have no outbound internet, so a CDN
   link would fail the same way -- the glyphs are inlined as SVG masks
   instead: no font file, no request, and mask + background-color means each
   icon inherits currentColor exactly as the font version would have. */
.bi {
    display: inline-block; width: 1em; height: 1em; vertical-align: -.125em;
    background-color: currentColor;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    -webkit-mask-position: center; mask-position: center;
    -webkit-mask-size: contain; mask-size: contain;
    flex-shrink: 0}
.bi-calendar3 { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='1.5' y='2.5' width='13' height='12' rx='1.5' fill='none' stroke='black' stroke-width='1.2'/%3E%3Cpath d='M1.5 6h13' stroke='black' stroke-width='1.2'/%3E%3Cpath d='M5 1.5v2M11 1.5v2' stroke='black' stroke-width='1.2' stroke-linecap='round'/%3E%3Ccircle cx='5' cy='9' r='.9' fill='black'/%3E%3Ccircle cx='8' cy='9' r='.9' fill='black'/%3E%3Ccircle cx='11' cy='9' r='.9' fill='black'/%3E%3Ccircle cx='5' cy='12' r='.9' fill='black'/%3E%3Ccircle cx='8' cy='12' r='.9' fill='black'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='1.5' y='2.5' width='13' height='12' rx='1.5' fill='none' stroke='black' stroke-width='1.2'/%3E%3Cpath d='M1.5 6h13' stroke='black' stroke-width='1.2'/%3E%3Cpath d='M5 1.5v2M11 1.5v2' stroke='black' stroke-width='1.2' stroke-linecap='round'/%3E%3Ccircle cx='5' cy='9' r='.9' fill='black'/%3E%3Ccircle cx='8' cy='9' r='.9' fill='black'/%3E%3Ccircle cx='11' cy='9' r='.9' fill='black'/%3E%3Ccircle cx='5' cy='12' r='.9' fill='black'/%3E%3Ccircle cx='8' cy='12' r='.9' fill='black'/%3E%3C/svg%3E")}
.bi-chevron-down { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 6 8 10.5 12.5 6' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 6 8 10.5 12.5 6' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")}
.bi-chevron-up { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 10 8 5.5 12.5 10' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 10 8 5.5 12.5 10' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")}
.bi-chevron-left { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M10 3.5 5.5 8 10 12.5' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M10 3.5 5.5 8 10 12.5' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")}
.bi-chevron-right { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 3.5 10.5 8 6 12.5' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 3.5 10.5 8 6 12.5' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")}
.bi-clipboard-check { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='3' y='2.5' width='10' height='12' rx='1.5' fill='none' stroke='black' stroke-width='1.2'/%3E%3Crect x='6' y='1' width='4' height='2.6' rx='.8' fill='black'/%3E%3Cpath d='M5.8 9.2 7.3 10.7 10.4 7.6' fill='none' stroke='black' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='3' y='2.5' width='10' height='12' rx='1.5' fill='none' stroke='black' stroke-width='1.2'/%3E%3Crect x='6' y='1' width='4' height='2.6' rx='.8' fill='black'/%3E%3Cpath d='M5.8 9.2 7.3 10.7 10.4 7.6' fill='none' stroke='black' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")}
.bi-clipboard2-data-fill { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='3' y='2.5' width='10' height='12' rx='1.5' fill='black'/%3E%3Crect x='6' y='1' width='4' height='2.6' rx='.8' fill='black'/%3E%3Cpath d='M5.8 11.6V9.2M8 11.6V7M10.2 11.6v-3' stroke='white' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='3' y='2.5' width='10' height='12' rx='1.5' fill='black'/%3E%3Crect x='6' y='1' width='4' height='2.6' rx='.8' fill='black'/%3E%3Cpath d='M5.8 11.6V9.2M8 11.6V7M10.2 11.6v-3' stroke='white' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E")}
.bi-clock-history { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 3.2a4.8 4.8 0 1 1-4.53 6.4' fill='none' stroke='black' stroke-width='1.3' stroke-linecap='round'/%3E%3Cpath d='M3.1 6.4 2.6 9.2 5.4 9.6' fill='none' stroke='black' stroke-width='1.3' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M8 5.6V8.2l1.9 1.1' fill='none' stroke='black' stroke-width='1.3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 3.2a4.8 4.8 0 1 1-4.53 6.4' fill='none' stroke='black' stroke-width='1.3' stroke-linecap='round'/%3E%3Cpath d='M3.1 6.4 2.6 9.2 5.4 9.6' fill='none' stroke='black' stroke-width='1.3' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M8 5.6V8.2l1.9 1.1' fill='none' stroke='black' stroke-width='1.3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")}
.bi-exclamation-triangle-fill { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.1 2.2 1.3 12.4A1 1 0 0 0 2.2 14h11.6a1 1 0 0 0 .87-1.5L8.9 2.2a1 1 0 0 0-1.74 0Z' fill='black'/%3E%3Cpath d='M8 6v3.1' stroke='white' stroke-width='1.5' stroke-linecap='round'/%3E%3Ccircle cx='8' cy='11.6' r='.85' fill='white'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.1 2.2 1.3 12.4A1 1 0 0 0 2.2 14h11.6a1 1 0 0 0 .87-1.5L8.9 2.2a1 1 0 0 0-1.74 0Z' fill='black'/%3E%3Cpath d='M8 6v3.1' stroke='white' stroke-width='1.5' stroke-linecap='round'/%3E%3Ccircle cx='8' cy='11.6' r='.85' fill='white'/%3E%3C/svg%3E")}
.bi-file-earmark-pdf-fill { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 1.5h5L13 5.5v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-12a1 1 0 0 1 1-1Z' fill='black'/%3E%3Cpath d='M9 1.6V5a.6.6 0 0 0 .6.6h3.3' fill='none' stroke='white' stroke-width='1.1'/%3E%3Cpath d='M5.4 12.4c1.6-.5 2.8-2.1 3.3-3.6.3-1-.1-1.7-.7-1.5-.7.2-.6 1.5.2 2.6.8 1.1 1.9 1.8 2.8 1.8' fill='none' stroke='white' stroke-width='1.1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 1.5h5L13 5.5v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-12a1 1 0 0 1 1-1Z' fill='black'/%3E%3Cpath d='M9 1.6V5a.6.6 0 0 0 .6.6h3.3' fill='none' stroke='white' stroke-width='1.1'/%3E%3Cpath d='M5.4 12.4c1.6-.5 2.8-2.1 3.3-3.6.3-1-.1-1.7-.7-1.5-.7.2-.6 1.5.2 2.6.8 1.1 1.9 1.8 2.8 1.8' fill='none' stroke='white' stroke-width='1.1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")}
.bi-flag-fill { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 1.6v13' stroke='black' stroke-width='1.5' stroke-linecap='round'/%3E%3Cpath d='M4.6 2.6h8.1l-1.9 2.9 1.9 2.9H4.6Z' fill='black'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 1.6v13' stroke='black' stroke-width='1.5' stroke-linecap='round'/%3E%3Cpath d='M4.6 2.6h8.1l-1.9 2.9 1.9 2.9H4.6Z' fill='black'/%3E%3C/svg%3E")}
.bi-geo-alt { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 1.6a4.6 4.6 0 0 0-4.6 4.6c0 3.3 4.6 8.2 4.6 8.2s4.6-4.9 4.6-8.2A4.6 4.6 0 0 0 8 1.6Z' fill='none' stroke='black' stroke-width='1.3' stroke-linejoin='round'/%3E%3Ccircle cx='8' cy='6.2' r='1.7' fill='none' stroke='black' stroke-width='1.3'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 1.6a4.6 4.6 0 0 0-4.6 4.6c0 3.3 4.6 8.2 4.6 8.2s4.6-4.9 4.6-8.2A4.6 4.6 0 0 0 8 1.6Z' fill='none' stroke='black' stroke-width='1.3' stroke-linejoin='round'/%3E%3Ccircle cx='8' cy='6.2' r='1.7' fill='none' stroke='black' stroke-width='1.3'/%3E%3C/svg%3E")}
.bi-people-fill { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='6' cy='5.2' r='2.5' fill='black'/%3E%3Cpath d='M1.4 13.4c0-2.4 2-4 4.6-4s4.6 1.6 4.6 4Z' fill='black'/%3E%3Ccircle cx='11.9' cy='5.6' r='2' fill='black'/%3E%3Cpath d='M11 9.6c2 0 3.6 1.3 3.6 3.1v.7h-2.8' fill='black'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='6' cy='5.2' r='2.5' fill='black'/%3E%3Cpath d='M1.4 13.4c0-2.4 2-4 4.6-4s4.6 1.6 4.6 4Z' fill='black'/%3E%3Ccircle cx='11.9' cy='5.6' r='2' fill='black'/%3E%3Cpath d='M11 9.6c2 0 3.6 1.3 3.6 3.1v.7h-2.8' fill='black'/%3E%3C/svg%3E")}
.bi-robot { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='2.5' y='5' width='11' height='8.2' rx='2' fill='none' stroke='black' stroke-width='1.25'/%3E%3Cpath d='M8 2.2v2.8' stroke='black' stroke-width='1.25' stroke-linecap='round'/%3E%3Ccircle cx='8' cy='1.9' r='1' fill='black'/%3E%3Ccircle cx='6' cy='8.6' r='1.15' fill='black'/%3E%3Ccircle cx='10' cy='8.6' r='1.15' fill='black'/%3E%3Cpath d='M6.2 11.3h3.6' stroke='black' stroke-width='1.25' stroke-linecap='round'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='2.5' y='5' width='11' height='8.2' rx='2' fill='none' stroke='black' stroke-width='1.25'/%3E%3Cpath d='M8 2.2v2.8' stroke='black' stroke-width='1.25' stroke-linecap='round'/%3E%3Ccircle cx='8' cy='1.9' r='1' fill='black'/%3E%3Ccircle cx='6' cy='8.6' r='1.15' fill='black'/%3E%3Ccircle cx='10' cy='8.6' r='1.15' fill='black'/%3E%3Cpath d='M6.2 11.3h3.6' stroke='black' stroke-width='1.25' stroke-linecap='round'/%3E%3C/svg%3E")}

/* ── Dropdown affordance ──
   Every <select> gets a visible chevron. Without it a select styled to match the
   surrounding inputs is indistinguishable from a text box, so nothing tells the
   user it opens a list -- which is exactly how the Category and Occupation
   fields on the voter form read.

   appearance:none removes the platform arrow (which is drawn light-on-light and
   effectively invisible in a dark WebView on Android), and the chevron is a data
   URI so it still renders with no network in the field. Padding on the right
   keeps a long option from running underneath it. */
select,
.vr-f select,
select.inp,
select.form-select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5' stroke='%23cbd5e1' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 12px;
    padding-right: 30px}

/* The native popup inherits the page, not the control, so option colours are set
   explicitly or they come out unreadable on a dark theme. */
select option { background: #161920; color: #f5f7fa}

/* A multi-select or a sized list is not a dropdown and must not claim to be. */
select[multiple],
select[size]:not([size="1"]) {
    background-image: none;
    padding-right: 10px}

/*  Was an orphan: the rule sat indented with a closing brace and no opener,
    so the brace closed nothing and everything after it parsed one level out.
    Restored to the small screen query it was clearly written for. */
@media (max-width: 560px) {
    .hero-card .hero-title { font-size: 20px}
}

/* ════════════════════════════════════════════════════════════════════════
   HERO HEADERS  (each card declares a plain background before the color-mix
   one: a color-mix fallback, since an unsupported value voids the whole
   declaration and would leave the card transparent)
   ────────────────────────────────────────────────────────────────────────
   Every page title becomes a card, and it is done HERE rather than in the
   227 .razor files that render one. They all already share .page-header,
   so restyling the class reaches every page at once -- including the sixty
   that go through <VotersHeader> -- with no markup to get wrong and one
   place to tune afterwards.

   The existing per-family accent is kept, not replaced: --ph-accent is
   already green for /voters, cyan for /booth and purple for AI, and it now
   drives the rail, the wash and the glow together so a page still announces
   which family it belongs to.
   ════════════════════════════════════════════════════════════════════════ */

.page-header {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;

    padding: 15px 17px 15px 19px;
    margin-bottom: 20px;
    border-radius: 0 12px 12px 0;

    border: 1px solid rgba(255,255,255,0.09);
    border-left: 0;

    /*  A wash angled off the family accent, at low enough alpha that the card
        reads as lit from the rail rather than tinted a colour. The second
        layer is the flat panel underneath it. */
    background:
        linear-gradient(105deg,
            color-mix(in srgb, var(--ph-accent, var(--green)) 13%, transparent) 0%,
            color-mix(in srgb, var(--ph-accent, var(--green)) 4%, transparent) 40%,
            transparent 74%),
        var(--bg2, #161920);

    box-shadow:
        inset 0 1px 0 rgba(15,23,42,.035),
        0 6px 18px rgba(0, 0, 0, .28)}

/*  The rail was a 3px bar floated inside the old flat header; as a card edge it
    runs the full height and carries a soft bloom so it looks lit rather than
    drawn on. */
.page-header::before {
    content: "";
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 3px;
    border-radius: 0;
    background: var(--ph-accent, var(--green));
    box-shadow: 0 0 12px -1px var(--ph-accent, var(--green))}

.page-title {
    font-family: inherit;
    font-size: 21px;
    font-weight: 800;
    letter-spacing: -.012em;
    line-height: 1.16;
    color: var(--text);
    text-wrap: balance}

/*  Subtitle as an uppercase mono eyebrow: it is almost always a location or a
    count -- "Booth 101 · AC 100" -- which is reference data, not prose, and
    reads faster spaced out than set as a sentence. */
.page-subtitle,
.page-header .page-sub {
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: .11em;
    text-transform: uppercase;
    color: var(--muted2, var(--muted));
    margin-top: 5px}

.page-hd { align-items: center; gap: 13px}

.page-icon,
.page-icon-svg {
    opacity: 1;
    margin-top: 0;
    filter: drop-shadow(0 2px 5px rgba(0, 0, 0, .45))}

.page-actions { align-self: center}

@media (max-width: 560px) {
    .page-header { padding: 13px 13px 13px 15px; border-radius: 0 10px 10px 0}
    .page-title  { font-size: 18px}
    .page-subtitle, .page-header .page-sub { font-size: 10px; letter-spacing: .09em}
}

/* ── Dashboard hero (Home) ────────────────────────────────────────────────
   Home names a place rather than a page, so it keeps its centred layout and
   gets the same card treatment around it. */
.bv-hero {
    position: relative;
    text-align: center;
    padding: 26px 18px 22px;
    margin: 0 0 22px;
    border-radius: 14px;
    border: 1px solid rgba(255,255,255,0.09);
    background: var(--bg2, #161920);
    background:
        radial-gradient(120% 130% at 50% -25%,
            color-mix(in srgb, var(--accent, #b45309) 15%, transparent) 0%,
            transparent 62%),
        var(--bg2, #161920);
    box-shadow:
        inset 0 1px 0 rgba(15,23,42,.04),
        0 8px 26px rgba(0, 0, 0, .32);
    overflow: hidden}

/*  A hairline across the top, brightest at the centre, so the card has a lip
    to catch the eye without a heavy border. */
.bv-hero::before {
    content: "";
    position: absolute;
    left: 12%; right: 12%; top: 0;
    height: 1px;
    background: linear-gradient(90deg,
        transparent,
        var(--accent, #b45309) 50%,
        transparent);
    opacity: .55}

.bv-hero h1 {
    font-size: clamp(19px, 5.2vw, 25px);
    font-weight: 800;
    letter-spacing: -.015em;
    line-height: 1.2;
    margin: 0;
    text-wrap: balance}

.bv-hero-sub {
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: .11em;
    text-transform: uppercase;
    color: var(--muted2, var(--muted));
    margin: 9px 0 0}

/* ── Section headers ──
   The same rail idea one level down, so a section reads as belonging to the
   page above it rather than as another unrelated band. */
.section-hdr {
    position: relative;
    padding-left: 11px;
    margin-top: 22px}

.section-hdr::before {
    content: "";
    position: absolute;
    left: 0; top: 50%;
    width: 3px; height: 13px;
    transform: translateY(-50%);
    border-radius: 2px;
    background: var(--accent, #b45309);
    opacity: .8}

/* ── Splitters ────────────────────────────────────────────────────────────
   One divider treatment, reached three ways so no page needs new markup to
   get it: the .split utility, any bare <hr> (there are eleven across the
   apps and none of them were styled -- they rendered as the browser's grey
   default inset line on a dark page), and the footer back-link, which is the
   same "end of content" moment on 65 pages.

   A rule that fades at both ends reads as a divider; one that runs edge to
   edge reads as a border and implies the thing below is a different region.
   The diamond marks the centre so the line has a reason to be there. */

.split,
hr {
    position: relative;
    height: 1px;
    border: 0;
    margin: 20px 0 22px;
    background: linear-gradient(90deg,
        transparent,
        rgba(255,255,255,0.156) 18%,
        rgba(255,255,255,0.156) 82%,
        transparent);
    overflow: visible}

.split::after,
hr::after {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    width: 7px; height: 7px;
    transform: translate(-50%, -50%) rotate(45deg);
    background: var(--accent, #b45309);
    box-shadow: 0 0 0 4px var(--bg, #161920), 0 0 10px rgba(180,83,9,.45)}

/*  A plain rule, for places where the diamond would be one mark too many. */
.split-plain::after, hr.plain::after { display: none}

/*  End-of-page back link. The divider is drawn above the link rather than
    added as an element, so all 65 pages that already render one pick it up
    without being touched. Width is capped so it stays a divider and does not
    become a full-bleed border on a wide screen. */
.back-link {
    position: relative;
    display: inline-block;
    margin-top: 30px}

.back-link::before {
    content: "";
    position: absolute;
    left: 50%; top: -17px;
    transform: translateX(-50%);
    width: min(340px, 66vw);
    height: 1px;
    background: linear-gradient(90deg,
        transparent,
        rgba(255,255,255,0.144) 22%,
        rgba(255,255,255,0.144) 78%,
        transparent)}

/* ── Analysis hero (compact variant) ──
   Home and Analysis were the same centred card carrying the same @GeoName and
   subtitles two words apart, so switching tabs looked like nothing had
   happened. Home names a PLACE and is the front door, so it keeps the tall
   centred treatment. Analysis is a working screen, so it gets a shorter
   left-aligned band with an eyebrow: different silhouette, and it gives back
   the vertical space to the charts that are the actual content. */
.bv-hero.hero-work {
    text-align: left;
    padding: 14px 16px 14px 18px;
    border-radius: 0 12px 12px 0;
    background: var(--bg2, #161920);
    background:
        linear-gradient(100deg,
            color-mix(in srgb, var(--purple, #6d28d9) 13%, transparent) 0%,
            color-mix(in srgb, var(--purple, #6d28d9) 4%, transparent) 42%,
            transparent 76%),
        var(--bg2, #161920)}

/*  Rail instead of the top hairline, so the two heroes do not even share an
    edge treatment. */
.bv-hero.hero-work::before {
    left: 0; right: auto; top: 0; bottom: 0;
    width: 3px; height: auto;
    background: var(--purple, #6d28d9);
    box-shadow: 0 0 12px -1px var(--purple, #6d28d9);
    opacity: 1}

.bv-hero.hero-work .hero-eyebrow {
    display: block;
    font-family: var(--mono);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--purple, #c4b5fd);
    margin: 0 0 5px}

.bv-hero.hero-work h1 {
    font-size: clamp(16px, 4.2vw, 19px);
    line-height: 1.25;
    text-wrap: balance}

.bv-hero.hero-work .bv-hero-sub {
    margin-top: 6px;
    letter-spacing: .09em}

/* ── Centred page header ──
   For a hub screen whose header names the place rather than labelling a list.
   Same card, same family rail, stacked and centred so it reads as an opening
   rather than a row of controls. */
.page-header.ph-center {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 10px;
    padding: 17px 16px 16px}

.page-header.ph-center > div { width: 100%}
.page-header.ph-center .page-actions { align-self: center}

/*  The rail would sit off to one side of centred text, so a centred header
    takes the hairline the dashboard hero uses instead. */
.page-header.ph-center::before {
    left: 12%; right: 12%; top: 0; bottom: auto;
    width: auto; height: 1px;
    background: linear-gradient(90deg, transparent,
        var(--ph-accent, var(--green)) 50%, transparent);
    box-shadow: none;
    opacity: .6}

.page-header.ph-center { border-left: 1px solid rgba(255,255,255,0.09); border-radius: 12px}

/* ── Centred hero background ──
   A plain panel under centred text looked empty, so the card gets three
   layers, all pure CSS so nothing has to load in the field:

     1. a soft dome of the family accent rising from the top edge,
     2. a fine diagonal weave at very low alpha for texture,
     3. the flat panel underneath.

   The weave is 1px lines on a 7px repeat -- close enough to read as a surface
   rather than as stripes, and it sits under the text at 2.5% so it never
   competes with it. */
.page-header.ph-center {
    background: var(--bg2, #161920);
    background:
        radial-gradient(125% 150% at 50% -30%,
            color-mix(in srgb, var(--ph-accent, var(--green)) 20%, transparent) 0%,
            color-mix(in srgb, var(--ph-accent, var(--green)) 5%, transparent) 38%,
            transparent 66%),
        repeating-linear-gradient(135deg,
            rgba(255,255,255,0.03) 0px,
            rgba(255,255,255,0.03) 1px,
            transparent 1px,
            transparent 7px),
        var(--bg2, #161920);
    overflow: hidden}

/*  A faint bloom behind the title, so the centre of the card is the brightest
    point and the eye lands on the name first. */
.page-header.ph-center .page-title {
    position: relative;
    text-shadow: 0 0 26px color-mix(in srgb, var(--ph-accent, var(--green)) 32%, transparent)}

/*  Corner ticks: two short rules at the lower corners that stop the textured
    panel from ending abruptly. */
.page-header.ph-center::after {
    content: "";
    position: absolute;
    left: 18%; right: 18%; bottom: 0;
    height: 1px;
    background: linear-gradient(90deg, transparent,
        color-mix(in srgb, var(--ph-accent, var(--green)) 45%, transparent) 50%,
        transparent);
    opacity: .5}

/*  first-thing tightening: <main> pads 16px all round, which suits a page that
    opens with text but leaves a visible band above a card meant to sit just
    under the top bar. */
main.main > .comms-row:first-child,
main.main > .page-header:first-child,
main.main > .bv-hero:first-child { margin-top: -6px}
/* ── Booth combobox ──
   A text input bound to a <datalist>: type a number or part of the name and the
   browser narrows the suggestions. Replaces a <select> that listed ~440 booths
   with no way in but scrolling.

   Sized and coloured to match the selects beside it so the filter bar still
   reads as one row of controls, and given the same chevron so it is obviously
   a list and not a free-text field. */
.bc-input {
    min-width: 210px;
    flex: 1 1 210px;
    padding: 7px 28px 7px 11px;
    border-radius: 8px;
    border: 1px solid rgba(255,255,255,0.168);
    background-color: rgba(255,255,255,0.06);
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5' stroke='%23cbd5e1' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 9px center;
    background-size: 12px;
    color: var(--text);
    font-family: inherit;
    font-size: 13px}

.bc-input::placeholder { color: var(--muted2, var(--muted)); opacity: .75}
.bc-input:focus { outline: none; border-color: var(--accent)}

/*  Typed text that does not resolve to a booth yet. Not an error -- it is what
    a half-finished word looks like -- so it warns rather than shouts, and the
    previously chosen booth is still what would submit. */
.bc-input.bc-bad { border-color: var(--amber, #b45309)}

/*  Chromium draws its own dropdown arrow inside a datalist input on some
    builds; ours is already in the background. */
.bc-input::-webkit-calendar-picker-indicator { opacity: 0}

@media (max-width: 480px) {
    .bc-input { min-width: 0; width: 100%; flex-basis: 100%}
}

/* ── Voter card, redesigned ──
   Four bands: locators, names, roll classification, actions -- with a rule
   between each so the card reads as a sequence rather than a paragraph. The
   old card led with "#3" and an EPIC number; a surveyor finds a voter by
   booth and serial, so those lead now. */

.voter-card .vc-r1 {
    display: flex; align-items: baseline; flex-wrap: wrap;
    /*  Spacing is a flex gap, not a separator pseudo-element. The row carries
        more locators than it used to (rank, section, house) and wraps to two
        lines on every phone width; the old `.cell + .cell::before` hairline was
        drawn INSIDE the following cell, so a wrapped line opened with a floating
        1px rule and a 15px indent. A pseudo-element cannot know it starts a
        line - a gap cannot dangle. */
    gap: 4px 14px;
    font-family: var(--mono); font-size: 11.5px; letter-spacing: .06em}
.voter-card .vc-r1 .cell { display: inline-flex; align-items: baseline; white-space: nowrap}
.voter-card .vc-r1 .k { color: var(--muted); text-transform: uppercase; margin-right: 4px}
.voter-card .vc-r1 .v { color: var(--text); font-weight: 700}


/*  The EPIC is an identifier you scan for, not something you read, so it sits
    out at the right rather than taking a line of its own. */
.voter-card .vc-r1 .epic { margin-left: auto; color: var(--muted2); letter-spacing: .04em}

.voter-card .vc-split {
    height: 1px; margin: 9px 0;
    background: linear-gradient(90deg, transparent,
        rgba(255,255,255,0.144) 12%, rgba(255,255,255,0.144) 88%, transparent)}

.voter-card .vc-r2 { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap}
.voter-card .vc-r2 .vc-name { font-size: 17px; font-weight: 700; letter-spacing: -.01em; line-height: 1.2}
.voter-card .vc-r2 .vc-hname { font-size: 14.5px; color: var(--muted2)}

.voter-card .vc-r3 {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    margin-top: 6px; font-size: 13px}
.voter-card .vc-r3 .vc-rel { color: var(--muted2)}
.voter-card .vc-r3 .vc-rel b { color: var(--text); font-weight: 600}
.voter-card .vc-r3 .muted { color: var(--muted); opacity: .75}

.voter-card .vc-chip {
    font-family: var(--mono); font-size: 10.5px; font-weight: 700; letter-spacing: .07em;
    padding: 2px 8px; border-radius: 11px;
    border: 1px solid rgba(255,255,255,0.192);
    color: var(--muted2); white-space: nowrap}
.voter-card .vc-chip.caste {
    color: #fbbf24; border-color: rgba(180,83,9,.4); background: rgba(180,83,9,.08)}

/*  The same four letters the rest of the app uses, so a chip here and a bar on
    an analysis page mean the same thing. */
.voter-card .pdag-P { color: #fbbf24; border-color: rgba(180,83,9,.5);  background: rgba(180,83,9,.10)}
.voter-card .pdag-D { color: #60a5fa; border-color: rgba(29,78,216,.5); background: rgba(29,78,216,.10)}
.voter-card .pdag-A { color: #3fd37e; border-color: rgba(21,128,61,.5); background: rgba(21,128,61,.10)}
.voter-card .pdag-G { color: #c4b5fd; border-color: rgba(109,40,217,.5); background: rgba(109,40,217,.10)}

/*  The number reads before the buttons that act on it, so it can be dictated or
    checked without tapping anything -- which is why it is set larger than the
    surrounding meta text and boxed. At 13px unboxed it sat quieter than the
    action icons beside it, which had the emphasis backwards: the number is the
    content, the icons are what you do with it. */
.voter-card .vc-mob {
    font-size: 15px;
    font-weight: 700;
    letter-spacing: .06em;
    color: var(--green);
    padding: 4px 11px;
    border-radius: 14px;
    border: 1px solid rgba(21,128,61,.42);
    background: rgba(21,128,61,.10);
    white-space: nowrap;
    margin-right: 4px}

/*  No number recorded: same chip, stated rather than shouted. Not hidden -- a
    missing chip would read as "no space for it" instead of "nobody has taken
    this down yet", which is the true claim for nearly every voter so far. */
.voter-card .vc-mob--off {
    font-size: 12.5px;
    font-weight: 400;
    letter-spacing: .04em;
    color: var(--muted);
    border-color: rgba(255,255,255,0.144);
    background: none}

@media (max-width: 380px) {
    .voter-card .vc-mob { font-size: 14px; padding: 3px 9px; letter-spacing: .04em}
}

@media (max-width: 420px) {
    .voter-card .vc-r1 .epic { margin-left: 0; flex-basis: 100%; margin-top: 3px}
}

/* ── Voter card actions, filled ──
   The row used emoji, which arrive at a different weight, size and colour on
   every Android build -- a flat outline receiver on one handset, a shaded 3-D
   one on the next -- so it never read as a single set of controls. These are
   drawn paths on a solid fill, in each service's own colour, so the row is
   recognisable at a glance and identical everywhere. */

.voter-card .vc-act {
    width: 34px; height: 34px;
    border-radius: 50%;
    display: grid; place-items: center;
    border: none;
    text-decoration: none;
    flex: 0 0 auto;
    transition: transform .1s, filter .15s}

.voter-card .vc-act svg { width: 17px; height: 17px; display: block}
.voter-card .vc-act:active { transform: scale(.92)}
.voter-card a.vc-act:hover { filter: brightness(1.12)}

/*  Three clearly separate hues, one per action.

    Call was #22c55e against WhatsApp's #25d366 -- two greens four points apart,
    which at 34px on a dark card were the same button twice. WhatsApp keeps its
    brand green because that is what makes it recognisable, so Call moved to
    teal: adjacent enough to still read as "connect", far enough that the two are
    never confused at a glance.

    Glyphs are near-black rather than white. On these mid-brightness fills a dark
    mark holds its shape at 17px where a light one blooms and loses detail --
    except WhatsApp, whose logo is a single filled path with a cut-out handset
    and needs the light treatment to keep it. */
.voter-card .act-call { background: var(--cyan); color: var(--on-accent)}
.voter-card .act-sms  { background: var(--blue); color: var(--on-accent)}
/*  WhatsApp is one filled path (bubble + handset together), so it takes a light
    glyph on the brand green -- the dark treatment used by the other two would
    swallow the handset cut-out. */
.voter-card .act-wa   { background: #15803d; color: #f5f7fa}

/*  Nothing recorded: the control stays in place, flat and unfilled. Removing it
    would say the app cannot call anyone; this says nobody has taken a number
    down for this voter yet, which is the true claim for nearly all of them. */
.voter-card .act-off {
    background: rgba(255,255,255,0.06);
    color: var(--muted);
    border: 1px solid rgba(255,255,255,0.12);
    opacity: .55}


/*  The 380px rule that shrank these to 31px is gone: smallest hit area
    on the narrowest, touch-only screens. */


/*  Actions on one line: three buttons, then Open pushed to the far edge.
    flex-wrap is off deliberately -- with the number chip and four circles this
    row used to wrap and drop Open onto a line of its own, which read as a
    detached control rather than the end of the row. */
/*  The number must not be the thing that gets cut.
    This row was nowrap with every item pinned at flex:0 0 auto EXCEPT the
    number, so the number was the only thing that could absorb the overflow.
    At 360px the content wants ~307px in ~295px of card, and the result was
    "98765..." beside three full-size icons - the exact inverse of the intent
    stated in VoterCard.razor, that the digits can be read and dictated without
    tapping. Wrapping instead lets the Open pill drop to a second line, which
    costs one row of height on the narrowest phones and nothing above 380px. */
.voter-card .vc-acts { display: flex; align-items: center; gap: 8px; flex-wrap: wrap}
.voter-card .vc-acts .vc-edit { margin-left: auto; flex: 0 0 auto}
.voter-card .vc-mob { flex: 0 0 auto}

/*  Free-text combobox panel (caste). Narrower rows than the booth list -- one
    value per row, no number column -- and the "not on the list" line is
    informational rather than an error: an unlisted caste is a valid answer. */
.ct-list { min-width: 220px}
.ct-item { display: block; font-size: 13.5px}
.ct-list .bc-none { font-style: italic}

/*  The caste combobox panel is positioned against its field wrapper. */
.vr-f { position: relative}

/* ── Booth combobox dropdown ──
   Drawn here rather than by a native <datalist>. On Android the WebView renders
   datalist suggestions as an unstyled list that floats over the page, ignores
   the theme and clips every booth name mid-word -- the number and the name
   arrive as one truncated string. This list is ordinary markup, so it is
   themed, scrollable, and can show the number and the name as two things. */
.bc-host { position: relative; display: inline-flex; flex: 1 1 240px; min-width: 0}

.bc-list {
    position: absolute; left: 0; right: 0; top: calc(100% + 4px);
    z-index: 60;
    max-height: min(52vh, 340px);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    background: var(--bg2, #161920);
    border: 1px solid rgba(255,255,255,0.168);
    border-radius: 10px;
    box-shadow: 0 12px 34px rgba(0, 0, 0, .55);
    padding: 4px}

.bc-item {
    display: flex; align-items: baseline; gap: 9px;
    padding: 9px 10px;
    border-radius: 7px;
    cursor: pointer;
    line-height: 1.25}

.bc-item + .bc-item { border-top: 1px solid rgba(255,255,255,0.06)}
.bc-item:hover, .bc-item.bc-on { background: rgba(255,255,255,0.084)}

/*  The number is the thing being submitted, so it is fixed-width and leads;
    the name wraps to a second line rather than being cut, which is the whole
    complaint about the native list. */
.bc-n {
    font-family: var(--mono); font-size: 12.5px; font-weight: 700;
    color: var(--accent); flex: 0 0 auto; min-width: 30px}
.bc-t { font-size: 13px; color: var(--text); overflow-wrap: anywhere}

.bc-none { padding: 12px 10px; font-size: 13px; color: var(--muted2, var(--muted))}

@media (max-width: 480px) {
    .bc-list { max-height: 46vh}
}

/*  Fixed variant: placed by JS so it can escape the .vr-sec card's
    overflow:hidden, which otherwise clips it to a few pixels. */
.bc-list.ct-fixed { position: fixed; right: auto; z-index: 200}

/* ── password reveal ── */
.pw-wrap { position:relative; display:block}
/*  Room for the eye so a long password does not run under it. */
.pw-input { padding-right:46px}
.pw-eye { position:absolute; top:50%; right:6px; transform:translateY(-50%); width:36px; height:36px; display:flex; align-items:center; justify-content:center; padding:0; background:transparent; border:none; border-radius:8px; color:var(--muted); cursor:pointer; transition:color .15s, background .15s}
.pw-eye:hover { color:var(--text); background:rgba(255,255,255,0.072)}
.pw-eye:focus-visible { outline:2px solid var(--login-accent, var(--accent)); outline-offset:-2px}
.pw-eye svg { width:19px; height:19px; fill:currentColor}
/*  Only one glyph shows at a time: open eye while hidden, struck-through while
    visible, so the icon reports the current state rather than the action. */
.pw-eye .pw-off { display:none}
.pw-eye.on { color:var(--login-accent, var(--accent))}
.pw-eye.on .pw-on { display:none}
.pw-eye.on .pw-off { display:block}

/* ══ ELEVATION ══
   Approved direction, applied at the token layer.

   This is a light to dark flip, so it redefines the palette the whole product
   already reads rather than overriding rules one at a time. Roughly 2,900
   var() call sites pick it up at once, which is why every site turns over
   together instead of drifting apart.

   Depth comes from two shadows on every surface: a tight contact shadow and a
   wide soft one. Colour is spent only where it carries meaning, green for
   gain and red for loss, so the accent never competes with the data.

   Contrast, measured on both grounds rather than assumed. Card value 16.4:1,
   headings 15.8:1, body 12.9:1, muted 4.8:1 at worst, positive 9.1:1, negative 5.9:1,
   link 6.9:1. The direction's own muted grey was 4.07:1 and has been nudged
   from #727A88 to #8e96a6, the smallest change that clears AA. */

:root {
    --bg:      #0e1014;
    --bg2:     #161920;
    --bg3:     #1f232c;
    --bg4:     #2a303c;

    --border:  #232833;
    --border2: #333b49;

    --text:    #f5f7fa;
    --muted:   #8e96a6;
    --muted2:  #aeb6c2;

    --green:   #3fd37e;  --green-dim:  rgba(63,211,126,0.14);
    --red:     #ff5c77;  --red-dim:    rgba(255,92,119,0.14);
    --amber:   #fbbf24;  --amber-dim:  rgba(251,191,36,0.14);
    --blue:    #60a5fa;  --blue-dim:   rgba(96,165,250,0.14);
    --cyan:    #4dd0e1;
    --purple:  #c4b5fd;  --purple-dim: rgba(196,181,253,0.14);

    --primary:   #60a5fa;
    --accent:    #60a5fa;
    --accent-dim: rgba(96,165,250,0.14);
    --on-accent: #0e1014;

    --shadow-sm: 0 1px 2px rgba(0,0,0,.40);
    --shadow:    0 1px 2px rgba(0,0,0,.40), 0 6px 16px -8px rgba(0,0,0,.55);
    --shadow-lg: 0 2px 4px rgba(0,0,0,.45), 0 18px 40px -16px rgba(0,0,0,.65);

    --radius: 11px}

html, body { background: #0e1014; color: #f5f7fa}

/*  Surfaces lift off the ground. The pair of shadows is the whole direction:
    a tight contact edge plus a wide soft one. */
.card, .stat-card, .acard, .tile, .pdag-tile, .progress-box, .table-wrap,
.voter-card, .filter-bar, .surname-cloud, .form-card, .login-card,
.hero-card, .vr-sec, .bv-sec, .panel {
    background: #161920;
    /*  Longhands, not the `border` shorthand. The shorthand also sets
        border-left-width, which flattened the 4px gender rail on every voter
        card and the accent rails on .vr-sec and .bv-sec. */
    border-color: #232833;
    border-style: solid;
    border-width: 1px;
    border-radius: 11px;
    box-shadow: 0 1px 2px rgba(0,0,0,.40), 0 6px 16px -8px rgba(0,0,0,.55)}

/*  A card inside a card was the same #161920 as its parent, so the inner one
    had no visible edge at all. Nested surfaces take the next step up the ramp
    and a lighter rule, which is what makes the boundary read. */
:is(.card, .stat-card, .panel, .vr-sec, .bv-sec, .table-wrap, .form-card,
    .progress-box, .hero-card)
:is(.card, .stat-card, .tile, .acard, .progress-box, .pdag-tile, .voter-card) {
    background: #1f232c;
    border-color: #333b49;
    box-shadow: 0 1px 2px rgba(0,0,0,.35)}

/*  Three deep is rare but happens in the war room panels. */
:is(.card, .stat-card, .panel) :is(.card, .stat-card)
:is(.card, .stat-card, .tile) {
    background: #262b36;
    border-color: #3c4553}

/*  The band is a surface too, one step up from the page. */
.topbar {
    background: #161920;
    border-bottom: 1px solid #232833;
    box-shadow: none;
    color: #f5f7fa}
/*  Six layer scoped rules set the brand to a per tier hue at 0,3,0 -- amber for
    booth, red for assembly and so on -- and outrank a plain .topbar .brand.
    The attribute selector matches whichever one this site emits and ties their
    specificity, and coming later it wins. Colour here would be decoration; in
    this direction colour is reserved for gain and loss. */
.topbar .brand,
.topbar[class*="layer-"] .brand,
.topbar a, .topbar span { color: #f5f7fa; text-shadow: none}
.topbar .brand .brand-sep { color: #8e96a6}

.bottombar {
    background: #161920;
    border-top: 1px solid #232833;
    -webkit-backdrop-filter: none;
    backdrop-filter: none}

/*  ── Dividers, carried over to dark and extended ──
    The stylesheet already had a good splitter: a rule that fades at both ends
    with a diamond marking the centre. Its gradient was rgba(15,23,42,.13),
    dark ink meant for a light page, which on #0e1014 is invisible. Same rule,
    inverted, plus the diamond glow moved off the retired amber.

    It is then extended to the seams that had no markup to hang a splitter on:
    the head of a big card, the top of a nested group, and repeating rows. A
    hairline at those joins is what stops a card reading as one undivided
    block. Deliberately faint at 1.19:1 on the card -- a divider is not content
    and the 3:1 minimum governs controls and meaningful graphics, not rules. */

.split, hr {
    background: linear-gradient(90deg,
        transparent,
        rgba(255,255,255,.14) 18%,
        rgba(255,255,255,.14) 82%,
        transparent)}
.split::after, hr::after {
    background: var(--accent, #60a5fa);
    box-shadow: 0 0 0 4px var(--bg, #0e1014), 0 0 10px rgba(96,165,250,.45)}

/*  Seam under a big card's own heading. Small tiles are excluded below. */
.stat-card > .card-label:first-child,
.card > .card-label:first-child,
.vr-sec > h3:first-child,
.bv-sec > h3:first-child {
    border-bottom: 1px solid #232833;
    padding-bottom: 9px;
    margin-bottom: 11px}

/*  A nested group is a new region, so it opens with a seam. */
.card > .cards-grid,
.stat-card > .cards-grid,
.vr-sec > .cards-grid,
.bv-sec > .cards-grid,
.panel > .cards-grid {
    border-top: 1px solid #232833;
    padding-top: 13px;
    margin-top: 13px}

/*  Repeating rows read as a list once ruled. The last carries none so the
    group does not end on a dangling line. */
.bc-item, .vr-row, .list-row, .feed-item { border-bottom: 1px solid #1b1f27}
.bc-item:last-child, .vr-row:last-child,
.list-row:last-child, .feed-item:last-child { border-bottom: 0}

/*  A tile has one region and needs no seam inside it. */
.pdag-tile > .card-label:first-child,
.tile > .card-label:first-child {
    border-bottom: 0; padding-bottom: 0; margin-bottom: 4px}

/*  Figures lead. A value outranks its own label. */
.card-value, .kpi-val, .pdag-n {
    color: #f5f7fa;
    font-weight: 800;
    letter-spacing: -.025em;
    font-variant-numeric: tabular-nums}
.card-label, .kpi-lbl { color: #8e96a6}
.card-sub { color: #8e96a6}

/*  Deltas carry direction, so a glance gives sign as well as size. */
.c-green .card-sub, .up   { color: #3fd37e}
.c-red   .card-sub, .down { color: #ff5c77}

table th { color: #8e96a6; border-bottom: 1px solid #232833}
table td { color: #d8dde5; border-bottom: 1px solid #1b1f27}

/*  Focus stays visible and never borrows the action colour. */
.btn:focus, .btn:active:focus, .btn:focus-visible,
button:focus-visible, a:focus-visible,
select:focus-visible, input:focus-visible {
    outline: 2px solid #60a5fa;
    outline-offset: 2px;
    box-shadow: none}

/*  Tap targets. A finger needs about 44px; these sat at 31-34px, and a
    @media (max-width:380px) rule made the circles SMALLER on exactly the
    screens that are touch-only. The narrow screen is the touch screen. */
.btn, .pager-btn, .vc-edit,
.filter-bar select, .filter-bar input, .search-box,
.sc-tag, .letter-picker a { min-height: 44px}

.vc-act, .voter-card .vc-act { width: 44px; height: 44px}
