/* ============================================================================
   FAS Helper — styles.css
   ----------------------------------------------------------------------------
   No build step, no preprocessor. Read top-down:
     1. :root design tokens — spacing, radius, color, shadow, z-index ladder.
     2. Resets + global rules ([hidden], .sr-only, prefers-reduced-motion,
        @supports fallback for backdrop-filter).
     3. Section blocks in roughly the same order as the page flow:
        auth → folder → summary → grid → modal → tag picker → upload → toasts.
     4. A "Polish pass" block at the end with rules that override earlier
        sections without restructuring the file above.

   KEY DESIGN DECISIONS
     - Mobile-first is NOT used. The desktop layout is the base; @media blocks
       at the bottom shrink things for tablet/mobile. We're targeting iPad.
     - Sticky offsets in .filterbar (top: 56px / 96px) assume the .topbar's
       rendered height. If you change topbar padding, update those offsets.
     - All elevation/depth comes from --sh-1/--sh-2/--sh-3. Don't hand-author
       box-shadow values; reach for the tokens.
     - .modal-open on <body> hides the toast stack so toasts don't collide
       with the photo control bar. JS toggles it (avoids :has() compat issues).
   ============================================================================ */

/* =================== design tokens — refreshed 2026-06-21 =================== */
:root {
  /* spacing — 4 / 8 / 12 / 16 / 24 / 32 ladder */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;

  /* radius scale — softened (modern apps land 6 / 10 / 14) */
  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 14px;
  --r-pill: 999px;

  /* color palette — neutral cool greys, indigo primary, modern accents */
  --c-bg: #f7f8fb;
  --c-surface: #ffffff;
  --c-surface-elev: #ffffff;
  --c-text: #0f1623;
  --c-text-dim: #5a6373;
  --c-text-mute: #8b93a4;
  --c-border: #e3e6ee;
  --c-border-soft: #eef0f6;
  --c-border-strong: #c8cfdb;

  --c-primary: #4f46e5;          /* indigo-600 */
  --c-primary-hover: #4338ca;    /* indigo-700 */
  --c-primary-soft: #eef2ff;     /* indigo-50  — for tinted backgrounds */
  --c-primary-text-on-soft: #3730a3;

  --c-warn-bg: #fff7ed;          /* amber-50  */
  --c-warn-border: #f59e0b;      /* amber-500 */
  --c-warn-text: #92400e;        /* amber-800 */

  --c-success-bg: #ecfdf5;       /* emerald-50  */
  --c-success-text: #047857;     /* emerald-700 */

  --c-danger-bg: #fef2f2;        /* red-50   */
  --c-danger-text: #b91c1c;      /* red-700  */

  /* shadow scale — for depth without overdoing it */
  --sh-1: 0 1px 2px rgba(15, 22, 35, 0.04), 0 1px 1px rgba(15, 22, 35, 0.03);
  --sh-2: 0 4px 12px rgba(15, 22, 35, 0.06), 0 1px 2px rgba(15, 22, 35, 0.04);
  --sh-3: 0 12px 28px rgba(15, 22, 35, 0.10), 0 4px 8px rgba(15, 22, 35, 0.05);
  --sh-focus: 0 0 0 3px rgba(79, 70, 229, 0.25);

  /* min tap target (Apple HIG / WCAG 2.5.8) */
  --tap-min: 44px;
  --tap-gap: 8px;

  /* z-index ladder — single source of truth, increment by 10 per layer */
  --z-sticky: 20;
  --z-topbar: 30;
  --z-bulkbar: 40;
  --z-footer: 50;
  --z-modal: 100;
  --z-sheet: 110;
  --z-kbd-overlay: 130;
  --z-toast: 200;
}

/* =================== reset / base ==========================================
   Minimal reset — no Normalize, no preflight. We rely on a small set of
   global rules: [hidden] guardrail, .sr-only utility, reduced-motion media
   query, the @supports fallback above, and root typography below.
   ========================================================================== */
* { box-sizing: border-box; }

/* Global guardrail: HTML `hidden` should ALWAYS win over any later
   display rule (this regression class has bitten .page, .modal-backdrop,
   .empty-state — fix it once for the whole codebase). */
[hidden] { display: none !important; }

/* Visually-hidden but readable to screen readers. Use for accessible names
   that don't have a visual counterpart (e.g. dialog titles handled by
   in-modal chrome that we want to keep visually minimal). */
.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;
}

/* Respect users with vestibular disorders. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Frosted-glass surfaces (topbar/filterbar/footer/toast/modal-backdrop/
   ctrl-group/bulkbar) use backdrop-filter. On browsers that don't support
   it AT ALL we fall back to opaque tints — otherwise these elements would
   render as transparent rectangles with no contrast against the content
   behind them. The blanket override below lands them safely. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .topbar, .filterbar, .footer, .toast,
  .modal-backdrop, .ctrl-group, .bulkbar {
    background-color: var(--c-surface) !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
  .topbar, .filterbar { background-color: rgba(255, 255, 255, 0.96) !important; }
  .modal-backdrop { background-color: rgba(15, 22, 35, 0.6) !important; }
  .toast, .ctrl-group, .bulkbar { background-color: rgba(15, 22, 35, 0.96) !important; }
}

html, body {
  margin: 0;
  padding: 0;
  background: var(--c-bg);
  color: var(--c-text);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';   /* Inter cleaner letterforms */
  font-size: 15px;
  line-height: 1.5;
  letter-spacing: -0.005em;
  -webkit-tap-highlight-color: transparent;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3 { letter-spacing: -0.02em; }

button {
  font-family: inherit;
  font-size: inherit;
  cursor: pointer;
}

button:focus-visible,
input:focus-visible,
[role="button"]:focus-visible,
[role="tab"]:focus-visible {
  outline: none;
  box-shadow: var(--sh-focus);
  border-color: var(--c-primary);
}

/* =================== page show/hide ========================================
   Each .page is a full-viewport <section>. showPage(id) in app.js sets the
   target page's `hidden` to false and hides every other; the global
   `[hidden]` rule does the actual display:none. The .wide variant removes
   the centered max-width so the grid / summary can fill the viewport.
   ========================================================================== */
.page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--sp-6) var(--sp-5);
}

/* Soft purple wash on auth-style pages — keeps them feeling "fresh" */
#page-login-email,
#page-login-password {
  background:
    radial-gradient(ellipse 80% 60% at 50% -10%, rgba(79, 70, 229, 0.08), transparent 60%),
    var(--c-bg);
}

/* Very subtle wash on the folder pick + summary so empty space doesn't feel flat */
#page-folder,
#page-summary {
  background:
    radial-gradient(ellipse 70% 40% at 50% 0%, rgba(79, 70, 229, 0.04), transparent 70%),
    var(--c-bg);
}

.page.wide {
  align-items: stretch;
  padding: 0;
}

.page[hidden] { display: none; }

/* =================== auth =================== */
.auth-card {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  padding: 40px 36px;
  width: 100%;
  max-width: 420px;
  text-align: center;
  margin-top: 60px;
  box-shadow: var(--sh-3);
}

.auth-card .logo {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-weight: 800;
  color: var(--c-primary);
  letter-spacing: 1.5px;
  margin-bottom: var(--sp-5);
  font-size: 13px;
}

.auth-card h1 {
  font-size: 24px;
  font-weight: 700;
  margin: 0 0 6px;
}

.auth-card .subtitle {
  color: var(--c-text-mute);
  margin-bottom: var(--sp-5);
  font-size: 14px;
}

.auth-card input {
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  font-size: 15px;
  margin-bottom: var(--sp-3);
  min-height: 48px;
  background: var(--c-surface);
  transition: border-color 0.12s, box-shadow 0.12s;
}

.auth-card input:focus {
  border-color: var(--c-primary);
  box-shadow: var(--sh-focus);
}

.auth-card .primary {
  width: 100%;
  margin-bottom: 12px;
}

.auth-card .link {
  display: block;
  font-size: 14px;
  color: #2c5fcf;
  text-decoration: none;
  margin-top: 12px;
  padding: 6px;
}

.auth-card .auth-error {
  margin-top: 0;
  margin-bottom: var(--sp-3);
  padding: 10px 12px;
  font-size: 14px;
  text-align: left;
}

.auth-card .auth-error[hidden] { display: none; }

.auth-help {
  font-size: 13px;
  color: #888;
  margin-top: 12px;
  line-height: 1.4;
}

.auth-link {
  display: inline-block;
  font-size: 13px;
  color: #2c5fcf;
  margin-top: 12px;
  cursor: pointer;
  text-decoration: none;
  user-select: none;
}
.auth-link:hover { text-decoration: underline; }

.auth-check {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  font-size: 13px;
  color: #555;
  cursor: pointer;
  user-select: none;
}
.auth-check input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }

.auth-footer {
  margin-top: auto;
  padding: 24px;
  font-size: 12px;
  color: #888;
  text-align: center;
}

.auth-footer a { color: #2c5fcf; text-decoration: none; }

.back-link {
  color: var(--c-text);
  text-decoration: none;
  font-weight: 500;
  font-size: 14px;
  padding: 6px 10px;
  border-radius: var(--r-md);
  min-height: 36px;
  display: inline-flex;
  align-items: center;
}

.back-link:hover {
  background: var(--c-bg);
  color: var(--c-primary);
}

/* On auth pages the back arrow floats above the card. */
#page-login-password .back-link {
  align-self: flex-start;
  margin: 20px 0 0 24px;
}

/* =================== buttons =================== */
.primary {
  background: var(--c-primary);
  color: white;
  border: 1px solid transparent;
  padding: 10px 18px;
  border-radius: var(--r-md);
  font-weight: 600;
  font-size: 14px;
  min-height: 40px;
  transition: background 0.12s, transform 0.05s, box-shadow 0.12s;
  box-shadow: var(--sh-1);
}

.primary:hover  { background: var(--c-primary-hover); }
.primary:active { transform: translateY(0.5px); }
.primary:disabled {
  background: var(--c-border-strong);
  color: white;
  cursor: not-allowed;
  box-shadow: none;
}
.primary.lg { font-size: 15px; padding: 12px 22px; min-height: 48px; border-radius: var(--r-md); }

.primary.alt {
  background: var(--c-surface);
  color: var(--c-primary);
  border: 1px solid var(--c-border);
  box-shadow: var(--sh-1);
}
.primary.alt:hover {
  background: var(--c-primary-soft);
  border-color: var(--c-primary);
}

.ghost {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  padding: 7px 13px;
  font-weight: 500;
  font-size: 14px;
  min-height: 40px;
  color: var(--c-text);
  transition: background 0.12s, border-color 0.12s;
}

.ghost:hover { background: var(--c-bg); border-color: var(--c-border-strong); }
.ghost.icon { padding: 8px 12px; min-width: 44px; min-height: 44px; }

/* =================== topbar ================================================
   Sticky frosted bar at the top of every "wide" page. Holds folder/page
   title (`.brand`), header chips (untagged-count / edited-count / active
   tag-filter), and the user email on the right.
   ========================================================================== */
.topbar {
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: saturate(160%) blur(12px);
  -webkit-backdrop-filter: saturate(160%) blur(12px);
  border-bottom: 1px solid var(--c-border);
  padding: var(--sp-3) var(--sp-5);
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  position: sticky;
  top: 0;
  z-index: var(--z-topbar);
}

.topbar .brand {
  font-weight: 700;
  flex: 1;
  font-size: 15px;
  letter-spacing: -0.015em;
}

.brand-stack { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.brand-stack .brand { flex: none; }
.brand-meta { color: var(--c-text-mute); font-size: 12px; }

.topbar .user { color: var(--c-text-dim); font-size: 13px; }

.header-chips { display: flex; gap: 8px; flex-wrap: wrap; }

.chip {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  color: var(--c-text);
  padding: 5px 12px;
  border-radius: var(--r-pill);
  font-size: 12px;
  font-weight: 500;
  min-height: 32px;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.chip:hover { background: var(--c-bg); border-color: var(--c-border-strong); }
.chip.warn { background: var(--c-warn-bg); border-color: var(--c-warn-border); color: var(--c-warn-text); }
.chip.warn:hover { background: #fef0d8; }
.chip.active {
  background: var(--c-primary);
  border-color: var(--c-primary);
  color: white;
}
.chip.active:hover { background: var(--c-primary-hover); }

.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: var(--sp-6) var(--sp-5) 80px;
  width: 100%;
}

h2 {
  margin: 0 0 var(--sp-4);
  font-weight: 700;
  font-size: 22px;
  letter-spacing: -0.02em;
}
h3 { margin-top: 24px; font-size: 16px; color: #444; }
h3 .hint { font-weight: 400; font-size: 12px; color: #888; margin-left: 8px; }

/* =================== folder picker =================== */
.dropzone {
  display: block;
  width: 100%;
  background:
    radial-gradient(ellipse at center top, rgba(79, 70, 229, 0.04), transparent 70%),
    var(--c-surface);
  border: 2px dashed var(--c-border);
  border-radius: var(--r-lg);
  padding: 80px 24px;
  text-align: center;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.18s, border-color 0.18s, transform 0.1s, box-shadow 0.18s;
  box-shadow: var(--sh-1);
}

.dropzone:hover, .dropzone.drag-over {
  background:
    radial-gradient(ellipse at center top, rgba(79, 70, 229, 0.10), transparent 70%),
    var(--c-surface);
  border-color: var(--c-primary);
  transform: translateY(-2px);
  box-shadow: var(--sh-2);
}

/* Icon container — gives the dropzone a visual anchor */
.dropzone-icon {
  width: 56px;
  height: 56px;
  margin: 0 auto var(--sp-3);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--c-primary-soft);
  color: var(--c-primary);
  border-radius: var(--r-lg);
  font-size: 28px;
  transition: transform 0.18s, background 0.18s;
}

.dropzone:hover .dropzone-icon,
.dropzone.drag-over .dropzone-icon {
  transform: scale(1.06);
  background: var(--c-primary);
  color: white;
}

.dropzone-title {
  font-size: 19px;
  font-weight: 600;
  margin: 0 0 6px;
  letter-spacing: -0.01em;
  color: var(--c-text);
}

.dropzone-hint {
  color: var(--c-text-mute);
  font-size: 14px;
}

.error {
  color: #c0392b;
  background: #fdecea;
  padding: 12px;
  border-radius: 6px;
  margin-top: 16px;
}


/* =================== summary =================== */

.summary-container {
  display: flex;
  justify-content: center;
  padding-top: var(--sp-5);
}

.summary-card {
  width: 100%;
  max-width: 680px;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
  box-shadow: var(--sh-2);
}

.summary-hero {
  margin-bottom: var(--sp-4);
}
.summary-hero h2 {
  margin: 0 0 6px;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.02em;
}
.summary-hero-meta {
  color: var(--c-text-dim);
  font-size: 13px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}
.summary-hero-meta .dot { color: var(--c-text-mute); }

/* Stat tiles inside the summary card (denser variant of completion grid). */
.summary-stat-grid {
  grid-template-columns: repeat(4, 1fr);
  margin: 0 0 var(--sp-4);
}
.stat-cell.stat-warn {
  background: var(--c-warn-bg);
  border-color: var(--c-warn-border);
}
.stat-cell.stat-warn .stat-num { color: var(--c-warn-text); }
.stat-cell.stat-warn .stat-label { color: var(--c-warn-text); }

/* Collapsible details blocks ("Tag breakdown", "Capture details") */
.summary-section {
  border: 1px solid var(--c-border-soft);
  border-radius: var(--r-md);
  background: var(--c-bg);
  margin-bottom: var(--sp-3);
  overflow: hidden;
}
.summary-section > summary {
  list-style: none;
  cursor: pointer;
  padding: var(--sp-3) var(--sp-4);
  font-weight: 600;
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.summary-section > summary::-webkit-details-marker { display: none; }
.summary-section > summary::after {
  content: '▾';
  color: var(--c-text-mute);
  transition: transform 0.2s;
}
.summary-section[open] > summary::after { transform: rotate(180deg); }
.summary-section > summary .hint {
  font-weight: 400;
  font-size: 12px;
  color: var(--c-text-mute);
  margin-left: auto;
  margin-right: var(--sp-3);
}
.summary-section .tag-bars,
.summary-section table.meta {
  margin: 0;
  border-radius: 0;
  border: none;
  border-top: 1px solid var(--c-border-soft);
  background: var(--c-surface);
  padding: var(--sp-2);
  max-width: 100%;
}
.summary-section table.meta { padding: var(--sp-3) var(--sp-4); }

/* Warning row stays prominent but lives inside the card now. */
.summary-card .warning {
  background: var(--c-warn-bg);
  border-left: 3px solid var(--c-warn-border);
  color: var(--c-warn-text);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  margin: 0 0 var(--sp-3);
  font-size: 13px;
  max-width: 100%;
}

.summary-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--sp-4);
}
.summary-actions .primary { min-width: 200px; }

@media (max-width: 540px) {
  .summary-card { padding: var(--sp-4); }
  .summary-actions { justify-content: stretch; }
  .summary-actions .primary { width: 100%; min-width: 0; }
}

table.meta {
  width: 100%;
  max-width: 700px;
  border-collapse: collapse;
  margin: 16px 0;
}

table.meta td {
  padding: 10px 0;
  border-bottom: 1px solid var(--c-border-soft);
  vertical-align: top;
}

table.meta td:first-child {
  color: #666;
  width: 200px;
}

.mono {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 12px;
}

.tag-bars {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  padding: var(--sp-2);
  max-width: 700px;
  box-shadow: var(--sh-1);
}

.tag-bar {
  display: grid;
  grid-template-columns: 1fr 110px;
  gap: 12px;
  align-items: center;
  padding: 10px 12px;
  border-radius: 6px;
  cursor: pointer;
  min-height: 44px;
}

.tag-bar:hover {
  background: var(--c-primary-soft);
  transform: translateX(2px);
  transition: background 0.12s, transform 0.12s;
}
.tag-bar-name { font-size: 14px; font-weight: 500; }
.tag-bar-name.untagged { color: var(--c-warn-text); }

.tag-bar-bar {
  height: 6px;
  background: linear-gradient(90deg, var(--c-primary), #6366f1);
  border-radius: var(--r-pill);
  margin-top: 6px;
}

.tag-bar-bar.untagged {
  background: linear-gradient(90deg, var(--c-warn-border), #fbbf24);
}

.tag-bar-count {
  text-align: right;
  font-size: 12px;
  font-weight: 500;
  color: var(--c-text-dim);
}

.warning {
  background: #fff5e0;
  border-left: 4px solid #e67e22;
  padding: 12px 16px;
  margin: 16px 0;
  border-radius: 0 6px 6px 0;
  max-width: 700px;
  color: #6c440b;
}

.actions { margin-top: 24px; display: flex; gap: 12px; flex-wrap: wrap; }
.actions.right { justify-content: flex-end; }

/* =================== filter / grid =========================================
   Sticky filter chips below the topbar, then the photo card grid.
   Two magic-numbers below — `top: 56px` (desktop) and `top: 96px` (mobile
   override) — are calibrated to the topbar's rendered height. If you change
   topbar padding/typography, update these so the filterbar still snaps
   flush against it instead of overlapping or floating.
   ========================================================================== */
.filterbar {
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: saturate(160%) blur(12px);
  -webkit-backdrop-filter: saturate(160%) blur(12px);
  border-bottom: 1px solid var(--c-border);
  padding: var(--sp-3) var(--sp-5);
  display: flex;
  gap: var(--tap-gap);
  align-items: center;
  position: sticky;
  top: 56px;             /* matches topbar height — see section banner above */
  z-index: var(--z-sticky);
  flex-wrap: wrap;
}

.filterbar-spacer { flex: 1; }

.filter {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-pill);
  padding: 7px 14px;
  font-size: 13px;
  font-weight: 500;
  color: var(--c-text);
  min-height: 36px;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}

.filter:hover { background: var(--c-bg); border-color: var(--c-border-strong); }

.filter.active {
  background: var(--c-text);
  color: var(--c-surface);
  border-color: var(--c-text);
}

.filter[aria-pressed="true"] {
  background: var(--c-primary);
  color: white;
  border-color: var(--c-primary);
}

.filter .count {
  margin-left: 6px;
  font-size: 11px;
  font-weight: 600;
  background: rgba(255, 255, 255, 0.2);
  padding: 1px 7px;
  border-radius: var(--r-pill);
}

.filter:not(.active) .count {
  background: var(--c-border-soft);
  color: var(--c-text-dim);
}

.photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 18px;
}

.photo-card {
  /* reset native <button> styling */
  appearance: none;
  -webkit-appearance: none;
  font: inherit;
  text-align: left;
  color: inherit;
  margin: 0;
  padding: 0;

  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  overflow: hidden;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  position: relative;
  transition: transform 0.12s, box-shadow 0.18s, border-color 0.12s;
  user-select: none;
  -webkit-user-select: none;
  box-shadow: var(--sh-1);
  /* Avoid iOS Safari's 300ms tap delay; eliminates pointerdown→click races. */
  touch-action: manipulation;
}

.photo-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--sh-2);
  border-color: var(--c-border-strong);
}


.photo-card.excluded {
  opacity: 0.7;
  background: #fafbfc;
}

.photo-card.excluded .photo-thumb {
  filter: grayscale(0.85);
  position: relative;
}

/* Diagonal "Excluded" banner across the thumbnail makes the state
   unmissable at a glance. */
.photo-card.excluded .photo-thumb::after {
  content: 'EXCLUDED';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(192, 57, 43, 0.18);
  color: white;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 2px;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
  pointer-events: none;
}

.photo-card.selected {
  outline: 3px solid #2c5fcf;
  outline-offset: -3px;
  box-shadow: 0 6px 16px rgba(44, 95, 207, 0.18);
}

.photo-card.modified::after {
  content: '↻ Edited';
  position: absolute;
  bottom: 8px;
  right: 8px;
  background: #2c5fcf;
  color: white;
  padding: 3px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.2px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}

/* Rotation indicator on the thumbnail itself — separate from the
   "Edited" badge so engineers can see which photos have rotation applied. */
.rotation-badge {
  position: absolute;
  top: 8px;
  right: 8px;                            /* moved so it doesn't collide with the selection check */
  background: rgba(20, 20, 30, 0.78);
  color: white;
  padding: 3px 8px;
  border-radius: var(--r-pill);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.2px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.25);
  pointer-events: none;
  z-index: 2;                            /* sit above the selection check if rendering allows */
}

/* Add-to-selection affordance at the top-left of every photo card.
   The "+" glyph signals "tap to ADD this photo to a working selection" —
   it does NOT mean "include in upload" (which is the photo's separate
   excluded/included state). Selected state flips to a filled indigo "✓".
   Resting state uses solid-white-pill + indigo glyph so it stays
   discoverable on bright photo content (a dashed translucent version
   was vanishing against light backgrounds). */
.select-check {
  position: absolute;
  top: 8px;
  left: 8px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.92);
  border: 1.5px solid rgba(255, 255, 255, 0.98);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  box-shadow:
    0 1px 3px rgba(0, 0, 0, 0.18),
    0 0 0 1px rgba(15, 22, 35, 0.06);     /* hairline ring keeps it legible on white walls */
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 17px;
  font-weight: 600;
  line-height: 1;
  color: var(--c-primary);                /* "+" reads as the action color */
  cursor: pointer;
  z-index: 3;
  opacity: 0.92;
  transition: transform 0.12s, background 0.12s, color 0.12s, opacity 0.12s, box-shadow 0.12s;
}
.select-check:hover {
  transform: scale(1.12);
  background: white;
  opacity: 1;
  color: var(--c-primary-hover);
  box-shadow:
    0 2px 8px rgba(79, 70, 229, 0.28),
    0 0 0 1px rgba(79, 70, 229, 0.35);
}
.select-check.on {
  background: var(--c-primary);
  border: 2px solid white;
  color: white;
  font-size: 14px;
  font-weight: 800;
  opacity: 1;
  box-shadow: 0 2px 6px rgba(79, 70, 229, 0.4);
}

/* One-time pulse on the first photo's checkbox the FIRST time the grid
   renders this session. Draws the engineer's eye to the affordance
   without animating every card forever. */
.photo-card.hint-pulse .select-check {
  animation: select-check-pulse 1.6s ease-out 2;
}
@keyframes select-check-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(79, 70, 229, 0.55), 0 1px 3px rgba(0, 0, 0, 0.18); }
  60%  { box-shadow: 0 0 0 10px rgba(79, 70, 229, 0),  0 1px 3px rgba(0, 0, 0, 0.18); }
  100% { box-shadow: 0 0 0 0 rgba(79, 70, 229, 0),     0 1px 3px rgba(0, 0, 0, 0.18); }
}

.photo-thumb {
  aspect-ratio: 4 / 3;
  background: #eee;
  overflow: hidden;
  position: relative;
}

.photo-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.photo-thumb .skeleton {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, #eee, #f5f5f5, #eee);
  background-size: 200% 100%;
  animation: pulse 1.4s linear infinite;
}

@keyframes pulse {
  0% { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

.photo-meta {
  padding: 12px;
  font-size: 13px;
  border-top: 1px solid #eef0f4;
}

.photo-time { color: #888; margin-bottom: 6px; font-size: 12px; }

.tag-pill {
  display: inline-block;
  background: var(--c-primary-soft);
  color: var(--c-primary-text-on-soft);
  padding: 3px 10px;
  border-radius: var(--r-pill);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: -0.005em;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  border: 1px solid transparent;
}

.tag-pill.untagged { background: var(--c-warn-bg); color: var(--c-warn-text); }
.tag-pill.excluded { background: var(--c-danger-bg); color: var(--c-danger-text); }

.empty-state {
  padding: 80px 20px;
  text-align: center;
  color: var(--c-text-mute);
  font-size: 15px;
  background: var(--c-surface);
  border: 1px dashed var(--c-border);
  border-radius: var(--r-lg);
  margin-top: var(--sp-3);
}

/* selection mode + bulkbar */
.bulkbar {
  position: sticky;
  bottom: 72px; /* sit clearly above the footer (footer ≈ 60px + 8px gap) */
  background: rgba(15, 22, 35, 0.96);
  color: white;
  padding: 10px 20px;
  display: flex;
  gap: var(--sp-2);
  align-items: center;
  flex-wrap: wrap;
  z-index: var(--z-bulkbar);
  border-radius: var(--r-lg);
  margin: 0 var(--sp-3);
  box-shadow: var(--sh-3);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.bulkbar[hidden] { display: none; }

.bulkbar .ghost {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.2);
  color: white;
}
.bulkbar .ghost:hover { background: rgba(255, 255, 255, 0.2); }

/* =================== footer =================== */
.footer {
  position: sticky;
  bottom: 0;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: saturate(160%) blur(12px);
  -webkit-backdrop-filter: saturate(160%) blur(12px);
  border-top: 1px solid var(--c-border);
  padding: var(--sp-3) var(--sp-5);
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-3);
  z-index: var(--z-footer);
}

.footer-stats { font-size: 13px; color: var(--c-text-dim); }

/* =================== modal =================================================
   Photo detail dialog. The wrap (.modal-photo-wrap) holds the <img> and
   the floating .photo-controls bar; the img is transformed by JS via
   translate→scale→rotate in screen-space pixels so pan deltas line up
   1:1 with pointer movement at any zoom level (see applyViewerTransform
   in app.js).
   ========================================================================== */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 22, 35, 0.55);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: var(--z-modal);
  padding: var(--sp-4);
}

.modal-backdrop[hidden] { display: none; }
.modal-backdrop.sm .confirm-card { width: 100%; max-width: 440px; }

.modal {
  background: var(--c-surface);
  border-radius: var(--r-lg);
  width: 100%;
  max-width: 900px;
  /* Use dynamic viewport height where supported so the modal shrinks
     properly when iOS soft keyboard shows. Fallback for older browsers. */
  max-height: 92vh;
  max-height: 92dvh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  box-shadow: var(--sh-3);
}

.modal-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--c-border-soft);
  position: sticky;
  top: 0;
  background: white;
  z-index: 1;
}

.modal-header .spacer { flex: 1; }
.modal-pos { color: #666; font-size: 13px; padding: 0 8px; }

.modal-photo-wrap {
  background: #1a1a1a;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 280px;
  height: 60vh;
  overflow: hidden;
  position: relative;
  user-select: none;
  -webkit-user-select: none;
  /* Allow vertical scrolling on iOS so the modal body still scrolls past
     the photo, but reserve horizontal & pinch for our gesture handlers. */
  touch-action: pan-y;
}

.modal-photo-wrap img {
  max-width: none;
  max-height: none;
  display: block;
  pointer-events: none;
  transform-origin: center center;
  will-change: transform;
}

.modal-photo-wrap.grabbing { cursor: grabbing; }
.modal-photo-wrap.grab { cursor: grab; }

.photo-controls {
  position: absolute;
  bottom: 14px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: var(--sp-3);
  z-index: 5;
  pointer-events: auto;
}

.ctrl-group {
  display: inline-flex;
  align-items: stretch;
  gap: 0;
  background: rgba(15, 22, 35, 0.78);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--r-pill);
  padding: 4px;
  backdrop-filter: blur(12px) saturate(140%);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  box-shadow: var(--sh-2);
}

.ctrl-btn,
.ctrl-readout {
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: none;
  color: white;
  min-width: var(--tap-min);
  min-height: var(--tap-min);
  padding: 0 10px;
  border-radius: var(--r-pill);
  font-size: 13px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.12s, transform 0.05s;
}

.ctrl-btn svg { display: block; }
.ctrl-btn:hover,
.ctrl-readout:hover { background: rgba(255, 255, 255, 0.14); }
.ctrl-btn:active,
.ctrl-readout:active { transform: scale(0.96); }

/* Readout sits in the middle of each group, taps act as reset */
.ctrl-readout {
  min-width: 56px;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  position: relative;
}

/* subtle dividers between buttons within a group */
.ctrl-group .ctrl-btn + .ctrl-readout,
.ctrl-group .ctrl-readout + .ctrl-btn {
  position: relative;
}
.ctrl-group .ctrl-btn + .ctrl-readout::before,
.ctrl-group .ctrl-readout + .ctrl-btn::before {
  content: '';
  position: absolute;
  left: 0;
  top: 22%;
  bottom: 22%;
  width: 1px;
  background: rgba(255, 255, 255, 0.08);
}

.ctrl-readout:focus-visible,
.ctrl-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px rgba(165, 180, 252, 0.7);
}

@media (max-width: 700px) {
  .photo-controls { bottom: 10px; gap: var(--sp-2); }
  .ctrl-readout { min-width: 48px; }
}

.modal-meta { padding: 16px 20px 24px; }

.field { display: block; margin: 16px 0 8px; }
.field span { display: block; color: #666; margin-bottom: 6px; font-size: 13px; }

.picker-button {
  width: 100%;
  text-align: left;
  padding: 12px 14px;
  border: 1px solid var(--c-border);
  border-radius: 6px;
  background: white;
  font-size: 15px;
  min-height: 48px;
  position: relative;
}

.picker-button::after {
  content: '▾';
  position: absolute;
  right: 14px;
  color: #888;
}

.picker-button:hover { border-color: #2c5fcf; }

.field-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 16px;
  cursor: pointer;
  padding: 6px 0;
  min-height: 44px;
}

.field-row input[type="checkbox"] { width: 22px; height: 22px; }

.hint { color: #888; font-size: 12px; margin: 4px 0 0; }

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-2);
  margin-top: var(--sp-4);
  flex-wrap: wrap;
  /* Sticky to the bottom of the scrollable modal so Done/Next stay reachable
     even when meta + tag picker push the page down on iPad portrait. */
  position: sticky;
  bottom: 0;
  background: linear-gradient(180deg, transparent 0, var(--c-surface) 8px);
  padding: var(--sp-3) 0 var(--sp-1);
  margin-left: calc(-1 * var(--sp-5));
  margin-right: calc(-1 * var(--sp-5));
  padding-left: var(--sp-5);
  padding-right: var(--sp-5);
}

/* confirm dialog */
.confirm-card {
  background: white;
  border-radius: 10px;
  padding: 24px;
}
.confirm-card h3 { margin-top: 0; }

/* =================== tag picker sheet ======================================
   Bottom sheet on mobile (align-items: flex-end), centered card ≥700px
   wide (media query below). Uses dvh for max-height so the sheet shrinks
   when the iOS soft keyboard pops, keeping the close button reachable.
   ========================================================================== */
.sheet-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(20, 25, 35, 0.55);
  display: flex;
  justify-content: center;
  align-items: flex-end;
  z-index: var(--z-sheet);
}

.sheet-backdrop[hidden] { display: none; }

.sheet {
  background: var(--c-surface);
  width: 100%;
  max-width: 560px;
  /* dvh shrinks correctly when iOS soft keyboard pops; vh is the fallback */
  max-height: 80vh;
  max-height: 80dvh;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  display: flex;
  flex-direction: column;
  padding: var(--sp-4);
  box-shadow: var(--sh-3);
}

@media (min-width: 700px) {
  .sheet-backdrop { align-items: center; }
  .sheet { border-radius: var(--r-lg); }
}

.sheet-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
}
.sheet-header span { font-weight: 700; font-size: 16px; }

#tag-search {
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  font-size: 15px;
  margin-bottom: var(--sp-3);
  min-height: 48px;
  background: var(--c-bg);
  transition: background 0.12s, border-color 0.12s, box-shadow 0.12s;
}
#tag-search:focus {
  outline: none;
  background: var(--c-surface);
  border-color: var(--c-primary);
  box-shadow: var(--sh-focus);
}

.recent-tags {
  display: flex;
  gap: var(--tap-gap);
  flex-wrap: wrap;
  margin-bottom: var(--sp-2);
}

.recent-tags .tag-pill {
  cursor: pointer;
  background: #eef1f6;
  color: #1a1a1a;
}

.recent-tags .tag-pill:hover { background: #2c5fcf; color: white; }

.tag-list {
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  flex: 1;
}

.tag-list li {
  padding: 14px 12px;
  border-bottom: 1px solid var(--c-border-soft);
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  min-height: 48px;
}

.tag-list li:hover { background: var(--c-bg); }
.tag-list li.selected { background: var(--c-primary-soft); color: var(--c-primary-text-on-soft); }
.tag-list li .id { font-size: 11px; color: var(--c-text-mute); font-family: ui-monospace, 'SF Mono', Menlo, monospace; }

/* =================== upload ================================================
   Progress bar + per-photo list during the upload, then the completion
   card (.complete-card) below with success icon, stat grid, receipt
   disclosure, and "what happens next" sidebar.
   ========================================================================== */
.progress-wrap { margin: 16px 0; }
.progress-bar {
  height: 14px;
  background: #e8edf5;
  border-radius: 7px;
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background: #2c5fcf;
  width: 0;
}

.progress-text { margin-top: 8px; color: #666; font-size: 13px; }

.upload-list {
  list-style: none;
  padding: 0;
  background: white;
  border: 1px solid var(--c-border);
  border-radius: 6px;
  margin: 16px 0;
}

.upload-list li {
  padding: 12px 16px;
  border-bottom: 1px solid var(--c-border-soft);
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  align-items: center;
  gap: 12px;
}

.upload-list li:last-child { border-bottom: none; }

.upload-status.pending { color: #888; }
.upload-status.uploading { color: #2c5fcf; }
.upload-status.done { color: #27ae60; }
/* Sent to SIMS, but without the engineer's tag/rotation edits — amber, not
   red: the photo is delivered, so it must not read as "retry this". */
.upload-status.warn { color: #9e6500; }
.upload-status.failed { color: #c0392b; }

.up-line { display: flex; flex-direction: column; gap: 2px; }
.up-error {
  color: #c0392b;
  font-size: 11px;
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
}

.save-badge {
  display: inline-block;
  align-self: flex-start;
  font-size: 11px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  margin-top: 2px;
}
.save-badge.ok   { background: #e0f5e9; color: #1d6e3a; }
.save-badge.warn { background: #fff5e0; color: #9e6500; }
.save-badge.none { background: #f0f1f4; color: #777; }

/* "just-saved" pulse on grid card after successful EXIF write */
.photo-card.just-saved {
  animation: just-saved-pulse 1.6s ease-out;
}

.photo-card.just-saved::after {
  content: '💾 Saved';
  position: absolute;
  bottom: 8px;
  right: 8px;
  background: #1d6e3a;
  color: white;
  padding: 3px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.2px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.18);
}

@keyframes just-saved-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(29, 110, 58, 0.55); }
  60%  { box-shadow: 0 0 0 8px rgba(29, 110, 58, 0); }
  100% { box-shadow: 0 0 0 0 rgba(29, 110, 58, 0); }
}

.info-box {
  background: var(--c-primary-soft);
  border-left: 3px solid var(--c-primary);
  padding: var(--sp-4);
  margin: var(--sp-4) 0;
  border-radius: 0 var(--r-md) var(--r-md) 0;
  color: var(--c-primary-text-on-soft);
}
.info-box strong { color: var(--c-text); }

.info-box strong { display: block; margin-bottom: 8px; }
.info-box ul, .info-box ol { margin: 0; padding-left: 20px; }
.info-box li { margin: 4px 0; }

/* ============ Completion page redesign ============ */
.complete-container {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: var(--sp-6);
}

.complete-card {
  width: 100%;
  max-width: 640px;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  padding: 40px 40px 32px;
  text-align: center;
  box-shadow: var(--sh-2);
}

.success-icon {
  width: 80px;
  height: 80px;
  background: linear-gradient(135deg, #10b981, #059669);
  color: white;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto var(--sp-4);
  box-shadow:
    0 0 0 8px rgba(16, 185, 129, 0.10),
    0 12px 24px rgba(16, 185, 129, 0.25);
  animation: success-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.complete-card h2 {
  margin: 0 0 var(--sp-2);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-align: center;
}

.complete-sub {
  margin: 0 0 var(--sp-5);
  color: var(--c-text-dim);
  font-size: 14px;
  text-align: center;
}

@keyframes success-pop {
  0%   { transform: scale(0.5); opacity: 0; }
  60%  { transform: scale(1.05); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

/* Stat grid — 4 equal cells, primary highlighted */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--sp-2);
  margin: 0 0 var(--sp-5);
}

.stat-cell {
  background: var(--c-bg);
  border: 1px solid var(--c-border-soft);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-2);
  text-align: center;
}

.stat-cell.stat-primary {
  background: var(--c-primary-soft);
  border-color: rgba(79, 70, 229, 0.2);
}

.stat-num {
  font-size: 24px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  color: var(--c-text);
  line-height: 1.1;
}

.stat-cell.stat-primary .stat-num { color: var(--c-primary); }
.stat-num.stat-dim { color: var(--c-text-mute); }

.stat-label {
  margin-top: 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--c-text-mute);
}

@media (max-width: 720px) {
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 540px) {
  .complete-card { padding: var(--sp-5) var(--sp-4); }
  .stat-num { font-size: 20px; }
}

/* Receipt disclosure — neater chevron and proper spacing */
.receipt {
  margin: 0 0 var(--sp-5);
  text-align: left;
  border: 1px solid var(--c-border-soft);
  border-radius: var(--r-md);
  background: var(--c-bg);
  overflow: hidden;
}

.receipt summary {
  cursor: pointer;
  padding: var(--sp-3) var(--sp-4);
  font-weight: 500;
  font-size: 14px;
  color: var(--c-text-dim);
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.receipt summary::-webkit-details-marker { display: none; }
.receipt summary::after {
  content: '▾';
  color: var(--c-text-mute);
  transition: transform 0.2s;
}
.receipt[open] summary::after { transform: rotate(180deg); }
.receipt .upload-list {
  border: none;
  border-top: 1px solid var(--c-border-soft);
  border-radius: 0;
  margin: 0;
}

/* "What happens next" — flatter, more like a sidebar than a banner */
.next-steps {
  text-align: left;
  background: var(--c-primary-soft);
  border-radius: var(--r-md);
  padding: var(--sp-4);
  margin: 0 0 var(--sp-5);
}
.next-steps-title {
  margin: 0 0 var(--sp-2);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--c-primary-text-on-soft);
}
.next-steps-list {
  margin: 0;
  padding-left: 20px;
  font-size: 13px;
  color: var(--c-primary-text-on-soft);
  line-height: 1.55;
}
.next-steps-list li { margin: 4px 0; }

.complete-actions {
  display: flex;
  justify-content: center;
}
.complete-actions .primary { min-width: 220px; }

/* When some photos failed, soften the celebration. */
.complete-card.has-failures .success-icon {
  background: linear-gradient(135deg, #f59e0b, #d97706);
  box-shadow:
    0 0 0 8px rgba(245, 158, 11, 0.10),
    0 12px 24px rgba(245, 158, 11, 0.25);
}

/* =================== toasts ================================================
   Bottom-center stack. .toast-stack is pointer-events:none so the page
   stays interactive; individual .toast elements re-enable pointer events
   so the Undo button is clickable. Hidden while the photo modal is open
   (body.modal-open rule in the polish-pass section below).
   ========================================================================== */
.toast-stack {
  position: fixed;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
  width: min(420px, calc(100vw - 32px));
}

.toast {
  background: rgba(15, 22, 35, 0.96);
  color: white;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--r-md);
  padding: 12px 16px;
  font-size: 14px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  pointer-events: auto;
  box-shadow: var(--sh-3);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  animation: slideUp 0.2s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.toast button {
  background: transparent;
  border: none;
  color: #a5b4fc;
  font-weight: 600;
  padding: 4px 8px;
  margin: -4px -8px;
  transition: color 0.12s;
}
.toast button:hover { color: #c7d2fe; }

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

/* =================== responsive ============================================
   Only ONE breakpoint (700px). Below that, topbar wraps, filterbar moves
   its sticky offset down to clear the wrapped header (top: 96px instead
   of 56px), the photo grid uses smaller cards, and the modal goes
   full-screen with squared corners.
   ========================================================================== */
@media (max-width: 700px) {
  .topbar {
    padding: 8px 12px;
    gap: 8px;
    flex-wrap: wrap;
  }
  .topbar .user { display: none; }
  .filterbar { padding: 8px 12px; top: 96px; gap: var(--tap-gap); }
  .container { padding: 16px 12px 100px; }
  .photo-grid { gap: 12px; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
  .footer { padding: 10px 12px; }
  .modal { max-height: 100vh; border-radius: 0; }
  .modal-photo-wrap { max-height: 50vh; }
  .header-chips { width: 100%; }
}

/* ========================================================
   Polish pass — overrides at end of stylesheet so they win
   without restructuring the file above.
   ======================================================== */

/* Inactive filter pill count chip — was grey-on-grey */
.filter:not(.active) .count {
  background: #d8dde5;
  color: #333;
}

/* Untagged card uses an inset shadow instead of a 2px border so
   neighbouring cards don't shift. Keeps geometry stable. */
.photo-card.untagged {
  border-color: var(--c-border);
  border-width: 1px;
  box-shadow: inset 0 0 0 2px var(--c-warn-border);
  background: #fffaf0;
}

/* Hide toasts while modal is open so they don't collide with
   the photo control bar. JS sets body.modal-open to avoid relying on
   the :has() pseudo-class (not in older browsers). */
body.modal-open .toast-stack {
  opacity: 0;
  pointer-events: none;
}

/* Header brand-stack: folder name dominates, "FAS Helper" smaller */
.brand-stack .brand {
  font-size: 16px;
  line-height: 1.25;
}
.brand-stack .brand-meta { font-size: 11px; color: #888; }

/* Empty-state action button */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
}
.empty-state[hidden] { display: none; }

.empty-state .empty-action {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  color: var(--c-primary);
  padding: 10px 16px;
  border-radius: var(--r-md);
  font-weight: 600;
  cursor: pointer;
  min-height: 40px;
}
.empty-state .empty-action:hover { background: var(--c-primary-soft); }

/* "Demo" badge in auth screens to flag this isn't production. */
/* Floating build tag — bottom-left of the viewport so it stays out of the
   way of the user-email chip and footer actions. Pure visual marker, not
   interactive. */
.build-tag {
  position: fixed;
  bottom: var(--sp-3);
  left: var(--sp-3);
  background: rgba(79, 70, 229, 0.92);
  color: white;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 1.5px;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  z-index: var(--z-toast);
  pointer-events: none;
  box-shadow: 0 2px 8px rgba(79, 70, 229, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.14);
  user-select: none;
  -webkit-user-select: none;
}

/* Folder confirm dialog (for #32 wrong-folder guard) — reuses confirm-card */
.confirm-card .confirm-detail {
  margin: var(--sp-2) 0 var(--sp-4);
  background: var(--c-bg);
  padding: var(--sp-3);
  border-radius: var(--r-sm);
  font-size: 13px;
  border: 1px solid var(--c-border-soft);
}
.confirm-card .confirm-detail .row {
  display: flex;
  justify-content: space-between;
  padding: 2px 0;
}
.confirm-card .confirm-detail .row strong { color: var(--c-text); }

/* Confirm-dialog checklist (used for the Upload? confirmation) */
.checklist {
  margin: var(--sp-3) 0 var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.check-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border-radius: var(--r-sm);
  background: var(--c-bg);
  border: 1px solid var(--c-border-soft);
  font-size: 14px;
}
.check-icon {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-weight: 700;
  flex-shrink: 0;
}
.check-ok .check-icon   { background: var(--c-success-bg); color: var(--c-success-text); }
.check-warn .check-icon { background: var(--c-warn-bg);    color: var(--c-warn-text); }
.check-warn             { border-left: 3px solid var(--c-warn-border); }
.check-dim .check-icon  { background: #eef0f4;             color: var(--c-text-dim); }
.check-dim              { color: var(--c-text-dim); }

/* Acknowledgement row. Used when the confirmation is not just informational
   and the engineer has to actively accept a consequence before Upload unlocks
   (today: untagged photos, which upload but do not reach the assessment). */
.confirm-ack {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-4);
  padding: var(--sp-3);
  border-radius: var(--r-sm);
  background: var(--c-warn-bg);
  border: 1px solid var(--c-warn-border);
  color: var(--c-warn-text);
  font-size: 14px;
  cursor: pointer;
}
.confirm-ack input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin: 1px 0 0;
  flex-shrink: 0;
  cursor: pointer;
}

/* Keyboard-shortcut overlay (toggled with `?`) */
.kbd-overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 25, 35, 0.55);
  z-index: var(--z-kbd-overlay);
  display: flex;
  justify-content: center;
  align-items: center;
  padding: var(--sp-4);
}
.kbd-overlay[hidden] { display: none; }
.kbd-card {
  background: white;
  border-radius: var(--r-md);
  padding: var(--sp-5);
  width: 100%;
  max-width: 520px;
  max-height: 90vh;
  overflow-y: auto;
}
.kbd-card h3 {
  margin: 0 0 var(--sp-4);
  font-size: 18px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.kbd-card .kbd-section {
  margin: 0 0 var(--sp-4);
}
.kbd-card .kbd-section-title {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--c-text-dim);
  margin-bottom: var(--sp-2);
}
.kbd-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 0;
  border-bottom: 1px solid var(--c-border-soft);
  font-size: 14px;
}
.kbd-row:last-child { border-bottom: none; }
.kbd-keys { display: flex; gap: 4px; }
kbd {
  display: inline-block;
  background: var(--c-bg);
  border: 1px solid var(--c-border);
  border-bottom-width: 2px;
  border-radius: var(--r-sm);
  padding: 2px 8px;
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 12px;
  font-weight: 600;
  min-width: 24px;
  text-align: center;
}

/* --- pilot additions: logout + folder-load progress --- */
.logout-btn {
  margin-left: 12px;
  padding: 6px 12px;
  font-size: 13px;
  border: 1px solid #d0d5dd;
  border-radius: 8px;
  background: #fff;
  color: #475467;
  cursor: pointer;
}
.logout-btn:hover { background: #f2f4f7; }
.ingest-progress {
  margin-top: 16px;
  font-size: 14px;
  color: #475467;
  text-align: center;
}
.ingest-progress b { color: #3730a3; }

/* Read-only session banner (no File System Access API).
   Fixed to the top so it survives page switches and scrolling — the whole point
   is that it cannot be missed or dismissed while the condition holds. */
.readonly-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-toast);
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 10px var(--sp-3);
  background: var(--c-warn-bg);
  color: var(--c-warn-text);
  border-bottom: 2px solid var(--c-warn-border);
  font-size: 13px;
  line-height: 1.45;
}
.readonly-banner[hidden] { display: none; }
.readonly-banner-icon { flex: none; font-size: 15px; line-height: 1.3; }
/* Push the app down so the banner never covers a topbar. */
body.has-readonly-banner { padding-top: 52px; }
@media (max-width: 640px) {
  body.has-readonly-banner { padding-top: 76px; }
}

/* Mock-mode banner. Shown only when the backend reports SIMS_MODE=mock, i.e.
   the photos never reached SIMS. Fixed to the top of the viewport and above
   everything else on purpose: a mocked batch is otherwise indistinguishable
   from a delivered one, and mistaking the two is the expensive error. */
#mock-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 9999;
  padding: 10px 16px;
  background: #b45309;
  color: #fff;
  font-weight: 600;
  font-size: 14px;
  line-height: 1.4;
  text-align: center;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
