/* ── Base ───────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

body {
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: calc(1rem * var(--font-scale));
  min-height: 100dvh;
  margin: 0;
  transition: var(--transition-theme);
  -webkit-tap-highlight-color: transparent;
}

#app {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/* ── Splash ─────────────────────────────────────────────────── */
/* Same face as the login hero — Sky Blue 40% in light, Midnight in dark —
   so the Sky Blue helix always has contrast (brand guide p.12) and the
   splash hands over to login without a colour jump. */
.splash-screen {
  background-color: var(--color-bg-login);
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 1rem;
}

.splash-logo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
}

/* The logo is wider than it is tall, so width leads and height follows —
   forcing a box would squash it. */
.splash-logo img {
  width: min(260px, 70vw);
  height: auto;
}

.splash-tagline {
  font-family: var(--font-hero);
  font-size: 1.05rem;
  color: var(--color-text);
  margin: 0;
  text-align: center;
}

/* ── Logo ───────────────────────────────────────────────────── */
/* envirodnaLogo() emits both colour variations; show the one that suits
   the theme (Blue & Black on light, Blue & White on dark). */
.brand-logo { display: inline-block; max-width: 100%; height: auto; }
.brand-logo--dark { display: none; }
[data-theme="dark"] .brand-logo--light { display: none; }
[data-theme="dark"] .brand-logo--dark { display: inline-block; }

/* Hero messages — Merriweather, per the brand guide's typography page. */
.hero-line {
  font-family: var(--font-hero);
  font-size: 1.05rem;
  line-height: 1.5;
  color: var(--color-text);
}
.hero-line--sm { font-size: 0.9rem; }

.brand-name {
  font-size: 2rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  color: var(--color-white);
}

/* ── Login ──────────────────────────────────────────────────── */
.login-page {
  background-color: var(--color-bg-login);
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  transition: var(--transition-theme);
}

.login-hero {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 2.5rem 1.5rem 1.5rem;
  text-align: center;
}

.login-hero .brand-name {
  font-size: 1.8rem;
  color: var(--color-primary);
  margin: 0;
}

.login-tagline {
  font-family: var(--font-hero);
  font-size: 1.05rem;
  font-weight: 400;
  line-height: 1.5;
  max-width: 300px;
  color: var(--color-text);
  margin: 0.5rem 0 0;
}

.login-tagline .highlight {
  color: var(--color-accent);
  font-weight: 700;
}

[data-theme="dark"] .login-tagline .highlight {
  color: var(--color-accent-alt);
}

.login-card {
  background: var(--color-surface);
  border-radius: 1.5rem 1.5rem 0 0;
  padding: 2rem 1.5rem 2.5rem;
  box-shadow: 0 -4px 24px rgba(0,0,0,0.08);
  transition: var(--transition-theme);
}

@media (min-width: 640px) {
  .login-page {
    align-self: center;
    width: 100%;
    max-width: 460px;
    margin: 2.5rem auto;
    min-height: auto;
    border-radius: 2rem;
    overflow: hidden;
    box-shadow: 0 8px 48px rgba(var(--color-dark-rgb), 0.15);
  }

  .login-hero {
    flex: none;
    padding: 3rem 2rem 2rem;
  }

  .login-card {
    border-radius: 0;
    padding: 2rem 2rem 2.5rem;
    box-shadow: none;
  }
}

/* ── Page Shell ─────────────────────────────────────────────── */
.page-shell {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  background: var(--color-bg);
  transition: var(--transition-theme);
}

.page-header {
  background: var(--color-surface);
  padding: 0.6rem 0.75rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--color-surface-alt);
  position: sticky;
  top: 0;
  z-index: 10;
  transition: var(--transition-theme);
}

.page-title {
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--color-text);
  margin: 0;
}

.page-content {
  flex: 1;
  overflow-y: auto;
  padding-bottom: calc(var(--action-bar-height) + 1.5rem + env(safe-area-inset-bottom, 0px));
}

@media (min-width: 768px) {
  #app {
    max-width: 75%;
    margin: 0 auto;
    min-height: 100dvh;
    box-shadow: 0 0 60px rgba(var(--color-dark-rgb), 0.1);
  }

  .page-content {
    padding-bottom: 1.5rem;
  }

  .action-bar {
    position: relative;
    bottom: auto;
    left: auto;
    right: auto;
  }
}

/* ── Samples Table ──────────────────────────────────────────── */
.samples-table {
  width: 100%;
  border-collapse: collapse;
}

.samples-table thead th {
  background: var(--color-accent);
  color: var(--color-white);
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.55rem 0.75rem;
  border: none;
}

.sample-sort-btn {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  color: inherit;
  font: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  cursor: pointer;
  white-space: nowrap;
}

.sample-sort-btn:hover,
.sample-sort-btn:focus-visible {
  text-decoration: underline;
}

/* The batch sort sits on the icon column (no room for a "Batch" column at
   phone width): a box icon on the header, ringed when active. The header row
   is Indigo, so the icon is white there regardless of _batchIcon()'s colour. */
.sample-sort-btn--icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
}
.sample-sort-btn--icon svg { color: currentColor !important; }
.sample-sort-btn--icon.active { background: rgba(255, 255, 255, 0.25); }

/* ── Batches ────────────────────────────────────────────────── */
.status-pill {
  display: inline-block;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 0.1rem 0.5rem;
  border-radius: 2rem;
  white-space: nowrap;
  vertical-align: middle;
}
.status-pill--packing  { background: var(--color-surface-alt); color: var(--color-text); border: 1px solid var(--color-border-strong); }
.status-pill--sent     { background: var(--color-primary); color: var(--color-on-primary); }
.status-pill--received { background: #198754; color: #fff; }

/* "No more samples to come" — what the lab is waiting to hear. */
.final-badge {
  display: inline-block;
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  padding: 0.1rem 0.5rem;
  border-radius: 2rem;
  background: var(--color-accent);
  color: #fff;
  vertical-align: middle;
}

.samples-table tbody tr.batch-group-row td {
  background: var(--color-surface-alt);
  font-size: 0.8rem;
  font-weight: 600;
  padding-top: 0.55rem;
  padding-bottom: 0.55rem;
}
.batch-group-row[role="button"] { cursor: pointer; }
.batch-group-row[role="button"]:hover td { background: rgba(var(--color-primary-rgb), 0.12); }
.batch-group-row td > * + * { margin-left: 0.35rem; }
.batch-group-id { font-family: monospace; }
.batch-group-id svg { vertical-align: -3px; }
.batch-group-count { color: var(--color-text-muted); font-weight: 500; }
.batch-group-row--none td { color: var(--color-text-muted); }
.batch-group-chev { float: right; font-size: 1.1rem; line-height: 1; color: var(--color-text-muted); }

.batch-notice {
  background: rgba(var(--color-primary-rgb), 0.12);
  border-left: 4px solid var(--color-primary);
  border-radius: 0.5rem;
  padding: 0.6rem 0.8rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-text);
}

.batch-hero {
  background: var(--color-surface);
  border-radius: 0.75rem;
  padding: 0.9rem 1rem;
}
.batch-hero-id { font-family: monospace; font-weight: 700; font-size: 1.05rem; color: var(--color-text); word-break: break-all; }
.batch-hero-meta { font-size: 0.8rem; color: var(--color-text-muted); margin-top: 0.25rem; }

.batch-panel { border-radius: 0.75rem; padding: 0.75rem 1rem; font-size: 0.85rem; }
.batch-panel--received { background: rgba(25, 135, 84, 0.12); border-left: 4px solid #198754; color: var(--color-text); }

.shipment-summary { margin: 0; font-size: 0.85rem; }
.shipment-summary > div { display: flex; justify-content: space-between; gap: 1rem; padding: 0.3rem 0; border-bottom: 1px solid var(--color-surface-alt); }
.shipment-summary dt { font-weight: 500; color: var(--color-text-muted); }
.shipment-summary dd { margin: 0; text-align: right; color: var(--color-text); word-break: break-word; }

.more-to-come { border-radius: 0.5rem; padding: 0.55rem 0.8rem; font-size: 0.85rem; }
.more-to-come--final { background: var(--color-accent); color: #fff; }
.more-to-come--yes   { background: var(--color-surface-alt); color: var(--color-text); }
.more-to-come div    { font-weight: 400; margin-top: 0.2rem; white-space: pre-wrap; }

.batch-sample { display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0; border-bottom: 1px solid var(--color-surface-alt); }
.batch-sample:last-child { border-bottom: none; }
.batch-sample-open {
  flex: 1; min-width: 0; text-align: left; background: none; border: none; padding: 0.2rem 0;
  color: var(--color-text); display: flex; flex-direction: column; cursor: pointer;
}
.batch-sample-site { font-weight: 600; font-size: 0.9rem; }
.batch-sample-meta { font-size: 0.75rem; color: var(--color-text-muted); }

.optional { text-transform: none; font-weight: 400; letter-spacing: 0; }

/* The banner on a batched sample's form. Orange is the warning colour; the
   buttons are outlined in white so they read as part of it. */
.batch-banner {
  background: #e07b00;
  color: #fff;
  border-radius: 0.5rem;
  padding: 0.6rem 0.9rem;
  margin-bottom: 1rem;
  font-size: 0.85rem;
  font-weight: 600;
}
.batch-banner-text { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.batch-banner-actions { display: flex; gap: 0.5rem; margin-top: 0.5rem; flex-wrap: wrap; }
.batch-banner-btn {
  background: transparent;
  color: #fff;
  border: 1.5px solid rgba(255, 255, 255, 0.85);
  border-radius: 2rem;
  padding: 0.2rem 0.75rem;
  font-size: 0.8rem;
  font-weight: 600;
}
.batch-banner-btn:hover, .batch-banner-btn:focus-visible { background: rgba(255, 255, 255, 0.2); }

.samples-table tbody tr {
  border-bottom: 1px solid var(--color-surface-alt);
  cursor: pointer;
  transition: background 0.1s;
}

.samples-table tbody tr:hover,
.samples-table tbody tr:active {
  background: var(--color-surface-alt);
}

.samples-table tbody td {
  padding: 0.75rem;
  font-size: 0.85rem;
  color: var(--color-text);
  background: var(--color-surface);
  transition: var(--transition-theme);
}

.samples-table tbody tr:hover td,
.samples-table tbody tr:active td {
  background: var(--color-surface-alt);
}

/* ── Badge ──────────────────────────────────────────────────── */
.badge-count {
  background: #28a745;
  color: white;
  border-radius: 50%;
  font-size: 0.65rem;
  font-weight: 700;
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  vertical-align: middle;
}

.badge-count--no-url {
  background: #dc3545;
}

/* ── Action Bar ─────────────────────────────────────────────── */
.action-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: auto;
  min-height: var(--action-bar-height);
  background: var(--color-surface);
  border-top: 1px solid var(--color-surface-alt);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 1rem;
  padding-bottom: calc(0.75rem + env(safe-area-inset-bottom, 0px));
  z-index: 10;
  transition: var(--transition-theme);
}

.action-bar .btn { flex: 1; }

/* ── Overflow Menu ──────────────────────────────────────────── */
.overflow-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  background: var(--color-primary);
  border-radius: 0.75rem;
  padding: 0.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 150px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.25);
  z-index: 100;
}

.overflow-menu button {
  background: none;
  border: none;
  color: var(--color-on-primary);
  text-align: left;
  padding: 0.5rem 0.75rem;
  border-radius: 0.5rem;
  font-size: 0.875rem;
  cursor: pointer;
  transition: background 0.1s;
}

.overflow-menu button:hover,
.overflow-menu button:active {
  background: rgba(255,255,255,0.3);
}

/* ── Entry Form ─────────────────────────────────────────────── */
.form-label {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 0.3rem;
}

.form-control,
.form-select {
  background: var(--color-surface);
  border-color: var(--color-surface-alt);
  color: var(--color-text);
  transition: var(--transition-theme);
}

.form-control::placeholder { color: var(--color-text-muted); }

.form-control:focus,
.form-select:focus {
  background: var(--color-surface);
  border-color: var(--color-primary);
  color: var(--color-text);
  box-shadow: 0 0 0 0.2rem rgba(var(--color-primary-rgb), 0.25);
}

[data-theme="dark"] .form-control,
[data-theme="dark"] .form-select {
  border-color: var(--color-border-strong);
}

/* Bootstrap compiles its own blue into these; pin them to the brand. */
.form-check-input:checked {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
}
.form-check-input:focus {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 0.2rem rgba(var(--color-primary-rgb), 0.25);
}
::selection { background: rgba(var(--color-primary-rgb), 0.3); }

.input-scan-group { position: relative; }

.input-scan-group .form-control { padding-right: 2.5rem; }

.scan-icon {
  position: absolute;
  right: 0.75rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--color-text-muted);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
}

/* ── Nav Menu Overlay ───────────────────────────────────────── */
/* The menu is a drawer, not a full-screen takeover. .nav-menu-overlay is now
   the backdrop (full viewport, dims the app, closes on tap) and
   .nav-menu-panel is the sheet itself, capped at half the app's width so it
   never swallows the screen on a desktop monitor. */
.nav-menu-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(0, 0, 0, 0.45);
  opacity: 0;
  /* visibility (not display) so the panel can still transition, while keeping
     the closed menu out of the tab order and away from screen readers. */
  visibility: hidden;
  transition: opacity 0.28s cubic-bezier(0.4, 0, 0.2, 1),
              visibility 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}

.nav-menu-overlay.open {
  opacity: 1;
  visibility: visible;
}

.nav-menu-panel {
  position: absolute;
  /* top / right / bottom / left — pinned to the left edge, full height. */
  inset: 0 auto 0 0;
  /* Never wider than half the app column (the overlay below is sized to it,
     and this is positioned against that). The rem term is what actually
     applies on a desktop, where half a column would still be enormous. */
  width: min(20rem, 50%);
  background: var(--color-menu-bg);
  display: flex;
  flex-direction: column;
  /* Left padding deliberately matches .page-header's (0.75rem) rather than
     the 1.25rem used on the other sides: the drawer's close button is the
     same .icon-btn as the hamburger it replaces, so this lands its glyph on
     exactly the same left edge and the header doesn't shift as it opens. */
  padding: 1.25rem 1.25rem 1.25rem 0.75rem;
  overflow-y: auto;
  /* No slide-in transform: the panel simply appears with the backdrop, which
     fades via .nav-menu-overlay's opacity above. A horizontal slide read as
     off against the app column it now sits inside. */
}

/* On wide screens #app is a centred max-width: 75% column (see the
   min-width: 768px block above), so an overlay pinned to the viewport would
   slide the drawer in from the empty margin beside the app rather than from
   the app's own left edge, and would dim page background that isn't the app.
   Matching #app's 12.5% side margins keeps the drawer inside the column; the
   panel follows automatically, being positioned against this element. */
@media (min-width: 768px) {
  .nav-menu-overlay {
    left: 12.5%;
    right: 12.5%;
  }
}

.nav-menu-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 2.5rem;
}

.nav-menu-items {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.nav-menu-item {
  background: none;
  border: none;
  color: var(--color-menu-fg);
  /* Down from 1.9rem: that was sized for a full-screen menu and overflows a
     half-width drawer, especially once an icon shares the row. */
  font-size: 1.15rem;
  font-weight: 700;
  text-align: left;
  /* 0.35rem of left padding = .icon-btn's own padding. The items are plain
     buttons with no such inset, so without this their glyphs would sit ~6px
     left of the hamburger/close glyphs above them.
     The extra 0.5rem each side (cancelled by the negative margin below) gives
     the hover fill room without shifting the glyphs off that alignment. */
  padding: 0.55rem 0.5rem 0.55rem 0.85rem;
  margin: 0 -0.5rem;
  border-radius: 0.5rem;
  cursor: pointer;
  /* .70 not .45: against the light drawer, .45 drops the label to 2.47:1,
     under the 4.5:1 minimum. This keeps the active/inactive distinction while
     staying readable. */
  opacity: 0.70;
  transition: opacity 0.15s, background 0.15s;
  line-height: 1.3;

  display: flex;
  align-items: center;
  gap: 0.85rem;
  width: 100%;
}

/* Keep the glyph from being squeezed when a long label wraps. */
.nav-menu-item svg {
  flex: 0 0 auto;
}

.nav-menu-item.active,
.nav-menu-item:hover,
.nav-menu-item:focus-visible {
  opacity: 1;
}

/* Semi-transparent white so the highlight sits *on* the drawer colour rather
   than replacing it — it stays right if --color-menu-bg changes. .35 lifts
   the row clearly (1.19:1) while the label gets brighter, not dimmer
   (#00253D reaches 11.72:1 on the hover fill). */
.nav-menu-item:hover,
.nav-menu-item:focus-visible {
  background: rgba(255, 255, 255, 0.35);
}

.nav-menu-item:active {
  background: rgba(255, 255, 255, 0.55);
}

.nav-menu-footer { padding-top: 1.5rem; }

/* ── Theme Toggle Pill ──────────────────────────────────────── */
.theme-toggle {
  background: rgba(0,0,0,0.10);
  border: none;
  border-radius: 2rem;
  display: flex;
  align-items: center;
  padding: 3px;
  gap: 2px;
}

.theme-toggle-btn {
  background: none;
  border: none;
  border-radius: 2rem;
  padding: 0.2rem 0.65rem;
  font-size: 0.85rem;
  color: rgba(var(--color-dark-rgb), 0.60);
  cursor: pointer;
  transition: background 0.2s, color 0.2s;
  line-height: 1.6;
}

.theme-toggle-btn.active {
  background: white;
  color: var(--color-menu-fg);
}

/* ── Bottom Sheet / Modal ───────────────────────────────────── */
.bottom-sheet-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 1100;
  display: flex;
  align-items: flex-end;
}

.bottom-sheet-backdrop.d-none { display: none !important; }

.bottom-sheet {
  background: var(--color-surface);
  border-radius: 1.5rem 1.5rem 0 0;
  padding: 2rem 1.5rem 2.5rem;
  width: 100%;
  /* A tall sheet (the preset form) scrolls rather than running off a short
     phone screen with the keyboard up. */
  max-height: 90dvh;
  overflow-y: auto;
  transition: var(--transition-theme);
}

/* Pull to sync on the samples list (app-samples.js _bindPullToSync): a strip
   under the header that grows with the pull. */
.pull-to-sync {
  height: 0;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  color: var(--color-text-muted);
  background: var(--color-surface-alt);
}
.pull-to-sync.ready { color: var(--color-primary); font-weight: 600; }

/* ── Buttons ────────────────────────────────────────────────── */
/* Set through Bootstrap's own button variables so every state — hover,
   focus, pressed, disabled — is brand-coloured, not just the resting one.
   Midnight text, not white: white on Sky Blue is 3.0:1, Midnight is 5.3:1.
   For the same reason hover/pressed get *lighter* (Sky tints), not darker. */
.btn-primary {
  --bs-btn-color: var(--color-on-primary);
  --bs-btn-bg: var(--color-primary);
  --bs-btn-border-color: var(--color-primary);
  --bs-btn-hover-color: var(--color-on-primary);
  --bs-btn-hover-bg: var(--color-primary-hover);
  --bs-btn-hover-border-color: var(--color-primary-hover);
  --bs-btn-focus-shadow-rgb: var(--color-primary-rgb);
  --bs-btn-active-color: var(--color-on-primary);
  --bs-btn-active-bg: var(--color-primary-active);
  --bs-btn-active-border-color: var(--color-primary-active);
  --bs-btn-disabled-color: var(--color-on-primary);
  --bs-btn-disabled-bg: var(--color-primary);
  --bs-btn-disabled-border-color: var(--color-primary);
}

.btn-outline-primary {
  --bs-btn-color: var(--color-primary);
  --bs-btn-border-color: var(--color-primary);
  --bs-btn-hover-color: var(--color-on-primary);
  --bs-btn-hover-bg: var(--color-primary);
  --bs-btn-hover-border-color: var(--color-primary);
  --bs-btn-focus-shadow-rgb: var(--color-primary-rgb);
  --bs-btn-active-color: var(--color-on-primary);
  --bs-btn-active-bg: var(--color-primary-active);
  --bs-btn-active-border-color: var(--color-primary-active);
  --bs-btn-disabled-color: var(--color-primary);
  --bs-btn-disabled-border-color: var(--color-primary);
}

[data-theme="dark"] .btn-outline-secondary {
  color: var(--color-text);
  border-color: var(--color-border-strong);
}

[data-theme="dark"] .btn-outline-secondary:hover {
  background: var(--color-surface-alt);
  color: var(--color-text);
}

/* ── Icon Buttons ───────────────────────────────────────────── */
.icon-btn {
  background: none;
  border: none;
  color: var(--color-text);
  padding: 0.35rem;
  border-radius: 0.4rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s;
}

/* A header action that commits, e.g. the ✓ (Save) on New Shipment. */
.icon-btn--primary { color: var(--color-primary); }

.icon-btn:hover { background: var(--color-surface-alt); }

/* ── QR Scanner Overlay ─────────────────────────────────────── */
#qr-overlay {
  position: fixed;
  inset: 0;
  z-index: 3000;
}

.qr-overlay-backdrop {
  display: flex;
  flex-direction: column;
  align-items: center;
  background: #000;
  min-height: 100dvh;
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

.qr-overlay-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 0.75rem 1rem;
  padding-top: calc(0.75rem + env(safe-area-inset-top, 0px));
  background: rgba(0,0,0,0.6);
}

.qr-overlay-title {
  color: white;
  font-weight: 600;
  font-size: 1rem;
}

.qr-viewfinder-wrap {
  position: relative;
  width: 100%;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

#qr-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.qr-viewfinder-frame {
  position: absolute;
  width: min(70vw, 280px);
  height: min(70vw, 280px);
  border: 3px solid rgba(255,255,255,0.9);
  border-radius: 1rem;
  box-shadow: 0 0 0 9999px rgba(0,0,0,0.45);
  pointer-events: none;
}

/* Animated corner accents */
.qr-viewfinder-frame::before,
.qr-viewfinder-frame::after {
  content: '';
  position: absolute;
  width: 24px;
  height: 24px;
  border-color: var(--color-primary);
  border-style: solid;
}
.qr-viewfinder-frame::before {
  top: -3px; left: -3px;
  border-width: 4px 0 0 4px;
  border-radius: 1rem 0 0 0;
}
.qr-viewfinder-frame::after {
  bottom: -3px; right: -3px;
  border-width: 0 4px 4px 0;
  border-radius: 0 0 1rem 0;
}

.qr-hint {
  color: rgba(255,255,255,0.7);
  font-size: 0.85rem;
  text-align: center;
  margin: 1rem 1rem 0.25rem;
}

.qr-status {
  font-size: 0.9rem;
  font-weight: 600;
  text-align: center;
  min-height: 1.5rem;
  padding: 0 1rem 1rem;
  color: rgba(255,255,255,0.5);
}

/* ── Toast notifications ────────────────────────────────────── */
#toast-container {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + 0.75rem);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  z-index: 9999;
  pointer-events: none;
  width: calc(100% - 2rem);
  max-width: 420px;
}

.edna-toast {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 1rem;
  border-radius: 0.65rem;
  font-size: 0.9rem;
  font-weight: 500;
  color: white;
  box-shadow: 0 4px 20px rgba(0,0,0,0.25);
  pointer-events: all;
  cursor: pointer;
  width: 100%;
  opacity: 0;
  transform: translateY(-12px);
  transition: opacity 0.22s ease, transform 0.22s ease;
}

.edna-toast--visible {
  opacity: 1;
  transform: translateY(0);
}

.edna-toast--success { background: #198754; }
.edna-toast--error   { background: #dc3545; }
.edna-toast--warning { background: #e07b00; }
.edna-toast--info    { background: var(--color-primary); color: var(--color-on-primary); }

.edna-toast-icon {
  font-size: 1rem;
  flex-shrink: 0;
  opacity: 0.9;
}

/* ── Projects Page ──────────────────────────────────────────── */
.project-item {
  display: flex;
  align-items: center;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-surface-alt);
  transition: background 0.1s;
}

.project-item:last-child { border-bottom: none; }

.project-item--active {
  background: rgba(var(--color-primary-rgb), 0.07);
}

.project-select-btn {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  background: none;
  border: none;
  padding: 0.75rem;
  text-align: left;
  cursor: pointer;
  color: var(--color-text);
}

.project-active-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 2px solid var(--color-primary);
  background: transparent;
  flex-shrink: 0;
  transition: background 0.15s;
}

.project-active-dot.visible {
  background: var(--color-primary);
}

.project-info {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.project-name {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--color-text);
}

.project-code-label {
  font-size: 0.72rem;
  color: var(--color-text-muted);
  font-family: monospace;
}

/* Marks a project that lives on this device only and has no API key to sync
   with — see isLocalProject() in js/store.js. */
.project-local-tag {
  font-family: inherit;
  font-style: italic;
}

.project-active-badge {
  margin-left: auto;
  font-size: 0.65rem;
  font-weight: 700;
  background: var(--color-primary);
  color: var(--color-on-primary);
  padding: 0.15rem 0.5rem;
  border-radius: 2rem;
  white-space: nowrap;
}

/* ── Config Page ────────────────────────────────────────────── */
.config-section-title {
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--color-text);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.config-section-desc {
  font-size: 0.8rem;
  color: var(--color-text-muted);
  margin-bottom: 0.75rem;
}

.config-type-list {
  border: 1px solid var(--color-surface-alt);
  border-radius: 0.6rem;
  overflow: hidden;
}

.config-type-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.6rem 0.75rem;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-surface-alt);
  transition: background 0.1s;
}

.config-type-item:last-child { border-bottom: none; }

.config-type-label {
  font-size: 0.875rem;
  color: var(--color-text);
}

.config-type-actions {
  display: flex;
  gap: 0.1rem;
}

.config-type-actions .icon-btn {
  font-size: 0.85rem;
  padding: 0.25rem 0.4rem;
  color: var(--color-text-muted);
}

.config-type-actions .icon-btn:disabled {
  opacity: 0.25;
  cursor: default;
}

/* ── Help Page ──────────────────────────────────────────────── */
/* Scoped rather than folded into .config-section-title, which app-config.js
   also uses for headings that have no icon. */
.help-cat-title {
  display: flex;
  align-items: center;
  gap: 0.45rem;
}

/* Stop the glyph being squashed if a long category label wraps. */
.help-cat-title svg { flex: 0 0 auto; }

.help-item-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 0.7rem 0.85rem;
  background: var(--color-surface);
  border: none;
  border-bottom: 1px solid var(--color-surface-alt);
  text-align: left;
  cursor: pointer;
  transition: background 0.1s;
}

.help-item-btn:last-child { border-bottom: none; }

.help-item-btn:active,
.help-item-btn:hover {
  background: var(--color-surface-alt);
}

.help-item-chevron {
  color: var(--color-text-muted);
  font-size: 1.1rem;
  flex-shrink: 0;
  margin-left: 0.5rem;
}

.config-type-empty {
  padding: 1rem;
  text-align: center;
  color: var(--color-text-muted);
  font-size: 0.85rem;
  background: var(--color-surface);
}

/* ── Copy mode row highlight ────────────────────────────────── */
.copy-mode-row:hover td,
.copy-mode-row:active td {
  background: rgba(var(--color-primary-rgb), 0.12) !important;
}

/* ── Delete mode row highlight ──────────────────────────────── */
.delete-mode-row:hover td,
.delete-mode-row:active td {
  background: rgba(220, 53, 69, 0.08) !important;
}

.delete-selected td {
  background: rgba(220, 53, 69, 0.14) !important;
}

.delete-selected:hover td,
.delete-selected:active td {
  background: rgba(220, 53, 69, 0.22) !important;
}

/* ── Batch and copy-to-project mode row highlight ───────────────
   Same multi-select mechanics as delete mode, but tinted with the primary
   colour rather than the danger red — nothing is being destroyed here, and
   red on a batch or a copy reads as a warning the action does not deserve. */
.pick-mode-row:hover td,
.pick-mode-row:active td {
  background: rgba(var(--color-primary-rgb), 0.08) !important;
}

.pick-selected td {
  background: rgba(var(--color-primary-rgb), 0.14) !important;
}

.pick-selected:hover td,
.pick-selected:active td {
  background: rgba(var(--color-primary-rgb), 0.22) !important;
}

/* ── About ──────────────────────────────────────────────────────
   Small floating entry point to the About page from the login screen —
   pinned to the viewport so it survives the form scrolling. */
.about-fab {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  border: 1px solid var(--color-surface-alt);
  background: var(--color-surface);
  color: var(--color-text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
  cursor: pointer;
  z-index: 10;
}

.about-fab:hover,
.about-fab:focus-visible {
  color: var(--color-primary);
  border-color: var(--color-primary);
}

.about-label {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: 0.15rem;
}

.about-value {
  font-size: 0.95rem;
  color: var(--color-text);
  text-decoration: none;
  word-break: break-word;
}

a.about-value:hover {
  color: var(--color-primary);
  text-decoration: underline;
}

/* Links that leave the app are marked as links up front, not only on hover. */
.about-link {
  color: var(--color-primary);
  text-decoration: underline;
}

/* ── Utilities ──────────────────────────────────────────────── */
.pos-relative { position: relative; }
.text-accent   { color: var(--color-accent) !important; }
.text-accent-alt { color: var(--color-accent-alt) !important; }

/* ── Batches list (☰ → Batches) ─────────────────────────────── */
.batch-filters { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.batch-filter {
  border: 1px solid var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-text);
  border-radius: 2rem;
  padding: 0.25rem 0.75rem;
  font-size: 0.8rem;
  font-weight: 600;
}
.batch-filter.active { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-on-primary); }
.batch-filter-count { font-weight: 500; opacity: 0.75; margin-left: 0.15rem; }

.batch-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  width: 100%;
  text-align: left;
  background: var(--color-surface);
  border: none;
  border-radius: 0.75rem;
  padding: 0.8rem 2.2rem 0.8rem 1rem;
  margin-bottom: 0.6rem;
  color: var(--color-text);
  box-shadow: 0 1px 3px rgba(var(--color-dark-rgb), 0.08);
}
.batch-card:hover, .batch-card:focus-visible { box-shadow: 0 0 0 2px rgba(var(--color-primary-rgb), 0.5); }
.batch-card-top { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.batch-card-id { font-family: monospace; font-weight: 700; font-size: 0.95rem; word-break: break-all; }
.batch-card-meta { font-size: 0.8rem; color: var(--color-text-muted); }
.batch-card-flags { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-top: 0.15rem; }
.batch-card-more { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; color: var(--color-text-muted); }
.batch-card-waiting { font-size: 0.75rem; color: var(--color-text-muted); }
.batch-card-chev { position: absolute; right: 0.9rem; top: 50%; transform: translateY(-50%); font-size: 1.3rem; color: var(--color-text-muted); }

/* Duplicate Kit ID — a warning, not an error: saving is still allowed. */
.kit-id-warning { font-size: 0.8rem; font-weight: 600; color: #a35500; margin-top: 0.3rem; }  /* 4.9:1 on the page grey */
[data-theme="dark"] .kit-id-warning { color: #ffb259; }
.form-control.is-duplicate { border-color: #e07b00; }

/* ── Presets from EnviroDNA (Settings) ──────────────────────── */
.server-presets {
  border: 1px solid var(--color-border-strong);
  border-radius: 0.75rem;
  padding: 0.75rem 0.9rem;
  background: var(--color-surface);
}
.server-presets-title {
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
}
.server-preset-tag {
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  padding: 0.1rem 0.5rem;
  border-radius: 2rem;
  background: var(--color-primary);
  color: var(--color-on-primary);
  white-space: nowrap;
}

/* Marks a required field on the entry forms. */
.required-mark { color: #dc3545; font-weight: 700; margin-left: 0.15rem; }
[data-theme="dark"] .required-mark { color: #ff8a95; }

/* ── Sample counts ───────────────────────────────────────────── */
.samples-summary {
  font-size: 0.8rem;
  color: var(--color-text-muted);
  padding: 0.45rem 0.9rem;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-surface-alt);
}
.samples-summary strong { color: var(--color-text); font-weight: 600; }

.shipment-tally {
  background: var(--color-surface-alt);
  border-radius: 0.5rem;
  padding: 0.5rem 0.8rem;
  font-size: 0.82rem;
}
.shipment-tally dl > div { display: flex; justify-content: space-between; gap: 1rem; padding: 0.15rem 0; }
.shipment-tally dt { font-weight: 500; color: var(--color-text-muted); }
.shipment-tally dd { margin: 0; font-weight: 600; color: var(--color-text); text-align: right; }
