/* ============================================================================
 * PhotograPick — css/style.css
 * ----------------------------------------------------------------------------
 * Design system: tokens → reset → primitives → layout → components → pages
 * → responsive.
 *
 * Aesthetic: "Light Glassmorphism 2026" — bright neutral canvas, translucent
 * frosted panels, hairline white borders, soft ambient shadows, vibrant
 * indigo→violet accent, emerald for success states.
 *
 * Mobile rule: primary controls MUST stay side-by-side (horizontal) on narrow
 * viewports. Sticky headers collapse into a single glass rail with a
 * horizontally scrollable row — never a tall vertical stack.
 * ==========================================================================*/

/* ==========================================================================
   1. DESIGN TOKENS
   ========================================================================*/

:root {
  /* --- Brand ramp --------------------------------------------------------*/
  --indigo-50: #eef2ff;
  --indigo-100: #e0e7ff;
  --indigo-200: #c7d2fe;
  --indigo-400: #818cf8;
  --indigo-500: #6366f1;
  --indigo-600: #4f46e5;
  --indigo-700: #4338ca;

  --violet-400: #a78bfa;
  --violet-500: #8b5cf6;
  --violet-600: #7c3aed;

  --emerald-50: #ecfdf5;
  --emerald-400: #34d399;
  --emerald-500: #10b981;
  --emerald-600: #059669;

  --rose-50: #fff1f2;
  --rose-400: #fb7185;
  --rose-500: #f43f5e;
  --rose-600: #e11d48;

  --amber-400: #fbbf24;
  --amber-500: #f59e0b;

  /* --- Neutrals (slate) --------------------------------------------------*/
  --slate-50: #f8fafc;
  --slate-100: #f1f5f9;
  --slate-200: #e2e8f0;
  --slate-300: #cbd5e1;
  --slate-400: #94a3b8;
  --slate-500: #64748b;
  --slate-600: #475569;
  --slate-700: #334155;
  --slate-800: #1e293b;
  --slate-900: #0f172a;

  /* --- Semantic ----------------------------------------------------------*/
  --bg: #f6f7fb;
  --bg-elevated: #ffffff;
  --text: var(--slate-800);
  --text-strong: var(--slate-900);
  --text-muted: var(--slate-500);
  --text-faint: var(--slate-400);
  --border: rgba(15, 23, 42, 0.08);
  --border-strong: rgba(15, 23, 42, 0.14);
  --glass-border: rgba(255, 255, 255, 0.6);
  --glass-bg: rgba(255, 255, 255, 0.94);
  --glass-bg-strong: #ffffff;
  --glass-bg-soft: rgba(255, 255, 255, 0.86);

  --accent: var(--indigo-600);
  --accent-hover: var(--indigo-700);
  --accent-soft: rgba(99, 102, 241, 0.1);
  --accent-ring: rgba(99, 102, 241, 0.38);
  --accent-gradient: linear-gradient(135deg, var(--indigo-500) 0%, var(--violet-500) 100%);
  --success: var(--emerald-500);
  --success-soft: rgba(16, 185, 129, 0.12);
  --danger: var(--rose-500);
  --danger-soft: rgba(244, 63, 94, 0.1);
  --warn: var(--amber-500);
  --warn-soft: rgba(245, 158, 11, 0.14);

  /* --- Elevation (soft ambient shadows) ---------------------------------*/
  --shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.05);
  --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04), 0 4px 12px -4px rgba(15, 23, 42, 0.08);
  --shadow-md: 0 2px 4px rgba(15, 23, 42, 0.04), 0 12px 28px -8px rgba(15, 23, 42, 0.12);
  --shadow-lg: 0 4px 8px rgba(15, 23, 42, 0.04), 0 24px 56px -12px rgba(15, 23, 42, 0.16);
  --shadow-xl: 0 8px 16px rgba(15, 23, 42, 0.06), 0 40px 80px -20px rgba(15, 23, 42, 0.22);
  --shadow-accent: 0 8px 24px -6px rgba(79, 70, 229, 0.42);
  --shadow-inner-top: inset 0 1px 0 rgba(255, 255, 255, 0.7);

  /* --- Radii -------------------------------------------------------------*/
  --r-xs: 8px;
  --r-sm: 12px;
  --r-md: 16px;
  --r-lg: 22px;
  --r-xl: 30px;
  --r-full: 999px;

  /* --- Spacing -----------------------------------------------------------*/
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 20px;
  --s-6: 24px;
  --s-8: 32px;
  --s-10: 40px;
  --s-12: 56px;

  /* --- Motion ------------------------------------------------------------*/
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
  --t-fast: 160ms;
  --t-base: 260ms;
  --t-slow: 480ms;

  /* --- Layout ------------------------------------------------------------*/
  --page-max: 1320px;
  --gutter: clamp(14px, 3vw, 28px);
  --header-h: 68px;

  /* --- Typography --------------------------------------------------------*/
  --font-sans: 'Inter var', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI',
    system-ui, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas,
    'Liberation Mono', monospace;

  color-scheme: light;
}

/* ==========================================================================
   2. RESET & BASE
   ========================================================================*/

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

* {
  margin: 0;
  padding: 0;
}

/* `hidden` must win over component display rules (flex/grid). Without this,
   elements like the lock banner stayed visible on PENDING albums. */
[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  height: 100%;
}

body {
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.55;
  color: var(--text);
  background: var(--bg);
  min-height: 100%;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: 'cv02', 'cv03', 'cv04', 'ss01';
  text-rendering: optimizeLegibility;
}

/* Body scroll lock while a modal is open. The body is fixed so it cannot move;
   `top` is set inline by the modal helper to preserve the scroll position. */
body.is-locked {
  position: fixed;
  left: 0;
  right: 0;
  width: 100%;
  overflow: hidden;
}

img,
svg,
video {
  display: block;
  max-width: 100%;
}

button,
input,
textarea,
select {
  font: inherit;
  color: inherit;
}

button {
  background: none;
  border: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

h1,
h2,
h3,
h4 {
  line-height: 1.2;
  color: var(--text-strong);
  font-weight: 700;
  letter-spacing: -0.021em;
}

h1 {
  font-size: clamp(1.45rem, 1.1rem + 1.5vw, 2rem);
}

h2 {
  font-size: clamp(1.15rem, 1rem + 0.6vw, 1.4rem);
}

h3 {
  font-size: 1.03rem;
}

::selection {
  background: var(--indigo-200);
  color: var(--slate-900);
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

/* --- Scrollbars ---------------------------------------------------------*/
* {
  scrollbar-width: thin;
  scrollbar-color: var(--slate-300) transparent;
}

::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: var(--slate-300);
  border-radius: var(--r-full);
  border: 3px solid transparent;
  background-clip: content-box;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--slate-400);
  background-clip: content-box;
}

/* ==========================================================================
   3. AMBIENT BACKGROUND
   ========================================================================*/

.ambient {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

.ambient::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image: radial-gradient(rgba(15, 23, 42, 0.035) 1px, transparent 1px);
  background-size: 22px 22px;
  mask-image: radial-gradient(ellipse 90% 60% at 50% 0%, #000 30%, transparent 78%);
  -webkit-mask-image: radial-gradient(ellipse 90% 60% at 50% 0%, #000 30%, transparent 78%);
}

.ambient__blob {
  position: absolute;
  border-radius: 50%;
  /* No blur filter and no drift animation: these are 38-46vw elements, so a
     blur() plus an infinite transform kept the compositor busy forever. The
     radial gradients already fade out, so they read as soft without either. */
  opacity: 0.4;
}

.ambient__blob--1 {
  width: 46vw;
  height: 46vw;
  min-width: 320px;
  min-height: 320px;
  top: -16vw;
  left: -10vw;
  background: radial-gradient(circle at 30% 30%, rgba(129, 140, 248, 0.7), rgba(129, 140, 248, 0) 70%);
}

.ambient__blob--2 {
  width: 40vw;
  height: 40vw;
  min-width: 300px;
  min-height: 300px;
  top: -8vw;
  right: -12vw;
  background: radial-gradient(circle at 60% 40%, rgba(167, 139, 250, 0.62), rgba(167, 139, 250, 0) 70%);
}

.ambient__blob--3 {
  width: 38vw;
  height: 38vw;
  min-width: 280px;
  min-height: 280px;
  bottom: -18vw;
  left: 32vw;
  background: radial-gradient(circle at 50% 50%, rgba(52, 211, 153, 0.42), rgba(52, 211, 153, 0) 70%);
}

/* ==========================================================================
   4. LAYOUT PRIMITIVES
   ========================================================================*/

.shell {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 var(--gutter) var(--s-12);
}

.shell--wide {
  max-width: 1520px;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--s-6);
}

/* The workhorse: keeps controls side-by-side and wraps only when necessary. */
.row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  min-width: 0;
}

.row--nowrap {
  flex-wrap: nowrap;
}

.row--between {
  justify-content: space-between;
}

.row--end {
  justify-content: flex-end;
}

.row--tight {
  gap: var(--s-2);
}

.grow {
  flex: 1 1 auto;
  min-width: 0;
}

.spacer {
  flex: 1 1 auto;
}

/* --- Surfaces -----------------------------------------------------------*/
.glass {
  background: #fff;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}

.panel {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--s-6);
}

.panel--flush {
  padding: 0;
  overflow: hidden;
}

.panel__head {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  padding: var(--s-5) var(--s-6);
  border-bottom: 1px solid var(--border);
}

.panel__head--plain {
  border-bottom: none;
  padding-bottom: 0;
}

.panel__body {
  padding: var(--s-6);
}

.panel__title {
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  display: flex;
  align-items: center;
  gap: var(--s-2);
}

.panel__hint {
  font-size: 0.8125rem;
  color: var(--text-muted);
}

/* ==========================================================================
   5. TYPOGRAPHY UTILITIES
   ========================================================================*/

.eyebrow {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}

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

.mono {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: -0.01em;
}

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

.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.gradient-text {
  background: var(--accent-gradient);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

/* Font Awesome inherits icon colour/size from the element it sits in. */
.fa-solid,
.fa-regular,
.fa-brands {
  line-height: 1;
}

.btn > i,
.menu__item > i {
  font-size: 0.875em;
}

/* ==========================================================================
   6. BUTTONS
   ========================================================================*/

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  padding: 0 var(--s-4);
  height: 42px;
  border-radius: var(--r-full);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  border: 1px solid transparent;
  color: var(--text);
  background: transparent;
  transition: transform var(--t-fast) var(--ease-spring), box-shadow var(--t-base) var(--ease-out-expo),
    background-color var(--t-fast) var(--ease-smooth), border-color var(--t-fast) var(--ease-smooth),
    color var(--t-fast) var(--ease-smooth), opacity var(--t-fast) var(--ease-smooth);
  user-select: none;
}

.btn svg {
  width: 17px;
  height: 17px;
  flex: none;
}

.btn:active {
  transform: scale(0.965);
}

.btn[disabled],
.btn.is-disabled {
  opacity: 0.5;
  pointer-events: none;
}

/* Primary — gradient with glow */
.btn--primary {
  background-image: var(--accent-gradient);
  background-color: var(--indigo-600);
  color: #fff;
  box-shadow: var(--shadow-accent);
}

.btn--primary:hover {
  box-shadow: 0 12px 30px -6px rgba(79, 70, 229, 0.55);
  transform: translateY(-1px);
}

.btn--success {
  background-image: linear-gradient(135deg, var(--emerald-500), #14b8a6);
  color: #fff;
  box-shadow: 0 8px 24px -8px rgba(16, 185, 129, 0.6);
}

.btn--success:hover {
  transform: translateY(-1px);
}

.btn--danger {
  background: var(--danger);
  color: #fff;
}

.btn--danger-ghost {
  color: var(--rose-600);
  background: var(--danger-soft);
}

.btn--danger-ghost:hover {
  background: rgba(244, 63, 94, 0.18);
}

/* Secondary — frosted glass */
.btn--glass {
  background: var(--glass-bg-strong);
  border-color: var(--glass-border);
  box-shadow: var(--shadow-sm);
  color: var(--text-strong);
}

.btn--glass:hover {
  background: #fff;
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

.btn--ghost {
  color: var(--text-muted);
}

.btn--ghost:hover {
  background: rgba(15, 23, 42, 0.05);
  color: var(--text-strong);
}

.btn--sm {
  height: 34px;
  padding: 0 var(--s-3);
  font-size: 0.8125rem;
}

.btn--sm svg {
  width: 15px;
  height: 15px;
}

.btn--lg {
  height: 50px;
  padding: 0 var(--s-6);
  font-size: 0.9375rem;
}

.btn--icon {
  width: 42px;
  padding: 0;
}

.btn--icon.btn--sm {
  width: 34px;
}

.btn--block {
  width: 100%;
}

.btn.is-copied {
  background-image: linear-gradient(135deg, var(--emerald-500), #14b8a6) !important;
  background-color: var(--emerald-500) !important;
  color: #fff !important;
  box-shadow: 0 8px 20px -8px rgba(16, 185, 129, 0.6) !important;
}

.btn.is-failed {
  background: var(--rose-500) !important;
  color: #fff !important;
}

/* ==========================================================================
   7. FORM CONTROLS
   ========================================================================*/

.field {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  min-width: 0;
}

.field__label {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--slate-700);
  display: flex;
  align-items: center;
  gap: var(--s-2);
}

.field__hint {
  font-size: 0.75rem;
  color: var(--text-faint);
}

.input,
.textarea,
.select {
  width: 100%;
  padding: 0 var(--s-4);
  height: 44px;
  background: rgba(255, 255, 255, 0.74);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  color: var(--text-strong);
  font-size: 0.875rem;
  transition: border-color var(--t-fast) var(--ease-smooth), box-shadow var(--t-fast) var(--ease-smooth),
    background-color var(--t-fast) var(--ease-smooth);
}

.input::placeholder,
.textarea::placeholder {
  color: var(--text-faint);
}

.input:hover,
.textarea:hover,
.select:hover {
  border-color: var(--slate-300);
}

.input:focus,
.textarea:focus,
.select:focus {
  outline: none;
  background: #fff;
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft);
}

.textarea {
  height: auto;
  min-height: 148px;
  padding: var(--s-3) var(--s-4);
  resize: vertical;
  line-height: 1.6;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
}

.select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: var(--s-8);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--s-3) center;
  background-size: 16px;
  cursor: pointer;
}

.grid-2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--s-4);
}

/* --- Segmented control (filter switch) ----------------------------------*/
.segmented {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding: 4px;
  gap: 2px;
  background: rgba(15, 23, 42, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.7);
  border-radius: var(--r-full);
  box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.05);
  flex: none;
}

.segmented__thumb {
  position: absolute;
  top: 4px;
  bottom: 4px;
  border-radius: var(--r-full);
  background: #fff;
  box-shadow: var(--shadow-sm);
  transition: transform var(--t-base) var(--ease-out-expo), width var(--t-base) var(--ease-out-expo);
  pointer-events: none;
  z-index: 0;
}

.segmented__btn {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 var(--s-4);
  height: 32px;
  border-radius: var(--r-full);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
  transition: color var(--t-fast) var(--ease-smooth);
}

.segmented__btn svg {
  width: 15px;
  height: 15px;
}

.segmented__btn[aria-selected='true'] {
  color: var(--accent);
}

.segmented__btn:hover {
  color: var(--text-strong);
}

.segmented__count {
  font-variant-numeric: tabular-nums;
  opacity: 0.72;
  font-size: 0.75rem;
}

/* ==========================================================================
   8. BADGES / CHIPS / PILLS
   ========================================================================*/

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 var(--s-3);
  border-radius: var(--r-full);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  white-space: nowrap;
  background: var(--slate-100);
  color: var(--slate-600);
  border: 1px solid rgba(15, 23, 42, 0.05);
}

.badge svg {
  width: 13px;
  height: 13px;
}

.badge--pending {
  background: var(--warn-soft);
  color: #b45309;
  border-color: rgba(245, 158, 11, 0.22);
}

.badge--submitted {
  background: var(--success-soft);
  color: var(--emerald-600);
  border-color: rgba(16, 185, 129, 0.22);
}

.badge--accent {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: rgba(99, 102, 241, 0.2);
}

.badge--neutral {
  background: rgba(15, 23, 42, 0.05);
  color: var(--text-muted);
}

.badge--danger {
  background: var(--danger-soft);
  color: var(--rose-600);
  border-color: rgba(244, 63, 94, 0.2);
}

.dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
  background: var(--slate-400);
}

.dot--online {
  background: var(--emerald-500);
  box-shadow: 0 0 0 3px var(--success-soft);
  animation: pulse-dot 2.4s var(--ease-smooth) infinite;
}

.dot--offline {
  background: var(--rose-500);
  box-shadow: 0 0 0 3px var(--danger-soft);
}

@keyframes pulse-dot {
  0%,
  100% {
    box-shadow: 0 0 0 3px var(--success-soft);
  }
  50% {
    box-shadow: 0 0 0 6px rgba(16, 185, 129, 0.04);
  }
}

/* ==========================================================================
   9. APP HEADER — standard solid navigation bar
   ========================================================================
   A plain full-width bar: solid background, hairline bottom border, content
   constrained to the page width. No floating rounded card, no mid-scroll
   resizing — that animation cost a layout pass on every scroll frame.
   ========================================================================*/

.appbar {
  position: sticky;
  top: 0;
  z-index: 60;
  background: #fff;
  border-bottom: 1px solid var(--border);
}

/* A shadow is the only scroll affordance, applied once and cheap to paint. */
.appbar.is-stuck {
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06), 0 6px 18px -12px rgba(15, 23, 42, 0.25);
}

.appbar__inner {
  width: 100%;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.appbar--wide .appbar__inner {
  max-width: 1520px;
}

.appbar__card {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  min-height: 62px;
  padding: var(--s-2) 0;
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-width: 0;
  flex: 1 1 auto;
}

.brand__mark {
  width: 40px;
  height: 40px;
  border-radius: 13px;
  display: grid;
  place-items: center;
  flex: none;
  background-image: var(--accent-gradient);
  color: #fff;
  box-shadow: var(--shadow-accent);
}

.brand__mark svg {
  width: 22px;
  height: 22px;
}

.brand__text {
  min-width: 0;
}

.brand__title {
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.25;
  color: var(--text-strong);
}

.brand__sub {
  font-size: 0.75rem;
  color: var(--text-muted);
  line-height: 1.3;
  display: flex;
  align-items: center;
  gap: 6px;
}

.appbar__actions {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex: none;
  flex-wrap: nowrap;
}

.appbar__divider {
  width: 1px;
  align-self: stretch;
  margin: 4px 0;
  background: var(--border-strong);
  flex: none;
}

/* --- Counter + quota meter ---------------------------------------------*/
.counter {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, 0.7);
  border: 1px solid var(--glass-border);
  box-shadow: var(--shadow-xs);
  transition: background-color var(--t-base) var(--ease-smooth), box-shadow var(--t-base) var(--ease-smooth);
  flex: none;
}

.counter__figures {
  display: flex;
  align-items: baseline;
  gap: 3px;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  white-space: nowrap;
}

.counter__value {
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--accent);
  transition: color var(--t-base) var(--ease-smooth), transform var(--t-base) var(--ease-spring);
}

.counter__value.is-bump {
  transform: scale(1.28);
}

.counter__total {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-faint);
}

.counter__label {
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: 0.01em;
  line-height: 1.2;
}

.counter__meta {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 72px;
}

.counter__bar {
  width: 100%;
  height: 5px;
  border-radius: var(--r-full);
  background: rgba(15, 23, 42, 0.08);
  overflow: hidden;
}

.counter__fill {
  height: 100%;
  width: 0;
  border-radius: var(--r-full);
  background-image: var(--accent-gradient);
  transition: width var(--t-slow) var(--ease-out-expo), background-image var(--t-base) var(--ease-smooth);
}

/* State: approaching limit */
.counter.is-warning {
  background: rgba(255, 251, 235, 0.92);
  box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.28), var(--shadow-xs);
}

.counter.is-warning .counter__value {
  color: #b45309;
}

.counter.is-warning .counter__fill {
  background-image: linear-gradient(135deg, var(--amber-400), var(--amber-500));
}

/* State: quota reached */
.counter.is-full {
  background: rgba(236, 253, 245, 0.94);
  box-shadow: 0 0 0 1px rgba(16, 185, 129, 0.32), var(--shadow-xs);
}

.counter.is-full .counter__value {
  color: var(--emerald-600);
}

.counter.is-full .counter__fill {
  background-image: linear-gradient(135deg, var(--emerald-400), var(--emerald-600));
}

/* ==========================================================================
   10. HERO / ALBUM META
   ========================================================================*/

.album-meta {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  flex-wrap: wrap;
  padding: var(--s-2) 0;
}

.album-meta__client {
  font-size: clamp(1.3rem, 1rem + 1.4vw, 1.85rem);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.15;
}

.album-meta__facts {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 var(--s-3);
  border-radius: var(--r-full);
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--slate-600);
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid var(--glass-border);
  box-shadow: var(--shadow-xs);
  white-space: nowrap;
}

.chip svg {
  width: 14px;
  height: 14px;
  color: var(--text-faint);
}

/* ==========================================================================
   11. TOOLBAR (sticky control rail)
   ========================================================================*/

.toolbar {
  position: sticky;
  top: calc(var(--header-h) + 10px);
  z-index: 50;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  padding: var(--s-3);
  border-radius: var(--r-md);
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--t-base) var(--ease-smooth);
}

.toolbar.is-stuck {
  box-shadow: var(--shadow-md);
}

.search {
  position: relative;
  flex: 1 1 190px;
  min-width: 0;
  max-width: 340px;
}

.search svg {
  position: absolute;
  left: var(--s-3);
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 16px;
  color: var(--text-faint);
  pointer-events: none;
}

.search .input {
  height: 38px;
  padding-left: 38px;
  border-radius: var(--r-full);
  font-size: 0.8125rem;
}

/* ==========================================================================
   12. GALLERY GRID
   ========================================================================*/

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: clamp(10px, 1.4vw, 18px);
  padding-bottom: var(--s-8);
}

.gallery__empty {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-12) var(--s-6);
  text-align: center;
  color: var(--text-muted);
  animation: fade-up var(--t-slow) var(--ease-out-expo) both;
}

.gallery__empty svg {
  width: 46px;
  height: 46px;
  color: var(--slate-300);
}

/* --- Photo card ---------------------------------------------------------*/
/* NOTE: this rule is applied to hundreds of elements. Anything expensive here
   (filter, backdrop-filter, will-change, multi-property transitions) multiplies
   across the whole grid, so keep it deliberately cheap. */
.card {
  position: relative;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--slate-100);
  aspect-ratio: 4 / 3;
  cursor: pointer;
  border: 1px solid rgba(15, 23, 42, 0.08);
  box-shadow: none;
  /* Only the border/background changes on selection — no transform, so the
     browser never starts a compositing layer per card. */
  transition: border-color var(--t-fast) linear, box-shadow var(--t-fast) linear;
  /* content-visibility keeps offscreen cards cheap with 1000+ photos */
  content-visibility: auto;
  contain-intrinsic-size: 200px 150px;
}

/* Entrance animation only for the first screenful, so a 60-card batch does not
   trigger 60 simultaneous animations. */
.card.is-entering {
  animation: card-in var(--t-base) var(--ease-out-expo) both;
}

@keyframes card-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@media (hover: hover) {
  .card:hover {
    box-shadow: var(--shadow-sm);
    border-color: rgba(15, 23, 42, 0.18);
    z-index: 2;
  }

  .card:hover .card__img {
    transform: scale(1.03);
  }

  .card:hover .card__toolbar {
    opacity: 1;
    transform: translateY(0);
  }
}

.card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.card__media {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.card__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Simple fade only. A transform here forced a compositing layer for every
     thumbnail in the grid, which is what made tapping feel heavy. */
  opacity: 1;
  background: var(--slate-100);
  /* The placeholder is absolutely positioned, so without this the image (static)
     would paint UNDERNEATH it and the tile would stay grey. `position: relative`
     + z-index is enough and costs no compositing layer. */
  position: relative;
  z-index: 1;
}

/* --- Loading placeholder ------------------------------------------------ */
/* Sits behind the image. The image itself has an opaque background, so the
   placeholder never needs an opacity animation to disappear. */
.skeleton {
  position: absolute;
  inset: 0;
  background: var(--slate-100);
}

/* --- Scrim + caption ----------------------------------------------------*/
.card__scrim {
  position: absolute;
  inset: auto 0 0 0;
  height: 62%;
  background: linear-gradient(to top, rgba(15, 23, 42, 0.72), rgba(15, 23, 42, 0.28) 45%, transparent);
  opacity: 0.92;
  pointer-events: none;
}

.card__caption {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3);
  pointer-events: none;
}

.card__filename {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 600;
  color: #fff;
  background: rgba(15, 23, 42, 0.42);
  border: 1px solid rgba(255, 255, 255, 0.22);
  padding: 3px 8px;
  border-radius: var(--r-full);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

.card__index {
  position: absolute;
  top: var(--s-2);
  left: var(--s-2);
  font-size: 0.625rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: #fff;
  background: rgba(15, 23, 42, 0.4);
  border: 1px solid rgba(255, 255, 255, 0.2);
  padding: 2px 7px;
  border-radius: var(--r-full);
  opacity: 0;
  transition: opacity var(--t-base) var(--ease-smooth);
}

.card:hover .card__index {
  opacity: 1;
}

/* --- Circular checkbox overlay -----------------------------------------*/
.check {
  position: absolute;
  top: var(--s-3);
  right: var(--s-3);
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, 0.82);
  border: 1.5px solid rgba(255, 255, 255, 0.9);
  box-shadow: 0 2px 10px rgba(15, 23, 42, 0.22);
  color: #fff;
  transition: transform var(--t-base) var(--ease-spring), background-color var(--t-base) var(--ease-smooth),
    box-shadow var(--t-base) var(--ease-smooth), border-color var(--t-base) var(--ease-smooth);
  z-index: 3;
}

.check svg {
  width: 16px;
  height: 16px;
  stroke-width: 3.2;
  opacity: 0;
  transform: scale(0.4);
  transition: opacity var(--t-fast) var(--ease-smooth), transform var(--t-base) var(--ease-spring);
}

.check:hover {
  transform: scale(1.1);
  background: #fff;
}

.check::after {
  content: '';
  position: absolute;
  inset: -8px; /* larger tap target on mobile */
}

.card.is-selected .check {
  background-image: var(--accent-gradient);
  background-color: var(--indigo-600);
  border-color: rgba(255, 255, 255, 0.95);
  box-shadow: 0 4px 16px -2px rgba(79, 70, 229, 0.6);
}

.card.is-selected .check svg {
  opacity: 1;
  transform: scale(1);
}

/* --- Selected card state ------------------------------------------------ */
/* Deliberately cheap: one border colour, one small shadow, one badge. The old
   version stacked a 2.5px ring, a 7px halo, a 34px 3-stop glow, a tint gradient
   overlay and an animated badge — all of which repainted on every tap. */
.card.is-selected {
  border-color: var(--indigo-600);
  box-shadow: 0 0 0 2px var(--indigo-600);
}

/* Bouncy pop feedback on toggle — kept tiny, and only on the check itself. */
.card.is-popping .check {
  animation: pop-bounce 240ms var(--ease-out-expo);
}

@keyframes pop-bounce {
  0% {
    transform: scale(1);
  }
  45% {
    transform: scale(1.18);
  }
  100% {
    transform: scale(1);
  }
}

/* Selected badge indicator — solid colour, no gradient, no shadow. */
.card__selected-flag {
  position: absolute;
  left: var(--s-2);
  top: var(--s-2);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 9px 3px 6px;
  border-radius: var(--r-full);
  font-size: 0.625rem;
  font-weight: 800;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: #fff;
  background: var(--indigo-600);
  opacity: 0;
  transition: opacity var(--t-fast) linear;
  z-index: 3;
}

.card__selected-flag svg {
  width: 11px;
  height: 11px;
  stroke-width: 3;
}

.card.is-selected .card__selected-flag {
  opacity: 1;
}

/* Blocked shake when quota is full */
.card.is-blocked {
  animation: shake 420ms var(--ease-smooth);
}

@keyframes shake {
  0%,
  100% {
    transform: translateX(0);
  }
  20% {
    transform: translateX(-6px);
  }
  40% {
    transform: translateX(5px);
  }
  60% {
    transform: translateX(-3px);
  }
  80% {
    transform: translateX(2px);
  }
}

/* Quick-select action inside the card (appears on hover) */
.card__toolbar {
  position: absolute;
  right: var(--s-3);
  bottom: var(--s-3);
  display: flex;
  gap: 6px;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity var(--t-base) var(--ease-smooth), transform var(--t-base) var(--ease-out-expo);
  z-index: 3;
}

.card__action {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--slate-700);
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid rgba(255, 255, 255, 0.85);
  box-shadow: 0 3px 12px rgba(15, 23, 42, 0.2);
  transition: transform var(--t-fast) var(--ease-spring), background-color var(--t-fast) var(--ease-smooth);
}

.card__action svg {
  width: 16px;
  height: 16px;
}

.card__action:hover {
  transform: scale(1.12);
  background: #fff;
  color: var(--accent);
}

/* ==========================================================================
   13. LOCKED (SUBMITTED) STATE
   ========================================================================*/

.lock-banner {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  flex-wrap: wrap;
  padding: var(--s-4) var(--s-5);
  border-radius: var(--r-lg);
  background: linear-gradient(120deg, rgba(236, 253, 245, 0.95), rgba(224, 242, 254, 0.92));
  border: 1px solid rgba(16, 185, 129, 0.28);
  box-shadow: var(--shadow-sm), var(--shadow-inner-top);
  animation: fade-up var(--t-slow) var(--ease-out-expo) both;
}

.lock-banner__icon {
  width: 46px;
  height: 46px;
  border-radius: var(--r-md);
  display: grid;
  place-items: center;
  flex: none;
  color: #fff;
  background-image: linear-gradient(135deg, var(--emerald-500), #14b8a6);
  box-shadow: 0 8px 20px -8px rgba(16, 185, 129, 0.7);
}

.lock-banner__icon svg {
  width: 24px;
  height: 24px;
}

.lock-banner__title {
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--emerald-600);
  display: flex;
  align-items: center;
  gap: var(--s-2);
}

.lock-banner__text {
  font-size: 0.8125rem;
  color: var(--slate-600);
}

/* Read-only mode dims interactive affordances across the gallery. */
body.is-readonly .card {
  cursor: zoom-in;
}

body.is-readonly .check,
body.is-readonly .card__toolbar,
body.is-readonly .toolbar__select-all {
  display: none !important;
}

body.is-readonly .card.is-selected {
  box-shadow: 0 0 0 2.5px var(--emerald-500), 0 0 0 7px rgba(16, 185, 129, 0.14),
    0 14px 34px -12px rgba(16, 185, 129, 0.42);
  border-color: var(--emerald-500);
}

body.is-readonly .card__selected-flag {
  background-image: linear-gradient(135deg, var(--emerald-500), #14b8a6);
  box-shadow: 0 4px 14px -4px rgba(16, 185, 129, 0.7);
}

/* ==========================================================================
   14. STAT CARDS (admin)
   ========================================================================*/

.stats {
  display: grid;
  /* Four across on wide screens; the media queries below fold it down to 2x2
     and then keep 2x2 on phones, which stays readable inside one screen. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s-4);
}

/* KPI card: the number leads, the label supports it. No decorative icons or
   watermarks — they added noise without carrying information. */
.stat {
  position: relative;
  padding: var(--s-5);
  border-radius: var(--r-lg);
  background: #fff;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* A soft tinted wash plus a solid left rule marks each metric's meaning. */
.stat::before {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: 4px;
  background: var(--slate-300);
}

.stat--accent {
  background: linear-gradient(180deg, rgba(99, 102, 241, 0.07), rgba(99, 102, 241, 0.02));
}
.stat--accent::before {
  background: var(--accent);
}
.stat--accent .stat__value {
  color: var(--accent);
}

.stat--warn {
  background: linear-gradient(180deg, rgba(245, 158, 11, 0.08), rgba(245, 158, 11, 0.02));
}
.stat--warn::before {
  background: #f59e0b;
}
.stat--warn .stat__value {
  color: #b45309;
}

.stat--success {
  background: linear-gradient(180deg, rgba(16, 185, 129, 0.08), rgba(16, 185, 129, 0.02));
}
.stat--success::before {
  background: var(--emerald-500);
}
.stat--success .stat__value {
  color: var(--emerald-600);
}

.stat__value {
  font-size: clamp(1.75rem, 1.5rem + 0.9vw, 2.1rem);
  font-weight: 800;
  letter-spacing: -0.045em;
  line-height: 1.05;
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}

.stat__label {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.005em;
  margin-top: 4px;
}

.stat__foot {
  font-size: 0.75rem;
  color: var(--text-faint);
  line-height: 1.35;
  margin-top: 1px;
}

/* ==========================================================================
   14b. CREATE FORM ACTIONS (admin)
   ========================================================================*/

.form-actions {
  display: flex;
  align-items: stretch;
  justify-content: flex-end;
  gap: var(--s-3);
  flex-wrap: wrap;
  padding-top: var(--s-4);
  margin-top: var(--s-2);
  border-top: 1px solid var(--border);
}

.form-actions .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 var(--s-5);
  white-space: nowrap;
}

.form-actions .btn > i {
  font-size: 0.9375rem;
  line-height: 1;
}

/* Generate is the main action, so it is the widest. */
#btnCreateAlbum {
  min-width: 240px;
}

/* Inert until "Proses" has run — make that unmistakable. */
#btnCreateAlbum:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  box-shadow: none;
}

body.is-processing #btnResolveNames {
  pointer-events: none;
  opacity: 0.7;
}

@media (max-width: 720px) {
  /* Stacked full-width buttons, in the order the work happens:
     Proses → Generate → Bersihkan. A 2-column grid left an empty cell because
     Generate spans the row, which looked broken. */
  .form-actions {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-2);
  }

  .form-actions .btn {
    width: 100%;
    min-width: 0;
    padding: 0 var(--s-4);
  }

  #btnCreateAlbum {
    min-width: 0;
  }
}

/* ==========================================================================
   15. ALBUM CARDS (admin)
   ========================================================================
   Replaces the old table. Each album is a self-contained block whose actions
   are always visible, so nothing depends on hover (unusable on touch).
   ========================================================================*/

.album-list {
  display: grid;
  gap: var(--s-4);
  padding: var(--s-5) var(--s-6) var(--s-6);
}

.album-card {
  display: grid;
  gap: var(--s-4);
  padding: var(--s-4) var(--s-5);
  border-radius: var(--r-lg);
  background: #fff;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}

/* A finished album gets a subtle emerald edge so status is readable at a
   glance, not only from the badge text. */
.album-card--done {
  border-color: rgba(16, 185, 129, 0.4);
  background: linear-gradient(0deg, rgba(16, 185, 129, 0.04), rgba(16, 185, 129, 0.04)), #fff;
}

.album-card__top {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-width: 0;
}

.album-card__avatar {
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  border-radius: var(--r-md);
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: 0.875rem;
  letter-spacing: 0.02em;
  color: #fff;
  background: var(--accent-gradient);
}

.album-card__id {
  min-width: 0;
  flex: 1 1 auto;
}

.album-card__name {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-strong);
  letter-spacing: -0.015em;
}

.album-card__date {
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin-top: 1px;
}

.album-card__meter {
  display: grid;
  gap: 6px;
}

.album-card__meter-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
}

.album-card__meter-label {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text);
}

.album-card__meter-sub {
  font-size: 0.75rem;
  color: var(--text-faint);
}

.progress {
  display: block;
  height: 6px;
  border-radius: var(--r-full);
  background: var(--slate-100);
  overflow: hidden;
}

.progress__fill {
  display: block;
  height: 100%;
  border-radius: var(--r-full);
  background: var(--accent);
}

.progress__fill.is-full {
  background: var(--emerald-500);
}

/* Four equal columns, one per action, so they line up on every card. */
.album-card__actions {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s-2);
  padding-top: var(--s-4);
  border-top: 1px solid var(--border);
}

.album-card__actions .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  width: 100%;
  min-height: 42px;
  padding: 0 var(--s-3);
}

.album-card__actions .btn > i {
  font-size: 0.875rem;
  line-height: 1;
}

/* Delete is outlined in red rather than filled: it must be findable but never
   the most prominent thing on the card. */
.album-card__delete {
  color: var(--rose-600);
  background: #fff;
  border: 1px solid rgba(244, 63, 94, 0.32);
}

.album-card__delete:hover {
  background: var(--danger-soft);
  border-color: rgba(244, 63, 94, 0.5);
}

@media (max-width: 900px) {
  .album-card__actions {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 560px) {
  .album-list {
    padding: var(--s-4);
  }

  .album-card__actions .btn {
    padding: 0 var(--s-2);
    font-size: 0.8125rem;
  }
}

/* ==========================================================================
   17. MODALS
   ========================================================================*/

.modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--gutter);
  background: rgba(15, 23, 42, 0.36);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--t-base) var(--ease-smooth), visibility var(--t-base);
}

.modal.is-open {
  opacity: 1;
  visibility: visible;
}

.modal__dialog {
  position: relative;
  width: 100%;
  max-width: 520px;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  border-radius: var(--r-xl);
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid var(--glass-border);
  box-shadow: var(--shadow-xl);
  overflow: hidden;
  transform: scale(0.94) translateY(18px);
  transition: transform var(--t-slow) var(--ease-out-expo);
}

.modal.is-open .modal__dialog {
  transform: scale(1) translateY(0);
}

.modal__dialog--lg {
  max-width: 900px;
}

.modal__head {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-5) var(--s-6);
  border-bottom: 1px solid var(--border);
  flex: none;
}

.modal__title {
  font-size: 1.03rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  min-width: 0;
}

.modal__sub {
  font-size: 0.75rem;
  color: var(--text-muted);
}

.modal__body {
  padding: var(--s-6);
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 0;
}

.modal__foot {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4) var(--s-6);
  border-top: 1px solid var(--border);
  background: rgba(248, 250, 252, 0.7);
  flex: none;
  flex-wrap: wrap;
}

.modal__close {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--text-muted);
  flex: none;
  transition: background-color var(--t-fast) var(--ease-smooth), color var(--t-fast) var(--ease-smooth),
    transform var(--t-fast) var(--ease-spring);
}

.modal__close:hover {
  background: rgba(15, 23, 42, 0.06);
  color: var(--text-strong);
  transform: rotate(90deg);
}

/* ==========================================================================
   18. LIGHTBOX
   ========================================================================*/

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: flex;
  flex-direction: column;
  background: rgba(9, 12, 24, 0.86);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--t-base) var(--ease-smooth), visibility var(--t-base);
}

.lightbox.is-open {
  opacity: 1;
  visibility: visible;
}

.lightbox__bar {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  flex: none;
  color: #fff;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.lightbox__bar .btn--glass {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.18);
  color: #fff;
  box-shadow: none;
}

.lightbox__bar .btn--glass:hover {
  background: rgba(255, 255, 255, 0.22);
}

.lightbox__bar .btn--ghost {
  color: rgba(255, 255, 255, 0.7);
}

.lightbox__bar .btn--ghost:hover {
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
}

.lightbox__filename {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  font-weight: 600;
  color: #fff;
}

.lightbox__counter {
  font-size: 0.75rem;
  color: rgba(255, 255, 255, 0.6);
  font-variant-numeric: tabular-nums;
}

.lightbox__stage {
  position: relative;
  flex: 1 1 auto;
  display: grid;
  place-items: center;
  min-height: 0;
  padding: var(--s-4);
  overflow: hidden;
}

.lightbox__img {
  max-width: 100%;
  max-height: 100%;
  border-radius: var(--r-md);
  box-shadow: 0 30px 90px -20px rgba(0, 0, 0, 0.8);
  animation: fade-scale var(--t-slow) var(--ease-out-expo) both;
}

@keyframes fade-scale {
  from {
    opacity: 0;
    transform: scale(0.97);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

.lightbox__spinner {
  position: absolute;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, 0.18);
  border-top-color: #fff;
  animation: spin 800ms linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.lightbox__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 50px;
  height: 50px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #fff;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.35);
  transition: transform var(--t-fast) var(--ease-spring), background-color var(--t-fast) var(--ease-smooth),
    opacity var(--t-fast) var(--ease-smooth);
  z-index: 3;
}

.lightbox__nav svg {
  width: 22px;
  height: 22px;
}

.lightbox__nav:hover {
  background: rgba(255, 255, 255, 0.26);
  transform: translateY(-50%) scale(1.08);
}

.lightbox__nav[disabled] {
  opacity: 0.28;
  pointer-events: none;
}

.lightbox__nav--prev {
  left: clamp(10px, 2.5vw, 26px);
}

.lightbox__nav--next {
  right: clamp(10px, 2.5vw, 26px);
}

.lightbox__foot {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  padding: var(--s-4);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}

.lightbox__foot .btn--glass {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.18);
  color: #fff;
  box-shadow: none;
}

.lightbox__foot .btn--glass:hover {
  background: rgba(255, 255, 255, 0.22);
}

.lightbox__foot .btn--primary {
  box-shadow: 0 8px 26px -8px rgba(79, 70, 229, 0.8);
}

.lightbox__foot .btn--success {
  background-image: linear-gradient(135deg, var(--emerald-400), #14b8a6);
}

/* ==========================================================================
   19. SELECTED PHOTOS MODAL (admin export)
   ========================================================================*/

.picker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
  gap: var(--s-3);
}

.picker-grid__item {
  position: relative;
  aspect-ratio: 1;
  border-radius: var(--r-sm);
  overflow: hidden;
  border: 1px solid var(--glass-border);
  box-shadow: var(--shadow-xs);
  animation: card-in var(--t-base) var(--ease-out-expo) both;
}

.picker-grid__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.picker-grid__name {
  position: absolute;
  inset: auto 0 0 0;
  padding: 5px 7px;
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(to top, rgba(15, 23, 42, 0.8), transparent);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.export-box {
  width: 100%;
  min-height: 132px;
  max-height: 240px;
  padding: var(--s-4);
  border-radius: var(--r-sm);
  background: var(--slate-900);
  color: #a5f3fc;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  line-height: 1.7;
  border: 1px solid rgba(255, 255, 255, 0.08);
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  resize: vertical;
}

.file-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  max-height: 132px;
  overflow-y: auto;
  padding: 2px;
}

.file-chip {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 600;
  padding: 4px 9px;
  border-radius: var(--r-full);
  background: var(--accent-soft);
  color: var(--accent);
  border: 1px solid rgba(99, 102, 241, 0.18);
  white-space: nowrap;
}

/* ==========================================================================
   20. TOASTS
   ========================================================================*/

.toast-host {
  position: fixed;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  z-index: 200;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  pointer-events: none;
  width: max-content;
  max-width: calc(100vw - 24px);
}

.toast {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-full);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-strong);
  background: rgba(255, 255, 255, 0.94);
  border: 1px solid var(--glass-border);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transform: translateY(14px) scale(0.96);
  transition: opacity var(--t-base) var(--ease-out-expo), transform var(--t-base) var(--ease-out-expo);
  max-width: 100%;
}

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

.toast__icon {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  flex: none;
  color: #fff;
  background: var(--slate-400);
}

.toast__icon svg {
  width: 14px;
  height: 14px;
}

.toast--success .toast__icon {
  background: var(--emerald-500);
}

.toast--error .toast__icon {
  background: var(--rose-500);
}

.toast--warn .toast__icon {
  background: var(--amber-500);
}

.toast--info .toast__icon {
  background: var(--accent);
}

.toast__text {
  min-width: 0;
}

/* ==========================================================================
   21. SUCCESS INDICATOR
   ========================================================================
   The particle confetti was removed: 90 animating elements plus a 4s timer made
   the submit feel slow on phones. A single static checkmark communicates the
   same thing instantly.
   ========================================================================*/

/* Success check badge used in the confirm modal */
.success-burst {
  width: 84px;
  height: 84px;
  margin: 0 auto var(--s-4);
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #fff;
  background-image: linear-gradient(135deg, var(--emerald-400), #14b8a6);
}

.success-burst svg {
  width: 40px;
  height: 40px;
  stroke-width: 2.6;
}



/* ==========================================================================
   22. NOTICE / CALLOUT
   ========================================================================*/

.callout {
  display: flex;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-sm);
  font-size: 0.8125rem;
  line-height: 1.5;
  background: var(--accent-soft);
  color: var(--slate-700);
  border: 1px solid rgba(99, 102, 241, 0.16);
}

.callout svg {
  width: 17px;
  height: 17px;
  flex: none;
  color: var(--accent);
  margin-top: 1px;
}

.callout--warn {
  background: var(--warn-soft);
  border-color: rgba(245, 158, 11, 0.24);
}

.callout--warn svg {
  color: #b45309;
}

.callout--success {
  background: var(--success-soft);
  border-color: rgba(16, 185, 129, 0.22);
}

.callout--success svg {
  color: var(--emerald-600);
}

.callout--danger {
  background: var(--danger-soft);
  border-color: rgba(244, 63, 94, 0.2);
}

.callout--danger svg {
  color: var(--rose-600);
}

/* Explicit alias so markup can say `callout--info` and mean the neutral blue. */
.callout--info {
  background: var(--accent-soft);
  border-color: rgba(99, 102, 241, 0.16);
}

/* Keyboard keys inside help text. */
.callout kbd {
  display: inline-block;
  padding: 1px 5px;
  border-radius: 5px;
  background: #fff;
  border: 1px solid rgba(15, 23, 42, 0.16);
  border-bottom-width: 2px;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--text-strong);
}

/* Parser result summary list */
.parse-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 152px;
  overflow-y: auto;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm);
  background: rgba(15, 23, 42, 0.035);
  border: 1px solid var(--border);
}

.parse-list__item {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-size: 0.75rem;
  min-width: 0;
}

.parse-list__item svg {
  width: 13px;
  height: 13px;
  flex: none;
  color: var(--emerald-500);
}

.parse-list__item--bad svg {
  color: var(--rose-500);
}

/* ==========================================================================
   23. EMPTY / LOADING STATES
   ========================================================================*/

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  padding: var(--s-12) var(--s-6);
  text-align: center;
}

.empty__icon {
  width: 62px;
  height: 62px;
  border-radius: var(--r-lg);
  display: grid;
  place-items: center;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid rgba(99, 102, 241, 0.16);
}

.empty__icon svg {
  width: 28px;
  height: 28px;
}

.empty__title {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-strong);
}

.empty__text {
  font-size: 0.8125rem;
  color: var(--text-muted);
  max-width: 340px;
}

.skeleton-row {
  height: 56px;
  border-radius: var(--r-sm);
  background: var(--slate-100);
}

/* ==========================================================================
   24. FOOTER
   ========================================================================*/

.foot {
  margin-top: var(--s-8);
  padding: var(--s-6) 0;
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  flex-wrap: wrap;
  font-size: 0.75rem;
  color: var(--text-faint);
}

/* ==========================================================================
   25. FAB (mobile primary action)
   ========================================================================*/

.fab {
  position: fixed;
  right: var(--gutter);
  bottom: calc(var(--gutter) + 4px);
  z-index: 55;
  display: none;
  align-items: center;
  gap: var(--s-3);
  height: 54px;
  padding: 0 var(--s-5) 0 var(--s-4);
  border-radius: var(--r-full);
  font-size: 0.875rem;
  font-weight: 700;
  color: #fff;
  background-image: var(--accent-gradient);
  box-shadow: 0 14px 34px -10px rgba(79, 70, 229, 0.7), 0 4px 12px rgba(15, 23, 42, 0.16);
  transition: transform var(--t-base) var(--ease-spring), box-shadow var(--t-base) var(--ease-out-expo);
}

.fab svg {
  width: 19px;
  height: 19px;
}

.fab:active {
  transform: scale(0.95);
}

.fab__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 24px;
  padding: 0 6px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, 0.24);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   26. ANIMATIONS
   ========================================================================*/

@keyframes fade-up {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.animate-in {
  animation: fade-up var(--t-slow) var(--ease-out-expo) both;
}

.delay-1 {
  animation-delay: 60ms;
}
.delay-2 {
  animation-delay: 120ms;
}
.delay-3 {
  animation-delay: 180ms;
}

/* ==========================================================================
   27. RESPONSIVE — MOBILE FIRST HORIZONTAL PRIORITY
   ========================================================================
   Requirement: on narrow viewports, headers / controls / counters / primary
   actions stay side-by-side. We never let the header balloon vertically.
   ========================================================================*/

/* --- ≤ 900px: tighten chrome -------------------------------------------*/
@media (max-width: 900px) {
  :root {
    --gutter: 14px;
    --header-h: 62px;
  }

  .brand__sub {
    display: none;
  }

  
  /* --- Admin header: many controls on one scrollable horizontal rail -----
     Scoped to the admin page so the client header (which already fits) is
     never affected. */
  body[data-page='admin'] .appbar__card {
    flex-wrap: nowrap;
  }

  body[data-page='admin'] .brand {
    flex: 1 1 0;
    overflow: hidden;
  }

  body[data-page='admin'] .appbar__actions {
    flex: 0 1 auto;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
    -webkit-overflow-scrolling: touch;
    padding: 2px 0;
  }

  body[data-page='admin'] .appbar__actions::-webkit-scrollbar {
    display: none;
  }

  body[data-page='admin'] .appbar__actions > * {
    flex: none;
  }

  body[data-page='admin'] .appbar__actions .badge {
    height: 30px;
    font-size: 0.6875rem;
  }

  /* Icon-only for secondary admin actions; the primary CTA keeps its label. */
  body[data-page='admin'] .appbar__actions .btn--ghost,
  body[data-page='admin'] .appbar__actions .btn--glass {
    width: 40px;
    height: 40px;
    padding: 0;
    border-radius: 50%;
  }

  body[data-page='admin'] .appbar__actions .btn--ghost .btn__label,
  body[data-page='admin'] .appbar__actions .btn--glass .btn__label {
    display: none;
  }

  body[data-page='admin'] .appbar__actions .btn--primary {
    height: 40px;
    padding: 0 var(--s-3);
  }

  body[data-page='admin'] .appbar__actions .btn--primary .btn__label {
    display: inline;
  }

  }

/* --- ≤ 560px: admin brand collapses to just the mark so the control rail
   gets the width it needs without pushing the header into a second row. */
@media (max-width: 560px) {
  body[data-page='admin'] .brand__text {
    display: none;
  }

  body[data-page='admin'] .brand {
    flex: 0 0 auto;
  }

  body[data-page='admin'] .appbar__actions {
    flex: 1 1 auto;
    justify-content: flex-end;
  }
}

/* --- ≤ 720px: the mobile experience ------------------------------------*/
@media (max-width: 720px) {
  .appbar {
    padding: var(--s-2) 0;
  }

  .appbar__card {
    gap: var(--s-2);
    padding: var(--s-2);
    border-radius: var(--r-md);
  }

  .brand__mark {
    width: 36px;
    height: 36px;
    border-radius: 11px;
  }

  .brand__mark svg {
    width: 19px;
    height: 19px;
  }

  .brand__title {
    font-size: 0.875rem;
  }

  /* Counter shrinks but STAYS beside the actions. */
  .counter {
    padding: 5px var(--s-2);
    gap: var(--s-2);
    border-radius: var(--r-sm);
  }

  .counter__value {
    font-size: 1.06rem;
  }

  .counter__label,
  .counter__total {
    font-size: 0.625rem;
  }

  .counter__meta {
    min-width: 52px;
  }

  /* Icon-only submit so it fits next to the counter. */
  .appbar .btn__label {
    display: none;
  }

  .appbar .btn--primary {
    width: 44px;
    height: 44px;
    padding: 0;
    border-radius: 50%;
  }

  .appbar .btn--primary svg {
    width: 19px;
    height: 19px;
  }

  .appbar__divider {
    display: none;
  }

  /* Toolbar: single horizontal scroll rail — no vertical stacking. */
  .toolbar {
    top: calc(var(--header-h) + 6px);
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    padding: var(--s-2);
    gap: var(--s-2);
    scrollbar-width: none;
    -ms-overflow-style: none;
    scroll-snap-type: x proximity;
  }

  .toolbar::-webkit-scrollbar {
    display: none;
  }

  .toolbar > * {
    scroll-snap-align: start;
  }

  .toolbar .search {
    flex: 0 0 156px;
    max-width: 156px;
  }

  .segmented {
    flex: none;
  }

  .segmented__btn {
    padding: 0 var(--s-3);
    font-size: 0.75rem;
    height: 30px;
  }

  /* Stats: 2x2 grid. A horizontal scroll strip hid half the numbers behind a
     gesture nobody discovers. */
  .stats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-3);
  }

  .stat {
    padding: var(--s-4);
  }

  .stat__value {
    font-size: 1.5rem;
  }

  /* Gallery: denser 3-up grid on phones. */
  .gallery {
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
    gap: 8px;
  }

  .check {
    width: 26px;
    height: 26px;
    top: 6px;
    right: 6px;
  }

  .check svg {
    width: 14px;
    height: 14px;
  }

  .card__caption {
    padding: 7px;
  }

  .card__filename {
    font-size: 0.5625rem;
    padding: 2px 6px;
  }

  .card__toolbar {
    opacity: 1;
    transform: none;
    right: 6px;
    bottom: 6px;
  }

  .card__action {
    width: 30px;
    height: 30px;
  }

  /* Panels edge-to-edge to reclaim width. */
  .panel {
    border-radius: var(--r-md);
    padding: var(--s-4);
  }

  .panel__head,
  .panel__body {
    padding-left: var(--s-4);
    padding-right: var(--s-4);
  }

  /* Modal becomes a bottom sheet, actions stay side-by-side. */
  .modal {
    align-items: flex-end;
    padding: 0;
  }

  .modal__dialog {
    max-width: 100%;
    max-height: 92vh;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    transform: translateY(100%);
  }

  .modal.is-open .modal__dialog {
    transform: translateY(0);
  }

  .modal__head {
    padding: var(--s-4);
  }

  .modal__body {
    padding: var(--s-4);
  }

  .modal__foot {
    padding: var(--s-3) var(--s-4);
    flex-wrap: nowrap; /* keep actions on one row */
    gap: var(--s-2);
  }

  .modal__foot .btn {
    flex: 1 1 auto;
    min-width: 0;
  }

  .modal__foot .btn--ghost-only {
    flex: 0 0 auto;
  }

  /* Lightbox chrome compresses to two rows max. */
  .lightbox__bar {
    padding: var(--s-2) var(--s-3);
    gap: var(--s-2);
  }

  .lightbox__filename {
    font-size: 0.6875rem;
  }

  .lightbox__stage {
    padding: var(--s-2);
  }

  .lightbox__nav {
    width: 42px;
    height: 42px;
  }

  .lightbox__nav svg {
    width: 19px;
    height: 19px;
  }

  .lightbox__foot {
    padding: var(--s-3);
    gap: var(--s-2);
    flex-wrap: nowrap;
  }

  .lightbox__foot .btn {
    flex: 1 1 0;
    min-width: 0;
    padding: 0 var(--s-2);
  }

  .lightbox__foot .btn__label {
    display: none;
  }

  .lightbox__foot .btn--sm {
    height: 44px;
  }

  .gallery__empty {
    padding: var(--s-8) var(--s-4);
  }

  .album-meta {
    gap: var(--s-2);
  }

  .foot {
    flex-direction: row;
    justify-content: space-between;
    text-align: left;
  }

  /* Mobile-only sticky primary action when no room in the header. */
  .fab.is-shown {
    display: inline-flex;
  }

  body.has-fab {
    padding-bottom: 68px;
  }

  /* Table -> card list on phones (still horizontal inside each card). */







}

/* --- ≤ 460px: micro-tuning ---------------------------------------------*/
@media (max-width: 460px) {
  .gallery {
    grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
  }

  .brand__text {
    max-width: 108px;
  }

  .brand__title {
    font-size: 0.8125rem;
  }

  .counter__meta {
    display: none; /* value + label only; meter is redundant here */
  }

  .toast {
    border-radius: var(--r-md);
  }

  .picker-grid {
    grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  }
}

/* --- Landscape phones: keep header compact -----------------------------*/
@media (max-height: 460px) and (orientation: landscape) {
  .appbar {
    position: static;
  }

  .toolbar {
    position: static;
  }

  .lightbox__stage {
    padding: var(--s-1);
  }
}

/* ==========================================================================
   28. ACCESSIBILITY & PRINT
   ========================================================================*/

@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;
  }

  .ambient__blob {
    animation: none;
  }
}

@media (prefers-contrast: more) {
  :root {
    --border: rgba(15, 23, 42, 0.24);
    --border-strong: rgba(15, 23, 42, 0.36);
    --text-muted: var(--slate-600);
  }
}

@media print {
  .appbar,
  .toolbar,
  .fab,
  .ambient,
  .toast-host,
  .panel__head .btn,

  body {
    background: #fff;
  }

  .panel,
  .glass {
    box-shadow: none;
    border: 1px solid #ddd;
  }
}
