/* ============================================================
   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(185,28,28,.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:var(--red); text-transform:uppercase; text-shadow:0 0 14px rgba(185,28,28,.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(185,28,28,.85); border-color:rgba(185,28,28,.95); transform:translateY(-1px)}

/* Layer-specific brand colors */
.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 ── */
/* Bottom navigation lives at the end of this file — see "Bottom navigation". */

/* ── 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}
/* Optional leading mark for a /voters/* page header, matching the tile that
   links to it. Only the pages that pass VotersHeader an Icon grow one; the
   rest render the title block alone and are unaffected by these rules. */
.page-hd { display:flex; align-items:flex-start; gap:11px; min-width:0}
.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} }

/* ── 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)}
/* Completion thresholds on /booth/completion. Named for the state, not the colour,
   so the table and the KPI cards that count the same 40 / 15 bands cannot drift
   apart. .bar-green is an alias of .bar so all three read alike at the call site. */
.bar-green { background:var(--green)}
.bar-amber { background:var(--amber)}
.bar-red   { background:var(--red)}

/* ── 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}
.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.
   The superseded set that stood here (plus its unused .vc-layout / .vc-srno /
   .vc-content / .vc-row1 / .vc-en two-column layout, referenced by no component)
   has been removed so a single rule set owns the card. */
.vc-edit { flex-shrink:0; padding:4px 12px; border-radius:6px; background:rgba(185,28,28,.10); border:1px solid rgba(185,28,28,.25); color:var(--red); text-decoration:none; font-family:var(--mono); font-size:10px; font-weight:700; white-space:nowrap; transition:all .15s; letter-spacing:.03em}
.vc-edit:hover { background:rgba(185,28,28,.20); border-color:var(--red); box-shadow:0 0 8px rgba(185,28,28,.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(3,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}
.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:var(--red); color:var(--on-accent); border-color:var(--red); 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 ── */

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

/* Voter Card */

/* ── 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)}
.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-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)}

.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}

.vc-edit {
    display: inline-block;
    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; text-decoration: none;
    transition: background .15s ease, color .15s ease}
.vc-edit:hover { background: var(--green); color: var(--on-accent)}
.vc-acts .vc-edit { margin-left: auto}

.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}

/* ── 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. */
.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%}
/* Full-width card: used for wide series (top castes, candidate bars). */
.acard--wide { grid-column:1 / -1}
.acard--failed { border-color:var(--red)}
/* Which table the series actually came from — kept visible, not a tooltip. */
.acard-src { font-family:var(--mono); font-size:8.5px; color:var(--muted); margin-top:7px; padding-top:6px; border-top:1px solid var(--border); letter-spacing:.03em; word-break:break-word}

/* Wide tables must scroll inside their own box, never push the page sideways. */
.table-wrap { overflow-x:auto; -webkit-overflow-scrolling:touch}

/* ── Inline proportion bar (candidate votes, party strength) ── */
.bv-bar { display:block; width:100%; min-width:52px; height:7px; background:var(--bg4); border-radius:4px; overflow:hidden}
.bv-bar > i { display:block; height:100%; border-radius:4px}

/* Honest empty state: says which source was consulted, not "coming soon". */
.bv-nosrc { background:var(--bg2); border:1px dashed var(--border2); border-radius:var(--radius); padding:16px; color:var(--muted); font-size:12.5px; line-height:1.5}
.bv-nosrc b { color:var(--muted2); font-weight:700}
.bv-nosrc .mono { font-family:var(--mono); font-size:11px; color:var(--muted2)}


/* ══════════════════════════════════════════════════════════════════════════
   Bottom navigation

   Four tabs plus a raised centre action. Rebuilt because the previous version
   gave the active tab a full red slab (.bottombar a.active { background:red }),
   which the centre button inherited — it is an <a> too — and rendered as a red
   rectangle behind the circle.

   Notes:
     * Every .bottombar a rule is now scoped to .bb-tab, so the centre button
       cannot pick up tab padding, flex sizing or the active background.
     * 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 no overflow:visible is needed.
   ══════════════════════════════════════════════════════════════════════════ */
.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(--border2);
    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(--green)}
.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(--green)}
.bb-tab:active { color: var(--green); 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 and the bar needs no overflow:visible. */
.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(--green) 0%, #15803d 55%, var(--accent) 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(--green), 0 2px 10px rgba(21,128,61,.4)}

/* "AI" wordmark rather than a glyph — at this size two letters read instantly
   where a spark or star reads as decoration. */
.bb-fab-txt {
    font-family: var(--mono); font-size: 14px; font-weight: 800;
    letter-spacing: .06em; color: #f5f7fa; line-height: 1}

/* ── 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")}

/* 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)}
}

/* ── 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}
}

/* ── 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}
    .bc-item { padding: 11px 10px}   /* a comfortable touch target */
}

/* ── 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}

/* ── 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}

/* ══ Team Logins ══
   The two tier buttons, and the member cards under them.

   One card per login rather than a table: a row of eight columns is unusable
   on the 375px WebView these are read on, and the actions belong beside the
   person rather than in a far-right column the thumb never reaches. */

.ml-tabs { display: flex; flex-wrap: wrap; gap: 9px; align-items: center; margin-bottom: 14px}
.ml-tab { display: inline-flex; align-items: center; gap: 8px}
/*  The selected tier is the accent; the other stays a plain control. */
.ml-tab.on {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent, #0e1014);
    font-weight: 700}
.ml-n {
    font-family: var(--mono); font-size: 11px; font-variant-numeric: tabular-nums;
    padding: 1px 6px; border-radius: 3px; background: rgba(255,255,255,.10)}
.ml-tab.on .ml-n { background: rgba(0,0,0,.18)}

.ml-find {
    flex: 1 1 190px; min-width: 150px; padding: 9px 12px;
    background: var(--bg3); color: var(--text);
    border: 1px solid var(--border2); border-radius: var(--radius);
    font-size: 13px}

.ml-list { display: grid; gap: 12px}
.ml-card { display: grid; gap: 10px}

/*  Auto-fit rather than a fixed column count: three fields fit side by side on
    a tablet and stack on a phone without a second breakpoint. */
.ml-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 10px 14px}
.ml-f { display: flex; flex-direction: column; gap: 2px; min-width: 0}
.ml-k {
    font-family: var(--mono); font-size: 10px; letter-spacing: .12em;
    text-transform: uppercase; color: var(--muted)}
.ml-v { font-size: 13.5px; color: var(--text); overflow-wrap: anywhere}
.ml-none { font-size: 13px; color: var(--muted); font-style: italic}

.ml-edit { display: grid; gap: 9px}
.ml-in {
    padding: 9px 12px; background: var(--bg3); color: var(--text);
    border: 1px solid var(--border2); border-radius: var(--radius); font-size: 13.5px}
.ml-act { display: flex; flex-wrap: wrap; gap: 8px; align-items: center}

/*  The disclosure panel. Marked out rather than blended in: a password is on
    screen and the boundary is the point. */
.ml-cred {
    display: grid; gap: 10px; padding: 12px;
    background: var(--bg3); border: 1px solid var(--accent);
    border-radius: var(--radius)}
.ml-issued { display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; align-items: baseline}
.ml-code {
    font-family: var(--mono); font-size: 15px; font-weight: 700;
    color: var(--text); overflow-wrap: anywhere; user-select: all}

/*  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}
