body {
  margin: 0;
}

header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.scan-progress {
  margin-top: 16px;
  max-width: 560px;
}

.progress-container {
  background: #eee;
  border-radius: 8px;
  overflow: hidden;
  width: 100%;
  height: 18px;
}

.progress-bar {
  height: 100%;
  width: 0;
  background: #4caf50;
  transition: width 0.3s ease;
}

.scan-status {
  margin-top: 8px;
  font-size: 0.95rem;
  color: #333;
}

.compare-grid {
  /* display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  gap: 1rem;
  flex: 1 1 auto;
  min-height: 0;
  overflow-x: auto;
  padding-bottom: 1rem; */

  display: flex;
  flex-wrap: nowrap;
  justify-content: flex-start;
  gap: 1rem;
  flex: 1 1 auto;
  min-height: 0;
  overflow-x: auto;
  padding-bottom: 1rem;
}

.compare-grid.busy {
  opacity: 0.6;
  pointer-events: none;
}

.compare-grid .card {
  /* Desktop: cards aren't a fixed width — each is sized by a shared
     row height and its own photo's intrinsic aspect ratio via the img
     rule, so a landscape shot ends up wider than a portrait one
     instead of both being squeezed into the same box.

     That row height is --row-height, set by fitCompareRow() in
     main.js once every photo in the matchup has loaded: a *fixed*
     height (e.g. 100% of the available area, the fallback below) can
     make a wide combination of photos overflow the grid's width, since
     there's no pure-CSS way to solve "shared height, width
     proportional to aspect ratio, total width never exceeds the
     container" — it needs the actual aspect ratios. JS computes the
     tallest height that still keeps the row's total width within
     .compare-grid; until that runs (or if it can't — no JS, or an
     image fails to report its natural size), this falls back to 100%,
     the same fixed-fill behavior as before, with .compare-grid's
     overflow-x:auto as a last-resort escape hatch. */
  flex: 0 0 auto;
  height: var(--row-height, 100%);
  max-width: 100%;
  display: flex;
  flex-direction: column;
}

.compare-grid .card img {
  /* width:auto + height:100% on a replaced element makes the browser
     derive the used width from the image's own intrinsic ratio — the
     classic "justified row" trick, no JS needed. flex-basis:0 (not the
     default auto) is what makes flexbox compute that used height
     directly instead of going through percentage resolution, which is
     unreliable for flex items — see the mobile override below, where
     the same reasoning applies in the other axis. */
  flex: 1 1 0;
  min-height: 0;
  width: auto;
  height: 100%;
  max-width: 100%;
  object-fit: contain;
  cursor: pointer;
}

/* Compare page: let the page occupy exactly the viewport height, with
   the matchup grid absorbing whatever's left after the header/controls/
   hint. On mobile that gives a true one-screen, no-scroll layout (the
   site header is hidden there, below); on desktop the header stays
   visible and this still hands the grid the rest of the window's
   height, so cards claim as much room as the row allows. */
body.compare-page {
  /* height, not min-height: a flex container's main size has to be
     *definite* for flex-grow to distribute free space to its children.
     min-height only clamps the auto (content-driven) height *after*
     layout, so it never gives main/#compare-root/.compare-grid
     anything definite to grow into — every flex-grow item down the
     chain collapsed to its own hypothetical (~0) size instead. */
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
}

body.compare-page main {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* main carries Bootstrap's .mb-5 (margin-bottom: 3rem !important);
     only !important can beat !important here regardless of selector
     specificity. */
  margin-bottom: 0 !important;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

body.compare-page #compare-root {
  flex: 1 1 auto;
  min-height: 0;
}

.compare-grid .reject-btn {
  position: absolute;
  top: 5px;
  right: 5px;

  width: 28px;
  height: 28px;
  padding: 0;

  display: flex;
  align-items: center;
  justify-content: center;

  border-radius: 4px;
  z-index: 10;
  font-size: 18px;
  line-height: 1;
}

.compare-comparisons-badge {
  position: absolute;
  bottom: 5px;
  left: 5px;
  z-index: 10;

  background: rgba(0, 0, 0, 0.65);
  color: #fff;
  font-weight: 700;
  font-size: 0.75rem;
  padding: 2px 9px;
  border-radius: 999px;
  pointer-events: none;
}

@media (max-width: 768px) {
  #header {
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
  }

  #header .navbar-brand {
    font-size: 1.1rem;
  }

  #header .nav {
    gap: 0.5rem;
  }

  #compare-header {
    display: none;
  }

  /* Matchup size is a desktop convenience; drop it on mobile so the
     compare page stays a one-screen, no-scroll experience. */
  #matchupSizeField {
    display: none;
  }

  .compare-grid {
    flex-direction: column;
    align-items: stretch;
    padding-bottom: 0;
  }

  .compare-grid .card {
    flex: 1 1 0;
    min-height: 0;
    width: 100%;
    max-width: 100%;
    max-height: none;
    display: flex;
    flex-direction: column;
  }

  .compare-grid .card img {
    /* flex-basis:0 (not auto+height:100%) — a percentage height only
       resolves against a flex item's *used* main size in some browsers,
       and falls back to the image's intrinsic height in others, which
       let full-size photos overflow their card instead of being
       shrunk to fit. Basis 0 makes the flex algorithm compute the used
       height directly, sidestepping percentage resolution altogether. */
    flex: 1 1 0;
    min-height: 0;
    max-height: none;
    width: 100%;
    object-fit: scale-down;
  }

  /* Compare page only: the site header eats a full "row" of a phone
     screen for no benefit here, so hide it — main/#compare-root's
     fill-the-viewport flex sizing is universal (see above) and works
     unchanged whether #header is hidden (mobile) or visible (desktop). */
  body.compare-page #header {
    display: none;
  }

  body.compare-page #compare-root {
    gap: 0.5rem !important;
  }

  body.compare-page #compare-controls {
    margin-bottom: 0 !important;
    gap: 0.5rem !important;
  }

  /* Session-counter/Draw/Next stay on one row even on a ~360px-wide
     phone: shrink the buttons and let the counter (already trimmed to
     "Session N" via the template's responsive spans) shrink further
     before Draw/Next do, since it carries no action. */
  #compare-actions {
    flex-wrap: nowrap;
    width: 100%;
    gap: 0.375rem;
  }

  #compare-actions .btn {
    padding: 0.25rem 0.5rem;
    font-size: 0.8rem;
    white-space: nowrap;
  }

  #compare-actions .disabled {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  #compare-actions #draw,
  #compare-actions #next {
    flex: 0 0 auto;
  }

  body.compare-page #shortcut-hint {
    font-size: 0.7rem;
    flex: 0 0 auto;
  }
}

.card {
  border: 1px solid #ccc;
  padding: 8px;
  cursor: pointer;
}

#groups .card {
  border: 1px solid #ccc;
  padding: 8px;
  padding-bottom: 2rem;
  cursor: pointer;
}

.card img {
  width: 100%;
  height: 100%;
  object-fit: scale-down;
}

.thumb {
  width: 120px;
  height: 80px;
  object-fit: cover;
}

table {
  width: 100%;
  border-collapse: collapse;
}

td,
th {
  border: 1px solid #ddd;
  padding: 8px;
  text-align: left;
}

/* Shared "masonry-ish" photo grid + hover-overlay used by the home page,
   collection view, and full-screen gallery. Landscape photos span two
   columns so wide shots don't get letterboxed inside a square-ish cell. */
.tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 5px;
  grid-auto-flow: dense;
}

.tile-portrait {
  grid-column: span 1;
}

.tile-landscape {
  grid-column: span 2;
}

@media (max-width: 768px) {
  .tile-grid {
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  }

  .tile-landscape {
    grid-column: span 1;
  }
}

.tile-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  padding: 10px;
  gap: 6px;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0));
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
  color: white;
  font-size: 13px;
}

.tile-badges {
  display: flex;
  gap: 6px;
}

.photo-card:hover .tile-overlay,
.gallery-item:hover .tile-overlay {
  opacity: 1;
}

/* Home page collection cards */
.collection-card {
  overflow: hidden;
  height: 100%;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.collection-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
}

.collection-card .card-body {
  /* Translucent (not `opacity`, which would fade the text too) white
     background so the name/stats stay legible regardless of what's
     showing through from the cover image above. */
  background-color: rgba(255, 255, 255, 0.92);
}

.collection-cover {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 2px;
  height: 160px;
  background: #111;
}

.collection-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.collection-cover-empty {
  height: 160px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f1f3f5;
  color: #999;
  font-size: 0.9rem;
}

.leaderboard-rank {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 2;
  background: rgba(0, 0, 0, 0.65);
  color: #fff;
  font-weight: 700;
  font-size: 0.85rem;
  padding: 2px 9px;
  border-radius: 999px;
}

.leaderboard-compact .card {
  position: relative;
}

.history-thumb {
  width: 64px;
  height: 64px;
  object-fit: cover;
  border-radius: 6px;
  border: 2px solid transparent;
}

.history-thumb-missing {
  width: 64px;
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f1f3f5;
  color: #999;
  font-size: 0.7rem;
  border-radius: 6px;
  text-align: center;
}

.history-winner .history-thumb {
  border-color: #198754;
}

.history-timestamp {
  min-width: 130px;
}

[data-bs-theme="dark"] .history-thumb-missing {
  background: #333;
  color: #aaa;
}

[data-bs-theme="dark"] .progress-container {
  background: #333;
}

[data-bs-theme="dark"] .scan-status {
  color: #ccc;
}

[data-bs-theme="dark"] .card {
  border-color: #444;
}

[data-bs-theme="dark"] #groups .card {
  border-color: #444;
}

[data-bs-theme="dark"] td,
[data-bs-theme="dark"] th {
  border-color: #444;
}

[data-bs-theme="dark"] .collection-card .card-body {
  background-color: rgba(33, 37, 41, 0.92);
}

[data-bs-theme="dark"] .collection-cover-empty {
  background: #2b2f33;
  color: #888;
}

[data-bs-theme="dark"] .collection-cover {
  background: #000;
}

/* collection.html */
[data-bs-theme="dark"] .collection-toolbar {
  background: rgba(33, 37, 41, 0.92);
  border-bottom-color: #444;
}

[data-bs-theme="dark"] .photo-card {
  background: #212529;
  border-color: #444;
}

[data-bs-theme="dark"] .stat-box {
  background: #2b2f33;
}

[data-bs-theme="dark"] .stat-box small {
  color: #aaa;
}

[data-bs-theme="dark"] .stat-box-success {
  background: rgba(25, 135, 84, 0.2);
}

[data-bs-theme="dark"] .stat-box-success strong {
  color: #75dda6;
}

[data-bs-theme="dark"] .stat-box-danger {
  background: rgba(220, 53, 69, 0.2);
}

[data-bs-theme="dark"] .stat-box-danger strong {
  color: #f0919b;
}

/* similarity.html */
[data-bs-theme="dark"] .sim-summary .stat-box {
  background: #2b2f33;
}

[data-bs-theme="dark"] .sim-summary .stat-box small {
  color: #aaa;
}

[data-bs-theme="dark"] .sim-group {
  border-color: #444;
}

[data-bs-theme="dark"] .sim-tile {
  background: #2b2f33;
}

[data-bs-theme="dark"] .sim-tile-info {
  color: #ccc;
  background: #212529;
  border-top-color: #444;
}