:root {
  color-scheme: dark;
}

/* ── GLOBAL THEME ── */
body {
  background: #0a1628 !important;
  background-image:
    radial-gradient(ellipse 1000px 600px at 20% 10%, rgba(56, 100, 180, 0.35) 0%, transparent 60%),
    radial-gradient(ellipse 800px 500px at 80% 90%, rgba(30, 60, 140, 0.25) 0%, transparent 60%),
    radial-gradient(ellipse 600px 400px at 60% 40%, rgba(20, 80, 120, 0.15) 0%, transparent 55%) !important;
  background-attachment: fixed !important;
  color: white;
  font-family: 'Inter', sans-serif;
  margin: 0;
  -webkit-font-smoothing: antialiased;
}

/* ── PAGE LAYOUT ── */
.bv-page {
  padding: 40px;
  min-height: 100vh;
  box-sizing: border-box;
}

/* A band for a system-level notice sitting ABOVE the page content — today the
   ledger-halt banner. Deliberately not .bv-page: that carries min-height:100vh,
   so wrapping a short notice in one leaves a screen-tall empty gap between the
   notice and the page beneath it. Measured at 794px of blank space on the admin
   dashboard before this existed. Horizontal padding matches .bv-page so the
   notice lines up with the content under it. */
.bv-notice-band {
  padding: 40px 40px 0;
  box-sizing: border-box;
}

@media (max-width: 1023.98px) {
  .bv-notice-band {
    padding: 20px 20px 0;
  }
}

.bv-auth-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
}

.bv-dashboard-layout {
  display: flex;
  height: 100vh;
  width: 100vw;
  overflow: hidden;
  position: relative;
  z-index: 10;
}

.bv-main-content {
  flex-grow: 1;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
  background: transparent;
}

.bv-page-header {
  margin-bottom: 32px;
}

.bv-page-header h1 {
  color: white;
  font-size: 2rem;
  font-weight: 900;
  margin: 0 0 8px 0;
  letter-spacing: -0.02em;
}

.bv-page-header p {
  /* 0.6 like .bv-page-subheading, which this out-specifies: at 0.4 every
     page's subheading measured 3.8:1 whatever that rule said. */
  color: rgba(255,255,255,0.6);
  margin: 0;
  font-size: 14px;
}

/* A header with no subtitle under it. Both `.bv-page-header h1` and
   `.bv-page-title` set an 8px bottom margin to clear that paragraph, so
   dropping the paragraph left the gap behind. (0,2,1) out-specifies both
   without reaching for !important. */
.bv-page-header h1:last-child {
  margin-bottom: 0;
}

/* ── GRID LAYOUTS ── */
.bv-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.bv-grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 24px; }
.bv-grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
.bv-grid-auto { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }

/* ══ DRAWER OR SIDEBAR: the device decides, not just the width ═══════════
   Until 2026-09-16 this was width alone: 1024px and up got the fixed
   sidebar. So an iPad held sideways (1024-1366px) was treated as a desktop
   with a mouse -- the 268px sidebar stayed open, the election and registry
   tables had to be scrolled sideways (measured 900px of table in a 669px
   box), and every control stayed mouse-sized.

   `pointer: coarse` is the browser reporting that the PRIMARY input is a
   finger. It is what separates a tablet from a laptop of the same width,
   and unlike user-agent sniffing it cannot be fooled by iPadOS, which
   reports itself as a Mac. A touchscreen laptop still reports `fine` for
   its trackpad, so it keeps the sidebar.

   The two conditions, used verbatim wherever the layout branches:

     DRAWER   (max-width: 1023.98px), (pointer: coarse) and (max-width: 1366px)
     SIDEBAR  (min-width: 1024px) and (pointer: fine),
              (min-width: 1024px) and (pointer: none),
              (min-width: 1367px)

   1366 is the widest iPad (12.9" Pro, landscape). The SIDEBAR list is
   written out rather than as `not (pointer: coarse)` because that Level 4
   syntax fails to parse on iPadOS before 16.4, and a media query that
   fails to parse matches nothing -- the desktop rules would vanish.

   The same DRAWER string is duplicated in layouts/dashboard.blade.php for
   the script that closes the drawer. Change both together. */
.bv-hamburger { display: block; }
@media (min-width: 1024px) and (pointer: fine), (min-width: 1024px) and (pointer: none), (min-width: 1367px) {
  .bv-hamburger { display: none !important; }
}

/* ── TRUE FROSTED GLASS CARDS ── */
.bv-card, .glass-card, .glass-panel, .bv-card--lg {
  background: rgba(255, 255, 255, 0.085) !important;
  /* No backdrop-filter: this cost one compositor layer PER CARD, and the
     backdrop is an 80px-blurred gradient with no detail left to frost. */
  border: 1px solid rgba(255, 255, 255, 0.10) !important;
  border-top: 1px solid rgba(255, 255, 255, 0.18) !important;
  border-radius: 16px !important;
  box-shadow: 0 8px 32px rgba(0,0,0,0.2), inset 0 1px 0 rgba(255,255,255,0.08) !important;
  transition: transform 0.2s, border-color 0.2s;
}

.bv-card--accent {
  border-top: 2px solid rgba(255, 180, 0, 0.4) !important;
}

.bv-minimal-card {
  width: 400px;
  padding: 48px 40px;
  background: rgba(255, 255, 255, 0.03) !important;
  backdrop-filter: blur(32px) saturate(200%) !important;
  -webkit-backdrop-filter: blur(32px) saturate(200%) !important;
  border: 1px solid rgba(255, 255, 255, 0.06) !important;
  border-top: 1px solid rgba(255, 255, 255, 0.12) !important;
  border-radius: 20px !important;
  box-shadow: 0 32px 64px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.08) !important;
  box-sizing: border-box;
}

.bv-login-logo-container {
  text-align: center;
  margin-bottom: 20px;
}

.bv-login-logo {
  height: 44px;
  width: auto;
  display: inline-block;
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
}

.bv-login-title {
  font-size: 22px;
  font-weight: 700;
  color: white;
  text-align: center;
  letter-spacing: -0.02em;
  margin-bottom: 4px;
  margin-top: 20px;
}

.bv-login-subtitle {
  font-size: 10px;
  letter-spacing: 0.2em;
  color: rgba(255, 255, 255, 0.52);
  text-align: center;
  margin-bottom: 32px;
  text-transform: uppercase;
  font-weight: 700;
}

.bv-login-form-group {
  margin-bottom: 16px;
}

.bv-login-form-group--password {
  margin-bottom: 20px;
}

.bv-card__body {
  padding: 2rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.bv-card__header {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.bv-card__icon {
  width: 52px;
  height: 52px;
  background: rgba(255, 180, 0, 0.1);
  border: 1px solid rgba(255, 180, 0, 0.2);
  color: rgba(255, 180, 0, 0.8);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  border-radius: 8px;
  flex-shrink: 0;
}

/* ── Inline Icon Helpers ──────────────────────────────────────────
   Tailwind emits no utilities in this build (v3 directives in app.css
   under the v4 vite plugin), so mr-2 / text-xs / text-4xl are inert.
   Icons that sit inline with text use these instead. Colour helpers
   exist because the CSP has no style-src-attr, which drops style="".
   Flex containers (.bv-nav-item, .bv-btn) already space their icons
   with `gap` — do not add --lead there.                            */
.bv-icon--lead   { margin-right: 0.5rem; }
.bv-icon--trail  { margin-left: 0.5rem; }
.bv-icon--sm     { font-size: 0.75rem; }
.bv-icon--lg     { font-size: 1.25rem; }
.bv-icon--xl     { font-size: 2rem; }
.bv-icon--2xl    { font-size: 3rem; }
.bv-icon--muted  { opacity: 0.35; }
.bv-icon--gold   { color: #FFB400; }
.bv-icon--green  { color: #22c55e; }
.bv-icon--red    { color: #ef4444; }
.bv-icon--blue   { color: #3b82f6; }
.bv-icon--indigo { color: #6366f1; }
.bv-icon--amber  { color: #f59e0b; }

/* Card icon size + colour variants (were inline styles the CSP now drops). */
.bv-card__icon--sm { width: 32px; height: 32px; font-size: 0.75rem; border: none; }
.bv-card__icon--blue  { background: rgba(59,130,246,0.15);  border-color: rgba(59,130,246,0.3);  color: #3b82f6; }
.bv-card__icon--green { background: rgba(34,197,94,0.15);   border-color: rgba(34,197,94,0.3);   color: #22c55e; }
.bv-card__icon--red   { background: rgba(239,68,68,0.15);   border-color: rgba(239,68,68,0.3);   color: #ef4444; }
.bv-card__icon--amber { background: rgba(245,158,11,0.15); border-color: rgba(245,158,11,0.3); color: #f59e0b; }
.bv-card__icon--crimson { background: rgba(153,27,27,0.18);  border-color: rgba(153,27,27,0.4);   color: #dc2626; }

.bv-card__meta {
  font-size: 11px;
  font-weight: 800;
  color: rgba(255,255,255,0.52);
  text-transform: uppercase;
  letter-spacing: 0.15em;
  margin: 0 0 4px 0;
}

.bv-card__title {
  font-size: 18px;
  font-weight: 700;
  color: rgba(255,255,255,0.9);
  margin: 0;
}

.bv-card__stat {
  font-size: 1.1rem;
  font-weight: 600;
  color: white;
  margin-bottom: 8px;
}

.bv-card__desc {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.5);
  line-height: 1.7;
  margin: 0;
}

.bv-card__actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* ── BUTTONS ── */
.bv-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 14px 20px;
  border-radius: 10px;
  border: none;
  cursor: pointer;
  transition: all 0.2s;
  width: 100%;
  text-align: center;
}

.bv-btn--primary, .primary-yellow-btn {
  width: 100% !important;
  padding: 13px 20px !important;
  background: rgba(59, 130, 246, 0.2) !important;
  border: 1px solid rgba(100, 150, 255, 0.3) !important;
  color: white !important;
  backdrop-filter: blur(8px) !important;
  border-radius: 10px !important;
  font-weight: 700 !important;
  letter-spacing: 0.12em !important;
  text-transform: uppercase !important;
  text-align: center !important;
  cursor: pointer !important;
  /* flex, not block. `.bv-btn` sets `gap: 8px` to space an icon from its
     label, and gap only applies to a flex or grid container — `display: block`
     made it inert, so the icon sat flush against the first letter on all 16
     icon-bearing primary buttons ("VERIFY CHAIN INTEGRITY", "Execute Batch
     Import", "Add Candidate", …). align-items/justify-content are repeated
     here rather than inherited from `.bv-btn` so the rule does not depend on
     that class also being present. Width is untouched: ~20 views rely on
     `width: 100%`. */
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  margin-bottom: 12px !important;
}

.bv-btn--primary:hover, .primary-yellow-btn:hover {
  background: rgba(59, 130, 246, 0.4) !important;
  border-color: rgba(100, 150, 255, 0.5) !important;
}

.bv-btn--google {
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 10px;
  color: rgba(255, 255, 255, 0.7);
  font-size: 13px;
  font-weight: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: 12px;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s;
  box-sizing: border-box;
}

.bv-btn--google:hover {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.15);
}

.bv-btn--logout {
  background: transparent !important;
  border: none !important;
  color: rgba(255, 255, 255, 0.52) !important;
  padding: 10px 16px !important;
  font-size: 12px !important;
  font-weight: 600 !important;
  letter-spacing: 0.08em !important;
  text-transform: uppercase !important;
  display: flex !important;
  align-items: center !important;
  gap: 8px !important;
  cursor: pointer !important;
  transition: color 0.2s !important;
  text-decoration: none !important;
  width: 100% !important;
  justify-content: center !important;
}

.bv-btn--logout:hover {
  background: transparent !important;
  color: #ef4444 !important;
}

.bv-btn--secondary {
  background: transparent;
  color: rgba(255,255,255,0.6);
  padding: 10px;
}

.bv-btn--secondary:hover {
  color: white;
}

.bv-btn--danger {
  background: rgba(239, 68, 68, 0.1);
  border: 1px solid rgba(239, 68, 68, 0.3);
  color: #f87171;
}

.bv-btn--danger:hover {
  background: rgba(239, 68, 68, 0.2);
}

.bv-btn--signout {
  background: rgba(239, 68, 68, 0.08);
  border: 1px solid rgba(239, 68, 68, 0.2);
  color: rgba(239, 68, 68, 0.6);
  border-radius: 8px;
  padding: 14px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  cursor: pointer;
  transition: all 0.2s;
  text-decoration: none;
}
.bv-btn--signout:hover {
  background: rgba(239, 68, 68, 0.15);
  border-color: rgba(239, 68, 68, 0.4);
  color: #ef4444;
}

.bv-btn--ghost {
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.07);
  color: rgba(255,255,255,0.7);
}

/* ── BADGES ── */
.bv-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

/* #4ade80 text: #22c55e on its own tint measured 4.4:1 at badge size. */
.bv-badge--green {
  background: rgba(34, 197, 94, 0.2) !important;
  border: 1px solid rgba(34, 197, 94, 0.4) !important;
  color: #4ade80 !important;
}

.bv-badge--gold {
  background: rgba(255, 180, 0, 0.2) !important;
  border: 1px solid rgba(255, 180, 0, 0.4) !important;
  color: #FFB400 !important;
}

.bv-badge--red {
  background: rgba(239, 68, 68, 0.15) !important;
  border: 1px solid rgba(239, 68, 68, 0.3) !important;
  color: #f87171 !important;
}

/* Full-width alert banner (login / dashboard flash messages) */
.bv-alert {
  width: 100%;
  padding: 14px 16px;
  border-radius: 8px;
  text-align: left;
  box-sizing: border-box;
  margin-bottom: 20px;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
}

.bv-alert--green {
  background: rgba(34, 197, 94, 0.15);
  border: 1px solid rgba(34, 197, 94, 0.35);
  color: #22c55e;
}

.bv-alert--red {
  background: rgba(239, 68, 68, 0.12);
  border: 1px solid rgba(239, 68, 68, 0.3);
  color: #f87171;
}

/* The two grades between "worked" and "failed". Added 2026-09-12 with
   partials/flash.blade.php: until then the layouts rendered one key and
   painted it green whatever it said, so a refusal and a success were the
   same screen. bv-auth.blade.php carried a local --amber in its own
   <style> block; this is the one every layout can reach. */
.bv-alert--amber {
  background: rgba(245, 158, 11, 0.12);
  border: 1px solid rgba(245, 158, 11, 0.35);
  color: #F59E0B;
}

.bv-alert--blue {
  background: rgba(59, 130, 246, 0.12);
  border: 1px solid rgba(59, 130, 246, 0.3);
  color: #60a5fa;
}

/* Announced, not drawn. Colour alone cannot carry the difference between
   done and failed, so each alert states its grade in text that only a
   screen reader reaches. Tailwind's sr-only emits no rule in this build. */
.bv-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.bv-badge--amber {
  background: rgba(245, 158, 11, 0.15);
  border: 1px solid rgba(245, 158, 11, 0.4);
  color: #F59E0B;
}

/* Text at 0.72, border at 0.2. The grey pill is a real status now
   (Concluded, Draft Mode, Archived), and at 0.4 it measured 3.6:1. */
.bv-badge--outline {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.2);
  color: rgba(255, 255, 255, 0.72);
}

.bv-badge--vote-cast {
  background: rgba(34, 197, 94, 0.12) !important;
  border: 1px solid rgba(34, 197, 94, 0.3) !important;
  color: #22c55e !important;
  padding: 4px 14px !important;
  border-radius: 999px !important;
  font-size: 11px !important;
  font-weight: 700 !important;
  white-space: nowrap !important;
  display: inline-flex !important;
  align-items: center !important;
  gap: 5px !important;
  width: auto !important;
  max-width: fit-content !important;
}

.bv-badge--ballot-cast {
  white-space: nowrap !important;
  padding: 4px 12px !important;
  border-radius: 999px !important;
  font-size: 11px !important;
  font-weight: 700 !important;
  background: rgba(255, 180, 0, 0.12) !important;
  border: 1px solid rgba(255, 180, 0, 0.3) !important;
  color: #FFB400 !important;
  display: inline-block !important;
  width: auto !important;
}

.bv-badge--active {
  background: rgba(34, 197, 94, 0.15) !important;
  border: 1px solid rgba(34, 197, 94, 0.4) !important;
  color: #22c55e !important;
  padding: 4px 12px !important;
  border-radius: 999px !important;
  font-size: 11px !important;
  font-weight: 700 !important;
  display: inline-block !important;
  width: auto !important;
}

/* ── CANDIDATE BALLOT CARDS ── */
.bv-candidate-card {
  cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.10) !important;
  transition: border-color 0.2s, box-shadow 0.2s, transform 0.15s !important;
}

.bv-candidate-card:hover {
  border-color: rgba(255, 180, 0, 0.3) !important;
}

/* Selection ring indicator */
.bv-candidate-card__selector {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid rgba(255, 180, 0, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: border-color 0.2s;
}

/* Hide native inputs robustly */
input.peer.hidden {
  opacity: 0 !important;
  position: absolute !important;
  width: 0 !important;
  height: 0 !important;
  pointer-events: none !important;
}


.bv-candidate-card__selector-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #FFB400;
  opacity: 0;
  transform: scale(0);
  transition: opacity 0.2s, transform 0.2s;
}

/* Gold bottom bar — hidden by default */
.bv-candidate-card__bar {
  height: 3px;
  background: #FFB400;
  border-radius: 99px;
  margin-top: 16px;
  width: 0%;
  transition: width 0.3s ease-in-out;
  box-shadow: 0 0 8px rgba(255, 180, 0, 0.4);
}

/* Selected state — driven by peer checkbox */
label:has(input:checked) .bv-candidate-card {
  border-color: #FFB400 !important;
  background: rgba(255, 180, 0, 0.05) !important;
  box-shadow: 0 0 24px rgba(255, 180, 0, 0.12) !important;
}

label:has(input:checked) .bv-candidate-card__selector {
  border-color: #FFB400;
}

label:has(input:checked) .bv-candidate-card__selector-dot {
  opacity: 1;
  transform: scale(1);
}

label:has(input:checked) .bv-candidate-card__bar {
  width: 100%;
}

/* ── AVATAR ── */
.bv-avatar {
  width: 36px !important;
  height: 36px !important;
  min-width: 36px !important;
  min-height: 36px !important;
  border-radius: 50% !important;
  aspect-ratio: 1 / 1 !important;
  background: rgba(255, 180, 0, 0.05);
  border: 1px solid rgba(255, 180, 0, 0.2);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
  color: #FFB400;
  flex-shrink: 0;
}

.bv-avatar--lg {
  width: 52px;
  height: 52px;
  font-size: 16px;
}

/* ── INPUTS ── */
input, textarea, select, .bv-input {
  width: 100%;
  background: rgba(255, 255, 255, 0.03) !important;
  border: 1px solid rgba(255, 255, 255, 0.07) !important;
  border-radius: 10px !important;
  color: white !important;
  padding: 12px 16px !important;
  font-size: 14px;
  color-scheme: dark !important;
  transition: border-color 0.2s, box-shadow 0.2s !important;
  box-sizing: border-box !important;
}

textarea, textarea.bv-input {
  max-width: 100% !important;
  box-sizing: border-box !important;
  resize: vertical !important;
}

input:focus, textarea:focus, select:focus, .bv-input:focus {
  outline: none !important;
  border-color: rgba(100, 150, 255, 0.4) !important;
  box-shadow: 0 0 0 3px rgba(100, 150, 255, 0.08) !important;
}

.bv-input::placeholder {
  color: rgba(255, 255, 255, 0.52);
}

.bv-label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.6);
  text-transform: uppercase;
  letter-spacing: 0.15em;
  margin-bottom: 6px;
}

/* ── TABLE ── */
.bv-table {
  width: 100%;
  border-collapse: collapse;
}

.bv-table th {
  color: rgba(255, 255, 255, 0.52) !important;
  font-size: 10px !important;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em !important;
  padding: 16px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  text-align: left;
}

.bv-table td {
  padding: 16px;
  font-size: 14px;
  color: rgba(255, 255, 255, 0.8);
  border-bottom: 1px solid rgba(255, 255, 255, 0.03);
}

.bv-table tr:hover td {
  background: rgba(255, 255, 255, 0.01);
}

/* ── STATUS DOT ── */
.bv-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  display: inline-block;
  flex-shrink: 0;
}

.bv-dot--green {
  background: #22c55e;
  animation: bv-pulse 2s infinite;
}

.bv-dot--gold {
  background: #FFB400;
  animation: bv-pulse 2s infinite;
}

/* ── SECTION ── */
.bv-audit-section {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.bv-stat-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  margin-bottom: 20px;
}

.bv-profile-header {
  margin-bottom: 24px;
}

.bv-profile-avatar-label {
    display: block;
    cursor: pointer;
    overflow: hidden;
    border-radius: 50%;
    transition: transform 0.3s;
    border: 2px solid #FFB400;
    box-shadow: 0 10px 30px rgba(0,0,0,0.4);
    width: 80px;
    height: 80px;
    margin: 0;
    position: relative;
    outline: none !important;
}

.bv-profile-avatar-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    border-radius: 50%;
}

.bv-profile-avatar-placeholder {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 2rem;
    color: #FFB400;
    background: rgba(255, 180, 0, 0.1);
}

.bv-avatar-trigger {
    position: absolute;
    bottom: 0;
    right: 0;
    background: #FFB400;
    color: #0a1628;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 10px;
    border: 2px solid #0a1628;
    box-shadow: 0 2px 10px rgba(0,0,0,0.4);
    z-index: 5;
}

@media (max-width: 1023.98px) {
    .bv-profile-avatar-label {
        width: 64px;
        height: 64px;
    }
    .bv-profile-avatar-placeholder {
        font-size: 1.5rem;
    }
    .bv-avatar-trigger {
        width: 20px;
        height: 20px;
        font-size: 8px;
    }
}

.bv-profile-grid {
  display: grid;
  grid-template-columns: 1fr 400px;
  gap: 20px;
  align-items: start;
}

.bv-section {
  margin-bottom: 40px;
}

.bv-section__heading, .bv-section-heading {
  color: rgba(255, 255, 255, 0.5) !important;
  font-size: 11px !important;
  font-weight: 700 !important;
  text-transform: uppercase;
  letter-spacing: 0.15em !important;
  margin-bottom: 16px;
  padding-bottom: 8px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.bv-page-title {
  color: #FFB400 !important;
  font-size: 1.4rem !important;
  font-weight: 800 !important;
  letter-spacing: 0.05em !important;
  text-transform: uppercase !important;
  margin: 0 0 8px 0;
}

.bv-page-heading {
  font-size: 1.8rem;
  font-weight: 800;
  color: white;
  letter-spacing: -0.02em;
  margin: 0 0 6px 0;
}

.bv-page-subheading {
  font-size: 13px;
  /* Was 0.4, which is under 4.5:1 for 13px text on the page background. */
  color: rgba(255, 255, 255, 0.6);
  margin: 0 0 24px 0;
  font-weight: 400;
}

.bv-btn--verify {
  background: rgba(59, 130, 246, 0.2);
  border: 1px solid rgba(100, 150, 255, 0.3);
  color: white;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 12px 24px;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.2s;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.bv-btn--verify:hover {
  background: rgba(59, 130, 246, 0.35);
  border-color: rgba(100, 150, 255, 0.5);
  box-shadow: 0 0 16px rgba(59, 130, 246, 0.2);
}

.bv-divider {
  border: none;
  border-top: 1px solid rgba(255, 255, 255, 0.05);
  margin: 24px 0;
}

.bv-login-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 24px 0;
}

.bv-login-divider-line {
  flex: 1;
  height: 1px;
  background: rgba(255, 255, 255, 0.06);
}

.bv-login-divider-text {
  font-size: 10px;
  color: rgba(255, 255, 255, 0.52);
  letter-spacing: 0.1em;
  font-weight: 700;
}

.bv-login-footer {
  text-align: center;
  font-size: 9px;
  letter-spacing: 0.2em;
  color: rgba(255, 255, 255, 0.52);
  margin-top: 32px;
  text-transform: uppercase;
  font-weight: 700;
}

/* ── HASH / MONO ── */
.bv-hash {
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.52);
}

/* ── BRANDING ── */
.bv-logo-container {
  background: transparent !important;
  border: none !important;
  padding: 0;
  margin-bottom: 24px;
  display: inline-block;
}

.bv-logo, .login-logo {
  height: 40px !important;
  width: auto !important;
  display: block !important;
  margin: 0 auto !important;
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  padding: 0 !important;
}

/* ── DASHBOARD UI ── */
.bv-sidebar {
  width: 268px;
  flex-shrink: 0;
  background: rgba(7, 14, 38, 0.92) !important;
  /* No backdrop-filter: at 82% opaque it frosted only ~18% of each pixel,
     yet every hover repaint inside re-blurred this whole 268px column. */
  border-right: 1px solid rgba(255, 255, 255, 0.06) !important;
  display: flex;
  flex-direction: column;
  height: 100%;
  z-index: 50;
}

/* Header */
.bv-sidebar__header {
  padding: 1.5rem 1.5rem 1.25rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.bv-sidebar__brand {
  font-size: 1.15rem;
  font-weight: 800;
  color: #ffffff;
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  letter-spacing: -0.01em;
  transition: opacity 0.2s;
}

.bv-sidebar__brand:hover { opacity: 0.85; }

.bv-sidebar__brand-icon {
  font-size: 1.3rem;
  line-height: 1;
}

/* Wordmark: one flex item, so "Bee" and "ote" are not pushed apart by the
   parent's gap. The V is gold and tilted; inline-block is required because
   transforms do not apply to inline boxes. */
.bv-sidebar__wordmark {
  display: inline-block;
  white-space: nowrap;
}

.bv-sidebar__brand-v {
  display: inline-block;
  color: #FFB400;
  font-weight: 900;
  transform: rotate(-8deg);
  transform-origin: 50% 65%;
}

/* User Card */
.bv-sidebar__user {
  padding: 1rem 1.25rem;
  display: flex;
  align-items: center;
  gap: 0.85rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  background: rgba(255, 255, 255, 0.02);
}

.bv-sidebar__avatar {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid rgba(255, 180, 0, 0.6);
  flex-shrink: 0;
}

.bv-sidebar__avatar-placeholder {
  width: 38px !important;
  height: 38px !important;
  flex-shrink: 0;
}

.bv-sidebar__user-info {
  flex: 1;
  min-width: 0;
}

.bv-sidebar__user-name {
  font-size: 0.85rem;
  font-weight: 700;
  color: #ffffff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.2;
}

.bv-sidebar__user-role {
  margin-top: 4px;
}

.bv-sidebar__role-badge {
  display: inline-block;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 3px 10px;
  border-radius: 999px;
}

/* One hue per role, never shared. Admin had been moved onto COMELEC's gold,
   which made the two badges identical; the light theme still carried red. */
.bv-sidebar__role-badge--admin    { background: rgba(239,68,68,0.15); color: #f87171; border: 1px solid rgba(239,68,68,0.35); }
.bv-sidebar__role-badge--comelec  { background: rgba(255,180,0,0.15); color: #FFB400; border: 1px solid rgba(255,180,0,0.35); }
.bv-sidebar__role-badge--voter    { background: rgba(59,130,246,0.18); color: #60a5fa; border: 1px solid rgba(59,130,246,0.35); }
.bv-sidebar__role-badge--judge    { background: rgba(168,85,247,0.18); color: #c084fc; border: 1px solid rgba(168,85,247,0.35); }
.bv-sidebar__role-badge--organizer { background: rgba(16,185,129,0.18); color: #34d399; border: 1px solid rgba(16,185,129,0.35); }

/* Nav */
.bv-sidebar__nav {
  padding: 0.5rem 0 1rem;
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(255,255,255,0.08) transparent;
}

.bv-sidebar__nav::-webkit-scrollbar { width: 4px; }
.bv-sidebar__nav::-webkit-scrollbar-track { background: transparent; }
.bv-sidebar__nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 4px; }

/* Section Labels */
.bv-sidebar__section-label {
  padding: 1.1rem 1.5rem 0.35rem;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.70);
}

/* The event the two shortcuts below it point at. Named rather than left
   implicit, because both links resolve to one event and an organizer
   running two in a week needs to see which. */
.bv-sidebar__event-name {
  padding: 0 1.5rem 0.5rem;
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.35;
  color: rgba(255, 180, 0, 0.85);
}

/* Nav Items */
.bv-nav-item {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding: 0.65rem 1.25rem 0.65rem 1.5rem;
  color: rgba(255, 255, 255, 0.78);
  text-decoration: none;
  transition: background-color 0.18s ease, color 0.18s ease, border-left-color 0.18s ease;
  font-weight: 600;
  font-size: 0.875rem;
  border-left: 3px solid transparent;
  border-radius: 0 8px 8px 0;
  margin: 1px 0.75rem 1px 0;
  position: relative;
}

.bv-nav-item__icon {
  font-size: 1rem;
  width: 22px;
  text-align: center;
  flex-shrink: 0;
  opacity: 0.85;
  transition: opacity 0.18s ease;
}

.bv-nav-item:hover {
  color: #ffffff;
  background: rgba(255, 255, 255, 0.08);
  border-left-color: rgba(255, 255, 255, 0.3);
}

.bv-nav-item:hover .bv-nav-item__icon {
  opacity: 1;
}

.bv-nav-item--active {
  background: rgba(255, 180, 0, 0.12) !important;
  color: #FFB400 !important;
  border-left: 3px solid #FFB400 !important;
  font-weight: 700;
}

.bv-nav-item--active .bv-nav-item__icon {
  opacity: 1;
}

.bv-nav-item:active {
  background: rgba(255, 255, 255, 0.12);
}

/* Footer / Logout
   An outlined button, neutral at rest. It used to be faint text with no
   border, which read as a disabled nav item; a red box overcorrects, since
   signing out destroys nothing. Red is kept for hover and keyboard focus.
   The left padding puts its icon on the same line as the nav icons above:
   12px footer + 1px border + 14px = the 3px rule + 1.5rem of .bv-nav-item. */
.bv-sidebar__footer {
  padding: 1rem 0.75rem 1.25rem;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.bv-sidebar__logout {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0.875rem;
  width: 100%;
  padding: 0.65rem 1rem 0.65rem calc(0.75rem + 2px);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px;
  color: rgba(255, 255, 255, 0.78);
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease;
  box-sizing: border-box;
  margin: 0;
}

.bv-sidebar__logout .bv-nav-item__icon {
  opacity: 0.85;
}

.bv-sidebar__logout:hover,
.bv-sidebar__logout:focus-visible {
  color: #ffffff;
  background: rgba(239, 68, 68, 0.16);
  border-color: rgba(239, 68, 68, 0.45);
}

.bv-sidebar__logout:hover .bv-nav-item__icon,
.bv-sidebar__logout:focus-visible .bv-nav-item__icon {
  opacity: 1;
  color: #f87171;
}

.bv-sidebar__logout:active {
  background: rgba(239, 68, 68, 0.26);
}

/* Keyboard focus. The rail had no focus style at all, so tabbing through
   it gave no sign of where you were. Inset, because .bv-sidebar__nav
   scrolls and would clip an outline drawn outside the item. */
.bv-nav-item:focus-visible,
.bv-sidebar__logout:focus-visible {
  outline: 2px solid #FFB400;
  outline-offset: -2px;
}

/* ── PUBLIC ANCHORS ── */
.bv-public-anchors {
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 12px;
  padding: 24px 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 80px;
}

.bv-public-anchors__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #FFB400;
  flex-shrink: 0;
  animation: bv-pulse 1.5s infinite;
}

.bv-public-anchors__text {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: rgba(255, 255, 255, 0.6);
  text-transform: uppercase;
}

.bv-section-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: rgba(255, 255, 255, 0.52);
  text-transform: uppercase;
  margin-bottom: 10px;
}

.bv-topbar {
  min-height: 70px;
  padding: 20px 32px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: rgba(6, 13, 26, 0.78) !important;
  /* No backdrop-filter: same trap as the sidebar - a hover repaint inside a
     blurred surface re-blurs the entire surface, every frame of it. */
  border-bottom: 1px solid rgba(255, 255, 255, 0.05) !important;
  position: sticky;
  top: 0;
  z-index: 100;
}

.bv-topbar__title {
  font-size: 1.1rem;
  font-weight: 800;
  color: rgba(255, 255, 255, 0.8);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.bv-topbar-title {
  font-size: 1.5rem;
  font-weight: 800;
  color: #FFB400;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  margin: 0;
  line-height: 1;
}

/* The drawer used to be declared twice: once here at max-width:1024px (280px,
   no !important) and again at max-width:1023px (80%/300px, !important). At
   exactly 1024px this block AND the min-width:1024px desktop block both
   matched, so the sidebar became a fixed overlay that was simultaneously
   forced visible — it covered the page. The 1023px block is the authoritative
   one; this duplicate is removed rather than left to fight with it. */

@keyframes bv-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.4; transform: scale(0.8); }
}

@keyframes bv-simulation-pulse {
  0% { transform: scale(1); opacity: 0.5; }
  100% { transform: scale(1.5); opacity: 0; }
}

/* ── ANIMATIONS ── */
.animate-slide-up {
  animation: slideUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes slideUp {
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ── CUSTOM SCROLLBAR ── */
::-webkit-scrollbar {
  width: 4px;
}
::-webkit-scrollbar-track {
  background: transparent;
}
::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.1);
  border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.2);
}

/* ── SELECT DROPDOWNS ── */
select.bv-input,
select.form-control,
select {
    appearance: none !important;
    -webkit-appearance: none !important;
    -moz-appearance: none !important;
    background-color: rgba(15, 25, 50, 0.9) !important;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='rgba(255,255,255,0.7)' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E") !important;
    background-repeat: no-repeat !important;
    background-position: right 14px center !important;
    background-size: 16px 16px !important;
    padding-right: 42px !important;
    border: 1px solid rgba(255, 255, 255, 0.12) !important;
    color: #ffffff !important;
    /* Same box as a text input (12px 16px, 14px type, 10px corners), so a
       select sits level with the input beside it -- Year Level beside
       Section on the profile was shorter and out of line. */
    border-radius: 10px !important;
    padding-top: 12px !important;
    padding-bottom: 12px !important;
    padding-left: 16px !important;
    font-size: 14px !important;
    line-height: normal !important;
    color-scheme: dark !important;
    cursor: pointer !important;
}

select.bv-input:focus,
select.form-control:focus,
select:focus {
    border-color: rgba(255, 180, 0, 0.5) !important;
    outline: none !important;
    box-shadow: 0 0 0 2px rgba(255, 180, 0, 0.15) !important;
}

/* Force dark dropdown options */
select option {
    background: #0f1932 !important;
    color: white !important;
    padding: 10px 14px !important;
}

select option:hover,
select option:checked {
    background: rgba(255, 180, 0, 0.25) !important;
    color: #FFB400 !important;
}

/* ── GLOBALLY ADDED FIXES ── */
.bv-empty-state {
    text-align: center;
    margin-top: 40px;
    padding: 40px;
    background: rgba(255, 255, 255, 0.02);
    border-radius: 12px;
    border: 1px dashed rgba(255, 255, 255, 0.1);
}

.bv-empty-state p {
    color: rgba(255, 255, 255, 0.6);
    margin-bottom: 16px;
}

/* The three below complete .bv-empty-state. It previously styled only the
   container and its paragraph, so every caller invented its own icon and
   heading with inline styles -- which is how the voter dashboard ended up
   as a py-20 slab with a grey square floating in it. Light-theme
   counterparts live in beevote-light.css; anything added here needs one. */
.bv-empty-state__icon {
    width: 56px;
    height: 56px;
    margin: 0 auto 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    font-size: 22px;
    color: rgba(255, 255, 255, 0.6);
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
}

.bv-empty-state__title {
    font-size: 17px;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.85);
    margin: 0 0 6px;
}

/* .bv-btn is width:100% by design, which would stretch this edge to edge
   inside the centred column. inline-flex keeps it to its content. */
.bv-empty-state__action {
    width: auto;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 9px 18px;
    font-size: 12px;
}

.bv-btn--create {
    width: auto;
    align-self: flex-start;
    padding: 12px 24px;
    background: rgba(59, 130, 246, 0.2);
    border: 1px solid rgba(100, 150, 255, 0.3);
    color: white;
    border-radius: 8px;
    font-weight: 700;
    font-size: 13px;
    letter-spacing: 0.08em;
}

.bv-stat--critical { color: #ef4444; }
.bv-stat--warning  { color: #f59e0b; }
.bv-stat--normal   { color: white; }

.bv-error-context {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 600px;
}

/* ── MOBILE RESPONSIVENESS ── */
@media (max-width: 1023.98px) {
    .bv-page {
        padding: 20px !important;
    }

    .bv-page-header h1 {
        font-size: 1.5rem !important;
    }

    /* Grid Fixes

       minmax(0, 1fr), not 1fr. A bare `1fr` track is minmax(auto, 1fr), and
       `auto` lets the track grow to its widest unbreakable content. On System
       Settings a 42-character contract address did exactly that on a phone:
       the one column grew past the screen and BOTH cards in it were cut off
       by 38px, with nothing to scroll. A zero minimum keeps the column the
       width of the screen and makes long content wrap inside it instead. */
    .bv-grid-2, .bv-grid-3, .bv-grid-4, .bv-stat-grid, .bv-profile-grid {
        grid-template-columns: minmax(0, 1fr) !important;
        gap: 16px !important;
    }

    /* Small Stats can keep 2 columns if specifically needed, but default to 1 for safety */
    .bv-stat-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    }
}

/* ── The drawer itself ────────────────────────────────────────────
   Split out of the width block around it on 2026-09-16 so it can take the
   DRAWER condition (see the top of this file). Extending that whole block
   to touch tablets instead would also have handed an iPad held sideways
   the phone layout in it: single-column grids and 20px page padding on a
   1180px screen. Only these rules are about the drawer, so only these move.

   The width block resumes directly below, so source order -- and with it
   every cascade tie -- is unchanged. */
@media (max-width: 1023.98px), (pointer: coarse) and (max-width: 1366px) {
    .bv-sidebar {
        position: fixed !important;
        top: 0;
        left: 0;
        bottom: 0;
        z-index: 10000 !important;
        height: 100vh !important;
        width: 80% !important;
        max-width: 300px !important;
        /* Was `20px 0 60px rgba(0,0,0,0.8)`: a 60px-wide band of 80% black
           smeared across the page from the drawer's edge, sitting ON TOP of
           the backdrop's own 60% dim. Between the panel and the page you got
           three separate steps of darkness -- panel, black fade, dim -- which
           is what reads as bad layering. In light mode it was worse still: an
           80% black smear running off a white panel.

           One soft edge instead. It also repaints roughly a quarter of the
           area while the panel is in motion, and a moving element repaints
           its shadow every frame. */
        box-shadow: 6px 0 20px rgba(0, 0, 0, 0.3) !important;
        transform: translateX(-100%);
        /* 0.26s to match the backdrop above, on the same curve. The old 0.4s
           was long enough that the motion itself read as slow, independently
           of any dropped frames. */
        transition: transform 0.26s cubic-bezier(0.32, 0.72, 0, 1) !important;
        /* Same reason as the backdrop: promote before the animation, not on
           its first frame. */
        will-change: transform;
        display: flex !important;
    }

    /* Scrolling the drawer's own nav must not chain through to the page
       underneath once it hits the end. */
    .bv-sidebar__nav { overscroll-behavior: contain; }

    .bv-sidebar.open {
        transform: translateX(0) !important;
    }
}

/* A wide table scrolls inside its own box at EVERY width. This used to be
   set only inside the block below (<= 1023px), so on a desktop-width screen
   the box clipped instead: User Registry at 1024px cut the right 18px of its
   table off with nothing to scroll. Declared before that block so its
   narrower-screen rules still apply on top. Safe to widen: the two views
   using this wrapper hold no dropdown or popover that a scroll box would
   clip (checked 2026-09-16). */
.bv-table-container {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

@media (max-width: 1023.98px) {
    /* Table Responsiveness */
    .bv-table-container {
        width: 100%;
        overflow-x: auto !important;
        -webkit-overflow-scrolling: touch;
        margin-bottom: 1rem;
        background: rgba(255, 255, 255, 0.02);
        border-radius: 8px;
        position: relative;
    }

    /* Only where the table can scroll sideways. html and body are
       overflow-x:hidden, so a bare table forced to 600px inside a ~330px
       card had no scrollbar: four tables lost their right half on phones.
       A table outside these wrappers now shrinks to fit its card. */
    .bv-table-container .bv-table,
    .bv-log-scroll .bv-table,
    .bv-ledger-scroll .bv-table,
    .bv-spec .bv-table,
    .bv-table-frame .bv-table {
        min-width: 600px !important; /* Force scroll on small screens */
    }

    .bv-ledger-scroll {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    .bv-table th, .bv-table td {
        padding: 12px 10px !important;
        font-size: 11px !important;
    }

    /* Ellipsis for long names */
    .truncate-mobile {
        max-width: 120px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        display: block;
    }

    /* Badge scaling */
    .bv-badge {
        font-size: 9px !important;
        padding: 2px 6px !important;
    }

    /* Card Padding */
    .bv-card__body {
        padding: 1rem !important;
    }

    .bv-hash {
        word-break: break-all;
        white-space: normal;
        font-size: 9px;
        color: rgba(255,255,255,0.6);
    }
    
    .mobile-hidden {
        display: none !important;
    }
}

/* SIDEBAR condition (see the top of this file). This used to be a bare
   `min-width: 1024px`, and because it sits later in the file with
   !important it would out-rank the drawer on a touch tablet and pin the
   sidebar open again. */
@media (min-width: 1024px) and (pointer: fine), (min-width: 1024px) and (pointer: none), (min-width: 1367px) {
    .bv-hamburger {
        display: none !important;
    }

    .bv-sidebar {
        transform: translateX(0) !important;
        position: relative !important;
        display: flex !important;
    }
}

/* ── PAGINATION STYLING ── */
.pagination, [role="navigation"] nav {
    display: flex;
    gap: 8px;
    align-items: center;
    margin-top: 16px;
}

.pagination .page-item, [role="navigation"] nav a, [role="navigation"] nav span {
    background: rgba(255, 255, 255, 0.05) !important;
    border: 1px solid rgba(255, 255, 255, 0.1) !important;
    color: rgba(255, 255, 255, 0.6) !important;
    padding: 8px 14px !important;
    border-radius: 8px !important;
    font-size: 13px !important;
    font-weight: 700 !important;
    text-decoration: none !important;
    transition: all 0.2s !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    min-width: 40px !important;
}

.pagination .page-item:hover, [role="navigation"] nav a:hover {
    background: rgba(255, 255, 255, 0.1) !important;
    border-color: rgba(255, 255, 255, 0.2) !important;
    color: white !important;
}

.pagination .page-item.active, [role="navigation"] nav span[aria-current="page"], [role="navigation"] nav .bg-blue-600 {
    background: #FFB400 !important;
    border-color: #FFB400 !important;
    color: #0D1117 !important;
    box-shadow: 0 0 15px rgba(255, 180, 0, 0.3) !important;
}

.pagination .page-item.disabled, [role="navigation"] nav span[aria-disabled="true"] {
    opacity: 0.3 !important;
    cursor: not-allowed !important;
}

[role="navigation"] nav svg {
    width: 16px !important;
    height: 16px !important;
}

/* ── ADDITIONAL PAGINATION SELECTORS ── */
nav[role="navigation"] a, 
nav[role="navigation"] span,
.pagination li a, 
.pagination li span {
    background: rgba(255, 255, 255, 0.05) !important;
    border: 1px solid rgba(255, 255, 255, 0.1) !important;
    color: rgba(255, 255, 255, 0.6) !important;
    padding: 8px 14px !important;
    border-radius: 8px !important;
    font-size: 13px !important;
    font-weight: 700 !important;
    text-decoration: none !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    min-width: 40px !important;
    margin: 0 4px !important;
}

nav[role="navigation"] a:hover {
    background: rgba(255, 255, 255, 0.1) !important;
    color: white !important;
}

nav[role="navigation"] .active span,
nav[role="navigation"] span[aria-current="page"],
nav[role="navigation"] .bg-blue-600 {
    background: #FFB400 !important;
    border-color: #FFB400 !important;
    color: #0D1117 !important;
}

/* Secondary Button Styling (Used in Errors Pagination) */
.bv-btn--secondary {
    background: rgba(255, 255, 255, 0.05) !important;
    border: 1px solid rgba(255, 255, 255, 0.1) !important;
    color: rgba(255, 255, 255, 0.8) !important;
    font-weight: 700 !important;
    text-transform: uppercase !important;
    letter-spacing: 0.1em !important;
    font-size: 11px !important;
    transition: all 0.3s !important;
}

.bv-btn--secondary:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.1) !important;
    border-color: rgba(255, 255, 255, 0.2) !important;
    color: white !important;
    transform: translateY(-1px) !important;
}

[role="navigation"] div p, 
.bv-table-pagination-meta {
    color: rgba(255, 255, 255, 0.6) !important;
    font-size: 11px !important;
    font-weight: 500 !important;
    letter-spacing: 0.05em !important;
    margin: 8px 0 !important;
}

[role="navigation"] div p span,
.bv-table-pagination-meta span {
    color: white !important;
    font-weight: 700 !important;
}

/* ── PROFILE PAGE COMPONENTS ──────────────────────────────────────
   Class-based on purpose. This CSP sets style-src with a nonce and
   no style-src-attr, so inline style="" attributes are dropped by
   the browser — overlays, modals and "hidden" inputs cannot rely on
   them. Anything positional or state-bearing lives here instead.   */

/* Verified banner under the profile header */
.bv-verified-banner {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.9rem 1.25rem;
  margin-bottom: 24px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(255, 255, 255, 0.03);
  border-radius: 12px;
}
.bv-verified-banner > i { color: #FFB400; font-size: 0.95rem; }
.bv-verified-banner__label {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.7);
}

/* Avatar with an always-visible upload affordance */
.bv-avatar-upload {
  position: relative;
  display: inline-block;
  line-height: 0;
  cursor: pointer;
}
.bv-avatar-upload__img,
.bv-avatar-upload__initials {
  width: 84px;
  height: 84px;
  border-radius: 50%;
  border: 2px solid rgba(255, 180, 0, 0.5);
  object-fit: cover;
}
.bv-avatar-upload__initials {
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 180, 0, 0.15);
  color: #FFB400;
  font-size: 22px;
  font-weight: 800;
  line-height: 1;
}
.bv-avatar-upload__overlay {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  background: rgba(7, 14, 38, 0.66);
  color: #FFB400;
  font-size: 8px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1;
  opacity: 0.92;
  transition: opacity 0.2s ease, background 0.2s ease;
}
.bv-avatar-upload:hover .bv-avatar-upload__overlay {
  opacity: 1;
  background: rgba(7, 14, 38, 0.82);
}

/* The file input must be hidden by a class: style="display:none" is
   dropped by the CSP, which leaves a raw file picker on the page. */
.bv-visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

/* ── PROFILE SETTINGS (2026-09-15) ──────────────────────────────
   The header, photo, status rows and action buttons of profile/edit.
   They were inline styles with fixed colours the light theme could not
   reach, and a photo with an UPLOAD label printed over it. */
.bv-profile-hero {
  margin-bottom: 24px;
}

.bv-profile-hero__body {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2rem;
  padding: 1.5rem 2rem;
}

.bv-profile-photo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.bv-profile-photo form {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin: 0;
}

.bv-profile-photo__img,
.bv-profile-photo__initials {
  display: block;
  width: 112px;
  height: 112px;
  border-radius: 50%;
  border: 2px solid rgba(255, 180, 0, 0.5);
  object-fit: cover;
}

.bv-profile-photo__initials {
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 180, 0, 0.15);
  color: #FFB400;
  font-size: 30px;
  font-weight: 800;
}

/* The file input is visually hidden but focusable; its label is the
   visible control, so it wears the input's focus. */
.bv-profile-photo input:focus-visible + .bv-profile-photo__change {
  outline: 2px solid #FFB400;
  outline-offset: 2px;
}

.bv-profile-photo__error {
  max-width: 220px;
  margin: 0;
  text-align: center;
}

.bv-profile-hero__text {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.bv-profile-hero__name {
  margin: 0;
  font-size: 26px;
  font-weight: 700;
  line-height: 1.2;
  color: #ffffff;
}

.bv-profile-hero__email {
  margin: 0;
  font-size: 13px;
  word-break: break-all;
  color: rgba(255, 255, 255, 0.65);
}

.bv-profile-hero__text .bv-sidebar__role-badge {
  align-self: flex-start;
  margin-top: 4px;
}

/* Phones stack the photo above the name, and a stacked header reads as
   one centred column: left-aligned, the photo and its button sat off to
   one side of a card that is otherwise all empty space on the right. */
@media (max-width: 640px) {
  .bv-profile-hero__body {
    flex-direction: column;
    gap: 1.25rem;
    padding: 1.5rem 1.25rem;
    text-align: center;
  }

  .bv-profile-hero__text {
    align-items: center;
    width: 100%;
  }

  .bv-profile-hero__text .bv-sidebar__role-badge {
    align-self: center;
  }
}

.bv-profile-details {
  margin-top: 24px;
}

.bv-profile-details__summary {
  cursor: pointer;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
}

.bv-profile-details__form {
  margin-top: 16px;
}

.bv-profile-details__course {
  margin-top: 16px;
}

/* Fields level with each other whatever their labels do. "Section / Block"
   wraps to two lines in this column and "Year Level" does not, which put
   the Section input 13px below the Year Level select. The grid already
   makes both groups one height; packing each from the bottom lines the
   fields up. */
.bv-profile-details .bv-grid-2 > .bv-form-group {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

/* Clear of the note above it; the button sat against its last line. */
.bv-profile-details__save {
  margin-top: 20px;
}

.bv-profile-divider {
  margin: 24px 0 0;
  border: 0;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}

.bv-profile-subtitle {
  margin: 0;
  font-size: 16px;
}

.bv-profile-label {
  display: block;
  margin-bottom: 10px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.65);
}

/* Icon beside a title, a line and a time, the text left-aligned on its own
   column. The Recent Activity and Two-Factor cards drew this two ways. */
.bv-profile-status {
  display: flex;
  align-items: center;
  gap: 12px;
}

.bv-profile-status .bv-card__icon {
  flex-shrink: 0;
}

.bv-profile-status__text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.bv-profile-status__title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: #ffffff;
}

.bv-profile-status__line {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.72);
}

.bv-profile-status__meta {
  margin: 0;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.6);
}

/* A full-width action at the foot of a profile card or column. */
.bv-profile-action {
  width: 100%;
  min-height: 44px;
  padding: 12px 16px;
  font-size: 11px;
}

.bv-profile-2fa-off {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 0;
}

.bv-profile-hint {
  margin: 8px 0 0;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.65);
}

/* ── Photo crop dialog (Cropper.js) ── */
.bv-crop {
  max-width: 480px;
}

.bv-crop__hint {
  margin: 0 0 16px;
  font-size: 13px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.72);
}

.bv-crop__stage {
  width: 100%;
  height: 320px;
  overflow: hidden;
  border-radius: 12px;
  background: #0b1220;
}

.bv-crop__stage img {
  display: block;
  max-width: 100%;
}

/* A circle, because that is how the photo is shown everywhere it appears. */
.bv-crop .cropper-view-box,
.bv-crop .cropper-face {
  border-radius: 50%;
}

.bv-crop .cropper-view-box {
  outline: 2px solid rgba(255, 180, 0, 0.9);
}

.bv-crop__tools {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin: 14px 0 4px;
}

.bv-crop__tool {
  width: 44px;
  height: 44px;
  padding: 0;
}

/* ── VERIFY YOUR BALLOT (2026-09-16) ──────────────────────────────────
   voter/verify: the search card and its four result boxes. They were
   inline styles plus Tailwind spacing this build does not emit, so the
   icon, title, field and button stacked with no rhythm, and the button
   fell onto its own full-width line. */
.bv-verify {
  max-width: 760px;
  margin: 0 auto;
}

.bv-verify__body {
  align-items: center;
  padding: 2.5rem;
  text-align: center;
}

.bv-verify__icon {
  width: 64px;
  height: 64px;
  border-radius: 14px;
  font-size: 1.6rem;
}

.bv-verify__title {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  color: #ffffff;
}

.bv-verify__desc {
  max-width: 560px;
  margin: 0;
  font-size: 14px;
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.72);
}

.bv-verify__form {
  width: 100%;
}

.bv-verify__input {
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
}

.bv-verify__results {
  width: 100%;
  text-align: left;
}

.bv-verify-result {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 20px 22px;
  border: 1px solid transparent;
  border-left-width: 4px;
  border-radius: 12px;
}

/* The page shows one answer by putting .hidden on the other three. This
   sheet loads after the Tailwind build, so the display: flex above outranked
   .hidden and every box stayed on screen: a found ballot was shown together
   with "Transaction Not Found" and "INTEGRITY BREACH DETECTED". */
.bv-verify-result.hidden {
  display: none;
}

.bv-verify-result--found {
  background: rgba(34, 197, 94, 0.06);
  border-color: rgba(34, 197, 94, 0.35);
}

.bv-verify-result--missing {
  background: rgba(239, 68, 68, 0.06);
  border-color: rgba(239, 68, 68, 0.35);
}

.bv-verify-result--unavailable {
  background: rgba(255, 180, 0, 0.06);
  border-color: rgba(255, 180, 0, 0.35);
}

.bv-verify-result__head {
  display: flex;
  align-items: center;
  gap: 12px;
}

.bv-verify-result__head .bv-card__icon {
  flex-shrink: 0;
}

.bv-verify-result__title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
}

.bv-verify-result--found .bv-verify-result__title { color: #4ade80; }
.bv-verify-result--missing .bv-verify-result__title { color: #f87171; }
.bv-verify-result--unavailable .bv-verify-result__title { color: #FFB400; }

.bv-verify-result__text {
  margin: 2px 0 0;
  font-size: 13px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.72);
}

.bv-verify-result__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 16px;
  padding: 16px;
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.2);
}

.bv-verify-result__label {
  margin: 0 0 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-verify-result__value {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  word-break: break-word;
  color: #ffffff;
}

.bv-verify-result__hash {
  margin: 0;
  word-break: break-all;
}

.bv-verify-result__code {
  margin: 0;
  padding: 10px 12px;
  border: 1px solid rgba(239, 68, 68, 0.3);
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.3);
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 11px;
  color: #f87171;
}

/* Card heading with a trailing action button */
.bv-card__title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 2rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.bv-card__title-row .bv-card__title {
  margin: 0;
  font-size: 18px;
  color: #ffffff;
}

/* Round icon button used to reveal the voter hash */
/* ── SECURE HASH IDENTIFIER ───────────────────────────────────────
   The masked hash, its revealed form, and the eye that swaps them.

   Was `flex items-center gap-4 p-4 rounded-xl bg-black/30
   border-white/5` with `flex-1` on both text children. In this build
   only `flex`, `items-center` and `flex-1` emit anything -- the
   padding, the radius, the background and the border all draw nothing.

   The overflow is `min-w-0`, which emits nothing either. A flex child
   defaults to `min-width: auto`, so the thirty-bullet run could not
   shrink below its own width: at tablet the column got narrower than
   the bullets plus the button, and the row ran out past the card edge
   with the eye sitting in the gutter between columns.

   Both children carry `min-width: 0` here so they CAN shrink, and each
   handles its own overflow: the mask clips, the real hash wraps. */
.bv-hash {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px;
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.28);
  border: 1px solid rgba(255, 255, 255, 0.06);
  box-sizing: border-box;
  max-width: 100%;
}

/* The mask. One long unbroken run of bullets, so it is clipped rather
   than wrapped -- a second line of dots says nothing and costs height
   on the screen this page is trying to fit into one. */
.bv-hash__mask {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  letter-spacing: 0.18em;
  color: #818cf8;
}

/* The real thing. Wraps, because a hash the reader may need to compare
   character by character must be fully visible rather than clipped. */
.bv-hash__value {
  flex: 1 1 auto;
  min-width: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.6);
  word-break: break-all;
  overflow-wrap: anywhere;
}

/* Narrow columns: the eye drops under the hash rather than stealing
   34px of a row that is already short of room. */
@media (max-width: 1400px) {
  .bv-hash {
    flex-wrap: wrap;
    gap: 10px;
  }

  .bv-hash__mask,
  .bv-hash__value {
    flex: 1 1 100%;
  }
}

.bv-reveal-btn {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  cursor: pointer;
  background: transparent;
  border: 1px solid rgba(255, 180, 0, 0.4);
  color: #FFB400;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.bv-reveal-btn:hover {
  background: rgba(255, 180, 0, 0.1);
  border-color: #FFB400;
}

/* Modal shell — toggled with .is-open, never with an inline style */
.bv-modal {
  position: fixed;
  inset: 0;
  z-index: 99999;
  padding: 24px;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(7, 14, 38, 0.9);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
}
.bv-modal.is-open { display: flex; }
.bv-modal__card {
  position: relative;
  width: 100%;
  max-width: 520px;
  padding: 40px;
  border-radius: 16px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(15, 23, 42, 0.97);
  box-shadow: 0 32px 64px rgba(0, 0, 0, 0.5);
}
.bv-modal__close {
  position: absolute;
  top: 18px;
  right: 18px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: #ffffff;
  cursor: pointer;
  transition: background 0.2s ease;
}
.bv-modal__close:hover { background: rgba(255, 255, 255, 0.12); }
.bv-modal__title {
  font-size: 20px;
  font-weight: 800;
  color: #ffffff;
  margin: 0 0 24px;
}
.bv-modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 28px;
}
.bv-form-group { margin-bottom: 18px; }
.bv-field-error {
  margin: 6px 0 0;
  font-size: 11px;
  font-weight: 700;
  color: #f87171;
}

/* ── THE PERSONAL-ADDRESS WARNING ─────────────────────────────────
   Raised under an email field the moment what has been typed can no
   longer become an institutional address (partials/email-guard).

   AMBER, NOT RED. Nothing has failed yet -- the person is mid-form and
   is being told something before it costs them a round trip. Red is for
   what the server refused, and this screen already uses it for that;
   two reds would make the two indistinguishable. */
.bv-mailwarn {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 8px 0 0;
  padding: 9px 11px;
  border-radius: 8px;
  border: 1px solid rgba(245, 158, 11, 0.35);
  border-left-width: 3px;
  background: rgba(245, 158, 11, 0.1);
  color: #F59E0B;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.5;
  text-align: left;
}

/* The icon keeps its own line height so it sits on the first line of a
   sentence that wraps to three, rather than centring itself against all
   of them. */
.bv-mailwarn i {
  flex: 0 0 auto;
  margin-top: 1px;
  font-size: 12px;
}

/* The field itself, so the warning and its cause are visibly one thing.
   Border only: tinting the background of a field someone is still typing
   in makes their own text harder to read. */
/* !important because the base field rule is itself !important
   (`border: 1px solid ... !important` on every input in the sheet). Without
   it the message appeared beside a field that still looked untouched, and
   focusing the field painted the ring blue over the top of the warning. */
.bv-input--warn,
.bv-input--warn:focus {
  border-color: rgba(245, 158, 11, 0.75) !important;
  box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.14) !important;
}

/* Utility hide class — replaces style="display:none" which the CSP drops. */
.bv-hidden { display: none !important; }

/* Compact gold action button (card headers) */
/* The name is historical: this is the compact form of the primary button
   and wears the same blue, so an action looks the same on every screen. */
.bv-btn--gold-sm {
  width: auto;
  padding: 8px 16px;
  font-size: 10px;
  border-radius: 8px;
  background: rgba(59, 130, 246, 0.2);
  border: 1px solid rgba(100, 150, 255, 0.3);
  color: #ffffff;
}
.bv-btn--gold-sm:hover {
  background: rgba(59, 130, 246, 0.4);
  border-color: rgba(100, 150, 255, 0.5);
}

/* Auto-width button for action rows (.bv-btn is 100% wide by default) */
.bv-btn--auto { width: auto; padding: 12px 22px; }

/* ══════════════════════════════════════════════════════════════════
   DEVICE BREAKPOINTS
   Added because the interface had no phone breakpoint: every media
   query sat at the 1023/1024px boundary, so a 375px phone was styled
   exactly like a 1023px tablet. The 67 sm:/md:/lg: classes in the
   markup are Tailwind and emit nothing in this build, so any layout
   that must adapt has to be declared here.

   Boundaries are deliberately non-overlapping:
     phone   <= 640px
     tablet  641px - 1023.98px
     desktop >= 1024px
   ══════════════════════════════════════════════════════════════════ */

/* ── Stop the page sliding sideways ───────────────────────────────
   The decorative .blob elements are 450-578px wide and sit outside
   the viewport on a phone. With overflow visible they gave every
   dashboard page a few pixels of horizontal drag, which reads as a
   broken layout under the thumb. */
@media (max-width: 1023.98px) {
  html, body {
    overflow-x: hidden;
    max-width: 100%;
  }

  .blob, .blob-1, .blob-2, .blob-3 {
    pointer-events: none;
  }
}

/* ── Viewport height on mobile browsers ───────────────────────────
   100vh excludes the retracting address bar on iOS and Android, so a
   full-height shell is taller than the visible area and the bottom of
   the sidebar (including Sign Out) sits below the fold. dvh tracks the
   visible viewport; the vh line stays as the fallback. */
/* DRAWER condition: Safari's retracting address bar on an iPad held
   sideways hides Sign Out exactly as it does on a phone. */
@media (max-width: 1023.98px), (pointer: coarse) and (max-width: 1366px) {
  .bv-dashboard-layout { height: 100vh; height: 100dvh; }
  .bv-sidebar { height: 100vh !important; height: 100dvh !important; }
}

/* ── Drawer backdrop ──────────────────────────────────────────────
   Without it there is no way to dismiss the menu except the hamburger,
   and nothing signals that the page behind is inert. */
.bv-sidebar-backdrop {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: rgba(3, 7, 20, 0.6);
  /* The frost stays -- it is the agreed design. What changes is everything
     around it, so the browser can fade a blur it has already computed
     instead of computing a new one every frame. */
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  opacity: 0;
  /* pointer-events, not visibility. Transitioning `visibility` alongside
     `opacity` moves the element in and out of the paint tree mid-animation,
     and on an element carrying backdrop-filter that means re-capturing and
     re-blurring the whole viewport rather than fading a snapshot it already
     holds. pointer-events flips instantly, costs nothing, and is what
     actually stops taps landing on the dim. */
  pointer-events: none;
  /* Same duration and curve as the drawer, so the dim and the panel arrive
     together. They were 0.3s ease against the drawer's 0.4s cubic-bezier,
     which finished a tenth of a second apart -- the dim settled while the
     panel was still moving, and that mismatch reads as a stutter even when
     no frame is actually dropped. */
  transition: opacity 0.26s cubic-bezier(0.32, 0.72, 0, 1);
  /* Promote the layer up front. Without this the browser promotes on the
     first frame of the animation, and that promotion is itself the dropped
     frame -- the one you feel at the very start of the slide. */
  will-change: opacity;
}

.bv-sidebar-backdrop.is-visible {
  opacity: 1;
  pointer-events: auto;
}

/* Someone who has asked their device for less motion gets the drawer with
   no slide and no fade. The panel still opens; it just arrives. */
@media (prefers-reduced-motion: reduce) {
  .bv-sidebar,
  .bv-sidebar-backdrop { transition: none !important; }
}

/* SIDEBAR condition. A bare min-width here would remove the dim behind
   the drawer on a touch tablet, leaving an open menu with nothing to tap
   to dismiss it. */
@media (min-width: 1024px) and (pointer: fine), (min-width: 1024px) and (pointer: none), (min-width: 1367px) {
  .bv-sidebar-backdrop { display: none !important; }
}

/* ── Tablet: 641px - 1023.98px ────────────────────────────────────
   Enough width for two columns; the forced single column at this size
   left half the screen empty. */
@media (min-width: 641px) and (max-width: 1023.98px) {
  .bv-page { padding: 24px !important; }

  .bv-grid-2,
  .bv-grid-3,
  .bv-grid-4,
  .bv-stat-grid,
  .bv-profile-grid {
    /* minmax(0, 1fr): see "Grid Fixes" above -- a bare 1fr track grows to
       fit an unbreakable string and pushes the row off the screen. */
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    gap: 20px !important;
  }

  .bv-sidebar { width: 320px !important; }
}

/* ── Phone: <= 640px ──────────────────────────────────────────────── */
@media (max-width: 640px) {
  .bv-page { padding: 14px !important; }

  .bv-page-heading, .bv-page-header h1 { font-size: 1.25rem !important; }
  .bv-page-subheading { font-size: 12px !important; }

  /* One column everywhere, including the stat grid that stayed at two */
  .bv-grid-2,
  .bv-grid-3,
  .bv-grid-4,
  .bv-grid-auto,
  .bv-stat-grid,
  .bv-profile-grid {
    grid-template-columns: minmax(0, 1fr) !important;  /* not 1fr; see "Grid Fixes" */
    gap: 12px !important;
  }

  .bv-card__body { padding: 0.875rem !important; }
  .bv-card__icon { width: 42px; height: 42px; font-size: 1.15rem; }

  /* Touch targets: 42-43px measured, below the 44px minimum */
  .bv-btn,
  .bv-btn--verify,
  .bv-input,
  .bv-reveal-btn,
  select.bv-input,
  .bv-nav-item,
  .bv-sidebar__logout,
  .bv-log-receipt {
    min-height: 44px;
  }

  /* Actions stack instead of crowding onto one line */
  .bv-card__actions,
  .bv-modal__actions {
    flex-direction: column;
    align-items: stretch;
  }

  .bv-modal__actions .bv-btn,
  .bv-modal__actions .bv-btn--gold-sm { width: 100%; }

  .bv-modal { padding: 12px; }
  .bv-modal__card { padding: 24px 20px; }

  /* The login card is a fixed 400px; phones are narrower than that */
  .bv-minimal-card {
    width: 100% !important;
    max-width: 400px;
    padding: 32px 22px !important;
  }

  /* Topbar: the status pills crowd out the page title */
  .bv-topbar { padding: 0 14px !important; }
  .bv-topbar-title { font-size: 13px !important; }
  .bv-topbar .bv-card__meta { display: none; }

  .bv-sidebar { width: 86% !important; max-width: 320px !important; }

  /* Tables keep scrolling horizontally, but say so rather than
     looking truncated. The hint used to be an absolute ::after at
     right: 0 -- inside a scroll box that is the right edge of the
     CONTENT, not the box, so it scrolled with the rows and parked a
     dark band across the middle of the table. Background images with
     the default attachment stay put while the content scrolls, and a
     scroll timeline shows each edge's fade only while there is more
     table past that edge. Browsers without scroll timelines get no
     hint rather than a wrong one. */
  @supports (animation-timeline: scroll()) {
    .bv-table-container {
      background-image:
        linear-gradient(to left, rgba(5, 12, 24, 0.7), rgba(5, 12, 24, 0)),
        linear-gradient(to right, rgba(5, 12, 24, 0.7), rgba(5, 12, 24, 0));
      background-position: right top, left top;
      background-repeat: no-repeat;
      background-size: 0 100%, 0 100%;
      animation: bv-table-edge-hint linear both;
      animation-timeline: scroll(self inline);
    }
  }

  .bv-hash { font-size: 8px; }
  .bv-verified-banner { padding: 0.75rem 0.9rem; }
  .bv-avatar-upload__img,
  .bv-avatar-upload__initials { width: 68px; height: 68px; }
}

/* Very small phones (<=360px): stop the receipt/hash blocks overflowing */
@media (max-width: 360px) {
  .bv-page { padding: 10px !important; }
  .bv-card__body { padding: 0.75rem !important; }
  .bv-btn { font-size: 11px; letter-spacing: 0.06em; }
}

/* ══ FINGER-SIZED CONTROLS ON TOUCH SCREENS ═══════════════════════════════
   The phone block above already raised controls to 44px -- but keyed on
   WIDTH (<= 640px), so a tablet got mouse-sized controls at every size.
   Measured 2026-09-16 on a 768px and a 1024px screen: role dropdowns 25px
   tall, the criteria builder's open/close toggle 22x22, the tabulation
   scope toggle 23x19, "Edit Profile" 29px, the monitor's "Registry" link
   15px. 44px is the Apple HIG minimum for a finger; WCAG 2.5.8's floor is
   24px, and several of those were under even that.

   Keyed on `pointer: coarse`, so it follows the INPUT rather than the
   screen: a phone, an iPad upright or sideways, and a 12.9" iPad Pro all
   get it, while a laptop at any width keeps its current density. Desktop
   rendering does not change at all. */
@media (pointer: coarse) {
  /* Everything the phone block lists, extended to every touch screen */
  .bv-btn,
  .bv-btn--verify,
  .bv-input,
  .bv-reveal-btn,
  select.bv-input,
  .bv-nav-item,
  .bv-sidebar__logout,
  .bv-log-receipt,
  /* ...plus the controls the measurement found below it */
  .bv-input--compact,
  .bv-btn--row,
  .bv-btn--gold-sm,
  .bv-elist-go,
  .bv-tab-btn,
  .bv-sidebar__brand,
  .bv-profile-details__summary,
  .bv-field__slider,
  .bv-backlink {
    min-height: 44px;
  }

  /* The tabulation scope checkboxes are drawn 15px, but each sits inside
     its own <label>, and tapping the label toggles it. The label is the
     real target, so it is the thing made finger-tall -- the box itself
     keeps its size and the row does not look inflated. */
  .bv-scope__item { min-height: 44px; }

  /* Plain text links carry no box to grow, so give them one. */
  .bv-backlink {
    display: inline-flex;
    align-items: center;
  }

  /* Icon-only toggles in dense rows: enlarging the visible 22px square
     would crowd the criteria tree, so the TAP AREA grows instead and the
     drawing stays as it is. The pseudo-element is invisible and centred on
     the control; neither class uses ::after for anything else (checked). */
  .bv-node__grip--toggle,
  .bv-scope__toggle {
    position: relative;
  }
  .bv-node__grip--toggle::after,
  .bv-scope__toggle::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
  }

  /* The criteria builder deliberately forces its row inputs compact with
     `.bv-node__row .bv-input { min-height: 0 !important }` -- that rule sits
     LATER in this file, so a same-specificity override here would lose. The
     element selector lifts this to (0,2,1) so it wins on touch screens only;
     measured 27px tall on a tablet before. */
  .bv-node__row input.bv-input { min-height: 44px !important; }

  /* Its add / remove / labelled buttons are 26px tall. Growing the tap area,
     not the drawing, keeps the tree as compact as it was designed to be. */
  .bv-node__btn { position: relative; }
  .bv-node__btn::after {
    content: "";
    position: absolute;
    /* 10px each side, not 9: `100%` here is the padding box, which the
       button's 1px border leaves at 24px, and 24 + 18 measured 42.7px. */
    top: -10px;
    left: -10px;
    width: calc(100% + 20px);
    height: calc(100% + 20px);
  }
}

/* ── IDENTITY ROWS ────────────────────────────────────────────────
   Label/value pairs fronted by a tinted icon tile, as used on the
   profile's Verified Identity card. The tile carries the meaning at a
   glance; the label is secondary and the value leads.                */
.bv-id-row {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.85rem 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.bv-id-row:last-child { border-bottom: none; }

.bv-id-row__icon {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  font-size: 0.95rem;
  background: rgba(255, 180, 0, 0.1);
  border: 1px solid rgba(255, 180, 0, 0.2);
  color: #FFB400;
}
.bv-id-row__icon--indigo { background: rgba(99,102,241,0.12); border-color: rgba(99,102,241,0.25); color: #818cf8; }
.bv-id-row__icon--green  { background: rgba(34,197,94,0.12);  border-color: rgba(34,197,94,0.25);  color: #22c55e; }

/* min-width:0 lets a long email wrap instead of forcing the row wider. */
.bv-id-row__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.bv-id-row__label {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}
.bv-id-row__value {
  font-size: 15px;
  font-weight: 700;
  color: #ffffff;
  overflow-wrap: anywhere;
}

/* Status strip: the same idiom, boxed, for a single standout fact. */
.bv-id-status {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.9rem 1rem;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 180, 0, 0.18);
}
.bv-id-status > i { color: #FFB400; font-size: 1rem; flex-shrink: 0; }
.bv-id-status__label {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #FFB400;
}
.bv-id-status__value {
  font-size: 13px;
  font-weight: 700;
  color: #ffffff;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ── AVATAR SIZING ────────────────────────────────────────────────
   .bv-avatar already fixes a 36px circle with !important, which is what
   makes it safe on an <img>: an unconstrained avatar renders at its
   natural size, so an uploaded photo blew up to several hundred pixels
   and wrecked the user table. The Tailwind classes previously used for
   this (w-9 h-9 rounded-full object-cover) emit nothing in this build.
   object-fit applies to the <img> case and is inert on the initials div. */
.bv-avatar { object-fit: cover; }

.bv-avatar--xl {
  width: 80px !important;
  height: 80px !important;
  min-width: 80px !important;
  min-height: 80px !important;
  font-size: 24px;
}

/* ── BORDER COLOURS ───────────────────────────────────────────────
   The built Tailwind bundle emits `.border` (a 1px WIDTH) but none of
   the paired colour utilities — `.border-white/5`, `.border-red-500/30`
   and friends are absent, though views use them ~56 times. A width with
   no colour falls back to `currentColor`, so every bordered panel drew a
   hard white line. (Tailwind v3's preflight defaulted borders to grey;
   v4 defaults to currentColor, which is exactly this trap.)

   Two parts: a subtle default so anything missed can never be white
   again, then the specific colours the views actually ask for, so an
   emergency panel still reads red rather than flattening to grey. */
*,
::before,
::after { border-color: rgba(255, 255, 255, 0.08); }

/* Neutrals — the dark theme wants a faint light line, not literal grey. */
.border-white\/5    { border-color: rgba(255, 255, 255, 0.05) !important; }
.border-white\/10   { border-color: rgba(255, 255, 255, 0.10) !important; }
.border-white\/20   { border-color: rgba(255, 255, 255, 0.20) !important; }
.border-gray-100,
.border-gray-200,
.border-gray-300    { border-color: rgba(255, 255, 255, 0.12) !important; }
.border-gray-700,
.border-gray-800    { border-color: rgba(255, 255, 255, 0.09) !important; }

/* Semantic — these carry meaning and must survive. */
.border-red-500     { border-color: rgba(239, 68, 68, 0.35) !important; }
.border-red-500\/20 { border-color: rgba(239, 68, 68, 0.20) !important; }
.border-red-500\/30 { border-color: rgba(239, 68, 68, 0.30) !important; }
.border-green-500   { border-color: rgba(34, 197, 94, 0.35) !important; }
.border-green-500\/30 { border-color: rgba(34, 197, 94, 0.30) !important; }
.border-green-600   { border-color: rgba(22, 163, 74, 0.40) !important; }
.border-blue-500    { border-color: rgba(59, 130, 246, 0.35) !important; }
.border-yellow-500  { border-color: rgba(255, 180, 0, 0.40) !important; }
.border-yellow-500\/50 { border-color: rgba(255, 180, 0, 0.50) !important; }
.border-indigo-400,
.border-indigo-500,
.border-indigo-700  { border-color: rgba(99, 102, 241, 0.35) !important; }

/* ── DIGITAL BALLOT ───────────────────────────────────────────────
   The ballot gets its own grid and card rather than reusing
   .bv-grid-3 / .bv-candidate-card, for two reasons:

   1. .bv-grid-3 is three fixed 1fr tracks, so a position with two
      candidates rendered two cards each a third of the viewport wide
      with a dead column beside them. auto-fill keeps every candidate
      card the same size no matter how many contest the seat, which is
      also the fairer presentation.
   2. .bv-grid-3 is shared by ~30 other views. Tuning the ballot on it
      would move all of them.

   The photo is deliberately NOT .bv-avatar: that class pins a 36px
   circle with !important (see AVATAR SIZING above) and a 36px face
   does not help a voter who does not know the names.                */

.bv-ballot-form {
  padding-bottom: 96px;   /* Tailwind pb-24 emits nothing in this build */
}

.bv-ballot-section + .bv-ballot-section {
  margin-top: 48px;       /* replaces the inert space-y-12 */
}

.bv-ballot-headline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

/* Two separate faults were fixed here; both showed up as dead space.

   1. auto-FILL keeps its empty tracks. At 1600px it laid out seven
      213px columns, put four candidates in the first four, and left
      three phantom columns on the right with every portrait shrunk to
      fit a track count nobody needed. auto-FIT collapses the empties.
 
   2. The replacement, minmax(210px, 300px), was worse in a quieter
      way: when the MAX of a minmax() is a definite length, that is what
      auto-fit counts tracks with -- not the min. 300px + 20px gap into
      932px of content is two columns, so a four-candidate ballot came
      out two-up with half the row empty. The max has to stay 1fr; the
      MIN is what actually sets how big a card gets, because auto-fit
      packs as many min-width tracks as fit and then grows them.
 
   260px min => three ~300px cards on a 932px content column, five on a
   wide desktop, two on a small laptop. The card size is the point: a
   ballot is chosen from faces. */
.bv-ballot-grid {
  display: grid;
  /* auto-FILL, not auto-fit. Both lay out the same when the row is full;
     they differ entirely when it is not. auto-fit collapses the empty
     tracks and hands their width to the cards that exist, so a position
     with two candidates gave each an 800px column on a desktop -- and
     .bv-ballot-photo is width:100% with a 4/5 aspect ratio, so each
     portrait came out 800 by 1000. auto-fill keeps the empty tracks, so a
     card is the same size whether it has one neighbour or five. */
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 20px;
  align-items: stretch;
}

/* The <label> is the whole hit area. Content model is phrasing only,
   so everything inside is a span made block. */
.bv-ballot-option {
  display: block;
  position: relative;
  cursor: pointer;
  height: 100%;
  -webkit-tap-highlight-color: transparent;
}

/* The global `input { width: 100% !important }` rule further up would
   otherwise give this a box, so every property is forced. */
.bv-ballot-input {
  position: absolute !important;
  opacity: 0 !important;
  width: 1px !important;
  height: 1px !important;
  min-height: 0 !important;
  padding: 0 !important;
  margin: 0 !important;
  border: 0 !important;
  pointer-events: none !important;
}

.bv-ballot-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  height: 100%;
  padding: 14px;
  position: relative;
  overflow: hidden;
  transition: border-color 0.18s, box-shadow 0.18s, transform 0.15s;
}

.bv-ballot-option:hover .bv-ballot-card {
  border: 1px solid rgba(255, 180, 0, 0.35) !important;
  border-top: 1px solid rgba(255, 180, 0, 0.35) !important;
  transform: translateY(-2px);
}

/* Keyboard users get a visible target — the input itself is off-screen. */
.bv-ballot-input:focus-visible + .bv-ballot-card {
  outline: 2px solid #FFB400;
  outline-offset: 2px;
}

/* ── Photo ──
   4:5 portrait. object-fit: cover means an uploaded photo of any
   dimension crops to frame rather than stretching. */
.bv-ballot-photo {
  display: block;
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 5;
  border-radius: 12px;
  overflow: hidden;
  background: var(--bv-ballot-tint, rgba(255, 180, 0, 0.06));
  border: 1px solid rgba(255, 255, 255, 0.08);
  flex-shrink: 0;
}

.bv-ballot-photo__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* No photo at all: a large initials tile. This is the production
   fallback — real recognition comes from COMELEC uploading real
   photos, and this keeps the ballot legible until they do. */
.bv-ballot-photo__initials {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  font-size: clamp(1.75rem, 5vw, 2.5rem);
  font-weight: 800;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.72);
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
}

.bv-ballot-body {
  display: block;
  min-width: 0;
}

.bv-ballot-name {
  display: block;
  font-size: 0.95rem;
  font-weight: 700;
  line-height: 1.35;
  color: #ffffff;
  overflow-wrap: anywhere;
}

.bv-ballot-party {
  display: block;
  margin-top: 4px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

/* ── Selection check ──
   The previous card rendered an empty ring whose fill dot had been
   deleted from the markup, so clicking a candidate gave no positive
   confirmation. Driven by the adjacent-sibling selector rather than
   :has() so it does not depend on Safari 15.4+. */
.bv-ballot-check {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.45);
  background: rgba(6, 16, 30, 0.65);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  color: #06101e;
  transition: background 0.18s, border-color 0.18s;
}

.bv-ballot-check i {
  opacity: 0;
  transform: scale(0.4);
  transition: opacity 0.18s, transform 0.18s;
}

.bv-ballot-input:checked + .bv-ballot-card {
  /* .bv-card sets `border` AND `border-top` as !important shorthands, so the
     selected state restates both longhands rather than relying on a
     border-color shorthand outranking them. */
  border: 1px solid #FFB400 !important;
  border-top: 1px solid #FFB400 !important;
  background: rgba(255, 180, 0, 0.07) !important;
  box-shadow: 0 0 24px rgba(255, 180, 0, 0.18) !important;
}

.bv-ballot-input:checked + .bv-ballot-card .bv-ballot-check {
  background: #FFB400;
  border-color: #FFB400;
}

.bv-ballot-input:checked + .bv-ballot-card .bv-ballot-check i {
  opacity: 1;
  transform: scale(1);
}

.bv-ballot-input:checked + .bv-ballot-card .bv-ballot-photo {
  border-color: rgba(255, 180, 0, 0.45);
}

/* ── Tint palette for the initials fallback ──
   Assigned per candidate from a hash of the name, so a ballot of
   initials tiles is varied rather than 65 identical amber squares.
   A class, not a style attribute: the CSP drops inline styles. */
.bv-hue-0 .bv-ballot-photo { --bv-ballot-tint: linear-gradient(180deg, #1e3a5f, #16293f); }
.bv-hue-1 .bv-ballot-photo { --bv-ballot-tint: linear-gradient(180deg, #14494a, #0e3233); }
.bv-hue-2 .bv-ballot-photo { --bv-ballot-tint: linear-gradient(180deg, #3d2a4d, #291c34); }
.bv-hue-3 .bv-ballot-photo { --bv-ballot-tint: linear-gradient(180deg, #4a3a1c, #322612); }
.bv-hue-4 .bv-ballot-photo { --bv-ballot-tint: linear-gradient(180deg, #4a2530, #331920); }
.bv-hue-5 .bv-ballot-photo { --bv-ballot-tint: linear-gradient(180deg, #23422b, #17301d); }
.bv-hue-6 .bv-ballot-photo { --bv-ballot-tint: linear-gradient(180deg, #2a2f5c, #1c2040); }
.bv-hue-7 .bv-ballot-photo { --bv-ballot-tint: linear-gradient(180deg, #4a3326, #33221a); }

/* ── Tablet ── slightly wider tracks so names stop wrapping to 3 lines */
@media (min-width: 641px) and (max-width: 1023.98px) {
  .bv-ballot-grid { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }
  .bv-ballot-section + .bv-ballot-section { margin-top: 36px; }
}

/* ── Phone ──
   A 4:5 portrait per candidate would make a 65-candidate ballot an
   enormous scroll, so the card flips to a row: circular photo left,
   name right, check far right. Whole row stays one tap target. */
@media (max-width: 640px) {
  .bv-ballot-grid { grid-template-columns: 1fr; gap: 10px; }

  .bv-ballot-section + .bv-ballot-section { margin-top: 28px; }
  .bv-ballot-form { padding-bottom: 64px; }

  .bv-ballot-card {
    flex-direction: row;
    align-items: center;
    gap: 14px;
    padding: 10px 12px;
    min-height: 76px;     /* comfortably over the 44px touch minimum */
  }

  .bv-ballot-photo {
    width: 56px;
    height: 56px;
    aspect-ratio: 1 / 1;
    border-radius: 50%;
  }

  .bv-ballot-photo__initials { font-size: 1.05rem; }

  .bv-ballot-body { flex: 1 1 auto; }
  .bv-ballot-name { font-size: 0.9rem; }

  .bv-ballot-check {
    position: static;
    flex-shrink: 0;
  }

  .bv-ballot-headline { gap: 10px; }
}

/* The section heading previously leaned on Tailwind's `m-0`, which emits
   nothing in this build, so the h2 kept its UA margin and its 1.5em size
   (relative to the 11px the container forces). Made explicit. */
.bv-section__heading.bv-ballot-headline h2 {
  margin: 0;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.15em;
  color: rgba(255, 255, 255, 0.72);
}

/* ── ELECTION RESULTS ─────────────────────────────────────────────
   results/index.blade.php was the only view in the app that skipped
   .bv-page, so it rendered flush against the sidebar with no padding
   on any edge, and the fixed footer sat directly on its last row. It
   also carried Tailwind spacing classes (mb-8, p-6, mt-6) that this
   build emits nothing for — only gap-* utilities exist in the
   hand-rolled sheets — so none of its intended rhythm ever applied.

   Wrapping the page in .bv-page restores both. The classes below
   replace the inline styles it used for everything else, following
   the same reasoning as .bv-ballot-grid above: a named class rather
   than tuning .bv-grid-3, which ~30 other views share.             */

.bv-results-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 32px;
}

/* The select ran the full width of the card — a 1600px dropdown for a
   handful of short titles. Cap it and let the row breathe. */
.bv-results-toolbar__field {
  flex: 1 1 320px;
  max-width: 520px;
}

.bv-results-section + .bv-results-section {
  margin-top: 40px;
}

/* The heading row carries the denominator. A bare "71.4%" on a card
   answers nothing without the number it is a share of, and this page
   showed percentages with no total anywhere on it. */
.bv-results-heading {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* The total follows the position name. Pushed to the far edge it sat
     past the last card, reading as a caption for empty space. */
  justify-content: flex-start;
  gap: 16px;
  max-width: 1180px;
  margin: 0 0 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.bv-results-heading__title {
  margin: 0;
  color: #ffffff;
  font-size: 0.95rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.bv-results-heading__total {
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.65);
}

.bv-results-heading__total strong {
  color: #FFB400;
  font-weight: 800;
}

/* Was repeat(auto-fill, minmax(300px, 1fr)) — the only auto-fill in the
   app outside the ballot. auto-fill keeps every track that fits, so two
   candidates on a 1650px canvas built five tracks, filled two and
   reserved the other three: the cards huddled left under two thirds of
   dead space. auto-fit collapses the empty tracks instead, and the
   max-width stops a head-to-head from stretching two cards across the
   entire viewport. */
.bv-results-grid {
  display: grid;
  /* auto-fill: see .bv-ballot-grid. Two results in a row were each given
     half the container, and the portrait inside follows its box. */
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 18px;
  align-items: stretch;
  max-width: 1180px;
}

.bv-results-college {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 32px 0 16px;
}

.bv-results-college::before {
  content: "";
  width: 2px;
  height: 14px;
  background: #FFB400;
  border-radius: 2px;
  opacity: 0.8;
  box-shadow: 0 0 8px rgba(251, 191, 36, 0.4);
}

.bv-results-college__label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #94a3b8;
}

.bv-results-college + .bv-results-grid {
  margin-bottom: 24px;
}

.bv-results-footer {
  max-width: 1180px;
  margin-top: 56px;
  padding-top: 32px;
  border-top: 1px solid rgba(255, 255, 255, 0.05);
  text-align: center;
}

/* py-20 / py-32 / text-center / opacity-50 all emit nothing here. */
.bv-results-empty {
  max-width: 1180px;
  padding: 72px 32px;
  text-align: center;
}

.bv-results-empty p {
  margin: 16px 0 0;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

/* The share and the count are the two numbers a reader compares, but
   the share was rendered in .bv-card__meta — the same muted grey as the
   partylist label two lines above it. Lift it clear of the labels
   without letting it compete with the amber count. */
.bv-card__share {
  font-size: 15px;
  font-weight: 800;
  line-height: 1;
  color: rgba(255, 255, 255, 0.75);
}

/* `truncate` emits nothing in this build, so a long name overflowed its
   card instead of ellipsing. */
.bv-card__title--clip {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bv-results-college__total {
  margin-left: auto;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

.bv-results-college__total strong {
  color: #FFB400;
  font-weight: 800;
}

.bv-results-college {
  max-width: 1180px;
}

/* An unopposed seat is a single card in the row. With 1fr tracks that card
   took the full 1180px band, which looked broken next to the contested rows
   above it. The cap only bites at one card — a two-up row computes to 581px
   and is untouched — so a lone candidate now matches its neighbours. */
.bv-results-grid > * {
  max-width: 600px;
}

/* ── RESULT CARD PORTRAIT ─────────────────────────────────────────
   partials/candidate-card-results.blade.php drew a .bv-avatar with
   initials and never read $candidate->photo_url, even though every
   seeded candidate has a photo (images/candidates/*.svg, served by
   nginx) and three other views already use that accessor.

   .bv-avatar cannot carry the image: it pins 36px with !important
   (see AVATAR SIZING above), which is a token rather than a face on a
   card this wide. Same reasoning as the ballot photo.               */

.bv-result-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;   /* mb-4 emits nothing in this build */
}

.bv-result-avatar {
  width: 56px;
  height: 56px;
  min-width: 56px;
  border-radius: 50%;
  object-fit: cover;
  /* Portrait sources are 4:5 with the head around 40% down; centring the
     crop would clip foreheads on real uploads. */
  object-position: 50% 40%;
  display: block;
  background: rgba(255, 180, 0, 0.05);
  border: 2px solid rgba(255, 180, 0, 0.35);
}

/* Same box as the photo, so a candidate without one does not shift the
   row next to a candidate who has one. */
.bv-result-avatar--text {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 17px;
  font-weight: 800;
  color: rgba(255, 180, 0, 0.7);
}

/* mt-6 / mb-2 on the figures row emit nothing either, so the count sat
   flush against the partylist label. */
.bv-result-card__figures {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  margin: 24px 0 8px;
}

/* ══════════════════════════════════════════════════════════════════
   ADMIN IDENTITY DETAIL + SOC DASHBOARD
   Added 2026-08-23. Both screens were written almost entirely in
   Tailwind scale utilities, and this build emits none of them. Measured
   in the browser against the CSS the container actually serves, 50
   classes across the two views resolved to nothing: every `mb-*`,
   `mt-*`, `p-*`, `py-*`, `space-y-*`, every named colour
   (`text-green-400`, `bg-white/5`, `text-white/60` …), `font-bold`,
   `text-xs`, `text-2xl`, `tracking-widest`, `rounded-lg`, `rounded-xl`,
   `shadow-2xl`, `w-2`, `h-2`, `top-0`, `left-0` and `animate-pulse`.
   Arbitrary-value classes (`text-[10px]`, `min-w-[200px]`) DO survive
   the build, which is what made the wreckage look partial rather than
   total.

   Everything below is additive. No existing rule is edited, so no other
   view moves.                                                         */

/* ── MISSING BADGE MODIFIERS ──────────────────────────────────────
   `.bv-badge--blue` (COMELEC role, used here and in the user registry)
   and `.bv-badge--yellow` (login_failed in the audit trail) were
   referenced by four views and defined by none. Measured: transparent
   background, 0px border — the pill rendered as bare text. */
.bv-badge--blue {
  background: rgba(59, 130, 246, 0.18) !important;
  border: 1px solid rgba(59, 130, 246, 0.4) !important;
  color: #60a5fa !important;
}

.bv-badge--yellow {
  background: rgba(245, 158, 11, 0.15) !important;
  border: 1px solid rgba(245, 158, 11, 0.4) !important;
  color: #fbbf24 !important;
}

/* ── CONTROL + SUBMIT ROW ─────────────────────────────────────────
   The reason both role dropdowns collapsed to 30px. `.bv-btn--primary`
   carries `width: 100% !important; display: block !important`, so in a
   flex row the button's flex-basis is the whole container while
   `.flex-1` gives the select a basis of 0. There is no free space left
   and the select shrinks to its arrow. Measured in a 700px row:
   select 30px, button 654px.

   Fixed here rather than on `.bv-btn--primary`, which ~20 other views
   depend on being full width. `> .bv-btn` is two classes deep, so it
   outranks the single-class !important it has to beat. */
.bv-form-row {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 12px;
}

/* A 200px basis rather than auto so the control wraps onto its own full
   width line when the CARD is narrow, not just when the viewport is.
   The tablet breakpoint keeps .bv-grid-2 at two columns down to 641px,
   which leaves this row about 266px wide — a viewport media query would
   not catch that. */
.bv-form-row > .bv-input,
.bv-form-row > select.bv-input {
  flex: 1 1 200px;
  min-width: 0;
}

.bv-form-row > .bv-btn {
  flex: 0 0 auto;
  width: auto !important;
  margin-bottom: 0 !important;
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
}

@media (max-width: 640px) {
  .bv-form-row { flex-direction: column; }
  .bv-form-row > .bv-btn { width: 100% !important; }
}

/* ── FIELD LABEL + HELPER NOTE ────────────────────────────────────
   The helper text under each control used `.bv-card__meta`, which
   forces uppercase. A full sentence set in 10px capitals is close to
   unreadable — "PROMOTING TO COMELEC ALLOWS MANAGEMENT OF ELECTIONS
   AND CANDIDATES." Labels stay capitalised; prose does not. */
.bv-field-label {
  display: block;
  margin: 0 0 12px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

.bv-note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 10px 0 0;
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.6;
  letter-spacing: 0.01em;
  text-transform: none;
  color: rgba(255, 255, 255, 0.6);
}

.bv-note i {
  margin-top: 2px;
  color: rgba(255, 255, 255, 0.52);
  flex-shrink: 0;
}

.bv-note--error { color: #ff8080; }
.bv-note--error i { color: #ff8080; }

/* ── IDENTITY HEADER ──────────────────────────────────────────────
   `text-2xl` is dead, so the name measured 18px — the same size as the
   card title above it. The email used `.bv-card__meta`, which rendered
   an address in capitals. */
.bv-identity {
  display: flex;
  align-items: center;
  gap: 20px;
}

.bv-identity__name {
  margin: 0 0 5px;
  font-size: 22px;
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: #ffffff;
}

.bv-identity__email {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-transform: none;
  color: rgba(255, 255, 255, 0.6);
  word-break: break-all;
}

/* ── DETAIL LIST ──────────────────────────────────────────────────
   Was `space-y-4` + `py-3` + `border-b`. Only the border survived, so
   the four rows measured 0px of padding and stacked flush against each
   other. The status value also lost `text-green-400` / `text-red-400`
   and computed to pure white — "Operational" and "Suspended" rendered
   identically, which is the one distinction on this card that carries
   meaning. */
.bv-detail-list {
  display: flex;
  flex-direction: column;
}

.bv-detail-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 13px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.bv-detail-row:first-child { padding-top: 0; }

.bv-detail-row:last-child {
  padding-bottom: 0;
  border-bottom: none;
}

.bv-detail-row__label {
  margin: 0;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

.bv-detail-row__value {
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.01em;
  text-align: right;
  color: rgba(255, 255, 255, 0.92);
}

.bv-detail-row__value--ok,
.bv-detail-row__value--warn {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.bv-detail-row__value--ok   { color: #22c55e; }
.bv-detail-row__value--warn { color: #ef4444; }

/* ── SECURITY EVENT LIST ──────────────────────────────────────────
   `p-3 bg-white/5 rounded-lg` all resolved to nothing, so each event
   measured padding 0, transparent background, radius 0 — a bare 2px
   stripe with text jammed against it. */
.bv-event-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.bv-event {
  padding: 12px 14px;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-left: 2px solid rgba(59, 130, 246, 0.65);
  border-radius: 10px;
}

.bv-event--warning {
  border-left-color: rgba(245, 158, 11, 0.75);
}

.bv-event--critical {
  background: rgba(239, 68, 68, 0.06);
  border-left-color: rgba(239, 68, 68, 0.8);
}

.bv-event__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.bv-event__action {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.9);
}

.bv-event__time {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.03em;
  white-space: nowrap;
  color: rgba(255, 255, 255, 0.52);
}

.bv-event__detail {
  margin: 6px 0 0;
  font-size: 11.5px;
  line-height: 1.6;
  letter-spacing: 0;
  text-transform: none;
  color: rgba(255, 255, 255, 0.5);
}

.bv-event-empty {
  margin: 0;
  padding: 20px 0;
  text-align: center;
  font-size: 12px;
  font-style: italic;
  text-transform: none;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.52);
}

.bv-event-empty i {
  display: block;
  margin-bottom: 10px;
  font-size: 22px;
  font-style: normal;
  color: rgba(255, 255, 255, 0.52);
}

/* Vertical rhythm between top-level blocks on a page. `mb-12` / `mb-8`
   emit nothing, so the metric grid, the filter bar and the audit trail
   were stacked flush against each other. */
.bv-stack-lg { margin-bottom: 32px; }
.bv-stack-md { margin-bottom: 24px; }

/* ── CALLOUT ──────────────────────────────────────────────────────
   The "this is your own session" panel: `p-6 bg-blue-500/10
   rounded-xl text-blue-300` — every one of those is dead. */
.bv-callout {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 20px;
  border-radius: 14px;
}

.bv-callout--blue {
  background: rgba(59, 130, 246, 0.08);
  border: 1px solid rgba(59, 130, 246, 0.2);
}

.bv-callout__icon {
  flex-shrink: 0;
  font-size: 20px;
  line-height: 1.3;
  color: #60a5fa;
}

.bv-callout__text {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.65;
  letter-spacing: 0.01em;
  text-transform: none;
  color: rgba(147, 197, 253, 0.85);
}

/* ── WALLET HASH CARD ─────────────────────────────────────────────
   `bg-[#6366f1]/10` does survive the build (arbitrary values do), but
   `bg-black/20 p-4 rounded-xl` on the code block do not, so the hash
   sat naked on the card. */
.bv-card-heading {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  color: #ffffff;
}

.bv-hash-block {
  padding: 14px 16px;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: 12px;
}

.bv-hash-block code {
  display: block;
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 11px;
  line-height: 1.7;
  letter-spacing: 0.02em;
  color: #a5b4fc;
  word-break: break-all;
}

/* ── DESTRUCTIVE / RESTORATIVE BUTTONS ────────────────────────────
   Replaces the inline style attribute on Suspend and the dead
   `bg-green-600` on Re-activate. Both inherit the full-width default
   from `.bv-btn`, which is what these two want. */
.bv-btn--danger-ghost {
  background: rgba(239, 68, 68, 0.06) !important;
  border: 1px solid rgba(239, 68, 68, 0.25) !important;
  color: #f87171 !important;
}

.bv-btn--danger-ghost:hover {
  background: rgba(239, 68, 68, 0.14) !important;
  border-color: rgba(239, 68, 68, 0.45) !important;
  color: #fca5a5 !important;
}

.bv-btn--success {
  background: rgba(34, 197, 94, 0.14) !important;
  border: 1px solid rgba(34, 197, 94, 0.35) !important;
  color: #4ade80 !important;
}

.bv-btn--success:hover {
  background: rgba(34, 197, 94, 0.24) !important;
  border-color: rgba(34, 197, 94, 0.55) !important;
  color: #86efac !important;
}

/* ── SOC: LAST-UPDATED STAMP ──────────────────────────────────────
   The pulsing green dot and the LIVE FEED label were removed on
   2026-09-10. Their rules (.bv-live, .bv-live__dot, .bv-live__label
   and the bv-live-pulse keyframes) went with them; nothing else in the
   app used them. The 30-second stats poll in admin/soc/index.blade.php
   still runs and still rewrites this timestamp, which is now the only
   thing on the page that says the numbers are refreshing. */
.bv-live__stamp {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: right;
  color: rgba(255, 255, 255, 0.52);
}

/* ── SOC: KPI NUMBER ──────────────────────────────────────────────
   `.bv-card__stat` is 1.1rem, so the four headline counts rendered
   SMALLER than the card titles above them. Scoped to a modifier —
   `.bv-card__stat` itself is shared by a dozen other cards. */
.bv-card__stat--kpi {
  font-size: 2.4rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
  margin-bottom: 0;
}

/* ── SOC: FILTER BAR ──────────────────────────────────────────────
   Was `flex-wrap gap-6` with `border-r pr-6` dividers. `pr-6` is dead,
   so each label sat flush against the rule beside it. Dropped the
   rules; the gap already separates the fields, and the row now wraps
   into real columns instead of squeezing. */
.bv-filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 18px;
}

.bv-filter-bar__field {
  flex: 1 1 200px;
  min-width: 170px;
}

.bv-filter-bar__actions {
  display: flex;
  flex: 0 0 auto;
  gap: 10px;
}

.bv-filter-bar__actions .bv-btn {
  width: auto !important;
  margin-bottom: 0 !important;
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
}

/* ── SOC: AUDIT TRAIL ─────────────────────────────────────────────
   `max-h-[400px] overflow-y-auto` DOES apply (arbitrary values
   survive), so the log is a 400px scroll box nested inside the page's
   own scroll — and the header did not stick, so scrolling the log lost
   the column names. Sticky header, a slightly taller box, and an
   opaque header fill (a translucent one lets rows show through). */
.bv-log-scroll {
  max-height: 460px;
  overflow-y: auto;
  overflow-x: auto;
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: 12px;
}

.bv-log-scroll .bv-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: #192435;
}

.bv-log-scroll .bv-table td { vertical-align: top; }

.bv-log-time {
  display: block;
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 10.5px;
  line-height: 1.5;
  white-space: nowrap;
  color: #60a5fa;
}

.bv-log-time span {
  color: rgba(255, 255, 255, 0.6);
}

.bv-log-ip {
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 10.5px;
  color: #818cf8;
}

.bv-log-actor {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.bv-log-actor__name {
  font-size: 12px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.88);
}

.bv-log-actor__id {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: rgba(255, 255, 255, 0.52);
}

.bv-log-actor__anon {
  font-size: 12px;
  font-style: italic;
  color: rgba(255, 255, 255, 0.52);
}

/* Two lines, then ellipsis. The full text is on the cell's title
   attribute — the old hover panel used `bg-slate-900 shadow-2xl p-4
   rounded-lg top-0 left-0`, all dead, so it opened as a transparent
   unpositioned block on top of the row it was explaining. */
.bv-log-details {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin: 0;
  max-width: 460px;
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 10.5px;
  line-height: 1.55;
  white-space: pre-wrap;
  color: rgba(255, 255, 255, 0.6);
}

.bv-log-badge {
  font-size: 9px;
  letter-spacing: 0.08em;
  white-space: nowrap;
}

/* ── FIXED FOOTER CLEARANCE ───────────────────────────────────────
   layouts/dashboard.blade.php pins the footer with `position: fixed`,
   but `.bv-page` has no bottom padding to match, so the last ~40px of
   every page sits behind it — most visibly the final row of the audit
   trail. Applied to <main> rather than `.bv-page` because the layout
   renders flash messages in their own `.bv-page` with
   `padding-bottom: 0`, which this must not fight. */
.bv-main-content > main {
  padding-bottom: 48px;
}

/* ── SYSTEM SETTINGS: STATUS ROWS ─────────────────────────────────
   Added 2026-08-23. The two protocol rows were
   `p-4 bg-white/5 rounded-lg border border-white/5`. Only the two border
   classes exist in this build, so each row measured **padding 0,
   transparent background, radius 0** — a bare 1px hairline with the text
   pressed into it. The emergency block was the same shape: a 520px red
   box containing a 518px button, flush on every side. */
.bv-status-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.bv-status-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 16px;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 12px;
}

.bv-status-row__body { min-width: 0; }

/* A contract address is 42 characters with no break point. Without this it
   ran past the card on a phone even once the grid stopped growing to fit it. */
.bv-status-row code { overflow-wrap: anywhere; }

.bv-status-row__title {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, 0.92);
}

/* Sentence case on purpose. `.bv-card__meta` forces uppercase, and these
   lines are prose — "MANDATORY FOR ALL ADMIN/COMELEC ROLES" set in 9px
   capitals is not something anyone reads. */
.bv-status-row__desc {
  margin: 4px 0 0;
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.55;
  letter-spacing: 0.01em;
  text-transform: none;
  color: rgba(255, 255, 255, 0.6);
}

.bv-status-row__desc code {
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 10.5px;
  color: #a5b4fc;
}

.bv-status-row .bv-badge { flex-shrink: 0; }

/* A settings screen states policy. Where that policy is read from live
   configuration rather than typed in, say so — it is the difference
   between a panel that stays true and one that goes quietly stale. */
.bv-status-source {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0;
  font-size: 11px;
  font-weight: 500;
  line-height: 1.6;
  letter-spacing: 0.01em;
  text-transform: none;
  color: rgba(255, 255, 255, 0.52);
}

.bv-status-source i {
  margin-top: 2px;
  flex-shrink: 0;
  color: rgba(255, 255, 255, 0.52);
}

/* ══════════════════════════════════════════════════════════════════
   PAGEANT STRUCTURE BUILDER
   Added 2026-08-23. The builder injected its markup from JavaScript in
   Tailwind classes, almost none of which this build emits: `p-5`,
   `rounded-lg`, `bg-white/[0.03]`, `ml-6`, `pl-4`, `space-y-3`, `mb-3`,
   `!py-2`, `text-[9px]`, `w-24` and `flex-[2]` all resolved to nothing.
   That is why "ADD SEGMENT" collided with the next row's label, why no
   card ever appeared behind a group, and why the validation strip was a
   red line with unreadable text inside zero padding.

   The overlay also never set `overflow`, so once the structure grew past
   the viewport the dialog's own title became unreachable — which is what
   the reported screenshot shows.                                       */

/* ── Overlay ────────────────────────────────────────────────────── */
.bv-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: none;
  padding: 24px;
  background: rgba(15, 23, 42, 0.9);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  overflow-y: auto;              /* the fix for the unreachable header */
  -webkit-overflow-scrolling: touch;
}

.bv-modal-overlay.is-open {
  display: flex;
  align-items: flex-start;
  justify-content: center;
}

/* Never taller than the viewport; the body scrolls, the header stays. */
.bv-modal-panel {
  position: relative;
  width: 100%;
  max-width: 780px;
  margin: auto;
  max-height: calc(100vh - 48px);
  display: flex;
  flex-direction: column;
}

/* The <form> wraps the body and the footer, so IT is the panel's flex item --
   not the body. Capping the panel and telling the body to scroll achieved
   nothing while the form in between was free to grow to its content. Every link
   in the chain has to be allowed to shrink. */
.bv-modal-panel > form {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.bv-modal-panel__head {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 26px 30px 18px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.bv-modal-panel__title {
  margin: 0;
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: #ffffff;
}

.bv-modal-panel__body {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 24px 30px;
  overflow-y: auto;

  /* A flex item defaults to min-height:auto, which refuses to shrink below its
     content -- so overflow-y never engaged and a tall structure pushed straight
     out through the bottom of the dialog. */
  flex: 1 1 auto;
  min-height: 0;
}

.bv-modal-panel__foot {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  padding: 18px 30px 24px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.bv-modal-panel__foot .bv-btn {
  width: auto !important;
  margin-bottom: 0 !important;
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
}

.bv-modal-close {
  position: absolute;
  top: 18px;
  right: 18px;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 8px;
  color: rgba(255, 255, 255, 0.55);
  font-size: 13px;
  cursor: pointer;
  transition: color 0.15s, background 0.15s;
}

.bv-modal-close:hover {
  background: rgba(255, 255, 255, 0.08);
  color: #ffffff;
}

/* ── Structure builder ────────────────────────────── */
/* Revised 2026-08-24. The first version gave every node a full card: its own
   background, border, padding, two label rows and a full-width gold button.
   That reads fine with three items and becomes a wall at seventeen — a pageant
   with two rounds of three categories of three criteria. Nesting a card inside a
   card inside a card also flattens the hierarchy, because depth 3 looks exactly
   like depth 1.

   It is a compact row tree instead: one line per item, chrome removed from the
   node itself and carried by an indent guide, and the controls reduced to icons.
   A node went from ~145px tall to ~38px. */

.bv-builder__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.bv-builder__title {
  margin: 0;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}

.bv-builder__hint {
  margin: 8px 0 0;
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.6;
  text-transform: none;
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, 0.6);
}

.bv-builder__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 12px;
}

/* The node itself is now just a container. All the visual weight moved to the
   row, so nesting no longer stacks borders inside borders. */
.bv-node { display: block; }

.bv-node__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 5px 7px;
  border-radius: 8px;
  border: 1px solid transparent;
  transition: background 0.12s, border-color 0.12s;
}

.bv-node__row:hover { background: rgba(255, 255, 255, 0.03); }

.bv-node__row:focus-within {
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(100, 150, 255, 0.25);
}

/* A parent is the thing that carries a weight budget, so it gets the only
   emphasis in the tree — everything else stays quiet. */
.bv-node--parent > .bv-node__row {
  background: rgba(255, 255, 255, 0.035);
  border-color: rgba(255, 255, 255, 0.06);
}

.bv-node__grip {
  flex: 0 0 auto;
  width: 14px;
  text-align: center;
  font-size: 9px;
  color: rgba(255, 255, 255, 0.52);
}

.bv-node--parent > .bv-node__row .bv-node__grip { color: rgba(255, 180, 0, 0.5); }

/* Fields carry no visible label. Seventeen rows of "NAME / WEIGHT %" is the
   noise this layout exists to remove; the inputs keep aria-labels and
   placeholders instead. */
.bv-node__row .bv-input {
  padding: 6px 9px !important;
  font-size: 12px !important;
  border-radius: 7px !important;
  min-height: 0 !important;
}

.bv-node__name { flex: 1 1 150px; min-width: 110px; }

.bv-node__weight { flex: 0 0 64px; text-align: right; }

.bv-node__rule { flex: 0 0 156px; }

/* The scale, both ends. Narrower than the rest because both hold a small whole
   number in practice — "1" and "10", or "70" and "100" — and the row already
   carries a name, a weight, a rule and two tallies before it reaches them. */
.bv-node__min,
.bv-node__max { flex: 0 0 58px; text-align: right; }

/* Suffix so a bare number is not ambiguous once the labels are gone. */
.bv-node__unit {
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 800;
  color: rgba(255, 255, 255, 0.52);
}

.bv-node__btn {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border-radius: 7px;
  font-size: 10px;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.6);
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}

.bv-node__btn:hover {
  background: rgba(255, 255, 255, 0.09);
  color: #ffffff;
}

.bv-node__btn--add:hover {
  background: rgba(255, 180, 0, 0.16);
  border-color: rgba(255, 180, 0, 0.4);
  color: #FFB400;
}

.bv-node__btn--remove:hover {
  background: rgba(239, 68, 68, 0.16);
  border-color: rgba(239, 68, 68, 0.4);
  color: #f87171;
}

/* Indent guide. One line per level rather than a nested box, so depth is
   readable at a glance and costs 13px instead of a full card inset. */
.bv-node__children {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 2px 0 4px 13px;
  padding-left: 11px;
  border-left: 1px solid rgba(255, 180, 0, 0.18);
}

.bv-node__children:empty {
  display: none;
}

/* Running total for one sibling group, inline on the parent's own row. The
   first version put it on a separate line under a full-width button, which is
   most of why a three-level tree ran off the screen. */
.bv-node__tally {
  flex: 0 0 auto;
  padding: 2px 7px;
  border-radius: 999px;
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 0.06em;
  white-space: nowrap;
  background: rgba(255, 255, 255, 0.05);
  color: rgba(255, 255, 255, 0.6);
}

.bv-node__tally--ok {
  background: rgba(34, 197, 94, 0.14);
  color: #4ade80;
}

.bv-node__tally--over {
  background: rgba(239, 68, 68, 0.14);
  color: #f87171;
}

.bv-node__tally:empty { display: none; }

.bv-builder__summary {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  padding: 10px 13px;
  border-radius: 10px;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: 0.01em;
}

.bv-builder__summary--ok {
  background: rgba(34, 197, 94, 0.08);
  border: 1px solid rgba(34, 197, 94, 0.25);
  color: #4ade80;
}

.bv-builder__summary--warn {
  background: rgba(239, 68, 68, 0.07);
  border: 1px solid rgba(239, 68, 68, 0.22);
  color: #f87171;
}

.bv-builder__empty {
  margin: 12px 0 0;
  padding: 20px;
  text-align: center;
  font-size: 12px;
  font-style: italic;
  border: 1px dashed rgba(255, 255, 255, 0.1);
  border-radius: 12px;
  color: rgba(255, 255, 255, 0.52);
}

/* The old full-width gold pill, kept only for the one "Add item" at the top of
   the builder. Inside the tree it is an icon. */
.bv-node__add {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: rgba(255, 180, 0, 0.1);
  border: 1px solid rgba(255, 180, 0, 0.3);
  border-radius: 7px;
  padding: 5px 11px;
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #FFB400;
  cursor: pointer;
  transition: background 0.15s;
}

.bv-node__add:hover { background: rgba(255, 180, 0, 0.2); }

/* Narrow screens: the scoring pair drops to its own line rather than squeezing
   the name field to nothing. */
@media (max-width: 620px) {
  .bv-node__rule { flex: 1 1 140px; }
  .bv-node__name { flex: 1 1 100%; }
}

.bv-form-error {
  margin: 0;
  padding: 12px 14px;
  border-radius: 10px;
  background: rgba(239, 68, 68, 0.08);
  border: 1px solid rgba(239, 68, 68, 0.25);
  color: #f87171;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.55;
}

/* Sub-label inside a tabulation header cell and for absent values. */
.bv-tab-sub {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.52);
}

/* The pager wrapper on the candidate registry. mt-8 emits nothing in this
   build, so without this the pager sat flush against the table card. */
.bv-candidates-pager {
  /* Inside the table card, under its last row: padded off the card's
     edges and ruled from the rows. It sat 1px from the left edge and
     over the card's bottom border. */
  padding: 14px 20px 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

/* ── PAGER (resources/views/pagination/bv.blade.php) ──────────────
   Own markup, own styles. The [role="navigation"] nav rules above target
   the built-in Tailwind pager's nested structure and do not reach this
   one, which is why the five screens still on the default pager are
   unaffected by anything here. */

.bv-pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.bv-pager__meta {
  margin: 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  /* Was 0.35, measured 3.2:1 on the page background. */
  color: rgba(255, 255, 255, 0.6);
}

.bv-pager__meta span {
  color: rgba(255, 255, 255, 0.85);
  font-weight: 800;
}

.bv-pager__links {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
}

.bv-pager__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  height: 36px;
  padding: 0 10px;
  border-radius: 9px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(255, 255, 255, 0.05);
  color: rgba(255, 255, 255, 0.65);
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}

a.bv-pager__link:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.2);
  color: #ffffff;
}

.bv-pager__link--on {
  background: #FFB400;
  border-color: #FFB400;
  color: #0b1220;
}

.bv-pager__link--off {
  opacity: 0.3;
  cursor: default;
}

.bv-pager__gap {
  padding: 0 2px;
  color: rgba(255, 255, 255, 0.52);
  font-weight: 700;
}

/* Eleven 36px links in one unwrapping row is ~456px -- wider than a
   phone, so the row overflowed both edges and cut off the arrows.
   Smaller links, and a wrap as the fallback on the narrowest screens. */
@media (max-width: 640px) {
  .bv-pager { justify-content: center; }
  .bv-pager__links {
    margin-left: 0;
    flex-wrap: wrap;
    justify-content: center;
    gap: 3px;
  }
  .bv-pager__link {
    min-width: 28px;
    height: 32px;
    padding: 0 4px;
    font-size: 12px;
  }
}

/* The fade on each edge of a sideways-scrolling table (see
   .bv-table-container in the 640px block): the right one while more
   table lies to the right, the left one once rows have scrolled past. */
@keyframes bv-table-edge-hint {
  0%, 8%    { background-size: 20px 100%, 0 100%; }
  12%, 88%  { background-size: 20px 100%, 20px 100%; }
  92%, 100% { background-size: 0 100%, 20px 100%; }
}

/* ══════════════════════════════════════════════════════════════════
   BATCH VOTER IMPORT
   admin/users/import.blade.php was styled almost entirely with the
   dead half of the Tailwind build: mb-6 / p-4 / text-sm / font-semibold
   / text-gray-400 / bg-blue-900/20 / border-blue-500/30 / rounded-lg
   all emit nothing, so the page rendered as unpadded, single-weight
   white text with a 1px hairline where the drop target should be.
   Rebuilt here on real rules. Every selector below is a new name — no
   existing component is touched, so no other view can move.
   ══════════════════════════════════════════════════════════════════ */

/* .bv-card__body is `flex column; gap: 1.5rem`, but the form is a
   single child of it, so its own children need their own rhythm. */
.bv-import-form {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* ── DROP TARGET ──────────────────────────────────────────────────
   A <label for="file"> wrapping a .bv-visually-hidden input: the whole
   panel is the click target and the input stays keyboard-reachable,
   which `class="hidden"` (display:none) would have destroyed. */
.bv-dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 44px 24px;
  text-align: center;
  cursor: pointer;
  border: 2px dashed rgba(255, 180, 0, 0.22);
  border-radius: 16px;
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(255, 180, 0, 0.05) 0%, rgba(255, 180, 0, 0) 60%),
    rgba(0, 0, 0, 0.18);
  transition: border-color 0.2s, background-color 0.2s, box-shadow 0.2s;
}

.bv-dropzone:hover,
.bv-dropzone:focus-within,
.bv-dropzone.is-dragging {
  border-color: rgba(255, 180, 0, 0.6);
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(255, 180, 0, 0.10) 0%, rgba(255, 180, 0, 0) 62%),
    rgba(0, 0, 0, 0.28);
  box-shadow: 0 0 0 4px rgba(255, 180, 0, 0.07);
}

.bv-dropzone.is-dragging .bv-dropzone__icon {
  transform: translateY(-3px);
  border-color: rgba(255, 180, 0, 0.65);
}

.bv-dropzone__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  font-size: 26px;
  color: #FFB400;
  background: rgba(255, 180, 0, 0.10);
  border: 1px solid rgba(255, 180, 0, 0.28);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.10);
  transition: transform 0.2s, border-color 0.2s;
}

.bv-dropzone__title {
  font-size: 15px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.92);
  letter-spacing: 0.01em;
}

.bv-dropzone__link {
  color: #FFB400;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.bv-dropzone__hint {
  max-width: 460px;
  font-size: 12.5px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.6);
}

.bv-dropzone__chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin-top: 4px;
}

.bv-chip {
  padding: 4px 11px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

/* ── SELECTED FILE ────────────────────────────────────────────────
   The old page gave no confirmation at all: you picked a file, the
   dialog closed, and the panel still read "Click to upload". */
.bv-filecard {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  border-radius: 14px;
  background: rgba(34, 197, 94, 0.06);
  border: 1px solid rgba(34, 197, 94, 0.22);
}

.bv-filecard__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 42px;
  height: 42px;
  border-radius: 10px;
  font-size: 16px;
  color: #4ade80;
  background: rgba(34, 197, 94, 0.12);
  border: 1px solid rgba(34, 197, 94, 0.28);
}

.bv-filecard__body {
  flex: 1;
  min-width: 0;
}

.bv-filecard__name {
  display: block;
  font-size: 13.5px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.92);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bv-filecard__meta {
  display: block;
  margin-top: 3px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-filecard__remove {
  flex-shrink: 0;
  padding: 8px 14px;
  border-radius: 8px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.5);
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.12);
  transition: color 0.2s, border-color 0.2s, background-color 0.2s;
}

/* The card doubles as a drop target once a file is chosen — the empty
   drop panel is hidden by then. */
.bv-filecard.is-dragging {
  border-color: rgba(34, 197, 94, 0.55);
  background: rgba(34, 197, 94, 0.12);
  box-shadow: 0 0 0 4px rgba(34, 197, 94, 0.07);
}

.bv-filecard__remove:hover {
  color: #f87171;
  border-color: rgba(239, 68, 68, 0.45);
  background: rgba(239, 68, 68, 0.08);
}

/* ── FORMAT SPEC TABLE ────────────────────────────────────────────
   Frames a .bv-table so it reads as a panel rather than four naked
   columns. Scoped padding: the default 16px is tuned for the log
   tables, which carry far more text per cell than this one. */
.bv-spec {
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 12px;
  /* Scroll sideways, do not clip. `overflow: hidden` was here to round the
     table's corners, and on a phone it also cut off 239px of Voter Import's
     column-rules table -- most of it -- with no way to reach it. An auto
     overflow still clips to the rounded border, so the corners survive. */
  overflow-x: auto;
  overflow-y: hidden;
}

.bv-spec .bv-table th { padding: 12px 16px; }
.bv-spec .bv-table td { padding: 12px 16px; font-size: 13px; vertical-align: middle; }
.bv-spec .bv-table tr:last-child td { border-bottom: none; }

.bv-spec code {
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 12px;
  font-weight: 600;
  color: #FFB400;
  background: rgba(255, 180, 0, 0.08);
  border: 1px solid rgba(255, 180, 0, 0.16);
  border-radius: 6px;
  padding: 2px 7px;
  white-space: nowrap;
}

.bv-spec__example {
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.55);
}

.bv-spec__rules {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* Scoped to this form so no other view's disabled button changes. */
.bv-import-form .bv-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

@media (max-width: 640px) {
  .bv-dropzone { padding: 32px 18px; }
  .bv-dropzone__icon { width: 60px; height: 60px; font-size: 22px; }
  .bv-filecard { flex-wrap: wrap; }
  .bv-filecard__remove { width: 100%; }
}

/* ══════════════════════════════════════════════════════════════════
   JUDGING SHEET (judge/score-sheet.blade.php)
   Audited in the browser against the three stylesheets the container
   actually serves. The view was written almost entirely in Tailwind,
   and the measurements came back:
     - the criteria grid computed `display: flex`, not grid; `grid-cols-1`
       is emitted but `md:grid-cols-2` is not, so the second column never
       existed and the panels sized themselves to their content
     - every criterion panel: padding 0px, border-radius 0px (`p-8`,
       `rounded-xl` dead) — text flush to the edge of a grey rectangle
     - the criterion NAME rendered at rgba(255,255,255,0.35), because
       `.bv-label` won and `text-white` is dead, while its sub-label sat
       at full gold: the hierarchy was upside down
     - the section total — meant to be `text-6xl font-black text-gold` —
       computed 16px, weight 400, plain white, on a panel with no
       background, no padding and no radius (`bg-white/[0.02]` is dead
       despite the brackets), and its glow layer measured 0x0 (`inset-0`)
     - `mt-10`, `mt-8`, `mt-2`, `mb-2`, `gap-10`, `gap-5`, `text-sm`,
       `text-xs`, `text-2xl`, `text-3xl`, `font-black`, `font-bold`,
       `tracking-wider`, `tracking-widest`, `rounded-lg`, `rounded-xl`
       and every `md:` variant: all dead in this build
   Rebuilt below on real rules. New selectors only — nothing existing is
   touched, so no other screen can move.
   ══════════════════════════════════════════════════════════════════ */

/* ── STICKY SHEET HEADER ──────────────────────────────────────────
   Replaces a style="position:sticky;…" attribute on .bv-page-header. */
.bv-judge-head {
  position: sticky;
  top: 0;
  z-index: 20;
  margin-top: -20px;
  padding: 18px 0;
  background: #0a1628;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.bv-judge-head__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.bv-judge-head__left {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.bv-judge-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  color: rgba(255, 255, 255, 0.6);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  text-decoration: none;
  transition: color 0.2s, border-color 0.2s, background-color 0.2s;
}

.bv-judge-back:hover {
  color: #fff;
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.16);
}

/* ── ROUND TABS ───────────────────────────────────────────────────
   These were `px-5 py-2.5 rounded-lg text-sm font-black … bg-gold
   text-slate-900` — every one of those is dead, so the tabs rendered as
   bare underlined anchors. */
.bv-seg {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 5px;
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.06);
  overflow-x: auto;
  max-width: 100%;
}

.bv-seg__item {
  padding: 9px 18px;
  border-radius: 8px;
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  text-decoration: none;
  color: rgba(255, 255, 255, 0.6);
  transition: color 0.2s, background-color 0.2s;
}

.bv-seg__item:hover {
  color: #fff;
  background: rgba(255, 255, 255, 0.06);
}

.bv-seg__item--on {
  color: #0a1628;
  background: #FFB400;
  box-shadow: 0 0 16px rgba(255, 180, 0, 0.28);
}

.bv-seg__item--on:hover {
  color: #0a1628;
  background: #FFB400;
}

/* ── "NOW JUDGING" CONTEXT BAR ────────────────────────────────────
   `p-6 rounded-xl bg-white/[0.03]` produced no panel at all. */
.bv-judge-context {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin: 24px 0;
  padding: 20px 24px;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid rgba(255, 255, 255, 0.07);
}

.bv-judge-context__eyebrow {
  display: block;
  margin-bottom: 6px;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(255, 180, 0, 0.6);
}

.bv-judge-context__title {
  margin: 0;
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: #fff;
}

.bv-judge-context__share {
  text-align: right;
}

.bv-judge-context__value {
  font-size: 26px;
  font-weight: 900;
  line-height: 1;
  color: #FFB400;
}

/* ── CANDIDATE CARD ───────────────────────────────────────────────*/
.bv-score-stack {
  display: flex;
  flex-direction: column;
  gap: 28px;
}

.bv-score-card {
  overflow: hidden;
}

.bv-score-card--locked {
  border-color: rgba(34, 197, 94, 0.35) !important;
}

/* .bv-card__body is padded and gapped; this sheet lays out its own
   sections, so the body is reset and each section pads itself. */
.bv-score-card > .bv-card__body {
  padding: 0 !important;
  gap: 0 !important;
}

.bv-score-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 22px 26px;
  background: rgba(255, 255, 255, 0.02);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.bv-score-identity {
  display: flex;
  align-items: center;
  gap: 18px;
  min-width: 0;
}

.bv-score-num {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 56px;
  height: 56px;
  border-radius: 14px;
  font-size: 19px;
  font-weight: 900;
  letter-spacing: -0.02em;
  color: #FFB400;
  background: rgba(255, 180, 0, 0.09);
  border: 1px solid rgba(255, 180, 0, 0.22);
}

.bv-score-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 56px;
  height: 56px;
  min-width: 56px;
  border-radius: 50%;
  object-fit: cover;
  font-size: 16px;
  font-weight: 800;
  color: #FFB400;
  background: rgba(255, 180, 0, 0.1);
  border: 2px solid #FFB400;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
}

.bv-score-name {
  margin: 0;
  font-size: 21px;
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: #fff;
}

.bv-score-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-score-meta__role {
  color: #FFB400;
}

.bv-score-meta__dot {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.2);
}

/* ── SCORING BODY ─────────────────────────────────────────────────*/
.bv-score-body {
  padding: 26px;
}

/* auto-fit rather than a `md:` breakpoint: the md: variants emit nothing
   in this build, and a container-driven fit is what the layout wanted
   anyway — two panels side by side while each still fits 300px, one
   column below that, at any viewport. */
.bv-score-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 18px;
}

.bv-score-item {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 20px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid rgba(255, 255, 255, 0.06);
  overflow: hidden;
  transition: border-color 0.2s;
}

.bv-score-item:focus-within {
  border-color: rgba(255, 180, 0, 0.35);
}

/* The criterion name is the thing being judged — it outranks its own
   metadata. It read at 35% opacity while the share below it was solid
   gold, which is exactly backwards. */
.bv-score-item__name {
  display: block;
  margin: 0;
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.01em;
  text-transform: none;
  color: rgba(255, 255, 255, 0.94);
}

.bv-score-item__share {
  display: inline-block;
  margin-top: 6px;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #FFB400;
  background: rgba(255, 180, 0, 0.1);
  border: 1px solid rgba(255, 180, 0, 0.2);
}

.bv-score-item__row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.bv-score-input {
  flex: 1;
  min-width: 0;
  height: 62px !important;
  padding: 0 16px !important;
  font-size: 30px !important;
  font-weight: 800 !important;
  line-height: 1 !important;
  text-align: left !important;
  font-variant-numeric: tabular-nums;
  background: rgba(0, 0, 0, 0.28) !important;
  border: 1px solid rgba(255, 255, 255, 0.1) !important;
  border-radius: 12px !important;
  color: #fff !important;
}

.bv-score-input.is-dirty {
  border-color: rgba(255, 180, 0, 0.45) !important;
}

/* Replaces the four style-attribute writes the lock handler used to make. */
.bv-score-input.is-locked {
  background: transparent !important;
  border-color: transparent !important;
  color: #4ade80 !important;
  padding-left: 0 !important;
}

.bv-score-item__max {
  flex-shrink: 0;
  font-size: 17px;
  font-weight: 800;
  color: rgba(255, 255, 255, 0.52);
}

.bv-score-item__weighted {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 6px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

.bv-score-item__weighted span {
  font-size: 13px;
  letter-spacing: 0;
  color: rgba(255, 255, 255, 0.75);
  font-variant-numeric: tabular-nums;
}

.bv-score-item__veil {
  position: absolute;
  inset: 0;
  z-index: 10;
  background: rgba(10, 22, 40, 0.45);
  backdrop-filter: blur(1px);
}

/* ── SECTION TOTAL ────────────────────────────────────────────────*/
.bv-score-total {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 20px;
  padding: 20px 26px;
  border-radius: 16px;
  background:
    linear-gradient(135deg, rgba(255, 180, 0, 0.09) 0%, rgba(255, 180, 0, 0.02) 60%),
    rgba(255, 255, 255, 0.02);
  border: 1px solid rgba(255, 180, 0, 0.18);
}

.bv-score-total__label {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(255, 180, 0, 0.75);
}

.bv-score-total__value {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 40px;
  font-weight: 900;
  line-height: 1;
  color: #FFB400;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 2px 18px rgba(255, 180, 0, 0.25);
}

.bv-score-total__max {
  font-size: 16px;
  font-weight: 800;
  color: rgba(255, 180, 0, 0.45);
  text-shadow: none;
}

/* ── ACTIONS ──────────────────────────────────────────────────────
   `.bv-btn` is width:100% by default, so both buttons stretched to half
   the card each and read as equally weighted. Locking a score is
   irreversible; saving a draft is not. They should not look alike. */
.bv-score-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 20px;
}

.bv-score-actions > .bv-btn {
  width: auto !important;
  /* Sized by their own padding rather than a fixed floor, so the label sets
     the width: "Save Draft" and "Lock & Submit" measure 166px and 185px,
     close enough to read as a pair without forcing the two identical slabs
     the old flex-1 pair produced. */
  padding: 0 26px;
  height: 52px;
  margin-bottom: 0 !important;
  font-size: 12px;
  letter-spacing: 0.1em;
}

.bv-score-lock {
  background: rgba(34, 197, 94, 0.16) !important;
  border: 1px solid rgba(34, 197, 94, 0.4) !important;
  color: #4ade80 !important;
}

.bv-score-lock:hover {
  background: rgba(34, 197, 94, 0.26) !important;
  border-color: rgba(34, 197, 94, 0.6) !important;
}

/* Second click of the two-step confirm. Was four style-attribute writes
   inside the handler. */
.bv-score-lock.is-confirming {
  background: rgba(239, 68, 68, 0.18) !important;
  border-color: rgba(239, 68, 68, 0.45) !important;
  color: #f87171 !important;
}

.bv-score-locked {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 20px;
  padding: 16px;
  border-radius: 14px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #4ade80;
  background: rgba(34, 197, 94, 0.08);
  border: 1px solid rgba(34, 197, 94, 0.22);
}

@media (max-width: 640px) {
  .bv-judge-context { padding: 16px 18px; }
  .bv-score-head { padding: 18px; }
  .bv-score-body { padding: 18px; }
  .bv-score-actions > .bv-btn { width: 100% !important; }
  .bv-score-total { justify-content: center; text-align: center; }
}

/* ═══════════════════════════════════════════════════════════════════
   JUDGING DECK  —  resources/views/judge/deck.blade.php

   One contestant per screen, for a judge holding a tablet in a dark
   auditorium. Everything here is sized for that: the contestant number
   and the score field are the two things readable at arm's length, and
   the two navigation buttons sit under the thumbs at the bottom edge.

   Sized in ch/px rather than with Tailwind utilities — this build emits
   no spacing or colour scale (see beevote-frontend-pipeline). Reflow is
   driven by container width via auto-fit, not by viewport breakpoints,
   because a tablet in landscape inside the dashboard shell is not the
   width the viewport reports.
   ═══════════════════════════════════════════════════════════════════ */

.bv-deck {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 24px 24px 8px;
  box-sizing: border-box;
  min-height: calc(100vh - 64px);
}

/* ── Stage rail ─────────────────────────────────────────────────── */
.bv-deck-top {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 14px;
}

.bv-deck-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  color: rgba(255, 255, 255, 0.6);
  text-decoration: none;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.07);
  transition: color 0.2s, border-color 0.2s;
}

.bv-deck-back:hover {
  color: #FFB400;
  border-color: rgba(255, 180, 0, 0.35);
}

.bv-deck-stages {
  display: flex;
  flex: 1;
  flex-wrap: wrap;
  gap: 8px;
  min-width: 0;
}

.bv-deck-stage {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 18px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  color: rgba(255, 255, 255, 0.6);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.07);
  transition: color 0.2s, background 0.2s, border-color 0.2s;
}

.bv-deck-stage:hover {
  color: rgba(255, 255, 255, 0.8);
}

.bv-deck-stage.is-open {
  color: #0a1628;
  background: #FFB400;
  border-color: #FFB400;
}

/* A stage this judge has closed. Reachable — a judge may want to see
   what they gave — but visibly finished. */
.bv-deck-stage.is-done {
  color: #4ade80;
  border-color: rgba(34, 197, 94, 0.3);
}

.bv-deck-stage.is-done.is-open {
  color: #0a1628;
  background: #4ade80;
  border-color: #4ade80;
}

.bv-deck-who {
  flex-shrink: 0;
}

/* ── Progress ───────────────────────────────────────────────────── */
.bv-deck-progress {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 18px;
  padding: 16px 20px;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid rgba(255, 255, 255, 0.06);
}

.bv-deck-progress__label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.bv-deck-progress__stage {
  font-size: 17px;
  font-weight: 900;
  letter-spacing: -0.01em;
  color: #fff;
}

.bv-deck-progress__share {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-deck-progress__count {
  margin-left: auto;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #FFB400;
  white-space: nowrap;
}

/* The rail doubles as the map and the jump control: a judge who wants to
   revisit #3 taps #3 rather than swiping back through two cards. */
.bv-deck-rail {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  min-width: 0;
}

/* The crown a run of numbers belongs to. Contestant numbers restart at 01
   in each division, so without this the rail reads 01 02 01 02 with nothing
   explaining the repeat. Full-width so it forces a wrap and each crown
   starts its own line -- the boundary has to be visible at a glance to a
   judge holding a tablet in a dark auditorium. */
.bv-deck-rail__crown {
  flex: 0 0 100%;
  margin-top: 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--bv-gold, #b8862b);
}

.bv-deck-rail__crown:first-child {
  margin-top: 0;
}

/* The rounds an award is decided on. A wrapping row of checkboxes rather
   than a multi-select: a multi-select hides how many are chosen behind a
   scroll, and 'which rounds decide the crown' is a question the organizer
   has to be able to answer at a glance before the show starts. */
.bv-award-scope {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin-top: 4px;
}

.bv-award-scope__item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  cursor: pointer;
}

.bv-award-scope__item input {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  cursor: pointer;
}

/* The cut control on a round's roster panel. One row: a number and the
   button that proposes the cut, kept on the panel it applies to so the
   round is never ambiguous. */
.bv-roster__advance {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 10px 0 14px;
  padding-top: 12px;
  border-top: 1px solid var(--bv-rule, rgba(255,255,255,.08));
}

.bv-roster__advance .bv-label {
  margin: 0;
}

.bv-roster__advance input[type="number"] {
  width: 72px;
  flex: 0 0 auto;
}

.bv-deck-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  padding: 0;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 800;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.6);
  background: rgba(255, 255, 255, 0.03) !important;
  border: 1px solid rgba(255, 255, 255, 0.08) !important;
  transition: color 0.2s, border-color 0.2s, background 0.2s;
}

/* Scored. Not the same as "current" — a judge needs both at a glance. */
.bv-deck-dot.is-done {
  color: #4ade80;
  border-color: rgba(34, 197, 94, 0.35) !important;
  background: rgba(34, 197, 94, 0.08) !important;
}

.bv-deck-dot.is-at {
  color: #0a1628;
  background: #FFB400 !important;
  border-color: #FFB400 !important;
}

.bv-deck-dot--review {
  color: rgba(255, 255, 255, 0.55);
}

.bv-deck-sealed {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 20px;
  border-radius: 14px;
  font-size: 13px;
  font-weight: 600;
  color: #4ade80;
  background: rgba(34, 197, 94, 0.08);
  border: 1px solid rgba(34, 197, 94, 0.22);
}

/* ── Panels ─────────────────────────────────────────────────────── */
.bv-deck-track {
  position: relative;
  flex: 1;
  display: flex;
}

.bv-deck-panel {
  display: none;
  flex-direction: column;
  gap: 22px;
  width: 100%;
  padding: 28px;
  box-sizing: border-box;
  border-radius: 20px;
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid rgba(255, 255, 255, 0.06);
}

.bv-deck-panel.is-on {
  display: flex;
}

/* The slide. Direction carries meaning — forward comes from the right,
   back from the left — so a judge can tell a correction from an advance
   without reading the counter. */
.bv-deck-panel.is-in-right { animation: bv-deck-right 0.22s ease-out; }
.bv-deck-panel.is-in-left  { animation: bv-deck-left  0.22s ease-out; }

@keyframes bv-deck-right {
  from { opacity: 0; transform: translateX(36px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes bv-deck-left {
  from { opacity: 0; transform: translateX(-36px); }
  to   { opacity: 1; transform: translateX(0); }
}

@media (prefers-reduced-motion: reduce) {
  .bv-deck-panel.is-in-right,
  .bv-deck-panel.is-in-left { animation: none; }
}

/* ── Identity ───────────────────────────────────────────────────── */
.bv-deck-identity {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
}

/* A portrait, not a thumbnail. The brief is that the screen belongs to one
   contestant, and the seeded placeholders are 400x500 -- cropping those to a
   132px square threw away most of the picture and most of the point. Sized
   against the viewport so a phone, a tablet in portrait and a tablet in
   landscape each get as much portrait as they can afford, capped so it never
   pushes the score fields below the fold. */
.bv-deck-photo {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: clamp(150px, 26vw, 240px);
  aspect-ratio: 4 / 5;
  height: auto;
  border-radius: 20px;
  object-fit: cover;
  /* Faces sit in the upper half of a portrait; centring the crop cuts heads. */
  object-position: top center;
  background: rgba(255, 180, 0, 0.08);
  border: 2px solid rgba(255, 180, 0, 0.5);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
}

.bv-deck-photo--none {
  font-size: clamp(32px, 4vw, 52px);
  font-weight: 900;
  letter-spacing: -0.02em;
  color: #FFB400;
}

.bv-deck-who-block {
  min-width: 0;
}

/* The number is the largest thing on the card on purpose: it is what the
   host calls and what the judge matches against the stage. */
.bv-deck-number {
  font-size: 58px;
  font-weight: 900;
  line-height: 0.95;
  letter-spacing: -0.04em;
  color: #FFB400;
}

.bv-deck-name {
  margin: 6px 0 0;
  font-size: 26px;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: #fff;
}

.bv-deck-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-deck-meta__dot {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.2);
}

/* ── Fields ─────────────────────────────────────────────────────── */
.bv-deck-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 16px;
  align-items: start;
}

/* A heading only appears when the stage has more than one group under it,
   so a flat stage does not grow a redundant label. */
.bv-deck-group {
  grid-column: 1 / -1;
  padding-top: 6px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-deck-field {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.07);
  transition: border-color 0.2s;
}

.bv-deck-field:focus-within {
  border-color: rgba(255, 180, 0, 0.4);
}

.bv-deck-field__name {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 15px;
  font-weight: 700;
  color: #fff;
}

.bv-deck-field__share {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

.bv-deck-field__entry {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* The global `input` rule sets padding with !important, so the touch
   target has to out-shout it. 64px tall, 30px digits: legible and
   hittable without looking, which is the condition a judge is in. */
.bv-deck-input {
  flex: 1;
  min-width: 0;
  height: 64px;
  padding: 0 18px !important;
  font-size: 30px !important;
  font-weight: 800 !important;
  text-align: center;
  letter-spacing: -0.02em;
  border-radius: 14px !important;
  background: rgba(255, 255, 255, 0.05) !important;
  border: 1px solid rgba(255, 255, 255, 0.1) !important;
}

.bv-deck-input:focus {
  border-color: rgba(255, 180, 0, 0.5) !important;
  box-shadow: 0 0 0 4px rgba(255, 180, 0, 0.1) !important;
}

.bv-deck-input[readonly] {
  color: rgba(255, 255, 255, 0.55) !important;
  background: rgba(255, 255, 255, 0.02) !important;
}

.bv-deck-field__max {
  flex-shrink: 0;
  font-size: 15px;
  font-weight: 800;
  color: rgba(255, 255, 255, 0.52);
}

.bv-deck-total {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 10px;
  margin-top: auto;
  padding: 20px 24px;
  border-radius: 16px;
  background: rgba(255, 180, 0, 0.06);
  border: 1px solid rgba(255, 180, 0, 0.18);
}

.bv-deck-total__label {
  margin-right: auto;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-deck-total__value {
  font-size: 38px;
  font-weight: 900;
  line-height: 1;
  letter-spacing: -0.03em;
  color: #FFB400;
}

.bv-deck-total__max {
  font-size: 14px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.52);
}

/* ── Review ─────────────────────────────────────────────────────── */
.bv-deck-review-title {
  margin: 0;
  font-size: 24px;
  font-weight: 900;
  letter-spacing: -0.02em;
  color: #fff;
}

.bv-deck-review-sub {
  margin: 8px 0 0;
  font-size: 13px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.6);
}

.bv-deck-review-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.bv-deck-review-row {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  padding: 16px 20px;
  border-radius: 14px;
  text-align: left;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.07);
  transition: border-color 0.2s, background 0.2s;
}

.bv-deck-review-row:hover {
  border-color: rgba(255, 180, 0, 0.35);
  background: rgba(255, 255, 255, 0.05);
}

/* A contestant the judge left blank. The submit button refuses while any
   of these exist, and this is where it sends them. */
.bv-deck-review-row.is-missing {
  border-color: rgba(239, 68, 68, 0.4);
  background: rgba(239, 68, 68, 0.07);
}

.bv-deck-review-row__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  border-radius: 12px;
  font-size: 15px;
  font-weight: 900;
  color: #FFB400;
  background: rgba(255, 180, 0, 0.1);
  border: 1px solid rgba(255, 180, 0, 0.22);
}

.bv-deck-review-row__name {
  flex: 1;
  min-width: 0;
  font-size: 16px;
  font-weight: 700;
  color: #fff;
}

.bv-deck-review-row__score {
  font-size: 22px;
  font-weight: 900;
  letter-spacing: -0.02em;
  color: #fff;
}

.bv-deck-review-row.is-missing .bv-deck-review-row__score {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #f87171;
}

.bv-deck-review-row__go {
  flex-shrink: 0;
  color: rgba(255, 255, 255, 0.52);
}

/* ── Skipped ────────────────────────────────────────────────────── */
.bv-deck-skipped {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 20px;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px dashed rgba(255, 255, 255, 0.1);
}

.bv-deck-skipped__head {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-deck-skipped__row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 14px;
}

.bv-deck-skipped__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  font-size: 13px;
  font-weight: 800;
  color: rgba(255, 255, 255, 0.6);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.bv-deck-skipped__name {
  flex: 1;
  min-width: 0;
  font-size: 14px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.55);
}

.bv-deck-skipped__note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 4px 0 0;
  font-size: 12px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.6);
}

.bv-deck-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 40px;
  border-radius: 16px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
  background: rgba(255, 255, 255, 0.02);
  border: 1px dashed rgba(255, 255, 255, 0.08);
}

.bv-deck-final {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 20px;
  border-radius: 14px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #4ade80;
  background: rgba(34, 197, 94, 0.08);
  border: 1px solid rgba(34, 197, 94, 0.22);
}

.bv-deck-submit {
  padding: 20px !important;
  font-size: 14px !important;
  color: #0a1628 !important;
  background: #FFB400 !important;
  border: 1px solid #FFB400 !important;
}

.bv-deck-submit:hover {
  background: #ffc233 !important;
}

/* Armed for the second tap. Red, because this is the only thing a judge
   does that cannot be undone. */
.bv-deck-submit.is-armed {
  color: #fff !important;
  background: rgba(239, 68, 68, 0.9) !important;
  border-color: rgba(239, 68, 68, 0.9) !important;
}

.bv-deck-submit:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.bv-deck-review-warn:empty {
  display: none;
}

.bv-deck-review-warn {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  text-align: center;
  color: #f87171;
}

/* ── Footer navigation ──────────────────────────────────────────── */
/* Sticky rather than static: on a tall card the judge's thumbs should
   not have to chase the buttons down the page. */
.bv-deck-nav {
  position: sticky;
  bottom: 0;
  display: flex;
  gap: 14px;
  padding: 14px 0 18px;
  background: linear-gradient(to top, #0a1628 55%, rgba(10, 22, 40, 0));
}

/* .bv-btn is width:100%, which in a flex row claims the whole container
   as its basis and starves its sibling. Out-specified here rather than by
   editing .bv-btn, which ~20 views depend on. */
.bv-deck-nav > .bv-btn {
  flex: 1;
  width: auto !important;
  padding: 20px !important;
  font-size: 14px !important;
}

.bv-deck-prev {
  max-width: 200px;
}

.bv-deck-next {
  color: #0a1628 !important;
  background: #FFB400 !important;
  border: 1px solid #FFB400 !important;
}

.bv-deck-next:hover {
  background: #ffc233 !important;
}

/* Disabled means "this contestant is not fully scored yet", which is the
   whole gate. It has to look unavailable, not merely dimmed. */
.bv-deck-next:disabled {
  color: rgba(255, 255, 255, 0.52) !important;
  background: rgba(255, 255, 255, 0.04) !important;
  border-color: rgba(255, 255, 255, 0.08) !important;
  cursor: not-allowed;
}

.bv-deck-next.is-hidden {
  display: none;
}

@media (max-width: 700px) {
  .bv-deck { padding: 16px 16px 8px; gap: 14px; }
  .bv-deck-panel { padding: 20px; gap: 18px; }
  .bv-deck-photo { width: 124px; }
  .bv-deck-number { font-size: 44px; }
  .bv-deck-name { font-size: 21px; }
  .bv-deck-total { flex-wrap: wrap; justify-content: space-between; }
  .bv-deck-total__value { font-size: 30px; }
  .bv-deck-nav > .bv-btn { padding: 17px !important; }
  .bv-deck-prev { max-width: 130px; }
  .bv-deck-who { display: none; }
}

/* ── JUDGING PORTAL CARDS ─────────────────────────────────────────
   The portal was drawn in dead utilities and style attributes; these
   are the real rules behind judge/dashboard.blade.php. */
.bv-judge-card__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 20px;
}

.bv-judge-card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 13px;
  font-size: 17px;
  color: #FFB400;
  background: rgba(255, 180, 0, 0.1);
  border: 1px solid rgba(255, 180, 0, 0.22);
}

.bv-judge-card__title {
  margin: 0 0 8px;
  font-size: 17px;
  font-weight: 800;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: #fff;
}

.bv-judge-card__desc {
  margin: 0 0 20px;
  font-size: 12px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.6);
}

.bv-judge-card__facts {
  display: flex;
  gap: 10px;
  margin-bottom: 16px;
}

.bv-judge-card__fact {
  flex: 1;
  padding: 12px 14px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.06);
}

.bv-judge-card__fact span {
  display: block;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

.bv-judge-card__fact strong {
  display: block;
  margin-top: 4px;
  font-size: 20px;
  font-weight: 900;
  letter-spacing: -0.02em;
  color: #fff;
}

.bv-judge-card__stages {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 20px;
}

.bv-judge-card__stage {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 11px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.07);
}

.bv-judge-card__stage.is-done {
  color: #4ade80;
  background: rgba(34, 197, 94, 0.08);
  border-color: rgba(34, 197, 94, 0.25);
}

/* The round the organizer has put on stage. A judge may score this one
   and no other, so it is the loudest thing on the card. */
.bv-judge-card__stage.is-live {
  color: #ffb400;
  background: rgba(255, 180, 0, 0.1);
  border-color: rgba(255, 180, 0, 0.35);
}

.bv-judge-card__stage-live {
  font-size: 9px;
  letter-spacing: 0.08em;
  opacity: 0.75;
}

/* A deck with no round on stage. Rendered as a span rather than a link,
   because a button that leads to a refusal is worse than one that says so. */
.bv-btn.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

.bv-judge-empty {
  grid-column: 1 / -1;
  background: transparent !important;
  border: 1px dashed rgba(255, 255, 255, 0.08) !important;
}

.bv-judge-empty .bv-card__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 60px 20px !important;
  color: rgba(255, 255, 255, 0.52);
}

/* ── RUNNING ORDER (COMELEC roster control) ───────────────────────
   Lives on the tabulation screen because the person watching the
   standing is the person who knows number 5 is not in the wings. */
.bv-roster__intro {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0 0 20px;
  font-size: 13px;
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.6);
}

.bv-roster__intro strong {
  color: #FFB400;
}

.bv-roster__tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 18px;
}

.bv-roster__tab {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.6);
  background: rgba(255, 255, 255, 0.03) !important;
  border: 1px solid rgba(255, 255, 255, 0.07) !important;
  transition: color 0.2s, background 0.2s, border-color 0.2s;
}

.bv-roster__tab.is-on {
  color: #0a1628;
  background: #FFB400 !important;
  border-color: #FFB400 !important;
}

.bv-roster__tab-lock {
  font-size: 9px;
  opacity: 0.7;
}

.bv-roster__stage {
  display: none;
  flex-direction: column;
  gap: 8px;
}

.bv-roster__stage.is-on {
  display: flex;
}

.bv-roster__state {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  padding: 14px 16px;
  margin-bottom: 6px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid rgba(255, 255, 255, 0.05);
}

.bv-roster__state-note {
  font-size: 12px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.6);
}

/* Whether a round is on stage. Sits directly above .bv-roster__state and
   shares its shape on purpose: one is whether judging is allowed, the
   other is how far it got, and they belong to the same column. */
.bv-roster__gate {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  padding: 14px 16px;
  margin-bottom: 6px;
  border-radius: 12px;
  background: rgba(255, 180, 0, 0.04);
  border: 1px solid rgba(255, 180, 0, 0.16);
}

.bv-roster__gate .bv-roster__state-note {
  flex: 1 1 240px;
}

/* The live board.
   What the organizer watches during a show. The working table below it
   carries a column per judged item, which is right for checking
   arithmetic and wrong for reading a placement across a room. */
.bv-board {
  list-style: none;
  margin: 0 0 20px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.bv-board__row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 14px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid rgba(255, 255, 255, 0.05);
}

.bv-board__row.is-lead {
  background: rgba(255, 180, 0, 0.07);
  border-color: rgba(255, 180, 0, 0.3);
}

/* Scored on fewer rounds than the rows above, so it is shown but not
   presented as comparable. */
.bv-board__row.is-out {
  opacity: 0.45;
}

.bv-board__rank {
  flex: 0 0 auto;
  min-width: 28px;
  font-size: 20px;
  font-weight: 800;
  text-align: center;
  color: rgba(255, 255, 255, 0.5);
}

.bv-board__row.is-lead .bv-board__rank {
  color: #ffb400;
}

.bv-board__photo {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border-radius: 10px;
  object-fit: cover;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

/* A contestant with no photo gets their number in the same frame, so the
   row keeps its height and the column does not jump as the board
   reorders under a new score. */
.bv-board__photo--none {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  font-weight: 800;
  color: rgba(255, 255, 255, 0.6);
}

.bv-board__who {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.bv-board__name {
  font-size: 15px;
  line-height: 1.3;
}

.bv-board__number {
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-board__score {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  font-size: 17px;
}

/* Minting a judge account, on the board screen. */
.bv-panel-new {
  margin-top: 18px;
  padding: 16px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px dashed rgba(255, 255, 255, 0.1);
}

/* What you fill in on the left, what comes back on the right.

   Wrapping is driven by flex-basis rather than a viewport media query on
   purpose: this sits inside a card, and .bv-grid-2 holds two columns down
   to 641px, so a viewport rule would leave a 260px column on a tablet. The
   basis makes it fold on the CARD's width, which is the width that matters. */
.bv-panel-new {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 18px;
}

.bv-panel-new__fields {
  flex: 1 1 280px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
}

/* The old rule was `flex: 1 1 200px`, written when this was a row. In a
   column the basis lands on the main axis, which is now height -- every
   field would have been 200px tall.

   The padding is restated because `--compact` only ever existed as
   `select.bv-input--compact`. A text input beside it kept the base 12px
   and stood four pixels taller than the select under it, which is exactly
   the kind of mismatch that reads as a broken build. */
.bv-panel-new__fields .bv-input {
  width: 100% !important;
  flex: 0 0 auto;
  padding: 9px 12px !important;
  font-size: 12px !important;
}

/* Beats `select.bv-input--compact { width: auto !important }`, which is
   right for a control sitting in a row and wrong for one in a column. */
.bv-panel-new__fields select.bv-input--compact {
  width: 100% !important;
  padding: 9px 12px !important;
  font-size: 12px !important;
}

/* The button stops short of the full column instead of stretching across
   it: a 9.5px uppercase label on a 280px slab reads as a banner. */
.bv-panel-new__fields .bv-node__add {
  align-self: flex-start;
  margin-top: 2px;
}

/* The credentials appear once and are never re-fetchable, so the block
   that carries them is loud rather than tidy.

   It is always in the layout now. It used to be toggled with the `hidden`
   attribute, which never worked: `display: flex` here is an author rule and
   `[hidden] { display: none }` is the browser's own, so the attribute lost
   the cascade and an empty amber box rendered on every load. The resting
   state below is what the reader sees before an account exists. */
.bv-panel-new__result {
  flex: 1 1 320px;
  padding: 14px 16px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px dashed rgba(255, 255, 255, 0.10);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 8px;
  box-sizing: border-box;
}

.bv-panel-new__idle {
  margin: 0;
  font-size: 12px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.6);
}

/* Carrying a password it will never show again. Loud, and solid rather
   than dashed, so a filled pane is not mistaken for an empty one. */
.bv-panel-new__result.is-filled {
  justify-content: flex-start;
  background: rgba(255, 180, 0, 0.07);
  border: 1px solid rgba(255, 180, 0, 0.35);
}

.bv-panel-new__creds {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 14px;
  word-break: break-all;
}

.bv-panel-new__warn {
  font-size: 12px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.55);
}

/* The segment gate, beside the segments themselves. One row per round,
   in running order, coloured by state so the live one is findable at a
   glance from across a table. */
.bv-gate-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 14px;
}

.bv-gate-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 16px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid rgba(255, 255, 255, 0.06);
}

.bv-gate-row--open {
  background: rgba(255, 180, 0, 0.07);
  border-color: rgba(255, 180, 0, 0.35);
}

.bv-gate-row--closed {
  opacity: 0.6;
}

.bv-gate-row__order {
  flex: 0 0 auto;
  width: 26px;
  text-align: center;
  font-size: 15px;
  font-weight: 800;
  color: rgba(255, 255, 255, 0.6);
}

.bv-gate-row--open .bv-gate-row__order {
  color: #ffb400;
}

.bv-gate-row__name {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 14px;
  font-weight: 600;
  min-width: 0;
}

.bv-gate-row__state {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-gate-row--open .bv-gate-row__state {
  color: rgba(255, 180, 0, 0.85);
}

/* Which round a segment belongs to. Two stages can both hold a "Question
   and Answer", so the segment name alone is not always enough. */
.bv-gate-row__stage {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

/* Showing and hiding parts of the judging tree. A full event is three
   stages, seven segments and thirty-one criteria; rendered flat that is a
   wall nobody can find anything in. */
.bv-node.is-collapsed > .bv-node__children {
  display: none;
}

.bv-node__grip--toggle {
  background: none;
  border: 0;
  cursor: pointer;
  transition: transform 0.15s ease, opacity 0.15s ease;

  /* Wider and taller than the static grip it replaces. That one is a
     14px decorative marker; this is something you click on a laptop
     trackpad while reading a handout. */
  width: 22px;
  height: 22px;
  padding: 0;
  font-size: 11px;
  border-radius: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* Out-specifies .bv-node--parent > .bv-node__row .bv-node__grip, which
   would otherwise hold the colour through hover. */
.bv-node--parent > .bv-node__row .bv-node__grip--toggle:hover {
  color: #ffb400;
  background: rgba(255, 180, 0, 0.12);
}

/* The chevron points down when open and right when closed, which is the
   one disclosure convention every file tree already taught everybody. */
.bv-node.is-collapsed > .bv-node__row .bv-node__grip--toggle {
  transform: rotate(-90deg);
}

/* How much a closed row is hiding. Without it a collapsed segment and an
   empty one look identical. */
.bv-node__count {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
  white-space: nowrap;
}

.bv-builder__head-ops {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

/* The dashboard board. One card per crown, stacked. */
.bv-dash-live {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* The board is the whole point of this screen, so its rows are larger
   here than where they sit above a working table. */
.bv-dash-live .bv-board__photo {
  width: 56px;
  height: 56px;
}

.bv-dash-live .bv-board__rank {
  font-size: 24px;
  min-width: 34px;
}

.bv-dash-live .bv-board__name {
  font-size: 17px;
}

.bv-dash-live .bv-board__score {
  font-size: 20px;
}

.bv-roster__row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 14px;
  padding: 12px 16px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid rgba(255, 255, 255, 0.06);
}

.bv-roster__row.is-out {
  background: rgba(255, 255, 255, 0.012);
  border-style: dashed;
}

.bv-roster__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 11px;
  font-size: 13px;
  font-weight: 900;
  color: #FFB400;
  background: rgba(255, 180, 0, 0.09);
  border: 1px solid rgba(255, 180, 0, 0.2);
}

.bv-roster__row.is-out .bv-roster__num {
  color: rgba(255, 255, 255, 0.52);
  background: rgba(255, 255, 255, 0.03);
  border-color: rgba(255, 255, 255, 0.07);
}

.bv-roster__name {
  flex: 1;
  min-width: 160px;
  font-size: 14px;
  font-weight: 700;
  color: #fff;
}

.bv-roster__row.is-out .bv-roster__name {
  color: rgba(255, 255, 255, 0.6);
}

.bv-roster__status {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}

.bv-roster__note {
  font-size: 11px;
  font-style: italic;
  color: rgba(255, 255, 255, 0.52);
}

.bv-roster__actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}

.bv-roster__btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 9px 14px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.6);
  background: rgba(255, 255, 255, 0.04) !important;
  border: 1px solid rgba(255, 255, 255, 0.09) !important;
  transition: color 0.2s, background 0.2s, border-color 0.2s;
}

.bv-roster__btn:hover {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.2) !important;
}

.bv-roster__btn--cut:hover {
  color: #f87171;
  border-color: rgba(239, 68, 68, 0.4) !important;
}

.bv-roster__btn.is-armed {
  color: #fff !important;
  background: rgba(239, 68, 68, 0.85) !important;
  border-color: rgba(239, 68, 68, 0.85) !important;
}

.bv-roster__btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* A contestant who was cut still appears in the standing, ranked below
   everyone still competing. Dimmed so the line between the two groups is
   visible without reading the note. */
.bv-table tr.bv-row--out td {
  opacity: 0.55;
}

/* Cards in the portal grid are equal height, with the action pinned to the
   bottom edge so a row of them does not step. */
.bv-judge-card > .bv-card__body {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.bv-judge-card > .bv-card__body > .bv-btn {
  margin-top: auto;
}

/* The review panel is read, not filled in, so it is capped at a comfortable
   measure instead of stretching to a 1200px tablet in landscape. */
.bv-deck-panel--review {
  max-width: 820px;
  margin: 0 auto;
}

.bv-deck-review-head {
  padding-bottom: 4px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

/* ── DECK: LANDSCAPE PANEL ────────────────────────────────────────
   Stacked, the portrait and the score fields do not fit one tablet
   screen: on a 1024x768 tablet the sidebar leaves ~790px of content,
   and rail + progress + a 300px portrait + fields + total + the
   sticky nav runs about 825px. The judge would scroll to reach the
   inputs, which is the one thing the brief rules out — picture,
   number and score are meant to be on screen together.

   So above 1000px the panel becomes two columns: the contestant on
   the left, the criteria stacked on the right. The portrait gets
   bigger rather than smaller, because it now has a column of its own.

   Keyed on the viewport rather than the container because the panel
   has no container query support to rely on here; 1000px viewport is
   ~770px of content, which is the width at which two columns stop
   squeezing the inputs. */
@media (min-width: 1000px) {
  .bv-deck-panel.is-on {
    display: grid;
    grid-template-columns: minmax(230px, 330px) 1fr;
    grid-template-areas:
      "who fields"
      "who total";
    /* Rows, explicitly. The identity column spans both, so whichever row is
       left to absorb the portrait's extra height decides where the slack
       goes. Unspecified, it landed in the TOTAL row and stretched a 75px
       box into a ~350px slab. Given to the fields row instead: the criteria
       breathe, and the total stays its own size. */
    grid-template-rows: 1fr auto;
    column-gap: 34px;
    row-gap: 20px;
  }

  .bv-deck-identity {
    grid-area: who;
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
  }

  /* The number goes ABOVE the portrait, not below it. Measured at
     1280x720 with the portrait first, the name block landed at y=659
     -- under the sticky nav and off the screen -- which put the one
     thing the host actually calls out where the judge cannot see it.
     Ordered rather than reordered in the markup so the DOM still reads
     photo-then-name for assistive tech. */
  .bv-deck-who-block {
    order: -1;
  }

  /* Its own column now, so it is no longer competing with the inputs
     for width. Capped by height rather than width: uncapped it resolved
     to 363px from the 4:5 ratio and pushed the total row off screen.
     object-fit crops the overflow, and the crop is top-anchored. */
  .bv-deck-photo {
    width: 100%;
    max-width: none;
    max-height: 46vh;
  }

  /* 58px is right when the number sits beside a small photo; in this
     column it is competing with a portrait for the same vertical space. */
  .bv-deck-number {
    font-size: 46px;
  }

  .bv-deck-fields {
    grid-area: fields;
    grid-template-columns: 1fr;
    align-content: stretch;
    /* The row has to be told to take the space too, or the container grows
       and the cards sit at the top of it -- which is the same void, just
       moved inside an element with no background. */
    grid-auto-rows: minmax(150px, 1fr);
    /* And the base rule sets `align-items: start`, which pins every card to
       the top of its row no matter how tall the row is. That was the reason
       a 439px row still held a 153px card. */
    align-items: stretch;
  }

  /* A criterion card that has grown taller centres its input rather than
     leaving it stranded at the top of the extra space. */
  .bv-deck-field {
    justify-content: center;
  }

  /* align-self, because the default is `stretch` and the identity column
     spans BOTH rows: the total row therefore inherits whatever height the
     portrait leaves over, and the total box grew to fill it — a ~350px
     slab holding one number, next to a photo it dwarfed. Anchored to the
     end so its bottom edge lines up with the portrait's instead. */
  .bv-deck-total {
    grid-area: total;
    align-self: end;
    margin-top: 0;
    padding: 18px 24px;
  }
}

/* Wider still — a desktop or a large tablet in landscape — and the
   criteria can sit two across again without cramping. */
@media (min-width: 1500px) {
  .bv-deck-fields {
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  }
}

/* ═══════════════════════════════════════════════════════════════════
   DIGITAL BALLOT — redesign
   ═══════════════════════════════════════════════════════════════════ */

/* ── Contestant number ──
   Sits on the portrait, not under the name. A pageant audience is told
   "vote for number 3"; the number has to be findable at a glance down
   a grid of faces, which it is not when it trails the name in body
   text. Same numbering the judges' decks use. */
.bv-ballot-number {
  position: absolute;
  left: 10px;
  bottom: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  height: 40px;
  padding: 0 10px;
  border-radius: 12px;
  font-size: 17px;
  font-weight: 900;
  letter-spacing: -0.02em;
  color: #0a1628;
  background: #FFB400;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
}

/* Phone: the photo is a 56px circle, so a 40px badge inside it hid the
   face. The badge becomes a small chip on the circle's lower-right edge;
   the circle clip moves from the frame to the image so the chip can
   overhang it. */
@media (max-width: 640px) {
  .bv-ballot-photo { overflow: visible; border: 0; }
  .bv-ballot-photo__img,
  .bv-ballot-photo__initials {
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.12);
  }
  .bv-ballot-number {
    left: auto;
    right: -6px;
    bottom: -4px;
    min-width: 24px;
    height: 20px;
    padding: 0 5px;
    border-radius: 10px;
    font-size: 11px;
    border: 2px solid #1a2d4a;
    box-shadow: none;
  }
}

/* ── Selection ──
   This is the single most important state on the screen and it was a
   26px ring in the corner. Bigger, and the whole card now lifts. */
.bv-ballot-check {
  top: 12px;
  right: 12px;
  width: 34px;
  height: 34px;
  font-size: 15px;
}

.bv-ballot-input:checked + .bv-ballot-card {
  border: 2px solid #FFB400 !important;
  border-top: 2px solid #FFB400 !important;
  background: rgba(255, 180, 0, 0.09) !important;
  box-shadow: 0 0 0 1px rgba(255, 180, 0, 0.35), 0 10px 34px rgba(255, 180, 0, 0.16) !important;
}

/* Unselected cards recede once a pick is made in that race, so the
   chosen face is the one the eye lands on. */
.bv-ballot-input:checked + .bv-ballot-card .bv-ballot-name {
  color: #FFB400;
}

/* ── Header ── */
.bv-ballot-pool {
  color: #fff;
  font-weight: 700;
}

.bv-ballot-session {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 10px 18px;
  color: #4ade80;
  background: rgba(34, 197, 94, 0.08);
  border: 1px solid rgba(34, 197, 94, 0.3);
}

/* ── Live per-race count ──
   The badge read "REQUIRED: 1 CHOICE" and never changed, so on a long
   ballot a voter could not tell which races they had already filled
   without scrolling back through all of them. */
.bv-ballot-count {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: rgba(255, 255, 255, 0.72);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.14);
  transition: color 0.2s, background 0.2s, border-color 0.2s;
}

.bv-ballot-count.is-done {
  color: #4ade80;
  background: rgba(34, 197, 94, 0.12);
  border-color: rgba(34, 197, 94, 0.35);
}

/* The check appears only once the race is filled, so the finished state
   does not rest on colour alone. */
.bv-ballot-count__icon {
  display: none;
}

.bv-ballot-count.is-done .bv-ballot-count__icon {
  display: inline-block;
}

/* ── Ballot page heading (2026-09-15) ──
   The election is the page heading, one size up from other pages: it is
   the one thing a voter must be sure of before choosing. The race name
   was an 11px grey eyebrow ("REP") and is now a section title, since it
   is the question each grid answers. */
.bv-ballot-title {
  font-size: 2.2rem;
}

.bv-section__heading.bv-ballot-headline h2.bv-section-title {
  margin: 0;
  font-size: 1.15rem;
  letter-spacing: 0.02em;
  color: #ffffff;
}

.bv-ballot-commit__title {
  margin: 0 0 14px;
}

/* Which race a pick answers, in front of its number and name. */
.bv-ballot-commit__pick-pos {
  padding-left: 7px;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 180, 0, 0.9);
}

/* ── Review and commit ──
   Replaces a 3rem-padded box holding a heading, two lines of copy and
   a button — about 300px of vertical space that told the voter nothing
   about what they were making permanent. The ballot is irreversible
   and anonymous; the last thing on screen before committing should be
   the selection itself. */
.bv-ballot-commit {
  display: grid;
  grid-template-columns: 1fr minmax(280px, 380px);
  gap: 28px;
  align-items: center;
  margin-top: 36px;
  padding: 26px 28px;
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid rgba(255, 180, 0, 0.28);
}

/* min-width:0 so a long candidate name wraps inside the column instead
   of forcing the grid track wider and squeezing the action column. */
.bv-ballot-commit__review {
  min-width: 0;
}

.bv-ballot-commit__eyebrow {
  display: block;
  margin-bottom: 14px;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-ballot-commit__list {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.bv-ballot-commit__empty {
  font-size: 14px;
  font-style: italic;
  color: rgba(255, 255, 255, 0.6);
}

.bv-ballot-commit__pick {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 9px 16px 9px 9px;
  border-radius: 999px;
  background: rgba(255, 180, 0, 0.1);
  border: 1px solid rgba(255, 180, 0, 0.3);
}

.bv-ballot-commit__pick-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  height: 30px;
  padding: 0 8px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 900;
  color: #0a1628;
  background: #FFB400;
}

.bv-ballot-commit__pick-name {
  font-size: 14px;
  font-weight: 700;
  color: #fff;
}

.bv-ballot-commit__action {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.bv-ballot-commit__warn {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin: 0;
  font-size: 12px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.72);
}

.bv-ballot-commit__warn i {
  margin-top: 2px;
  color: #FFB400;
}

.bv-ballot-commit__btn {
  padding: 18px 24px !important;
  font-size: 13px !important;
  /* The primary blue of .bv-btn--primary: submitting is the page's main action. */
  background: rgba(59, 130, 246, 0.2) !important;
  border: 1px solid rgba(100, 150, 255, 0.3) !important;
  color: #ffffff !important;
}

.bv-ballot-commit__btn:hover:not(:disabled) {
  background: rgba(59, 130, 246, 0.4) !important;
  border-color: rgba(100, 150, 255, 0.5) !important;
}

/* Disabled means "this ballot is not complete yet", which is the whole
   gate. It has to look unavailable rather than merely dimmed. */
.bv-ballot-commit__btn:disabled {
  /* Readable, because the disabled label says what is still missing. */
  color: rgba(255, 255, 255, 0.6) !important;
  background: rgba(255, 255, 255, 0.04) !important;
  border-color: rgba(255, 255, 255, 0.09) !important;
  cursor: not-allowed;
}

@media (max-width: 900px) {
  .bv-ballot-commit {
    grid-template-columns: 1fr;
    gap: 22px;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   PAGEANT AWARDS
   A pageant produces a winner, runners-up, a "best in" per round and a
   set of specials. Each card states its winner AND the basis, because
   an award with no stated basis invites exactly the question it
   cannot answer.
   ═══════════════════════════════════════════════════════════════════ */

.bv-is-hidden {
  display: none !important;
}

.bv-award-empty {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 0 0 24px;
  font-size: 13px;
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.6);
}

.bv-award-empty i {
  margin-top: 3px;
  color: #FFB400;
}

.bv-award-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
  margin-bottom: 26px;
}

.bv-award {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px 20px;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.07);
}

/* Decided reads gold, because it is a result. Undecided stays neutral
   and dashed so a board mid-event does not look like six failures. */
.bv-award--decided {
  border-color: rgba(255, 180, 0, 0.32);
  background: rgba(255, 180, 0, 0.05);
}

.bv-award--tied {
  border-color: rgba(239, 68, 68, 0.35);
  background: rgba(239, 68, 68, 0.05);
}

.bv-award--undecided {
  border-style: dashed;
}

.bv-award__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}

.bv-award__name {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.bv-award--decided .bv-award__name,
.bv-award--tied .bv-award__name {
  color: #FFB400;
}

.bv-award__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 8px;
  font-size: 11px;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.52);
  background: transparent !important;
  border: 1px solid rgba(255, 255, 255, 0.08) !important;
  transition: color 0.2s, border-color 0.2s;
}

.bv-award__remove:hover {
  color: #f87171;
  border-color: rgba(239, 68, 68, 0.4) !important;
}

.bv-award__winners {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.bv-award__winner {
  display: flex;
  align-items: center;
  gap: 12px;
}

.bv-award__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  min-width: 38px;
  height: 38px;
  padding: 0 9px;
  border-radius: 11px;
  font-size: 14px;
  font-weight: 900;
  color: #0a1628;
  background: #FFB400;
}

.bv-award__who {
  font-size: 17px;
  font-weight: 800;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: #fff;
}

.bv-award__value {
  font-size: 20px;
  font-weight: 900;
  letter-spacing: -0.02em;
  color: #fff;
}

.bv-award__value span {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-award__tied {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #f87171;
}

.bv-award__pending {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

.bv-award__basis {
  font-size: 11px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.6);
}

/* ── Add form ── */
.bv-award-add {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 14px;
  padding-top: 22px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.bv-award-add__row {
  flex: 1;
  min-width: 190px;
}

.bv-award-add__row--go {
  flex: 0 0 auto;
  min-width: 0;
}

/* .bv-btn is width:100%, which in this flex row would claim the whole
   container as its basis and starve every field beside it. */
.bv-award-add__row--go > .bv-btn {
  width: auto !important;
}

.bv-award-add__note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0;
  padding: 12px 14px;
  border-radius: 10px;
  font-size: 12px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.6);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.06);
}

.bv-award-add__note i {
  margin-top: 2px;
  color: #FFB400;
}

/* ═══════════════════════════════════════════════════════════════════
   PRINT
   Until now the app had NO print rules at all — not one @media print
   block in either stylesheet — so window.print() sent the dark-navy
   application UI to paper verbatim. What came out, measured against a
   real print preview: the sidebar, the topbar, the theme toggle, the
   PRINT button itself, and the Running Order CONTROL PANEL with its
   round tabs and status badges; the fixed footer collapsed on top of
   the Awards section; and it ended at page 1 of 1, so the standing and
   the per-judge breakdown — the only things a tabulator needs — never
   printed at all.

   Two other things fight this by default: `body { color: white }` with
   `background: #0a1628 !important`, and browsers printing backgrounds
   OFF unless the user ticks the box. So the theme drops out and the
   white text lands on white paper.
   ═══════════════════════════════════════════════════════════════════ */

/* Shown only on paper. Kept out of the screen flow entirely rather than
   with visibility, so it takes no space and is not read out. */
.bv-print-only {
  display: none;
}

@media print {

  @page {
    margin: 14mm 12mm;
  }

  /* Every colour is restated because the theme sets them !important and
     because a printer that drops backgrounds would otherwise leave the
     white body text invisible on white paper. */
  html, body {
    background: #fff !important;
    background-image: none !important;
    color: #000 !important;
    font-size: 10pt;
  }

  /* The shell. None of it is content. The footer matters most: it is
     position:fixed, which in print repeats on every page and lands on
     top of whatever is underneath — visible in the preview as the
     copyright line sitting across the Awards section. */
  .bv-sidebar,
  .bv-topbar,
  .bv-app-footer,
  .bv-sidebar-backdrop,
  .bv-hamburger,
  .bv-theme-toggle,
  .bv-toast-container,
  #bv-toast-container {
    display: none !important;
  }

  .bv-dashboard-layout,
  .bv-main-content,
  .bv-main-content > main {
    display: block !important;
    height: auto !important;
    width: auto !important;
    overflow: visible !important;
    padding: 0 !important;
    margin: 0 !important;
    background: none !important;
  }

  .bv-page {
    padding: 0 !important;
    min-height: 0 !important;
  }

  /* Anything that performs an action cannot be actioned on paper. */
  .bv-btn,
  button,
  select,
  input,
  .bv-roster__btn,
  .bv-award__remove {
    display: none !important;
  }

  .bv-print-only {
    display: block !important;
  }

  /* Panels become plain ruled blocks. The glass treatment is all
     translucency and shadow, neither of which survives paper. */
  .bv-card,
  .glass-card,
  .glass-panel,
  .bv-card--lg {
    background: none !important;
    border: 1px solid #999 !important;
    border-top: 1px solid #999 !important;
    box-shadow: none !important;
    backdrop-filter: none !important;
    border-radius: 0 !important;
    break-inside: auto;
  }

  .bv-card__body {
    padding: 8pt !important;
  }

  .bv-section {
    break-inside: auto;
    margin-bottom: 10pt !important;
  }

  .bv-section__heading h2,
  .bv-page-title,
  .bv-page-heading {
    color: #000 !important;
    font-size: 12pt !important;
  }

  /* Measured white-on-white: .bv-page-header p is rgba(255,255,255,0.4),
     which survives the theme drop and prints invisibly. Every page has
     one, so this is fixed globally rather than per screen. */
  .bv-page-header p,
  .bv-page-subheading,
  .bv-card__meta,
  .bv-label {
    color: #333 !important;
  }

  /* On the tabulation sheet the screen header is redundant — the printed
     document header above it already carries the event, the venue and the
     timestamp — and printing both reads as a mistake. */
  .bv-print-doc > .bv-page-header {
    display: none !important;
  }

  /* ── Tables ──
     thead as a header group so a standing that runs over a page repeats
     its column names; without it page two is a grid of bare numbers. */
  .bv-table {
    width: 100% !important;
    border-collapse: collapse !important;
    font-size: 8.5pt !important;
  }

  .bv-table thead {
    display: table-header-group;
  }

  .bv-table th,
  .bv-table td {
    padding: 4pt 5pt !important;
    color: #000 !important;
    border: 1px solid #999 !important;
    background: none !important;
  }

  .bv-table th {
    font-weight: 700 !important;
    background: #eee !important;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  .bv-table tr {
    break-inside: avoid;
  }

  /* A cut contestant is dimmed on screen. On paper opacity prints as
     grey mush, so the fact is carried by the note in the cell instead. */
  .bv-table tr.bv-row--out td {
    opacity: 1 !important;
    color: #444 !important;
  }

  .bv-log-scroll {
    overflow: visible !important;
    max-height: none !important;
  }

  .bv-tab-sub,
  .bv-log-actor__name,
  .bv-status-source {
    color: #333 !important;
  }

  .bv-badge {
    color: #000 !important;
    background: none !important;
    border: 1px solid #999 !important;
    padding: 1pt 4pt !important;
    font-size: 7.5pt !important;
  }

  /* ─────────────────────────────────────────────────────────────────
     THE TABULATION SHEET
     ───────────────────────────────────────────────────────────────── */

  /* The Running Order panel is a live control — round tabs, Absent and
     Eliminate buttons, "round closed" notes. Printing it put a page of
     switches on paper. The same facts print as a plain list from
     .bv-print-exclusion instead. */
  .bv-print-doc [data-bv="roster"],
  .bv-print-doc .bv-award-add {
    display: none !important;
  }

  .bv-print-head {
    margin-bottom: 10pt;
    padding-bottom: 6pt;
    border-bottom: 2px solid #000;
  }

  .bv-print-head__org {
    font-size: 8pt;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: #444;
  }

  .bv-print-head__title {
    margin: 2pt 0 0;
    font-size: 17pt;
    font-weight: 800;
    letter-spacing: -0.01em;
    color: #000;
  }

  .bv-print-head__event {
    margin-top: 1pt;
    font-size: 11pt;
    font-weight: 600;
    color: #222;
  }

  .bv-print-meta {
    width: 100%;
    margin-top: 7pt;
    border-collapse: collapse;
    font-size: 8.5pt;
  }

  .bv-print-meta th {
    width: 15%;
    padding: 2pt 6pt 2pt 0;
    text-align: left;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #555;
  }

  .bv-print-meta td {
    width: 35%;
    padding: 2pt 10pt 2pt 0;
    color: #000;
  }

  .bv-print-block {
    margin-top: 10pt;
    break-inside: avoid;
  }

  .bv-print-block__title {
    margin: 0 0 5pt;
    padding-bottom: 2pt;
    font-size: 10pt;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #000;
    border-bottom: 1px solid #999;
  }

  .bv-print-exclusion {
    margin-bottom: 3pt;
    font-size: 8.5pt;
    color: #000;
  }

  .bv-print-exclusion__round {
    font-weight: 700;
  }

  .bv-print-exclusion__round::after {
    content: ' — ';
  }

  /* ── Awards on paper ── */
  .bv-print-doc .bv-award-grid {
    display: block !important;
  }

  .bv-print-doc .bv-award {
    display: block !important;
    padding: 4pt 0 !important;
    border: 0 !important;
    border-bottom: 1px solid #ccc !important;
    border-radius: 0 !important;
    background: none !important;
    break-inside: avoid;
  }

  .bv-print-doc .bv-award__name {
    color: #000 !important;
    font-size: 8.5pt !important;
  }

  .bv-print-doc .bv-award__who {
    font-size: 11pt !important;
    color: #000 !important;
  }

  .bv-print-doc .bv-award__num {
    color: #000 !important;
    background: none !important;
    border: 1px solid #999 !important;
    min-width: 0 !important;
    height: auto !important;
    padding: 0 3pt !important;
    font-size: 8.5pt !important;
  }

  /* The unit span carries its own colour at a deeper selector, so it
     survived the parent's override and printed white on white. */
  .bv-print-doc .bv-award__value span,
  .bv-print-doc .bv-award__value,
  .bv-print-doc .bv-award__basis,
  .bv-print-doc .bv-award__pending,
  .bv-print-doc .bv-award__tied {
    color: #333 !important;
    font-size: 8pt !important;
  }

  /* ── Certification ──
     The reason to print the sheet at all: it becomes the record of the
     night once the panel signs it, and there was nowhere to sign. */
  .bv-print-sign {
    margin-top: 14pt;
    break-inside: avoid;
  }

  .bv-print-sign__oath {
    margin: 0 0 12pt;
    font-size: 8.5pt;
    line-height: 1.5;
    color: #222;
  }

  .bv-print-sign__grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20pt 24pt;
  }

  .bv-print-sign__slot {
    break-inside: avoid;
  }

  .bv-print-sign__line {
    display: block;
    height: 20pt;
    border-bottom: 1px solid #000;
  }

  .bv-print-sign__name {
    display: block;
    margin-top: 2pt;
    font-size: 9pt;
    font-weight: 700;
    color: #000;
  }

  .bv-print-sign__role {
    display: block;
    font-size: 7.5pt;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #555;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   ELECTION REGISTRY  —  partials/election-list.blade.php
   Shared by Batch, Real-time and Pageant Events, which were three
   copies of the same 330-line screen.
   ═══════════════════════════════════════════════════════════════════ */

.bv-elist-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 20px;
}

.bv-elist-head__text {
  min-width: 0;
}

/* ── The button fix ──
   `.bv-btn--primary` is `display: block !important; width: 100% !important`.
   Two consequences, both visible on these screens:

     display:block  — the button stops being the flex container .bv-btn
                      declares, so it loses `gap: 8px` and the icon fuses
                      onto the label. "LIVE MONITOR" with a glyph stuck to
                      it was a correctly-rendered icon with no space in
                      front of it, not a stray emoji.
     width:100%     — beats an inline `style="width:auto"`, because a
                      stylesheet !important outranks a non-important inline
                      style. Hence a Create Election button spanning the
                      entire page.

   Not fixed by editing `.bv-btn--primary`: ~20 views rely on it being a
   full-width block. Out-specified here at two classes deep instead. */
.bv-elist-head > .bv-btn,
.bv-elist-actions > .bv-btn,
.bv-elist-empty > .bv-btn {
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: auto !important;
  margin-bottom: 0 !important;
  white-space: nowrap;
}

/* An icon-only button is a square, and the `width: auto !important` above
   takes that away from it. That rule exists to stop `.bv-btn--primary`
   stretching a LABELLED button across the row, but it is written two
   classes deep, so it also outranks `.bv-elist-icon`'s own
   `width: 36px !important` one class deep — collapsing Tabulation and
   Delete to the width of their glyph. Measured before this: 16.05px wide
   against a declared 36px, which is the amber sliver beside RESULTS and
   LIVE MONITOR on Pageant Events.

   Restored at three classes deep so it outranks the rule above without
   editing it. In the actions row the square also drops to 32px, matching
   the height of the `.bv-elist-go` pill it sits beside — at 36 against 32
   the two did not line up. */
.bv-elist-head > .bv-btn.bv-elist-icon,
.bv-elist-empty > .bv-btn.bv-elist-icon {
  width: 36px !important;
  flex-shrink: 0;
}

.bv-elist-actions > .bv-btn.bv-elist-icon {
  width: 32px !important;
  height: 32px !important;
  flex-shrink: 0;
}

.bv-elist-head > .bv-btn {
  padding: 13px 26px !important;
}

/* ── Table ── */
.bv-elist__table {
  min-width: 900px;
}

.bv-elist__actions-col {
  text-align: right;
}

/* The group row was drawn in bg-slate-900/80, py-4, px-6, border-b,
   font-black, text-xs and text-blue-400 — of which this build emits
   none, so it rendered as a bare line of text with no band behind it. */
.bv-elist__group td {
  padding: 14px 20px !important;
  background: rgba(255, 255, 255, 0.035) !important;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08) !important;
}

.bv-elist__group-label {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #7dabff;
}

.bv-elist__group-label.is-pageant {
  color: #FFB400;
}

.bv-elist__title {
  display: block;
  font-size: 14px;
  font-weight: 700;
  color: #fff;
}

.bv-elist__sub {
  display: block;
  margin-top: 3px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

.bv-elist__type {
  font-size: 9px;
}

.bv-elist__date {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.6);
  white-space: nowrap;
}

/* ── Actions ── */
.bv-elist-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

/* Icon-only. A label would triple the width of a column that already
   carries up to four controls. */
.bv-elist-icon {
  width: 36px !important;
  height: 36px;
  padding: 0 !important;
  flex-shrink: 0;
  background: rgba(255, 255, 255, 0.04) !important;
  border: 1px solid rgba(255, 255, 255, 0.09) !important;
  transition: border-color 0.2s, background 0.2s;
}

.bv-elist-icon--gold { color: #FFB400 !important; }
.bv-elist-icon--green { color: #10b981 !important; }
.bv-elist-icon--red { color: #ef4444 !important; }

.bv-elist-icon:hover {
  background: rgba(255, 255, 255, 0.08) !important;
  border-color: rgba(255, 255, 255, 0.2) !important;
}

.bv-elist-go {
  padding: 9px 16px !important;
  font-size: 10px !important;
}

/* ── Empty ──
   One state, not two. The old screen rendered an empty table — column
   headers and all — reading "No elections have been created yet", and
   then a second card below it reading "No elections registered yet"
   with its own call to action. */
.bv-elist-empty {
  display: flex !important;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px !important;
  padding: 70px 24px !important;
  text-align: center;
}

.bv-elist-empty__icon {
  font-size: 40px;
  color: rgba(255, 255, 255, 0.52);
}

.bv-elist-empty__title {
  margin: 14px 0 0;
  font-size: 18px;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: #fff;
}

.bv-elist-empty__copy {
  margin: 0;
  max-width: 460px;
  font-size: 13px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.6);
}

.bv-elist-empty__cta {
  margin-top: 20px !important;
  padding: 14px 28px !important;
}

@media (max-width: 700px) {
  .bv-elist-head { align-items: stretch; }
  .bv-elist-head > .bv-btn { width: 100% !important; }
}

/* ═══════════════════════════════════════════════════════════════════
   DISTRIBUTED LEDGER AUDIT
   Rebuilt on real figures. The stat values previously carried
   `.bv-hash` — the hash style, monospace at rgba(255,255,255,0.3),
   declared later than .bv-card__title so it won — which is why the
   headline numbers rendered at 30% opacity.
   ═══════════════════════════════════════════════════════════════════ */

.bv-verify {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 26px;
  padding: 18px 22px;
  border-radius: 16px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  background: rgba(255, 255, 255, 0.025);
}

.bv-verify.is-ok { border-color: rgba(34, 197, 94, 0.35); background: rgba(34, 197, 94, 0.07); }
.bv-verify.is-bad { border-color: rgba(239, 68, 68, 0.4); background: rgba(239, 68, 68, 0.07); }

.bv-verify__icon { font-size: 24px; }
.bv-verify.is-ok .bv-verify__icon { color: #4ade80; }
.bv-verify.is-bad .bv-verify__icon { color: #f87171; }

.bv-verify__title { font-size: 15px; font-weight: 700; color: #fff; }
.bv-verify__meta { margin-top: 3px; font-size: 12px; color: rgba(255, 255, 255, 0.6); }

/* ── Tiles ── */
.bv-audit-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 16px;
  margin-bottom: 30px;
}

.bv-audit-tile {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 22px;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.07);
}

.bv-audit-tile__label {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

/* Full white, not the 30%-opacity hash style the values inherited before. */
.bv-audit-tile__value {
  font-size: 26px;
  font-weight: 900;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: #fff;
}

.bv-audit-tile__value--num {
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 22px;
}

.bv-audit-tile__value.is-good { color: #4ade80; }
.bv-audit-tile__value.is-bad { color: #f87171; }

/* Nothing has been measured. It must not look like a pass. */
.bv-audit-tile__value.is-idle { color: rgba(255, 255, 255, 0.6); }

.bv-audit-tile__note {
  font-size: 11px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.6);
}

/* ── Provenance ── */
.bv-prov-bar {
  display: flex;
  height: 16px;
  margin-bottom: 22px;
  border-radius: 8px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.04);
}

.bv-prov-bar__band { height: 100%; }

.bv-prov-bar__band.is-onchain,   .bv-prov-item__dot.is-onchain   { background: #22c55e; }
.bv-prov-bar__band.is-simulated, .bv-prov-item__dot.is-simulated { background: #ef4444; }
.bv-prov-bar__band.is-unknown,   .bv-prov-item__dot.is-unknown   { background: #eab308; }
.bv-prov-bar__band.is-pending,   .bv-prov-item__dot.is-pending   { background: rgba(255, 255, 255, 0.28); }

.bv-prov-legend {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
  gap: 16px;
}

.bv-prov-item { display: flex; gap: 12px; }
.bv-prov-item.is-zero { opacity: 0.4; }

.bv-prov-item__dot {
  flex-shrink: 0;
  width: 10px;
  height: 10px;
  margin-top: 5px;
  border-radius: 3px;
}

.bv-prov-item__body { min-width: 0; }

.bv-prov-item__head {
  display: block;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.65);
}

.bv-prov-item__head strong {
  font-size: 17px;
  font-weight: 900;
  color: #fff;
}

.bv-prov-item__pct {
  margin-left: 6px;
  font-size: 11px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.6);
}

.bv-prov-item__note {
  display: block;
  margin-top: 3px;
  font-size: 11px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.52);
}

/* ── Public anchors ── */
.bv-anchor-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
}

.bv-anchor__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}

.bv-anchor__block { display: block; font-size: 15px; font-weight: 800; color: #fff; }

.bv-anchor__state {
  display: block;
  margin-top: 3px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.bv-anchor__state.is-onchain { color: #4ade80; }
.bv-anchor__state.is-simulated { color: #f87171; }
.bv-anchor__state.is-unknown { color: #eab308; }

.bv-anchor__hash {
  display: block;
  padding: 10px 12px;
  border-radius: 8px;
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 10px;
  line-height: 1.5;
  word-break: break-all;
  color: rgba(255, 255, 255, 0.5);
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.05);
}

/* ── Ledger ── */
.bv-ledger__index {
  display: inline-block;
  padding: 4px 10px;
  border-radius: 999px;
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 11px;
  font-weight: 700;
  color: #a5b4fc;
  background: rgba(99, 102, 241, 0.1);
  border: 1px solid rgba(99, 102, 241, 0.25);
}

.bv-ledger__pool { font-size: 13px; font-weight: 700; color: #fff; }

.bv-ledger__sig { display: flex; align-items: center; gap: 8px; }

/* Shown, not hidden behind a Show Signature button that had to be clicked
   once per row to read a page of fifteen. */
.bv-ledger__hash {
  display: block;
  flex: 1;
  min-width: 0;
  max-width: 260px;
  padding: 5px 9px;
  border-radius: 6px;
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 10px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: rgba(255, 255, 255, 0.55);
  background: rgba(0, 0, 0, 0.25);
}

.bv-ledger__tx {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 10px;
  color: #FFB400;
  text-decoration: none;
  border-bottom: 1px solid rgba(255, 180, 0, 0.4);
}

.bv-ledger__none { font-size: 11px; font-style: italic; color: rgba(255, 255, 255, 0.52); }

.bv-ledger__pager {
  padding: 16px 20px;
  border-top: 1px solid rgba(255, 255, 255, 0.05);
}

.bv-badge--red {
  color: #f87171;
  background: rgba(239, 68, 68, 0.12);
  border: 1px solid rgba(239, 68, 68, 0.3);
}

/* ═══════════════════════════════════════════════════════════════════
   CANDIDATE REGISTRY
   Grouped by election and reusing the bv-elist-* registry pieces, so
   the two roster screens now look and behave the same.
   ═══════════════════════════════════════════════════════════════════ */

.bv-croster-filter {
  margin-bottom: 22px;
}

.bv-croster-filter__row {
  display: flex;
  align-items: center;
  gap: 14px;
}

.bv-croster-filter__row > .bv-input {
  max-width: 340px;
}

.bv-croster-filter__clear {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  color: rgba(255, 255, 255, 0.6);
  transition: color 0.2s;
}

.bv-croster-filter__clear:hover { color: #FFB400; }

.bv-croster {
  min-width: 860px;
}

.bv-croster__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 38px;
  height: 34px;
  padding: 0 9px;
  border-radius: 10px;
  font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
  font-size: 13px;
  font-weight: 800;
  color: #FFB400;
  background: rgba(255, 180, 0, 0.09);
  border: 1px solid rgba(255, 180, 0, 0.2);
}

.bv-croster__who {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.bv-croster__photo {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  object-fit: cover;
  object-position: top center;
  border: 2px solid rgba(255, 180, 0, 0.5);
}

.bv-croster__photo--none {
  font-size: 13px;
  font-weight: 800;
  color: #FFB400;
  background: rgba(255, 180, 0, 0.08);
}

.bv-croster__ident { min-width: 0; }

/* Wraps rather than truncating. The old cell was `max-w-[200px] truncate`
   — both of which this build DOES emit — so a full name was cut mid-word
   and only a hover title carried the rest. */
.bv-croster__name {
  display: block;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.35;
  color: #fff;
}

.bv-croster__role {
  display: block;
  margin-top: 2px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

.bv-croster__course {
  font-size: 12px;
  line-height: 1.45;
  color: rgba(255, 255, 255, 0.6);
}

.bv-croster__meta {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.6);
}

/* Stated once on the group heading rather than as a LOCKED badge repeated
   in the actions column of every row of that election. */
.bv-croster__locked {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 14px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 9px;
  letter-spacing: 0.12em;
  color: #4ade80;
  background: rgba(34, 197, 94, 0.1);
  border: 1px solid rgba(34, 197, 94, 0.28);
}

/* A locked row: a lock in the same 32px tile as the edit and remove icons
   it stands in for, so the actions column lines up either way. The group
   row already says "List is final"; the tile's title says it on hover. */
.bv-croster__sealed {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.55);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.12);
}

/* Portal tile extras: a ratio reads as one figure, not two. */
.bv-audit-tile__of {
  margin-left: 6px;
  font-size: 14px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.52);
}

/* Simulated anchors exist. Saying so quietly is still saying so. */
.bv-audit-tile__note.is-warn {
  color: #f87171;
}

/* ── PAGINATED TABLE FRAME ────────────────────────────────────────
   The candidate roster was wrapped in `.bv-log-scroll`, which is built
   for the SOC log: `max-height: 460px` with its own vertical scrollbar
   and a sticky header. On a table that is ALREADY paginated that gives
   two navigation mechanisms at once — the pager said "showing 1-10 of
   12" while the box showed four and a half rows and clipped the fifth
   through the middle of its avatar.

   Same framing, no vertical scroll: a paginated table should render its
   page at natural height and let the pager do the paging. Horizontal
   overflow is kept for narrow screens.                               */
.bv-table-frame {
  overflow-x: auto;
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: 12px;
}

/* The receipt's copy control had no text of its own — the icon WAS the
   affordance — so removing the glyph would have left an invisible
   button. It is a word now, and the word changes on success. */
.bv-receipt-copy {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
  transition: color 0.2s;
}

.bv-receipt-copy.is-done {
  color: #22c55e;
}


/* ── REGISTRY STATUS TABS ─────────────────────────────────────────
   The three registries and the voting booth were unfiltered, unpaginated
   lists, so every concluded election stayed on screen for the life of the
   system. These sit above the listing and switch it between Current,
   Concluded and Archived.

   Additive only: .bv-seg / .bv-seg__item / .bv-seg__item--on already exist
   above (written gold for the judge round tabs, and used by no view until
   now). Nothing there is modified -- gold stays the organizer's accent on
   the pageant registry, and .bv-seg--blue is the COMELEC and voter variant. */

.bv-elist__tabs,
.bv-booth__tabs {
  margin-bottom: 20px;
}

/* The booth used to be one 560px column and its tabs stretched to fill it.
   The cards now run across the page in a grid, so the control is only as
   wide as its tabs, like the dashboard log's. */
.bv-booth__tabs .bv-seg {
  display: inline-flex;
}

/* Cards differ in height (a closed election without a ballot has no
   button), so each keeps its own height instead of stretching to the row. */
.bv-booth__grid {
  align-items: start;
}

.bv-booth__empty {
  grid-column: 1 / -1;
  margin-top: 0;
}

.bv-seg--blue .bv-seg__item--on,
.bv-seg--blue .bv-seg__item--on:hover {
  color: #fff;
  background: rgba(59, 130, 246, 0.32);
  border: 1px solid rgba(100, 150, 255, 0.45);
  /* The gold state has no border, so without this the blue one would sit a
     pixel taller than its neighbours and the row would jog on tab change. */
  padding: 8px 17px;
  box-shadow: 0 0 16px rgba(59, 130, 246, 0.22);
}

/* How many elections each tab holds. The count is the point: it says the
   archived ones still exist without putting them on screen. */
.bv-seg__count {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.04em;
  background: rgba(255, 255, 255, 0.09);
  color: inherit;
}

.bv-seg__item--on .bv-seg__count {
  background: rgba(0, 0, 0, 0.18);
}

/* Pager under a registry table and under the booth's card column. Matches
   .bv-candidates-pager; mt-8 emits nothing in this build. */
.bv-elist__pager {
  margin-top: 24px;
}

/* A log whose pager links jump back to it (#activity-log on the admin
   dashboard). The top bar is sticky inside .bv-main-content, the element
   that scrolls, so without this offset the jump parks the log's heading
   under the bar. The bar has a 70px min-height; the rest is breathing room. */
.bv-log-anchor {
  scroll-margin-top: 96px;
}

/* ── ACTIVITY LOG: SHARED FEED ────────────────────────────────────
   Added 2026-09-05 with the transparency-feed rebuild. That partial
   now reuses the SOC audit-trail vocabulary (.bv-log-time, -ip,
   -actor, -details) instead of the inline hex it carried before, so
   only the pieces the SOC table did not already own live here. */
.bv-log-badge {
  display: inline-flex !important;
  align-items: center;
  gap: 6px;
}

/* Relative age, under the absolute stamp rather than instead of it.
   The absolute time answers "when exactly", which an audit record
   has to; this answers "was that just now", which is the question
   somebody scanning their own history is actually asking. */
.bv-log-ago {
  display: block;
  margin-top: 4px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

/* A field the row genuinely does not have. Distinct from a value, so
   an empty IP does not read as an IP nobody can quite make out. */
.bv-log-muted {
  font-size: 11px;
  font-style: italic;
  color: rgba(255, 255, 255, 0.52);
}

@media (max-width: 1023.98px) {
  /* .bv-log-details caps at 460px. On a table already forced to a
     600px minimum and scrolled horizontally, that one cell pushes
     the timestamp column past the far edge. */
  .bv-log-details {
    max-width: 200px;
  }
}

/* ══════════════════════════════════════════════════════════════════
   ISO/IEC 25010 AUDIT
   The screen read as filler because of how it presented its data, not
   because the data was wrong:

     - the word "Validated" was repeated on a green pill for every
       passing boolean, eighteen times over. A page where every row
       shouts the same thing gives the eye nothing to land on, and an
       audit is read to find the ONE row that failed.
     - five progress bars, each carrying a single number, four of them
       identical full-width green. A bar pinned at 100% is decoration.
     - booleans and measurements shared one row shape, so the only real
       data on the page — 23 blocks verified, 0 failed jobs, 12 voters —
       was the least prominent thing on it.

   Rebuilt around that split: a check is a state and stays quiet when it
   passes, a measurement is a figure and is typeset as one.
   ══════════════════════════════════════════════════════════════════ */

.bv-iso-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
}

.bv-iso-stamp {
  text-align: right;
  font-size: 11px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.6);
}

.bv-iso-stamp strong {
  display: block;
  font-size: 12px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.7);
  font-variant-numeric: tabular-nums;
}

/* ── SUMMARY ──────────────────────────────────────────────────────
   The score earns its size; the pass ratio beside it is what makes the
   number credible. "100%" on its own is a claim — "100%, 18 of 18
   checks passed" is a result. */
.bv-iso-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 28px;
  margin: 20px 0 24px;
  padding: 22px 26px;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.07);
}

.bv-iso-summary__score {
  display: flex;
  align-items: baseline;
  gap: 4px;
  font-size: 46px;
  font-weight: 900;
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.bv-iso-summary__pct {
  font-size: 20px;
  font-weight: 800;
  opacity: 0.5;
}

.bv-iso-summary__label {
  display: block;
  margin-bottom: 8px;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-iso-summary__ratio {
  font-size: 15px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.85);
  font-variant-numeric: tabular-nums;
}

.bv-iso-summary__note {
  margin: 4px 0 0;
  font-size: 11.5px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.6);
  max-width: 46ch;
}

.bv-iso-summary__divider {
  width: 1px;
  align-self: stretch;
  background: rgba(255, 255, 255, 0.08);
}

/* ── CHARACTERISTIC CARD ──────────────────────────────────────────*/
.bv-iso-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 18px;
}

.bv-iso-card__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 14px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}

.bv-iso-card__name {
  margin: 0;
  font-size: 16px;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: rgba(255, 255, 255, 0.94);
}

.bv-iso-card__tally {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.6);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.bv-iso-card__tally b {
  color: rgba(255, 255, 255, 0.85);
}

/* ── CHECKS ───────────────────────────────────────────────────────
   A passing check is a tick and a sentence. No pill, no colour block,
   nothing repeated — so a failure, which gets all three, is the only
   thing on the card that draws attention. */
.bv-iso-checks {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
}

.bv-iso-check {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 0;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.72);
}

.bv-iso-check + .bv-iso-check {
  border-top: 1px solid rgba(255, 255, 255, 0.04);
}

.bv-iso-check__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  font-size: 9px;
  color: #4ade80;
  background: rgba(34, 197, 94, 0.12);
  border: 1px solid rgba(34, 197, 94, 0.3);
}

.bv-iso-check__label {
  flex: 1;
  min-width: 0;
}

.bv-iso-check--fail {
  margin: 0 -14px;
  padding: 9px 14px;
  border-radius: 8px;
  color: #fecaca;
  background: rgba(239, 68, 68, 0.09);
}

.bv-iso-check--fail + .bv-iso-check,
.bv-iso-check + .bv-iso-check--fail {
  border-top-color: transparent;
}

.bv-iso-check--fail .bv-iso-check__mark {
  color: #f87171;
  background: rgba(239, 68, 68, 0.14);
  border-color: rgba(239, 68, 68, 0.4);
}

.bv-iso-check__verdict {
  flex-shrink: 0;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #f87171;
}

/* ── MEASUREMENTS ─────────────────────────────────────────────────
   Not checks. These are the figures the audit actually took, so they
   are set as figures: tabular, larger than the label beside them. */
.bv-iso-measures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 2px 18px;
  margin-top: 4px;
  padding-top: 14px;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}

.bv-iso-measure {
  padding: 8px 0;
}

.bv-iso-measure__value {
  display: block;
  font-size: 22px;
  font-weight: 800;
  line-height: 1.1;
  color: rgba(255, 255, 255, 0.92);
  font-variant-numeric: tabular-nums;
}

.bv-iso-measure__label {
  display: block;
  margin-top: 3px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

.bv-iso-foot {
  margin-top: 20px;
  font-size: 11.5px;
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.6);
  max-width: 92ch;
}

@media (max-width: 640px) {
  .bv-iso-summary { gap: 18px; padding: 18px; }
  .bv-iso-summary__divider { display: none; }
  .bv-iso-summary__score { font-size: 38px; }
}

/* A transaction hash or a long token is one unbroken word. `pre-wrap`
   alone will not break it, so it runs horizontally out of the cell and
   the two-line clamp never reaches its ellipsis -- the string simply
   stops at the column edge, mid-character. Appears wherever an anchor
   detail lands in a feed, which the admin and COMELEC dashboards now
   render. Applies to the SOC audit trail too, where it is the same bug. */
.bv-log-details {
  overflow-wrap: anywhere;
}

/* ── TRANSPARENCY PORTAL: LEDGER ──────────────────────────────────
   Added 2026-09-05. The ledger table leaned on Tailwind classes this
   build does not emit: `w-32` (so `truncate` had no width to work
   against and the full 64-character vote hash took the widest column
   on the page), `max-h-96`, `p-6`, `border-white/5`, `text-blue-400`,
   `font-bold`, `text-white`. Checked against the bundle the container
   serves -- the host's public/build copy is stale and carries a
   different class set. */
.bv-hash-cell {
  display: block;
  max-width: 210px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bv-ledger-election {
  font-size: 12.5px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.88);
}

/* The one link on the page that leads off it, so it has to read as a
   link. `text-blue-400` emitted nothing, leaving it as body text. */
.bv-tx-link {
  color: #60a5fa !important;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.bv-tx-link:hover {
  color: #93c5fd !important;
}

/* A pager inside a card, where `p-6` emitted nothing and left it flush
   against the last row. Distinct from .bv-candidates-pager, which sits
   outside its card and so wants margin rather than padding. */
.bv-table-pager {
  padding: 6px 20px 18px;
}

.bv-avatar--sm {
  width: 24px;
  height: 24px;
  font-size: 8px;
}

/* ── ROOMY TABLE ROWS ─────────────────────────────────────────────
   `.bv-table td` pads 16px, which is right for a table of short
   values. The transparency ledger puts a hash, a link and a two-line
   timestamp on every row, and at 16px those rows read as a solid wall.
   Opt-in so the compact tables elsewhere are untouched. */
.bv-table--roomy th {
  padding-top: 18px;
  padding-bottom: 18px;
}

.bv-table--roomy td {
  padding-top: 20px;
  padding-bottom: 20px;
}

/* ── SETTINGS GRID ────────────────────────────────────────────────
   .bv-grid-2 stretches both cards to the taller one. On the settings
   screen the left card holds a single field and the right holds three
   status rows, so the left one gained a large void under its button.
   Opt-in: every other two-card row on the site wants equal heights. */
.bv-grid-2--top {
  align-items: start;
}

/* ── IDENTITY REGISTRY (admin/users) ──────────────────────────────
   Added 2026-09-05. Everything here replaces a Tailwind class this
   build does not emit, checked against the bundle the container
   serves. */

/* The search field earns more of the row than the role filter does.
   Needs !important to out-rank `.bv-form-row > .bv-input`, which is a
   class more specific. */
.bv-filter-search {
  flex: 2 1 260px !important;
}

/* Manage + role select, kept on one line and hard against the right
   edge of the cell. Was `flex justify-end gap-2`; only the first of
   those three emits a rule. */
.bv-user-ops {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
}

.bv-user-ops__form {
  margin: 0;
  display: flex;
  align-items: center;
}

/* `.bv-btn` pads 14px 20px and runs full width, which is right for a
   card action and far too heavy for a control sitting in a table row.
   This was six inline declarations on the anchor before. */
.bv-btn--row {
  width: auto !important;
  height: 32px;
  padding: 0 12px !important;
  font-size: 11px !important;
  margin-bottom: 0 !important;
}

/* Written as `select.bv-input--compact` on purpose: the rule it has to
   beat is `select.bv-input`, and a bare class would lose the
   specificity contest no matter where it sits in the file. */
select.bv-input--compact {
  width: auto !important;
  min-width: 124px;
  padding: 6px 10px !important;
  font-size: 11px !important;
  border-radius: 8px !important;
}

/* A pager sitting outside its card, where `mt-8` emitted nothing and
   left it flush against the table. The in-card case is
   .bv-table-pager, which wants padding instead. */
.bv-page-pager {
  margin-top: 24px;
}

/* ── JUDGING PORTAL: EVENT ROWS ───────────────────────────────────
   Added 2026-09-05. The portal listed events in .bv-grid-3, and a
   judge is assigned one or two pageants -- so the page rendered a
   single card beside two empty columns. A row uses whatever width it
   is given, at any count. The __title / __desc / __fact / __stage
   classes are deliberately reused from the card so the light theme,
   which styles each of them by name, needs nothing new. */
.bv-judge-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.bv-judge-row > .bv-card__body {
  display: flex;
  /* `.bv-card__body` is globally flex-direction: column. Without
     restating it here the row inherited that, and `flex: 1 1 300px`
     on the identity block then read as a 300px tall basis -- the card
     grew to 542px with its three blocks strung down it. */
  flex-direction: row;
  align-items: center;
  flex-wrap: wrap;
  gap: 24px;
}

/* 300px basis: the identity block keeps the row on one line on a
   laptop and drops the facts and action beneath it on a tablet. */
.bv-judge-row__main {
  flex: 1 1 300px;
  min-width: 0;
}

.bv-judge-row__head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 8px;
}

.bv-judge-row__head .bv-judge-card__title {
  margin: 0;
}

/* The card stacked these, so both carried a bottom margin sized for a
   column. In a row the last element is the chips. */
.bv-judge-row .bv-judge-card__desc {
  margin-bottom: 12px;
}

.bv-judge-row .bv-judge-card__stages {
  margin-bottom: 0;
}

.bv-judge-row__facts {
  flex: 0 0 auto;
  margin-bottom: 0 !important;
}

/* .bv-judge-card__fact is flex:1 for the card, where two of them split
   the width. Here they are fixed tiles beside a flexible block. */
.bv-judge-row__facts .bv-judge-card__fact {
  flex: 0 0 auto;
  min-width: 108px;
}

.bv-judge-row__action {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 196px;
}

.bv-judge-row__btn {
  margin-bottom: 0 !important;
  white-space: nowrap;
}

/* The label carries the round's name, and a long one ("Score Best in
   Long Gown and Evening Wear") ran past a 360px screen, where
   overflow-x:hidden cut it off. Narrow screens wrap it instead; the
   column must be allowed to shrink too, or it sizes itself to the
   unwrapped label and the button never gets the chance. */
@media (max-width: 480px) {
  .bv-judge-row__action {
    flex: 1 1 100%;
    min-width: 0;
  }

  .bv-judge-row__btn {
    white-space: normal;
    text-align: center;
  }
}

.bv-judge-row__note {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: center;
  color: rgba(255, 255, 255, 0.52);
}

/* The one genuinely evidential link in the personal log: a vote-cast
   row leading to that ballot receipt. A compact outlined button in the
   gold of .bv-btn--gold-sm, because it is an action. As bare text it
   read as part of the detail string printed above it. */
.bv-log-receipt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 30px;
  margin-top: 8px;
  padding: 0 12px;
  border: 1px solid rgba(255, 180, 0, 0.45);
  border-radius: 8px;
  background: rgba(255, 180, 0, 0.12);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  color: #FFB400;
  text-decoration: none;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.bv-log-receipt:hover {
  background: rgba(255, 180, 0, 0.22);
  border-color: #FFB400;
}

.bv-log-receipt:focus-visible {
  outline: 2px solid #FFB400;
  outline-offset: 2px;
}

/* ── VOTER DASHBOARD ──────────────────────────────────────────────
   .bv-section__heading stays the container (the rule and the spacing);
   .bv-section-title is the words inside it. The page drew its section
   titles three different ways, and the shared log drew a fourth. */
.bv-section-title {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  color: #ffffff;
}

.bv-section--spaced {
  margin-top: 64px;
}

.bv-open-ballot__text {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.bv-open-ballot__title {
  font-size: 20px;
}

.bv-open-ballot__date {
  margin: 0;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}

/* .bv-btn--primary carries margin-bottom: 12px !important for the stacked
   forms it was written for; at the foot of a card it is only dead space. */
.bv-open-ballot__action > .bv-btn {
  margin-bottom: 0 !important;
}

/* The log filter sits under the title, only as wide as its two tabs. */
.bv-log-tabs {
  margin-bottom: 16px;
}

.bv-log-tabs .bv-seg {
  display: inline-flex;
}


/* ── Board of judges ──────────────────────────────────────────────────────
   The panel editor on the judging-structure screen. Built on the same
   vocabulary as .bv-builder__list above rather than a new one: it sits in the
   same card stack and should read as another part of the same setup, not as a
   second design. */
.bv-panel-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 12px;
}

.bv-panel-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 12px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.02);
}

.bv-panel-row__who {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.bv-panel-row__name {
  font-size: 13px;
  font-weight: 700;
  color: #fff;
}

.bv-panel-row__role {
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-panel-row__ops {
  display: flex;
  align-items: center;
  gap: 8px;
}

.bv-panel-add {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 12px;
}

/* A seat that cannot be removed because the judge has already scored. The
   button stays visible rather than disappearing, so the reason can be read
   from its title instead of the row silently losing an action. */
.bv-node__add--danger {
  background: rgba(239, 68, 68, 0.1);
  border-color: rgba(239, 68, 68, 0.3);
  color: #f87171;
}

.bv-node__add--danger:hover { background: rgba(239, 68, 68, 0.2); }

.bv-node__add:disabled,
.bv-node__add--danger:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.bv-node__add:disabled:hover,
.bv-node__add--danger:disabled:hover {
  background: rgba(239, 68, 68, 0.1);
}

/* ── PAGER SUMMARY: FIGURES ARE PROSE, NOT CONTROLS ───────────────
   Added 2026-09-05. `nav[role="navigation"] span` (see ADDITIONAL
   PAGINATION SELECTORS above) paints every span inside a pager as a
   page button. That is right for the built-in pager, whose page links
   really are spans, and wrong for the "Showing 1-10 of 26" line: it
   put each figure in its own 40px bordered box, so the count read as
   three pages you could click. Both pagers keep that line in a <p>,
   which is the honest boundary between a control and a sentence. */
nav[role="navigation"] p span,
.bv-pager__meta span {
  display: inline !important;
  min-width: 0 !important;
  height: auto !important;
  margin: 0 !important;
  padding: 0 !important;
  border: none !important;
  background: none !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  font-size: inherit !important;
  line-height: inherit !important;
}

/* The figures still read brighter than the words between them. */
.bv-pager__meta span {
  color: rgba(255, 255, 255, 0.85) !important;
  font-weight: 800 !important;
}

/* ── TOPBAR / SIDEBAR SEAM ────────────────────────────────────────
   Added 2026-09-05. Both bands carry a bottom border and together they
   read as one line across the top of the app -- except the sidebar
   header measured 67px against the topbar's 70px, so the line stepped
   3px at the seam. The header was sized by padding plus whatever the
   wordmark happened to measure; it now matches the topbar explicitly
   and centres its content, so the two borders meet. */
.bv-sidebar__header {
  min-height: 70px;
  display: flex;
  align-items: center;
  padding: 0 1.5rem;
}

/* ── APP FOOTER ───────────────────────────────────────────────────
   Was `position: fixed` with a translucent fill and a blur, pinned
   across the bottom of every page. Anything scrolling passed beneath
   it and showed THROUGH it, which is what a half-visible table row
   behind the copyright line was. It also wraps to two or three lines
   below roughly 1000px -- measured at 109px against the 48px of
   clearance `.bv-main-content > main` reserves -- so at those widths
   it covered the end of the page outright, not just mid-scroll.

   Static instead, with `margin-top: auto` so it still sits at the
   bottom of the viewport on a short page. The blur goes with it: the
   codebase already notes elsewhere that a repaint inside a blurred
   surface re-blurs the whole surface, and nothing scrolls under this
   any more to justify the cost. */
.bv-app-footer {
  position: static !important;
  left: auto !important;
  right: auto !important;
  bottom: auto !important;
  margin-top: auto;
  backdrop-filter: none !important;
}

/* ── TALLY CARD ───────────────────────────────────────────────────
   Added 2026-09-05, replacing the .bv-result-* card on the live
   monitor and the results page. That card carried a 56px round avatar
   in one corner and stacked the figures under it, so most of the box
   was empty background -- very visible on a position with a single
   candidate. Here the portrait IS the card and the figures sit on a
   scrim over its lower third.

   The scrim stays dark in both themes deliberately: it covers a
   photograph, not the page, so light mode must not lighten it or the
   name stops being readable. */
.bv-tally {
  position: relative;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  padding: 0 !important;
  aspect-ratio: 3 / 4;

  /* THE ONE SIZING RULE FOR A CANDIDATE PORTRAIT.
     The card had no ceiling, so its height was whatever its column
     happened to be times 4/3: a position with a single candidate in a
     three-column grid produced a 500px-wide card 670px tall, and the
     same card on a phone was 340 by 450. clamp() ties it to the viewport
     between a floor and a ceiling, which is one rule for every screen
     instead of a breakpoint per device.

     min-height is gone deliberately. At the 200px floor it was taller
     than the aspect ratio calls for, which stretched the box and let the
     crop take the top of the head off. */
  width: 100%;
  max-width: clamp(200px, 22vw, 280px);
  margin-inline: auto;
}

/* The tally cards lay themselves out, rather than borrowing .bv-grid-3's
   fixed three columns -- which stretched two candidates across the full
   width for the same reason auto-fit did on the ballot. */
.bv-tally-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 24px;
  align-items: start;
}

.bv-tally__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Sources are 4:5 portraits with the head about 40% down. On a taller
     box a centred crop takes the forehead off. */
  object-position: 50% 28%;
  display: block;
}

/* No photo on file: the initials become the artwork rather than a
   68px circle floating in an empty card. */
.bv-tally__photo--text {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 64px;
  font-weight: 900;
  letter-spacing: 0.06em;
  color: rgba(255, 180, 0, 0.35);
  background: linear-gradient(160deg, rgba(255, 180, 0, 0.12), rgba(255, 255, 255, 0.02));
}

.bv-tally__flag {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 2;
  box-shadow: 0 2px 10px rgba(34, 197, 94, 0.3);
}

.bv-tally__body {
  position: relative;
  z-index: 1;
  width: 100%;
  padding: 64px 18px 18px;
  background: linear-gradient(to top,
      rgba(6, 13, 26, 0.96) 0%,
      rgba(6, 13, 26, 0.9) 45%,
      rgba(6, 13, 26, 0.55) 78%,
      rgba(6, 13, 26, 0) 100%);
}

.bv-tally__name {
  margin: 0;
  font-size: 15px;
  font-weight: 800;
  line-height: 1.25;
  color: #ffffff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bv-tally__party {
  margin: 3px 0 0;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.bv-tally__figures {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  margin-top: 14px;
}

.bv-tally__count {
  display: block;
  font-size: 30px;
  font-weight: 900;
  line-height: 1;
  letter-spacing: -0.02em;
  color: #FFB400;
}

.bv-tally__label {
  display: block;
  margin-top: 5px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-tally__share {
  font-size: 15px;
  font-weight: 800;
  color: rgba(255, 255, 255, 0.75);
}

.bv-tally__bar {
  margin-top: 12px;
  height: 6px;
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.14);
  overflow: hidden;
}

.bv-tally__bar span {
  display: block;
  height: 100%;
  border-radius: 99px;
  background: #FFB400;
  box-shadow: 0 0 8px rgba(255, 180, 0, 0.4);
  transition: width 1s ease-in-out;
}

/* .bv-card--leader was applied by the old card and defined nowhere, so
   the leader was distinguished by its badge alone. */
.bv-tally.bv-card--leader {
  border-color: rgba(34, 197, 94, 0.45) !important;
  box-shadow: 0 0 0 1px rgba(34, 197, 94, 0.25), 0 10px 30px rgba(34, 197, 94, 0.12);
}

/* ── Turnout by college ───────────────────────────────────────────────
   Used on the analytics panel. Deliberately not built on .bv-tally__bar:
   that one sits on a gradient scrim over a candidate photo and carries
   the positioning to match. Light-theme counterparts are in
   beevote-light.css -- the rgba values below vanish on a light canvas. */
.bv-turnout {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin: 18px 0 20px;
}

.bv-turnout__head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 6px;
}

.bv-turnout__name {
    font-size: 13px;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.82);
}

.bv-turnout__figures {
    font-size: 12px;
    color: rgba(255, 255, 255, 0.5);
    font-variant-numeric: tabular-nums;
}

.bv-turnout__figures--withheld {
    font-style: italic;
    color: rgba(255, 255, 255, 0.52);
}

.bv-turnout__bar {
    height: 6px;
    border-radius: 3px;
    background: rgba(255, 255, 255, 0.06);
    overflow: hidden;
}

.bv-turnout__fill {
    height: 100%;
    border-radius: 3px;
    background: #3b82f6;
    transition: width 0.6s ease;
}

/* ── Abstain option on the ballot ─────────────────────────────────────
   Deliberately quieter than a candidate card. Abstaining is a legitimate
   choice and has to be reachable, but it should not compete visually with
   the people standing for the seat. No photo, no partylist, muted fill. */
.bv-ballot-card--abstain {
    border-style: dashed;
    background: rgba(255, 255, 255, 0.015);
}

.bv-ballot-card--abstain .bv-ballot-photo {
    background: rgba(255, 255, 255, 0.03);
}

.bv-ballot-card--abstain .bv-ballot-photo__initials {
    color: rgba(255, 255, 255, 0.52);
    font-size: 20px;
}

.bv-ballot-card--abstain .bv-ballot-name {
    color: rgba(255, 255, 255, 0.7);
}

.bv-ballot-card--abstain .bv-ballot-party {
    color: rgba(255, 255, 255, 0.6);
}

/* Checked state still reads clearly -- a voter must be able to see that
   abstaining is what they chose, or they cannot confirm their own ballot. */
.bv-ballot-input:checked + .bv-ballot-card--abstain {
    border-style: solid;
}

/* ── Abstain row on the results monitor ───────────────────────────────
   Sits under a race's candidate cards and shares its denominator. Muted
   and full-width so it reads as a total for the race rather than as
   another contender. */
.bv-abstain {
    margin-top: 16px;
    padding: 12px 16px;
    border-radius: 8px;
    border: 1px dashed rgba(255, 255, 255, 0.1);
    background: rgba(255, 255, 255, 0.02);
}

.bv-abstain__head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 8px;
}

.bv-abstain__label {
    font-size: 13px;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.6);
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.bv-abstain__figures {
    font-size: 12px;
    color: rgba(255, 255, 255, 0.6);
    font-variant-numeric: tabular-nums;
}

.bv-abstain__bar {
    height: 6px;
    border-radius: 3px;
    background: rgba(255, 255, 255, 0.06);
    overflow: hidden;
}

.bv-abstain__fill {
    height: 100%;
    border-radius: 3px;
    background: rgba(255, 255, 255, 0.28);
    transition: width 0.6s ease;
}

/* ── TABULATION HEAD TOOLS ───────────────────────────────────────
   The event switcher beside Print. A flex row so the select does not
   claim the whole header: `.bv-btn` is width:100% and would push the
   two onto separate lines. */
.bv-tab-headtools {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.bv-tab-headtools select.bv-input--compact {
  min-width: 220px;
}

/* ── ROUND SCOPE ─────────────────────────────────────────────────
   Which rounds the standing below is decided on. One column per stage,
   its segments indented under it. Wraps on the CARD width rather than
   the viewport, for the reason .bv-grid-2 cannot be used here: it holds
   two columns down to 641px and would leave a 260px column on a tablet. */
.bv-scope {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 14px 20px;
}

/* Rounds STACK now rather than sitting in columns. Collapsed, three columns
   of one row each read as three orphaned labels; stacked, they read as a list
   of rounds you can open. */
.bv-scope__groups {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1 1 340px;
}

.bv-scope__group {
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.02);
  overflow: hidden;
}

/* The round's own row: its tick on the left, the opener on the right. */
.bv-scope__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 10px 7px 12px;
}

.bv-scope__head .bv-scope__item--stage {
  padding: 0;
  flex: 1 1 auto;
}

.bv-scope__toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: none;
  padding: 4px 6px;
  border-radius: 6px;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.6);
  font-size: 11px;
  width: auto !important;
  flex: 0 0 auto;
  transition: color 0.15s, background 0.15s;
}

.bv-scope__toggle:hover {
  color: #FFB400;
  background: rgba(255, 255, 255, 0.05);
}

.bv-scope__toggle i {
  transition: transform 0.18s;
}

.bv-scope__toggle[aria-expanded="true"] i {
  transform: rotate(180deg);
}

/* How many boxes are live inside a round. The whole reason a shut group is
   safe to leave shut: the count says there is something in there. */
.bv-scope__count {
  min-width: 18px;
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(255, 180, 0, 0.16);
  color: #FFB400;
  font-weight: 800;
  font-size: 10px;
  text-align: center;
}

/* Segments inside an open round, in columns so four segments of four criteria
   is a block rather than a twenty-row tower. Wraps on the PANEL's width. */
.bv-scope__panel {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 26px;
  padding: 4px 12px 12px 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.bv-scope__seg {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 190px;
  min-width: 180px;
}

.bv-scope__item {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.62);
  cursor: pointer;
  padding-left: 14px;
}

/* The round itself, above the segments it contains. */
.bv-scope__item--stage {
  padding-left: 0;
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255, 180, 0, 0.85);
}

/* A real checkbox, drawn rather than left to the platform.

   `accent-color` was the whole styling before this: it tints the native box
   and leaves everything else -- the size, the corner radius, the focus ring --
   to whatever the OS does, which is a grey 13px square that reads as a form
   from 1998 next to the rest of this panel. The input stays a real input, so
   the label, the keyboard and a screen reader all still work; only its paint
   is ours. */
.bv-scope__item input {
  appearance: none;
  -webkit-appearance: none;
  width: 15px !important;
  height: 15px;
  flex: 0 0 auto;
  margin: 0;
  padding: 0 !important;
  border: 1.5px solid rgba(255, 255, 255, 0.22) !important;
  border-radius: 4px !important;
  background: rgba(255, 255, 255, 0.04) !important;
  cursor: pointer;
  position: relative;
  transition: background 0.15s, border-color 0.15s;
}

.bv-scope__item:hover input {
  border-color: rgba(255, 180, 0, 0.55) !important;
}

.bv-scope__item input:checked {
  background: #FFB400 !important;
  border-color: #FFB400 !important;
}

/* The tick. Two borders on a rotated box, so it scales with the control and
   needs no image and no font. */
.bv-scope__item input:checked::after {
  content: '';
  position: absolute;
  left: 4px;
  top: 1px;
  width: 4px;
  height: 8px;
  border: solid #16233a;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.bv-scope__item input:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(255, 180, 0, 0.25) !important;
}

/* The row reads as one control: the whole strip highlights, not just the box. */
.bv-scope__item {
  border-radius: 6px;
  padding-top: 3px;
  padding-bottom: 3px;
  padding-right: 8px;
  transition: background 0.15s, color 0.15s;
}

.bv-scope__item:hover {
  background: rgba(255, 255, 255, 0.04);
  color: rgba(255, 255, 255, 0.9);
}

/* Checked rows are findable at a glance in a list of seventeen. */
.bv-scope__item:has(input:checked) {
  color: rgba(255, 255, 255, 0.92);
}

.bv-scope__item--stage:has(input:checked) {
  color: #FFB400;
}

/* The filter and the table are one box now, so they need a line between them
   rather than the gap two cards used to give. */
.bv-scope__rule {
  height: 1px;
  /* No margin of its own. `.bv-card__body` is a flex column with a 24px gap,
     so any margin here STACKS on that gap rather than replacing it -- 18px
     became 42px above the line and 54px below, which read as two panels that
     had failed to join rather than as one box with a divider in it. */
  margin: 0;
  background: rgba(255, 255, 255, 0.09);
  flex: 0 0 auto;
}

.bv-scope__working {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 180, 0, 0.8);
}

.bv-scope__actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

.bv-scope__note {
  margin: 12px 0 0 0;
  font-size: 12px;
  line-height: 1.6;
  color: rgba(255, 180, 0, 0.75);
}

/* The third level of the round scope. Criteria sit under their segment,
   indented again and quieter, because a list of seventeen boxes at one
   weight is a wall rather than a hierarchy. */
.bv-scope__item--leaf {
  padding-left: 28px;
  font-size: 11px;
  color: rgba(255, 255, 255, 0.5);
}

/* ══════════════════════════════════════════════════════════════════
   FULL-SCREEN JUDGING

   The screen a judge scores a segment on. Replaces the .bv-deck-* block
   above, which is left in place rather than deleted: this file is
   additive-only territory and it is served from a rebuilt public/ volume.

   Layout is a 30 / 70 split. The rail is the judge's own ranking and the
   navigation; the right side is one contestant at a time. Below 860px the
   rail turns into a horizontal strip above the card, because a 30% column
   on a phone is about 110px and fits neither a face nor a name.
   ══════════════════════════════════════════════════════════════════ */

.bv-judging {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

/* ── Header ─────────────────────────────────────────────────────── */
.bv-judging__top {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 20px;
  background: rgba(10, 18, 34, 0.86);
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
  flex: 0 0 auto;
}

.bv-judging__back {
  width: 38px;
  height: 38px;
  flex: 0 0 38px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.7);
  text-decoration: none;
  transition: background 0.2s, color 0.2s;
}

.bv-judging__back:hover {
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
}

.bv-judging__title {
  flex: 1;
  margin: 0;
  min-width: 0;
  font-size: 17px;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: #fbbf24;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bv-judging__theme {
  width: 38px;
  height: 38px;
  flex: 0 0 38px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: transparent;
  color: rgba(255, 255, 255, 0.7);
  cursor: pointer;
}

/* One glyph at a time. beevote-light.css flips which one, the same way it
   does for the topbar button. */
.bv-judging__theme .bv-judging__theme-light { display: none; }

/* ── The two columns ────────────────────────────────────────────── */
.bv-judge {
  flex: 1;
  display: flex;
  min-height: 0;
}

/* ── The rail ───────────────────────────────────────────────────── */
/* A FIXED TRACK, NOT A SHARE OF THE VIEWPORT.
   It was 30%, which on a wide screen handed the rail width it had nothing to
   put in -- the rows are one column of fixed-size cells, so past a point the
   extra only padded the right-hand edge while the deck beside it, which now
   shows three cards rather than one, went short. A width it actually wants is
   the same at every size. */
.bv-rail {
  flex: 0 0 248px;
  min-width: 248px;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: rgba(8, 15, 30, 0.75);
  border-right: 1px solid rgba(255, 255, 255, 0.07);
}

/* No header rules: the rail leads with the first contestant now. The list
   starts a little lower than its own padding would put it, so the top row
   is not welded to the topbar above it. */
.bv-rail__rows {
  flex: 1;
  overflow-y: auto;
  padding: 12px 0 6px;
  min-height: 0;
}

.bv-rail__row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 7px 14px;
  background: none;
  border: 0;
  border-left: 3px solid transparent;
  color: inherit;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
}

.bv-rail__row:hover { background: rgba(255, 255, 255, 0.04); }

.bv-rail__row.is-here {
  background: rgba(251, 191, 36, 0.09);
  border-left-color: #fbbf24;
}

.bv-rail__rank {
  flex: 0 0 16px;
  font-size: 14px;
  font-weight: 800;
  color: #fbbf24;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* The face. First thing in the row, because a judge picks a contestant out
   of a list by looking rather than by reading a name nobody told them. */
.bv-rail__face {
  position: relative;
  flex: 0 0 40px;
  width: 40px;
  height: 40px;
  border-radius: 9px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.05);
  display: flex;
  align-items: center;
  justify-content: center;
}

.bv-rail__face img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.bv-rail__initials {
  font-size: 13px;
  font-weight: 800;
  color: #fbbf24;
}

/* The number the room calls them by, stamped on the corner of the face so
   the row needs no separate column for it. */
.bv-rail__num {
  position: absolute;
  left: 0;
  bottom: 0;
  padding: 0 4px;
  background: #fbbf24;
  color: #10182a;
  font-size: 9px;
  font-weight: 800;
  border-radius: 0 6px 0 6px;
  font-variant-numeric: tabular-nums;
}

.bv-rail__who {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.bv-rail__who b {
  font-size: 12.5px;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bv-rail__who i {
  font-style: normal;
  font-size: 9.5px;
  letter-spacing: 0.05em;
  color: rgba(255, 255, 255, 0.6);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bv-rail__score {
  flex: 0 0 auto;
  font-size: 12.5px;
  font-weight: 800;
  color: rgba(255, 255, 255, 0.85);
  font-variant-numeric: tabular-nums;
}

/* Below the divider: in the run, not yet comparable. Dimmed rather than
   hidden, because a judge scanning for who is left needs to see them. */
.bv-rail__row.is-dim .bv-rail__rank { color: rgba(255, 255, 255, 0.52); }
.bv-rail__row.is-dim .bv-rail__who b { color: rgba(255, 255, 255, 0.62); font-weight: 500; }
.bv-rail__row.is-dim .bv-rail__face { opacity: 0.5; }
.bv-rail__row.is-dim .bv-rail__score {
  font-size: 10px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.6);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.bv-rail__divider {
  margin: 6px 0 2px;
  padding: 8px 16px 4px;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
  font-size: 9px;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

.bv-rail__foot {
  flex: 0 0 auto;
  padding: 11px 13px;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}

.bv-rail__review { width: 100%; }

/* ── The contestant side ────────────────────────────────────────── */
.bv-stage {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  padding: 16px;
  gap: 12px;
  box-sizing: border-box;
}

/* THREE COLUMNS ABOVE 1100px, ONE BELOW IT.
   The deck shows the contestant being judged with a neighbour either side, so
   a judge can see where they are in the line-up without reading the rail. The
   peeks are an affordance of a wide screen and nothing more: below 1100px the
   card and its criteria already fill the pane, and the same grid there would
   squeeze the thing actually being scored to make room for decoration.

   Declared single-column here and widened in the query, rather than the other
   way round, so the narrow case is the one that needs no override. */
.bv-stage__track {
  position: relative;
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
  column-gap: 14px;
  min-height: 0;
  overflow-y: auto;
  overflow-x: clip;
}

@media (min-width: 1101px) {
  .bv-stage__track {
    grid-template-columns: minmax(0, 0.55fr) minmax(0, 2fr) minmax(0, 0.55fr);
  }
}

/* ── JUDGE DECK CARDS ─────────────────────────────────────────────
   SCOPED TO THE DECK, and they have to stay scoped.

   These were written as bare `.bv-card`, which is the card class every
   screen in the application uses. The first rule below sets
   `display: none` and the deck turns one card on at a time with `.is-on`
   -- correct for a deck, catastrophic for a class shared app-wide: every
   card on every screen for every role went invisible, and pages rendered
   as a heading over empty space.

   The deck lays its cards inside .bv-stage__track (judge/deck.blade.php),
   so that is the fence. Anything added here later needs the same prefix,
   or it leaks right back out. */
.bv-stage__track .bv-card {
  display: none;
  width: 100%;
  gap: 16px;
  padding: 16px;
  box-sizing: border-box;
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid rgba(255, 255, 255, 0.06);
}

/* SIZED TO ITS CONTENTS, AND CENTRED IN WHAT IS LEFT.

   The card used to stretch to the full height of the track whatever was
   in it, so a contestant with four criteria left roughly a third of a
   bordered navy box empty underneath them -- the panel looked broken
   rather than roomy.

   `margin: auto 0` rather than `align-items: center` on the track: a
   centred flex item that outgrows its scrolling parent loses its top to
   the overflow and cannot be scrolled back to, and a long enough segment
   list would do exactly that. Auto margins centre the same way and give
   the top back the moment there is not enough room. */
.bv-stage__track .bv-card.is-on {
  display: flex;
  margin: auto 0;
  position: relative;
  z-index: 2;
}

/* ── The neighbours ───────────────────────────────────────────────
   Hidden by default and turned on only where there are columns to put them
   in, which is the same rule the grid above follows.

   NOT INTERACTIVE. A peek card still holds its criteria inputs, because the
   autosave reads their values and posts the dirty ones when the judge leaves a
   card -- taking them out of the document would silently drop a contestant's
   scores. They are not drawn and they cannot be reached by a finger, which is
   the distinction that matters: present to the script, absent to the judge. */
.bv-stage__track .bv-card.is-near-left,
.bv-stage__track .bv-card.is-near-right { display: none; }

@media (min-width: 1101px) {
  .bv-stage__track .bv-card.is-on { grid-column: 2; }

  .bv-stage__track .bv-card.is-near-left,
  .bv-stage__track .bv-card.is-near-right {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin: auto 0;
    padding: 10px;
    opacity: 0.45;
    pointer-events: none;
  }

  .bv-stage__track .bv-card.is-near-left  { grid-column: 1; }
  .bv-stage__track .bv-card.is-near-right { grid-column: 3; }

  /* A peek card is a face, a number and a state. Nothing else fits at this
     size and nothing else is worth saying about a contestant who is not the
     one in front of the judge. */
  .bv-stage__track .bv-card.is-near-left  .bv-card__crit,
  .bv-stage__track .bv-card.is-near-right .bv-card__crit { display: none; }

  .bv-stage__track .bv-card.is-near-left  .bv-card__id,
  .bv-stage__track .bv-card.is-near-right .bv-card__id {
    flex: 0 0 auto;
    width: 100%;
    align-self: stretch;
  }
}

/* The review is the exception: it is a list that scrolls on its own, so
   it takes the height it is given rather than asking for what it needs. */
.bv-stage__track .bv-card--review {
  flex-direction: column;
  overflow-y: auto;
}

.bv-stage__track .bv-card--review.is-on {
  align-self: stretch;
  margin: 0;
}

/* The slide. Direction carries meaning: forward comes from the right, back
   from the left, so a judge can tell a correction from an advance. */
.bv-stage__track .bv-card.is-in-right { animation: bv-deck-right 0.22s ease-out; }
.bv-stage__track .bv-card.is-in-left  { animation: bv-deck-left  0.22s ease-out; }

@media (prefers-reduced-motion: reduce) {
  .bv-stage__track .bv-card.is-in-right, 
  .bv-stage__track .bv-card.is-in-left { animation: none; }
}

/* ── Identity: the photo IS the column ──────────────────────────── */
/* THE PHOTO IS NEVER CROPPED AND NEVER STRETCHED. It was `object-fit:
   cover` in a column that stretched to whatever height the card happened
   to be, so a portrait headshot in a tall card lost both ends of itself,
   and on a portrait tablet the column ran past 1000px and the face was a
   sliver in the middle of it.

   `contain` shows the whole frame at its own proportions whatever the box
   does, and the box is now a 3:4 portrait that stops at the card's height
   instead of stretching -- which is the shape a pageant photo is taken
   in, so there is little left over for the frame to letterbox. */
.bv-stage__track .bv-card__id {
  position: relative;
  flex: 0 0 42%;
  align-self: flex-start;
  aspect-ratio: 3 / 4;
  max-height: 100%;
  min-height: 0;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid rgba(251, 191, 36, 0.35);
  background: rgba(4, 9, 20, 0.55);
}

.bv-stage__track .bv-card__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.bv-stage__track .bv-card__photo--none {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 62px;
  font-weight: 800;
  color: #fbbf24;
  letter-spacing: 0.04em;
}

.bv-stage__track .bv-card__num {
  position: absolute;
  top: 12px;
  left: 12px;
  padding: 2px 12px;
  border-radius: 9px;
  background: #fbbf24;
  color: #10182a;
  font-size: 26px;
  font-weight: 800;
  line-height: 1.25;
  font-variant-numeric: tabular-nums;
}

/* Name and course sit ON the photo. Stacking them above it would cost the
   photo about a third of its height, and the photo is the point. */
.bv-stage__track .bv-card__scrim {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 34px 14px 13px;
  background: linear-gradient(to top, rgba(5, 10, 20, 0.96) 32%, rgba(5, 10, 20, 0));
}

.bv-stage__track .bv-card__name {
  margin: 6px 0 0;
  font-size: 21px;
  font-weight: 800;
  line-height: 1.15;
  color: #fff;
}

.bv-stage__track .bv-card__meta {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 5px;
  font-size: 10px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.58);
}

.bv-stage__track .bv-card__dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.35);
}

/* ── Criteria, one block per open segment ───────────────────────── */
.bv-stage__track .bv-card__crit {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 11px;
}

/* SCOPED, AND IT HAS TO BE. `.bv-seg` is also the round-tabs component
   near the top of this file -- `display: flex; align-items: center;
   gap: 4px; overflow-x: auto` -- and this block never declared a display
   of its own, so that rule was still the one in force: every criterion in
   a segment was being laid out as one horizontal flex row with a
   scrollbar, which is why the names wrapped a word per line and the
   inputs sat in columns two characters wide.

   Same fence as the deck's cards, and it names its own display so the
   tabs rule cannot reach in again. */
.bv-stage__track .bv-seg {
  display: block;
  overflow: visible;
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 13px;
  padding: 13px;
  background: rgba(255, 255, 255, 0.02);
}

.bv-stage__track .bv-seg.is-sealed { opacity: 0.72; }

/* ── SEVERAL SEGMENTS OPEN AT ONCE ────────────────────────────────
   Three open segments made the score column far taller than the photo:
   it ran off the bottom of the track, nothing said there was more, and
   scrolling to it took the photo and number away with it.

   1. The photo stays put while the column scrolls, so the judge always
      sees who they are scoring. Sticky is still a positioned box, so the
      number and name laid over the photo keep their place.
   2. With two or more segments, every box and row gets tighter, and a
      criterion's share sits beside its name instead of on a line of its
      own. The score box and slider keep their size: they are tapped. */
.bv-stage__track .bv-card.is-on .bv-card__id {
  position: sticky;
  top: 0;
}

.bv-stage__track .bv-card__crit:has(> .bv-seg + .bv-seg) { gap: 8px; }
.bv-stage__track .bv-card__crit:has(> .bv-seg + .bv-seg) .bv-seg { padding: 10px 12px; }
.bv-stage__track .bv-card__crit:has(> .bv-seg + .bv-seg) .bv-seg__head { margin-bottom: 6px; }
.bv-stage__track .bv-card__crit:has(> .bv-seg + .bv-seg) .bv-seg__group { margin: 6px 0 4px; }
.bv-stage__track .bv-card__crit:has(> .bv-seg + .bv-seg) .bv-field { margin-bottom: 8px; }
.bv-stage__track .bv-card__crit:has(> .bv-seg + .bv-seg) .bv-field:last-child { margin-bottom: 0; }
.bv-stage__track .bv-card__crit:has(> .bv-seg + .bv-seg) .bv-field__name { margin-bottom: 4px; }
.bv-stage__track .bv-card__crit:has(> .bv-seg + .bv-seg) .bv-field__share {
  display: inline;
  margin: 0 0 0 8px;
}

/* 3. More below. The bottom of the track fades while there is more to
      scroll and stops fading at the end, driven by the track's own
      scroll position. A browser without scroll timelines gets no fade,
      which is how it looked before. A track with nothing to scroll has
      no active timeline, so it never fades. */
@property --bv-track-fade {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}

@keyframes bv-track-fade {
  from { --bv-track-fade: 40px; }
  92%  { --bv-track-fade: 40px; }
  to   { --bv-track-fade: 0px; }
}

@supports (animation-timeline: scroll()) {
  .bv-stage__track {
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--bv-track-fade)), transparent);
    mask-image: linear-gradient(to bottom, #000 calc(100% - var(--bv-track-fade)), transparent);
    animation: bv-track-fade linear both;
    animation-timeline: scroll(self);
  }
}

.bv-stage__track .bv-seg--none {
  display: flex;
  align-items: center;
  gap: 10px;
  color: rgba(255, 255, 255, 0.6);
  font-size: 13px;
  line-height: 1.5;
}

.bv-stage__track .bv-seg__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 11px;
}

.bv-stage__track .bv-seg__name {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #fbbf24;
}

.bv-stage__track .bv-seg__group {
  margin: 10px 0 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-field { margin-bottom: 14px; }
.bv-field:last-child { margin-bottom: 0; }

.bv-field__name {
  display: block;
  margin-bottom: 6px;
  font-size: 14px;
  font-weight: 700;
  color: #fff;
}

.bv-field__share {
  display: block;
  margin: 2px 0 0;
  font-size: 9px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

/* The segment's share of the whole event, printed beside its name on the
   judge's deck. Same voice as .bv-field__share one level down, so a judge
   reading "40% OF THE EVENT" and "60% OF SWIMSUIT" sees one kind of note
   rather than two. margin-left:auto pushes it to the end of the head row,
   clear of the "Did not walk" button. */
.bv-seg__weight {
  margin-left: auto;
  font-size: 9px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
  white-space: nowrap;
}

.bv-field__entry {
  display: flex;
  align-items: center;
  gap: 11px;
}

/* ── The score slider ──────────────────────────────────────────────
   A judge on a tablet drags; a judge who wants 8.75 types it in the box
   beside the track. The track is the wide half because dragging is the
   common case and a thumb needs room to be precise.

   Every part of a range input has to be drawn per engine: WebKit and
   Gecko name the track and the thumb differently and neither inherits
   from the element, so the two blocks below are the same design written
   twice rather than a duplicate. */
.bv-field__slider {
  flex: 1;
  min-width: 90px;
  height: 30px;
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
  /* The deck pages on a horizontal swipe. Dragging a thumb IS a
     horizontal swipe, so the track claims the gesture outright rather
     than leaving the two to race. */
  touch-action: none;
}

.bv-field__slider::-webkit-slider-runnable-track {
  height: 8px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.13);
  border: 1px solid rgba(255, 255, 255, 0.07);
}

.bv-field__slider::-moz-range-track {
  height: 8px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.13);
  border: 1px solid rgba(255, 255, 255, 0.07);
}

/* Gecko will fill the travelled part of the track for free. WebKit will
   not without a gradient recomputed from JS on every move, so the fill is
   a bonus where it exists and never the only thing carrying the value --
   the number sits in the box a finger-width away either way. */
.bv-field__slider::-moz-range-progress {
  height: 8px;
  border-radius: 999px;
  background: rgba(251, 191, 36, 0.55);
}

.bv-field__slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 26px;
  height: 26px;
  margin-top: -10px;
  border-radius: 50%;
  background: #fbbf24;
  border: 2px solid #0b1424;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
}

.bv-field__slider::-moz-range-thumb {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #fbbf24;
  border: 2px solid #0b1424;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
}

.bv-field__slider:focus { outline: none; }
.bv-field__slider:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px rgba(251, 191, 36, 0.28); }
.bv-field__slider:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 4px rgba(251, 191, 36, 0.28); }

.bv-field__slider:disabled { cursor: default; opacity: 0.5; }

/* Nothing entered yet. The thumb has to rest somewhere and it rests at
   the start of the track, so it is greyed until there is a score behind
   it -- a gold thumb sitting on the left would read as a 1. */
.bv-field.is-unset .bv-field__slider::-webkit-slider-thumb { background: rgba(255, 255, 255, 0.32); }
.bv-field.is-unset .bv-field__slider::-moz-range-thumb { background: rgba(255, 255, 255, 0.32); }
.bv-field.is-unset .bv-field__slider::-moz-range-progress { background: transparent; }

.bv-field__input {
  flex: 1;
  min-width: 0;
  padding: 9px 12px;
  border-radius: 9px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(5, 12, 26, 0.7);
  color: #fff;
  font-family: inherit;
  font-size: 19px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  box-sizing: border-box;
}

.bv-field__input:focus {
  outline: none;
  border-color: #fbbf24;
  box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.16);
}

.bv-field__input[readonly] {
  color: rgba(255, 255, 255, 0.55);
  border-style: dashed;
}

/* Fixed, not flexible: the track takes the room the box gives up, and a
   score reads the same width whether it is 8 or 8.75.

   NO SPINNERS. The stepper arrows a number input draws claim about a
   sixth of a box this narrow, and at tablet size that was enough to clip
   the second digit of a 10 -- a judge giving a perfect score saw a 1. They
   were never the way up or down on a touch screen either: the track beside
   the box is. */
.bv-field__input--compact {
  flex: 0 0 74px;
  padding: 9px 6px;
  text-align: center;
  -moz-appearance: textfield;
  appearance: textfield;
}

.bv-field__input--compact::-webkit-outer-spin-button,
.bv-field__input--compact::-webkit-inner-spin-button {
  -webkit-appearance: none;
  appearance: none;
  margin: 0;
}

.bv-field__max {
  flex: 0 0 auto;
  font-size: 11px;
  color: rgba(255, 255, 255, 0.6);
}

/* ── Review ─────────────────────────────────────────────────────── */
.bv-review__head { margin-bottom: 4px; }

.bv-review__title {
  margin: 0;
  font-size: 19px;
  font-weight: 800;
  color: #fff;
}

.bv-review__sub {
  margin: 5px 0 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.55);
}

.bv-review__seg {
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 14px;
  padding: 14px;
  background: rgba(255, 255, 255, 0.02);
}

.bv-review__seg-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}

.bv-review__seg-name {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #fbbf24;
}

.bv-review__seg-count {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.5);
}

.bv-review__row {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  padding: 9px 11px;
  margin-bottom: 5px;
  border-radius: 9px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(255, 255, 255, 0.02);
  color: inherit;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}

.bv-review__row:hover { background: rgba(255, 255, 255, 0.05); }
.bv-review__row.is-missing { border-color: rgba(248, 113, 113, 0.45); }

.bv-review__row-num {
  flex: 0 0 26px;
  font-weight: 800;
  color: #fbbf24;
  font-variant-numeric: tabular-nums;
}

.bv-review__row-name {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bv-review__row-score {
  font-size: 12.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.8);
}

.bv-review__row.is-missing .bv-review__row-score {
  color: #f87171;
  font-weight: 500;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.bv-review__row-go { color: rgba(255, 255, 255, 0.52); font-size: 11px; }

.bv-review__skipped {
  margin: 10px 0;
  padding: 11px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.025);
}

.bv-review__skipped-head,
.bv-review__skipped-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
}

.bv-review__skipped-head {
  margin-bottom: 7px;
  font-size: 9.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-review__skipped-row { padding: 4px 0; }
.bv-review__skipped-num { flex: 0 0 26px; font-weight: 800; color: rgba(255, 255, 255, 0.5); }
.bv-review__skipped-name { flex: 1; color: rgba(255, 255, 255, 0.6); }

.bv-review__submit { width: 100%; margin-top: 6px; }

.bv-review__submit.is-armed {
  background: #f87171 !important;
  color: #180a0a !important;
}

.bv-review__warn {
  margin: 7px 0 0;
  font-size: 12px;
  color: #f87171;
  min-height: 1em;
}

.bv-review__done,
.bv-review__empty {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-top: 8px;
  padding: 11px 13px;
  border-radius: 10px;
  font-size: 12.5px;
  background: rgba(16, 185, 113, 0.1);
  border: 1px solid rgba(16, 185, 113, 0.28);
  color: rgba(255, 255, 255, 0.82);
}

.bv-review__empty {
  background: rgba(255, 255, 255, 0.03);
  border-color: rgba(255, 255, 255, 0.08);
}

/* ── Footer navigation ──────────────────────────────────────────── */
.bv-stage__nav {
  flex: 0 0 auto;
  display: flex;
  gap: 11px;
}

.bv-stage__nav > .bv-btn { flex: 1; }
.bv-stage__nav > .bv-btn--primary,
.bv-stage__review.bv-btn--primary,
.bv-review__submit.bv-btn--primary { margin-bottom: 0 !important; }
.bv-stage__next { flex: 2 !important; }
.bv-stage__next.is-hidden { display: none; }

/* Next while the card is unfinished: still tappable (a tap names what is
   left), so it is dimmed rather than disabled. */
.bv-stage__next.is-blocked { opacity: 0.45; cursor: not-allowed; }

.bv-stage__hint {
  min-height: 18px;
  margin: 8px 0 0;
  font-size: 13px;
  font-weight: 600;
  text-align: center;
  color: #f87171;
}

/* !important: a site-wide input rule sets every border !important. */
.bv-field.is-missing .bv-field__input {
  border-color: #f87171 !important;
  box-shadow: 0 0 0 3px rgba(248, 113, 113, 0.22) !important;
}

/* ── The running order ───────────────────────────────────────────
   Wraps rather than scrolls. A judge glancing down mid-round should see the
   whole line-up at once; a strip that scrolled would hide the back half of a
   large field behind a gesture nobody would think to make. */
.bv-stage__order {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}

/* Out of everything currently on stage.
   A span, not a button, and styled to say so: there is no card to jump to, and
   a chip that looks pressable and does nothing is worse than one that plainly
   is not. Dashed, because the number is still theirs -- they are missing from
   this round, not struck from the running order. */
.bv-deck-dot.is-locked {
  cursor: default;
  color: rgba(255, 255, 255, 0.52);
  background: rgba(255, 255, 255, 0.015) !important;
  border-style: dashed !important;
}

/* Centred and capped rather than full width. Stretched across a wide deck it
   read as a page footer instead of the end of the running order. */
.bv-stage__review {
  flex: 0 0 auto;
  align-self: center;
  width: 100%;
  max-width: 460px;
}

/* ── A peek card's one line ──────────────────────────────────────
   SCOPED, and it has to stay scoped: `bv-card__state` is new but it sits on
   `.bv-card`, and this file has already been broken once by a bare rule on
   that class taking every card in the application with it. */
.bv-stage__track .bv-card__state { display: none; }

.bv-stage__track .bv-card.is-near-left  .bv-card__state,
.bv-stage__track .bv-card.is-near-right .bv-card__state {
  display: block;
  padding: 6px 10px;
  border-radius: 8px;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: center;
  color: rgba(255, 255, 255, 0.5);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.bv-stage__track .bv-card.is-done .bv-card__state {
  color: #4ade80;
  background: rgba(34, 197, 94, 0.1);
  border-color: rgba(34, 197, 94, 0.28);
}

/* Whether the card's finished scores are on the server (judge/deck). Empty
   until there is something to say; quiet when saved, red when not. */
.bv-stage__track .bv-card__save {
  min-height: 16px;
  margin-top: 8px;
  font-size: 12px;
  font-weight: 600;
  text-align: right;
  color: rgba(255, 255, 255, 0.55);
}

.bv-stage__track .bv-card__save[data-state="saved"]  { color: #4ade80; }
.bv-stage__track .bv-card__save[data-state="failed"] { color: #f87171; }

/* ── Waiting for the organizer ──────────────────────────────────── */
.bv-judging-wait {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  overflow-y: auto;
}

.bv-judging-wait__card {
  width: 100%;
  max-width: 470px;
  padding: 30px;
  border-radius: 18px;
  text-align: center;
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid rgba(255, 255, 255, 0.07);
}

.bv-judging-wait__glyph { font-size: 32px; color: #fbbf24; }

.bv-judging-wait__title {
  margin: 14px 0 0;
  font-size: 20px;
  font-weight: 800;
  color: #fff;
}

.bv-judging-wait__lead {
  margin: 8px 0 0;
  font-size: 13.5px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.6);
}

.bv-judging-wait__list {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
  text-align: left;
}

.bv-judging-wait__label {
  margin-bottom: 8px;
  font-size: 9.5px;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.bv-judging-wait__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
}

.bv-judging-wait__name { font-size: 13px; font-weight: 600; }

.bv-judging-wait__note {
  margin: 18px 0 0;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52);
}

/* ── Narrow screens ─────────────────────────────────────────────────
   A 30% column on a phone is about 110px, which fits neither a face nor a
   name. The rail becomes a horizontal strip above the card instead: same
   order, same tap target, no drawer to discover. */
/* ── THE JUDGE DECK ON A TABLET ──────────────────────────────────
   Judges score from a tablet, so the three-pane layout has to survive
   one rather than collapse into a column the moment the viewport is
   narrower than a laptop.

   A tablet in landscape is 1024, 1180 or 1280 wide and already kept the
   full layout. In PORTRAIT it is 768 or 820, which fell under the old
   860px collapse: the rail became a strip, the card stacked, and a
   300px photo pushed the score fields off the bottom of the screen.

   So the collapse moves down to phones, and between there and a laptop
   the panes stay side by side and simply get tighter. Nothing is hidden
   -- a judge has to read the name, the criterion and its weight, and a
   scoring screen that hides any of those is worse than a cramped one. */
@media (min-width: 701px) and (max-width: 1100px) {
  /* The rail earns less room: the judge is scoring one contestant, not
     reading the standing. Enough for a number, a face and a NAME -- at
     30% of a portrait tablet the row was rendering "Adrian Miguel Reyes"
     as "Adrian ...", which is the one thing the row exists to carry.

     So the course line goes and the name takes both lines. A judge picks
     a contestant out of this list by face and by name; the course was
     never how anyone found anybody. */
  .bv-rail { flex: 0 0 30%; min-width: 190px; }
  .bv-rail__row { padding: 8px 9px; gap: 8px; flex-wrap: wrap; row-gap: 3px; }
  .bv-rail__face { flex: 0 0 34px; width: 34px; height: 34px; }
  .bv-rail__who { flex: 1 1 auto; }
  .bv-rail__who i { display: none; }
  .bv-rail__who b {
    font-size: 12px;
    line-height: 1.25;
    white-space: normal;
    overflow: visible;
  }

  /* The score drops to its own line, indented past the face to sit under
     the name. Beside the name there was room for neither: "Carlo Emmanuel
     Vega" came out over three ragged lines to make space for the words NOT
     SCORED. The indent is rank plus gap plus face, so the two lines read as
     one block rather than as two columns that failed to line up. */
  .bv-rail__score {
    flex: 1 0 100%;
    box-sizing: border-box;
    padding-left: 58px;
  }

  .bv-rail__row.is-dim .bv-rail__score { font-size: 9px; letter-spacing: 0.05em; }

  .bv-stage { padding: 12px; gap: 10px; }
  .bv-stage__track .bv-card { gap: 12px; padding: 12px; }

  /* 34% here once, back when the column stretched to the card's height and
     a 42% share of 768px produced a photo a thousand pixels tall. It is a
     3:4 frame now, so width IS the size of the photo and starving it just
     makes the face small. */
  .bv-stage__track .bv-card__id { flex: 0 0 44%; }

  /* The name sits on the photo, and on a frame this size the gradient
     behind it was covering a third of the face. */
  .bv-stage__track .bv-card__scrim { padding: 22px 10px 9px; }
  .bv-stage__track .bv-card__name { font-size: 15px; }
  .bv-stage__track .bv-card__meta { font-size: 9px; gap: 5px; margin-top: 3px; }
  .bv-stage__track .bv-card__num { font-size: 20px; padding: 1px 9px; }

  /* Score entry is the job. Bigger targets than the desktop rule, not
     smaller: these are tapped with a finger, not clicked. */
  .bv-field__input { padding: 11px 12px; font-size: 16px; }
  .bv-field__input--compact { flex: 0 0 66px; padding: 11px 4px; }
  .bv-field__slider { min-width: 70px; }
  .bv-stage__track .bv-seg { padding: 11px; }
  .bv-stage__track .bv-seg__head { gap: 8px; }
}

/* Phones. Below this the panes genuinely cannot sit side by side. */
@media (max-width: 700px) {
  .bv-judge { flex-direction: column; }

  .bv-rail {
    flex: 0 0 auto;
    min-width: 0;
    border-right: 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.07);
  }

  .bv-rail__rows {
    display: flex;
    overflow-x: auto;
    overflow-y: hidden;
    padding: 8px 10px;
    gap: 8px;
  }

  .bv-rail__row {
    flex: 0 0 auto;
    width: auto;
    flex-direction: column;
    gap: 4px;
    padding: 6px 8px;
    border-left: 0;
    border-bottom: 3px solid transparent;
    border-radius: 10px;
  }

  .bv-rail__row.is-here { border-bottom-color: #fbbf24; }
  .bv-rail__who { display: none; }

  .bv-rail__divider {
    flex: 0 0 auto;
    margin: 0;
    padding: 0 6px;
    border-top: 0;
    border-left: 1px solid rgba(255, 255, 255, 0.1);
    writing-mode: vertical-rl;
    font-size: 8px;
  }

  /* Scoped like the rest of the deck. These were bare `.bv-card`, which
     is the card class every screen uses, so below 860px every card in
     the application was being turned into a column. Same commit, same
     mistake as the `display: none` rule above. */
  .bv-stage__track .bv-card { flex-direction: column; }

  /* The card is a COLUMN here, so align-self governs the width, and the
     `flex-start` the frame carries for the side-by-side layout would shrink
     it to the width of its contents -- which is nothing, because the photo
     inside is positioned absolutely. Full width, and the 3:4 frame gives it
     its height, capped so the criteria are not a whole screen further down. */
  .bv-stage__track .bv-card__id {
    flex: 0 0 auto;
    align-self: stretch;
    min-height: 0;
    max-height: 58vh;
  }

  .bv-stage { padding: 12px; }
}

/* The "show judges from archived events" toggle under the seat picker.
   Quiet on purpose: it is a way back to accounts whose work is done, not
   an action anybody takes on most visits. */
.bv-panel-retired {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  font-size: 11.5px;
  color: rgba(255, 255, 255, 0.6);
  cursor: pointer;
}

.bv-panel-retired:hover { color: rgba(255, 255, 255, 0.7); }

.bv-panel-retired input {
  width: auto !important;
  margin: 0;
  accent-color: #FFB400;
  flex: 0 0 auto;
}

/* ── TURNOUT BOARD ────────────────────────────────────────────────
   The COMELEC dashboard, which is now this and nothing else. Added
   2026-09-12 when the two navigation cards above it were removed.

   ONE VARIABLE CARRIES THE COLOUR. `--bv-turnout-accent` is set by the
   band class on the card and read by the percentage and the meter fill
   inside it, so a tile's colour is decided in one place and the bands
   themselves are decided in PHP (TurnoutService::row) rather than in a
   template that would drift from the summary above it. */
.bv-turnout--high  { --bv-turnout-accent: #22c55e; }
.bv-turnout--mid   { --bv-turnout-accent: #FFB400; }
.bv-turnout--low   { --bv-turnout-accent: #ef4444; }
.bv-turnout--empty { --bv-turnout-accent: #64748b; }

/* Each college in its onboarding colour (voter/onboarding.blade.php), so a
   department reads the same on the COMELEC board as on the card a student
   picked. Declared after the band rules above, so the college wins; a tile
   with no known college stays neutral instead of taking the low band's red. */
.bv-turnout--college-ccs       { --bv-turnout-accent: #FFD200; }
.bv-turnout--college-cthbm     { --bv-turnout-accent: #32CD50; }
.bv-turnout--college-cea       { --bv-turnout-accent: #C8D8E8; }
.bv-turnout--college-cspc-buhi { --bv-turnout-accent: #FF1493; }
.bv-turnout--college-ctde      { --bv-turnout-accent: #38BDF8; }
.bv-turnout--college-cas       { --bv-turnout-accent: #EF4444; }
.bv-turnout--college-chs       { --bv-turnout-accent: #A855F7; }
.bv-turnout--college-none      { --bv-turnout-accent: #64748b; }

.bv-turnout__tile .bv-turnout__code {
  color: var(--bv-turnout-accent);
  font-weight: 800;
}

.bv-turnout__picker {
  /* The label sits ABOVE the select, not beside it. .bv-label is a block
     with its own letter-spacing, and on one row it read as a fragment
     jammed against the control rather than as its caption. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  margin-bottom: 24px;
}

/* .bv-input is width:100% across this system, so the picker needs its
   own ceiling or it takes the full page width on a desktop. */
.bv-turnout__picker .bv-input {
  max-width: 420px;
}

.bv-turnout__total {
  position: relative;
  padding: 28px 32px;
  margin-bottom: 28px;
  border-radius: 16px;
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--bv-turnout-accent) 16%, transparent),
      color-mix(in srgb, var(--bv-turnout-accent) 4%, transparent));
  border: 1px solid color-mix(in srgb, var(--bv-turnout-accent) 32%, transparent);
}

.bv-turnout__grid {
  display: grid;
  /* auto-FILL. auto-fit collapses the empty tracks and hands their width
     to the tiles that exist, so two colleges would each claim half a
     desktop -- the same trap the ballot and results grids were in. */
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 20px;
  /* stretch, not start: a department with no voters has less to say than
     one with a percentage, and letting each tile size itself produced a
     ragged board where the difference read as breakage. */
  align-items: stretch;
}

.bv-turnout__tile {
  /* The accent as a hairline down the leading edge. Enough to read the
     band at a glance across a grid without colouring the whole card,
     which would turn a board of six departments into a traffic light. */
  border-left: 3px solid var(--bv-turnout-accent) !important;
  display: flex;
}

.bv-turnout__tile .bv-card__body {
  display: flex;
  flex-direction: column;
  width: 100%;
  padding: 20px !important;
}

/* Pinned to the bottom of the tile rather than sitting 14px under the
   count, so every bar on a row lands on one line and the board can be
   read across instead of tile by tile. */
.bv-turnout__tile .bv-turnout__meter {
  margin-top: auto;
}

.bv-turnout__tile-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}

.bv-turnout__label,
.bv-turnout__code {
  display: block;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #94a3b8;
}

.bv-turnout__rank {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--bv-turnout-accent);
}

.bv-turnout__percent {
  display: block;
  margin: 6px 0 2px;
  font-size: 44px;
  font-weight: 900;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--bv-turnout-accent);
  /* Tabular figures so a column of percentages does not jitter as the
     digits change width between one election and the next. */
  font-variant-numeric: tabular-nums;
}

.bv-turnout__percent--sm {
  font-size: 30px;
}

.bv-turnout__count {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: #94a3b8;
  font-variant-numeric: tabular-nums;
}

.bv-turnout__empty {
  display: block;
  margin: 10px 0 auto;
  font-size: 14px;
  font-weight: 700;
  color: #94a3b8;
}

.bv-turnout__meter {
  height: 6px;
  margin-top: 14px;
  border-radius: 999px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.08);
}

.bv-turnout__meter-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--bv-turnout-accent);
  /* Never zero-width: a department at 0% still has a bar, so the row
     reads as measured rather than as a rendering that failed. */
  min-width: 3px;
  transition: width 0.4s ease;
}

@media (max-width: 640px) {
  .bv-turnout__picker {
    flex-direction: column;
    align-items: stretch;
  }

  .bv-turnout__picker .bv-input {
    max-width: none;
  }

  .bv-turnout__total {
    padding: 22px 20px;
  }

  .bv-turnout__percent {
    font-size: 36px;
  }
}

/* ── "DID NOT WALK" ───────────────────────────────────────────────
   The no-show button in a segment header on the judge's deck. Quiet
   by default and loud only once armed: it is one tap from marking a
   contestant absent for the whole panel, and it sits beside the
   criteria a judge is typing into.

   NOT .bv-btn. That class is width:100% !important across this system
   and would claim the whole header row, pushing the segment name out
   -- the same trap .bv-form-row exists to work around. */
.bv-stage__track .bv-seg__skip {
  margin-left: auto;
  /* The head is align-items: baseline; a 32px button on a baseline row
     hangs low against the segment name. */
  align-self: center;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid rgba(248, 113, 113, 0.3);
  background: rgba(248, 113, 113, 0.08);
  color: #f87171;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
  /* A judge's tablet: the tap target stays thumb-sized even though the
     type is small. */
  min-height: 32px;
}

.bv-stage__track .bv-seg__skip:hover {
  background: rgba(248, 113, 113, 0.16);
  border-color: rgba(248, 113, 113, 0.5);
}

.bv-stage__track .bv-seg__skip.is-armed {
  background: #f87171;
  border-color: #f87171;
  color: #180a0a;
}

.bv-stage__track .bv-seg__skip:disabled {
  opacity: 0.6;
  cursor: default;
}

/* A HEAD THAT WRAPS WHOLE WORDS, NOT LETTERS. On a portrait tablet the
   criteria column is narrow enough that the name, the share and the
   button were squeezed side by side: "Did not walk" stood three lines
   tall and "Q and A" broke over two. Each piece now keeps to one line,
   and a piece that does not fit drops to the next row instead. A name
   longer than the whole column is cut with an ellipsis. */
.bv-stage__track .bv-seg__head {
  flex-wrap: wrap;
  row-gap: 6px;
}

.bv-stage__track .bv-seg__name {
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bv-stage__track .bv-seg__skip { white-space: nowrap; }

/* Armed, the label is a whole sentence and would run out of the box on
   one line. It takes the full row and wraps inside it. */
.bv-stage__track .bv-seg__skip.is-armed {
  white-space: normal;
  max-width: 100%;
}

@media (max-width: 640px) {
  /* Armed, the label becomes a sentence. On a phone it wraps rather
     than shouldering the segment name off the row. */
  .bv-stage__track .bv-seg__skip {
    margin-left: 0;
    width: 100%;
    margin-top: 8px;
  }
}

/* ── ADMIN SCREENS (2026-09-16) ─────────────────────────────────────
   One stat tile for every Admin count (the Transparency Portal's tile, with
   an icon or badge on the label line and an optional coloured top edge), one
   height for filter-row controls, and callouts in each status colour. The
   portal's own tiles pick up the contrast raise. */
.bv-audit-tile__label,
.bv-audit-tile__note,
.bv-audit-tile__of {
  color: rgba(255, 255, 255, 0.62);
}

.bv-audit-tile__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 36px;
}

.bv-audit-tile__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 10px;
  font-size: 15px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.bv-audit-tile__icon--blue   { color: #60a5fa; background: rgba(59, 130, 246, 0.12); border-color: rgba(59, 130, 246, 0.25); }
.bv-audit-tile__icon--green  { color: #4ade80; background: rgba(34, 197, 94, 0.12); border-color: rgba(34, 197, 94, 0.25); }
.bv-audit-tile__icon--indigo { color: #a5b4fc; background: rgba(99, 102, 241, 0.14); border-color: rgba(99, 102, 241, 0.28); }

.bv-audit-tile__value i {
  margin-right: 8px;
  font-size: 0.8em;
}

.bv-audit-tile__value.is-warn { color: #FFB400; }

.bv-audit-tile--accent        { border-top-width: 3px; }
.bv-audit-tile--green         { border-top-color: rgba(34, 197, 94, 0.7); }
.bv-audit-tile--red           { border-top-color: rgba(239, 68, 68, 0.75); }
.bv-audit-tile--gold          { border-top-color: rgba(255, 180, 0, 0.7); }
.bv-audit-tile--blue          { border-top-color: rgba(59, 130, 246, 0.75); }
.bv-audit-tile--amber         { border-top-color: rgba(245, 158, 11, 0.75); }
.bv-audit-tile--indigo        { border-top-color: rgba(99, 102, 241, 0.75); }

/* Every control in a filter row one height; the reset link a square of it. */
.bv-filter-bar .bv-input,
.bv-filter-bar__actions .bv-btn {
  height: 44px;
}

.bv-filter-bar__actions .bv-filter-bar__icon {
  width: 44px !important;
  padding: 0 !important;
}

.bv-form-row > select.bv-input.bv-filter-role {
  flex: 0 1 220px;
}

.bv-callout--green { background: rgba(34, 197, 94, 0.08); border: 1px solid rgba(34, 197, 94, 0.25); }
.bv-callout--red   { background: rgba(239, 68, 68, 0.08); border: 1px solid rgba(239, 68, 68, 0.25); }
.bv-callout--amber { background: rgba(255, 180, 0, 0.08); border: 1px solid rgba(255, 180, 0, 0.25); }

.bv-callout--green .bv-callout__icon,
.bv-callout--green .bv-callout__text { color: #86efac; }
.bv-callout--red .bv-callout__icon,
.bv-callout--red .bv-callout__text { color: #fca5a5; }
.bv-callout--amber .bv-callout__icon,
.bv-callout--amber .bv-callout__text { color: #fcd34d; }

.bv-callout__text p { margin: 0; }
.bv-callout__text p + p { margin-top: 6px; }

.bv-callout__link {
  display: inline-block;
  margin-top: 8px;
  word-break: break-all;
  color: #93c5fd;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.bv-ledger-callout { margin-top: 16px; }
.bv-ledger-source { margin-top: 14px; }
.bv-integrity-issues { display: flex; flex-direction: column; gap: 12px; }

/* Small print that was 0.30-0.45 white, under 4.5:1 on these cards. */
.bv-status-source,
.bv-status-row__desc,
.bv-live__stamp,
.bv-field-label,
.bv-dropzone__hint { color: rgba(255, 255, 255, 0.62); }
.bv-chip { color: rgba(255, 255, 255, 0.72); }
.bv-status-source code { color: #FFB400; }
.bv-dropzone__link { font-weight: 700; }

.bv-status-row__badge {
  min-width: 88px;
  text-align: center;
}

.bv-user-table { overflow: hidden; }

.bv-user-cell {
  display: flex;
  align-items: center;
  gap: 14px;
}

.bv-user-cell__name {
  display: block;
  font-size: 14px;
  font-weight: 700;
  color: #ffffff;
}

.bv-user-cell__email {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  word-break: break-all;
  color: rgba(255, 255, 255, 0.6);
}

.bv-health-feed { overflow: hidden; }

.bv-health-feed__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  padding: 18px 20px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.bv-health-feed__bar .bv-field-label { margin-bottom: 8px; }
.bv-health-feed__bar .bv-seg { display: inline-flex; }

.bv-health-event {
  display: block;
  margin-bottom: 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #ffffff;
}

.bv-health-feed__pager {
  padding: 14px 20px 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

/* Four counts in one row. At a 230px minimum a laptop-width content column
   fits three, so the fourth dropped onto a row of its own (SOC, and the
   Transparency Portal). */
.bv-audit-tiles--4 {
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}

/* Four fields and two buttons on one row at laptop width. At a 200px basis
   the buttons wrapped beneath the fields. */
.bv-filter-bar__field {
  flex: 1 1 150px;
  min-width: 140px;
}

/* ═══════════════════════════════════════════════════════════════════
   ORGANIZER SCREENS
   Pageant Operations, from "Pageant-Organizer-UI-na-need-e-fix.docx".
   These screens drew their own gold uppercase titles, a 25px dropdown
   beside a 42px button, and one small gold pill for every kind of button,
   so Create, Remove and Expand all looked the same. They now use the page
   header, dropdown and buttons the other roles use.
   ═══════════════════════════════════════════════════════════════════ */

/* The event dropdown in a page header: the height of the button beside
   it, 13px text, and the 42px right padding the arrow sits in. It was the
   compact select -- 25px tall, 11px text and 10px of room for a 16px
   arrow, so the arrow sat on the title. */
select.bv-input.bv-event-switch {
  width: auto !important;
  min-width: 240px;
  max-width: 340px;
  height: 44px;
  padding: 0 42px 0 14px !important;
  font-size: 13px !important;
}

/* Header buttons at the dropdown's 44px. */
.bv-tab-headtools > .bv-btn {
  flex: 0 0 auto;
  width: auto !important;
  height: 44px;
  min-height: 44px;
  padding: 0 20px !important;
  margin-bottom: 0 !important;
  white-space: nowrap;
}

/* Registry header: Import Slate and Add at one height. The outlined one
   measured 46px beside the filled 44px one. */
.bv-elist-head .bv-form-row > .bv-btn { height: 44px; padding-top: 0 !important; padding-bottom: 0 !important; }

/* Page dropdowns at 44px, level with the buttons beside them (41px). */
.bv-croster-filter__row > select.bv-input,
.bv-results-toolbar select.bv-input {
  height: 44px;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}

/* Results toolbar buttons. */
.bv-results-toolbar__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }

.bv-results-toolbar__actions > .bv-btn {
  width: auto !important;
  height: 44px;
  min-height: 44px;
  padding: 0 20px !important;
  margin-bottom: 0 !important;
  white-space: nowrap;
}

/* The live board sat flush on the note under it. */
.bv-dash-live + .bv-status-source { margin-top: 14px; }

/* Hints under a card title. 11.5px at 0.38 white measured under 3:1. */
.bv-builder__hint { font-size: 13px; color: rgba(255, 255, 255, 0.62); }

/* A card title beside its buttons. */
.bv-builder__head .bv-section-title { margin: 0; }

/* A disabled row button, e.g. Remove on a judge who has scored. Its title
   attribute says why. */
.bv-btn--row:disabled { opacity: 0.45; cursor: not-allowed; }

/* ── Criteria: the three levels ── */
.bv-guide {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
  margin: 16px 0 0;
  padding: 0;
  list-style: none;
}

.bv-guide__step {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 16px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.07);
}

.bv-guide__num {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1px solid;
  font-size: 13px;
  font-weight: 800;
}

.bv-guide__title { margin: 0 0 4px; font-size: 14px; font-weight: 800; color: #ffffff; }
.bv-guide__text { margin: 0; font-size: 13px; line-height: 1.6; color: rgba(255, 255, 255, 0.68); }

/* The level tag on every row of the tree, in the guide's colours. */
.bv-node__level {
  flex: 0 0 auto;
  min-width: 74px;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid;
  text-align: center;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.bv-node__level--stage { color: #FFB400; background: rgba(255, 180, 0, 0.1); border-color: rgba(255, 180, 0, 0.35); }
.bv-node__level--segment { color: #93c5fd; background: rgba(59, 130, 246, 0.12); border-color: rgba(100, 150, 255, 0.35); }
.bv-node__level--criterion { color: #4ade80; background: rgba(34, 197, 94, 0.1); border-color: rgba(34, 197, 94, 0.3); }
.bv-node__level--item { color: rgba(255, 255, 255, 0.65); background: rgba(255, 255, 255, 0.05); border-color: rgba(255, 255, 255, 0.14); }

/* "+ Add Segment" / "+ Add Criterion": the icon button with its words. */
.bv-node__btn.bv-node__btn--labeled {
  width: auto;
  gap: 6px;
  padding: 0 10px;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  color: #FFB400;
  background: rgba(255, 180, 0, 0.08);
  border-color: rgba(255, 180, 0, 0.3);
}

/* Keep-the-top number beside the 32px cut button. */
.bv-roster__advance .bv-input { height: 32px; padding: 0 10px !important; font-size: 13px !important; }

/* ── Board of Judges ── */
/* The card is the frame; the dashed box inside it was a second one. */
.bv-card__body > .bv-panel-new { margin-top: 16px; padding: 0; border: 0; background: none; }
.bv-panel-new__field { display: flex; flex-direction: column; gap: 6px; }
.bv-panel-new__field .bv-label { margin: 0; }

/* Full-size fields. They were 33px with 12px text. */
.bv-panel-new__fields .bv-input { height: 44px; padding: 0 16px !important; font-size: 14px !important; }
.bv-panel-new__fields select.bv-input { padding: 0 42px 0 16px !important; }

.bv-panel-new__fields > .bv-btn {
  align-self: flex-start;
  width: auto !important;
  height: 44px;
  padding: 0 22px !important;
  margin: 4px 0 0 !important;
}

/* An email is not a label; uppercasing it changed how it reads. */
.bv-panel-row__role { font-size: 12px; letter-spacing: 0; text-transform: none; color: rgba(255, 255, 255, 0.62); }
.bv-panel-row__ops { flex-wrap: wrap; }

.bv-panel-add { display: block; margin-top: 18px; padding-top: 16px; border-top: 1px solid rgba(255, 255, 255, 0.06); }
.bv-panel-add__row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 6px; }
.bv-panel-add__row > select.bv-input { flex: 1 1 240px; max-width: 360px; height: 44px; padding-top: 0 !important; padding-bottom: 0 !important; }
.bv-panel-add__row > .bv-btn { flex: 0 0 auto; width: auto !important; height: 44px; padding: 0 20px !important; margin: 0 !important; }

/* The resting line in the credentials pane (0.35 white, under 3:1). */
.bv-panel-new__idle { color: rgba(255, 255, 255, 0.62); }

/* ── Live Tabulation: section titles ──
   The shared section title, not the 11px grey uppercase label. */
.bv-section-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 14px; }
.bv-section-head--stack { flex-direction: column; align-items: flex-start; gap: 4px; }
.bv-section-head .bv-builder__hint { margin: 0; }

/* Verify Ballots, the one page-level button under a section title, at
   the 44px of the header buttons (41px). */
.bv-section-head ~ .bv-card .bv-btn--auto { height: 44px; padding-top: 0 !important; padding-bottom: 0 !important; }

@media print {
  .bv-section-title { color: #000 !important; font-size: 12pt !important; }
  .bv-builder__hint { color: #333 !important; }
}

/* ── PLAIN PAPER ──
   Printouts are plain black on white: no boxes around sections, no grey
   header band, no pills or icons. Kept last so it wins ties with the
   earlier print blocks. The html[data-theme] forms are needed because
   beevote-light.css themes .bv-table th/td at that weight, which out-ranks
   a bare .bv-table rule and printed the column names faint grey. */
@media print {
  /* Declaring the margin boxes, even empty, stops the browser printing
     its own header and footer - the date, page title, URL and page
     count. Checked in Edge: 167 text runs on a one-line page without
     this, one with it. */
  @page {
    @top-left { content: ""; }
    @bottom-left { content: ""; }
  }

  * {
    box-shadow: none !important;
    text-shadow: none !important;
  }

  /* The catch-all ink. body is black, but any element with its own
     colour keeps it, and paper always gets the DARK theme (the light
     sheet is media=screen), so every white or gold class this block
     did not name printed invisibly - the results title, the awards,
     the ledger-failure warning in pale pink. Lowest specificity on
     purpose: the deliberate #333/#555 greys above still win. Links get a
     second, stronger form because link classes (.bv-tx-link) colour
     themselves !important. */
  body *,
  body a[href] {
    color: #000 !important;
  }

  /* The layout's decorative background glows. */
  .blob {
    display: none !important;
  }

  .bv-card,
  .glass-card,
  .glass-panel,
  .bv-card--lg {
    border: 0 !important;
  }

  .bv-card__body {
    padding: 0 !important;
  }

  /* Below 1024px wide, which paper is, the table wrapper gets a tinted
     rounded box and the table a 600px minimum. */
  .bv-table-container {
    overflow: visible !important;
    background: none !important;
    border-radius: 0 !important;
    margin: 0 !important;
  }

  .bv-table {
    min-width: 0 !important;
    border-radius: 0 !important;
  }

  .bv-table th,
  .bv-table td,
  html[data-theme] .bv-table th,
  html[data-theme] .bv-table td {
    color: #000 !important;
    background: none !important;
    border: 1px solid #999 !important;
  }

  .bv-badge {
    border: 0 !important;
    padding: 0 !important;
  }

  .bv-print-doc i.fa-solid,
  .bv-print-doc i.fa-regular,
  .bv-print-doc i.fa-brands {
    display: none !important;
  }

  /* Tabulation: the "Decided on" round list printed gold, and the
     provisional banner as a pink box with red text. */
  .bv-scope__group,
  .bv-scope__item,
  .bv-scope__item *,
  .bv-builder__summary,
  .bv-builder__summary * {
    color: #000 !important;
    background: none !important;
    border: 0 !important;
  }

  .bv-builder__summary {
    padding-left: 0 !important;
    padding-right: 0 !important;
  }

  /* Election results: each candidate is a photo tile with a dark scrim
     and a gold count. On paper it becomes one plain line - name, party,
     votes, share - and the election picker above it is dropped. */
  .bv-results-toolbar,
  .bv-tally__photo,
  .bv-tally__bar {
    display: none !important;
  }

  .bv-results-heading,
  .bv-results-heading *,
  .bv-results-college,
  .bv-results-college * {
    color: #000 !important;
    background: none !important;
  }

  .bv-results-heading {
    border-bottom: 1px solid #000 !important;
    margin-bottom: 0 !important;
  }

  .bv-results-section {
    margin-bottom: 12pt !important;
    break-inside: avoid;
  }

  .bv-results-grid {
    display: block !important;
    max-width: none !important;
  }

  .bv-tally,
  .bv-tally.bv-card--leader {
    display: flex !important;
    flex-direction: row-reverse;
    align-items: baseline;
    gap: 12pt;
    aspect-ratio: auto !important;
    max-width: none !important;
    overflow: visible !important;
    margin: 0 !important;
    border-bottom: 1px solid #999 !important;
    break-inside: avoid;
  }

  .bv-tally__flag {
    position: static !important;
    flex: 0 0 auto;
    font-size: 7.5pt !important;
  }

  .bv-tally__body {
    flex: 1 1 auto;
    display: flex !important;
    align-items: baseline;
    gap: 12pt;
    padding: 4pt 0 !important;
    background: none !important;
  }

  .bv-tally__name {
    flex: 1 1 auto;
    white-space: normal !important;
    font-size: 10pt !important;
  }

  .bv-tally__figures {
    margin: 0 !important;
    gap: 12pt !important;
    align-items: baseline !important;
  }

  .bv-tally__name,
  .bv-tally__party,
  .bv-tally__count,
  .bv-tally__label,
  .bv-tally__share {
    color: #000 !important;
  }

  .bv-tally__count {
    display: inline !important;
    font-size: 10pt !important;
  }

  .bv-tally__label {
    display: inline !important;
    margin: 0 0 0 3pt !important;
  }
}
