/* ---------- Colors ----------
   Every color lives in a variable, so light and dark mode only swap these.
   Dark mode applies when the device is set to dark (unless the visitor picked
   light with the header button), or when the visitor picked dark.
   js/theme.js sets data-theme on <html> when they pick. */
:root {
  color-scheme: light;
  --bg: #fafaf9;
  --text: #1c1917;
  --muted: #78716c;
  --card: #ffffff;
  --border: #e7e5e4;
  --focus: #2563eb;
  --danger: #dc2626;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #0c0a09;
    --text: #f5f5f4;
    --muted: #a8a29e;
    --card: #1c1917;
    --border: #292524;
    --focus: #60a5fa;
    --danger: #f87171;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0c0a09;
  --text: #f5f5f4;
  --muted: #a8a29e;
  --card: #1c1917;
  --border: #292524;
  --focus: #60a5fa;
  --danger: #f87171;
}

/* ---------- Base ---------- */
* { box-sizing: border-box; }
/* "hidden" always wins, even over layout rules like display: flex below. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  line-height: 1.5;
}

main {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 16px 48px;
}

a { color: inherit; }

/* A clear outline for keyboard users, so they always see where they are. */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}
/* After a click or tap, no outline (data-pointer is set by theme.js; a key
   press removes it). Text boxes keep theirs, to show where typing goes. */
html[data-pointer] :focus-visible:not(input, textarea, select) { outline: none; }

/* Hidden on screen but still read by screen readers. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

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

/* ---------- Header ---------- */
.site-header {
  max-width: 1200px;
  margin: 0 auto;
  padding: 24px 16px;
}
.site-name {
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-decoration: none;
}

/* ---------- Home: grid of set cards ---------- */
/* auto-fill + minmax: as many 280 px columns as fit, so it works on any screen. */
.set-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr));
  gap: 20px;
}
/* Each card is one big cover photo. The title, date, and photo count sit on a
   dark fade at the bottom, so they read well on any photo. */
.set-card {
  position: relative;
  display: block;
  text-decoration: none;
  color: #fff;
  background: var(--card);
  border-radius: 10px;
  overflow: hidden;
  aspect-ratio: 4 / 3;
}
.set-card img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover; /* crop to fill the box instead of stretching */
  transition: transform 0.6s ease;
}
.set-card:hover img { transform: scale(1.03); }
.set-card-text {
  position: absolute;
  inset: auto 0 0 0;
  padding: 48px 20px 18px;
  /* Dark enough under the text to keep white titles readable on a bright photo
     (sky, snow), while the top of the photo stays clear. */
  background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.55) 40%, rgba(0, 0, 0, 0.75));
}
.set-card h2 {
  margin: 0;
  font-size: 1.35rem;
  line-height: 1.25;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  text-wrap: balance; /* even lines, not one word alone at the end */
}
.set-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin-top: 4px;
  font-size: 0.875rem;
  color: rgba(255, 255, 255, 0.85);
}
/* A set without photos: no picture, so plain text on the card color. */
.set-card.no-cover { border: 1px solid var(--border); color: var(--text); }
.set-card.no-cover .set-card-text { background: none; }
.set-card.no-cover h2 { text-shadow: none; }
.set-card.no-cover .set-card-meta { color: var(--muted); }

/* ---------- Home: hero ---------- */
/* The featured photo across the top, with the tagline and a link on it. */
.hero {
  position: relative;
  height: min(72vh, 640px);
  min-height: 320px;
  margin: 0 0 32px;
  border-radius: 12px;
  overflow: hidden;
  background: var(--card);
  color: #fff;
}
.hero-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero-text {
  position: absolute;
  inset: auto 0 0 0;
  padding: 96px clamp(20px, 4vw, 48px) clamp(20px, 4vw, 40px);
  background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.55) 45%, rgba(0, 0, 0, 0.75));
}
.hero-tagline {
  margin: 0 0 12px;
  font-size: clamp(1.6rem, 4.5vw, 3rem);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.01em;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
  text-wrap: balance;
}
.hero-link {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  color: #fff;
  text-decoration: none;
  font-size: 1rem;
}
.hero-link:hover { text-decoration: underline; text-underline-offset: 4px; }
/* The whole photo opens the featured set, not just this line of text. The
   layer reaches up over the photo and .hero (overflow: hidden) trims it. */
.hero-link::after { content: ""; position: absolute; inset: -100vh 0 0 0; }
.hero-label {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: rgba(255, 255, 255, 0.85);
}
@media (max-width: 600px) {
  .hero { height: 60vh; margin-bottom: 24px; border-radius: 8px; }
}

/* ---------- Set page ---------- */
.back-link {
  color: var(--muted);
  font-size: 0.875rem;
  /* A taller tap area for fingers; the negative margin keeps the layout as is. */
  display: inline-block;
  padding: 12px 0;
  margin: -12px 0;
}
.set-description {
  color: var(--muted);
  max-width: 65ch;
  white-space: pre-line; /* keep the line breaks you typed */
}

/* Justified rows: every photo in a row gets the same height, and each keeps
   its own shape (portrait stays tall, landscape stays wide).

   The shape comes from --ratio (width / height), which set.js copies from the
   database. Because the space for each photo is decided by that number alone,
   nothing moves when the images finish loading. (The previous CSS "columns"
   layout let the browser rebalance the columns as images arrived, which made
   photos jump between columns while scrolling.)

   How the trick works: each photo starts at a width proportional to its ratio
   (flex-basis) and grows by the same proportion (flex-grow). Growing every photo
   in a row by the same factor keeps their heights equal. */
.photo-grid {
  list-style: none;
  margin: 24px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  --row-height: 260px;
}
/* An invisible last item that soaks up the leftover space in the final row,
   so the last few photos are not stretched to fill the whole width. */
.photo-grid::after {
  content: "";
  flex-grow: 1000;
}
.photo-grid li {
  flex-grow: var(--ratio);
  flex-basis: calc(var(--ratio) * var(--row-height));
}
.photo-grid img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: var(--ratio);
  object-fit: cover;
  border-radius: 4px;
  background: var(--border);
}
@media (max-width: 600px) {
  .photo-grid { --row-height: 160px; }
}

/* ---------- Admin ---------- */
.muted { color: var(--muted); }

button {
  font: inherit;
  padding: 10px 16px;
  border: 0;
  border-radius: 6px;
  background: var(--text);
  color: var(--bg);
  cursor: pointer;
}
button:disabled { opacity: 0.6; cursor: wait; }
.button-secondary {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
}

input {
  font: inherit;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card);
  color: var(--text);
}

.login {
  max-width: 360px;
  padding-top: 15vh;
}
.card-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 24px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.form-error {
  margin: 0;
  min-height: 1.5em;
  color: var(--danger);
}

.admin-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.admin-header nav {
  display: flex;
  align-items: center;
  gap: 16px;
}

.stat-card {
  max-width: 480px;
  padding: 20px 24px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.stat-card h2 { margin: 0 0 4px; font-size: 1rem; }
.meter {
  height: 8px;
  border-radius: 4px;
  background: var(--border);
  overflow: hidden;
}
.meter-fill {
  width: 0;
  height: 100%;
  background: var(--focus);
}

.inline-form {
  display: flex;
  gap: 8px;
  max-width: 480px;
}
.inline-form input { flex: 1; min-width: 0; }

.admin-set-list {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  max-width: 480px;
}
.admin-set-list li {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
}

/* ---------- Upload page ---------- */
.panel {
  margin: 0 0 24px;
  padding: 20px 24px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.panel h2 { margin: 0 0 12px; font-size: 1rem; }
.panel > label:not(.drop-zone) { display: block; margin-bottom: 6px; }
.panel input[type="text"] { width: 100%; max-width: 360px; }
.panel > label.checkbox { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
input:disabled { opacity: 0.6; }

.drop-zone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 40px 16px;
  border: 2px dashed var(--border);
  border-radius: 8px;
  text-align: center;
  cursor: pointer;
}
.drop-zone:hover,
.drop-zone.dragging { border-color: var(--focus); }
/* The file input itself is hidden, so show its focus ring on the box instead. */
.drop-zone:has(+ #file-input:focus-visible) {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

progress { width: 100%; height: 8px; }

.wm-preview { margin: 16px 0; }
.wm-preview img {
  display: block;
  max-width: 100%;
  max-height: 60vh;
  border-radius: 4px;
}

.actions { display: flex; gap: 8px; margin: 16px 0; }

.queue {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 12px;
}
.queue li { font-size: 0.85rem; }
.queue img {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 4px;
  background: var(--border);
}
.queue p { margin: 4px 0 0; overflow-wrap: anywhere; }
.queue-name { font-weight: 600; }
.queue li[data-status="error"] .queue-status { color: #dc2626; }
.queue li[data-status="done"] .queue-status { color: #16a34a; }


/* ---------- Deleting ---------- */
.button-danger {
  background: transparent;
  color: #dc2626;
  border: 1px solid currentColor;
  padding: 4px 10px;
  font-size: 0.85rem;
}
.button-danger:hover { background: #dc2626; color: #fff; }

.admin-set-list li { align-items: center; }
.admin-set-list a { flex: 1; }


.danger-zone {
  margin-top: 48px;
  padding: 20px 24px;
  border: 1px solid #dc2626;
  border-radius: 8px;
}
.danger-zone h2 { margin: 0 0 4px; font-size: 1rem; color: #dc2626; }

/* ---------- Tappable photos ---------- */
.thumb-button {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: none;
  cursor: zoom-in;
}
.thumb-button img { transition: opacity 0.15s; }
.thumb-button:hover img { opacity: 0.9; }

/* ---------- Full-screen viewer ---------- */
.no-scroll { overflow: hidden; }
/* The spoken "Photo 2 of 5" (set.html) stays inside the viewer, so the viewer
   never gets a few pixels to scroll. */
#lightbox-status { top: 0; left: 0; margin: 0; }

.lightbox {
  /* <dialog> has a default box; make it cover the whole screen instead. */
  width: 100vw;
  height: 100vh;
  height: 100dvh; /* "dynamic" height: correct on phones, where the address bar comes and goes */
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: rgba(0, 0, 0, 0.94);
  color: #fff;
  /* Let our code see horizontal swipes, but keep two-finger pinch zoom working. */
  touch-action: pinch-zoom;
  user-select: none;
  -webkit-user-select: none;
}
.lightbox::backdrop { background: rgba(0, 0, 0, 0.94); }

.lightbox-stage {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Room for the counter at the top, without covering the photo. */
  padding: 48px 0;
}
/* "contain" = as big as possible while showing the whole photo. A portrait
   photo on a phone held upright fills the height; a landscape one fills the width. */
.lightbox-stage img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  -webkit-touch-callout: none; /* no "Save Image" pop-up on long press in iOS Safari */
  /* Its final size from the start (lightbox.js sets --ratio and --w from the
     photo), so it does not grow when the sharper file arrives. */
  aspect-ratio: var(--ratio);
  width: min(100%, var(--w), calc((100dvh - 96px) * var(--ratio)));
  height: auto;
  /* Springs back after a swipe that was too short (see lightbox.js). */
  transition: transform 0.15s ease;
}

.lightbox-counter {
  position: absolute;
  top: 0;
  left: 0;
  margin: 0;
  padding: 14px 16px;
  font-size: 0.9rem;
  color: rgba(255, 255, 255, 0.7);
}
.lightbox-info {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  text-align: center;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.6));
}
.lightbox-info p { margin: 0; }
.lightbox-camera {
  margin-top: 2px !important;
  font-size: 0.8rem;
  color: rgba(255, 255, 255, 0.7);
  letter-spacing: 0.02em;
}

.lightbox-button {
  position: absolute;
  width: 48px;
  height: 48px;
  padding: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  font-size: 2rem;
  line-height: 1;
}
.lightbox-button:hover { background: rgba(255, 255, 255, 0.25); }
.lightbox-button svg {
  display: block;
  margin: auto;
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.lightbox-close { top: 4px; right: 8px; }
.lightbox-prev,
.lightbox-next { top: 50%; transform: translateY(-50%); }
.lightbox-prev { left: 12px; }
.lightbox-next { right: 12px; }

/* On phones, swiping replaces the arrows, so they stop covering the photo.
   They are only hidden from sight: screen readers (which use one-finger swipes
   for themselves) still find them. */
@media (hover: none) and (pointer: coarse) {
  .lightbox-prev,
  .lightbox-next {
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* ---------- Managing sets and photos ---------- */
.small { font-size: 0.85rem; margin: 4px 0 12px; }

textarea {
  font: inherit;
  width: 100%;
  max-width: 560px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card);
  color: var(--text);
}
.panel input[type="text"],
.panel textarea { margin-bottom: 12px; }
.slug-field { display: flex; align-items: baseline; gap: 4px; max-width: 360px; }
.slug-field input { flex: 1; min-width: 0; margin-bottom: 0 !important; }

.save-status { font-size: 0.85rem; color: #16a34a; }
.save-status[data-state="error"] { color: #dc2626; }
.actions .save-status { align-self: center; }

.icon-button {
  width: 36px;
  height: 36px;
  padding: 0;
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
  font-size: 1rem;
  line-height: 1;
}
.icon-button:hover { border-color: var(--text); }
.icon-button.active { color: #ca8a04; border-color: #ca8a04; }
.icon-button.danger { color: #dc2626; }
.icon-button.danger:hover { background: #dc2626; border-color: #dc2626; color: #fff; }

/* ---------- Drag to reorder (see js/sortable.js) ---------- */
.drag-handle,
.drag-surface {
  cursor: grab;
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
}
/* "none": a finger on the handle drags the item instead of scrolling the page. */
.drag-handle { touch-action: none; color: var(--muted); font-size: 1.2rem; padding: 4px; }

/* Lets sortable.js read item positions relative to the list. */
.admin-set-list,
.photo-editor { position: relative; }

/* The empty slot that shows where the item will land. */
.drag-slot {
  outline: 2px dashed var(--focus);
  outline-offset: -2px;
  border-radius: 8px;
}
.drag-slot > * { visibility: hidden; }

/* The floating copy that follows the pointer. */
.drag-ghost {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1000;
  margin: 0 !important;
  padding: 0 !important;
  max-width: none !important;
  display: block !important;
  list-style: none;
  pointer-events: none; /* so we can see what is underneath it */
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.25);
  border-radius: 8px;
  background: var(--card);
  opacity: 0.95;
}
.drag-ghost > li { border-bottom: 0 !important; }
body.is-dragging,
body.is-dragging * { cursor: grabbing !important; user-select: none; }

.photo-grip {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
}
.admin-set-list li { gap: 8px; }
.admin-set-list .muted { margin-right: 8px; }

.photo-editor {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 16px;
}
.photo-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.photo-card.is-cover { border-color: #ca8a04; }
.photo-card-image { position: relative; }
.photo-card-image img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: 4px;
  cursor: grab;
}
.cover-badge {
  position: absolute;
  top: 6px;
  left: 6px;
  padding: 2px 8px;
  border-radius: 4px;
  background: #ca8a04;
  color: #fff;
  font-size: 0.75rem;
  font-weight: 600;
}
.photo-card-field { display: flex; flex-direction: column; gap: 2px; font-size: 0.8rem; color: var(--muted); }
.photo-card-field input { font-size: 0.9rem; padding: 6px 8px; }
.photo-card-tools { display: flex; gap: 6px; }
.photo-card-tools .danger { margin-left: auto; }

/* ---------- Storage by set ---------- */
.storage-details { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 12px; }
.storage-details summary { cursor: pointer; font-weight: 600; font-size: 0.9rem; }
.storage-list { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 12px; }
.storage-list li {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4px 12px;
  font-size: 0.9rem;
}
.storage-list a { overflow-wrap: anywhere; }
.storage-size { color: var(--muted); white-space: nowrap; }
.storage-list .meter { grid-column: 1 / -1; height: 6px; }

/* ---------- Footer ---------- */
.site-footer {
  max-width: 1200px;
  margin: 0 auto;
  padding: 32px 16px 48px;
  color: var(--muted);
  font-size: 0.875rem;
  border-top: 1px solid var(--border);
}

/* ---------- Drafts ---------- */
.draft-badge {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  background: #fef3c7;
  color: #92400e;
  font-size: 0.75rem;
  font-weight: 600;
  vertical-align: middle;
}
.draft-banner {
  margin: 0 0 16px;
  padding: 10px 14px;
  border-radius: 8px;
  background: #fef3c7;
  color: #92400e;
}
.publish-panel .actions { align-items: center; margin-bottom: 0; }
.draft-badge.scheduled { background: #dbeafe; color: #1e40af; }
#set-title[data-state="scheduled"]::after {
  content: "Scheduled";
  margin-left: 12px;
  padding: 2px 10px;
  border-radius: 999px;
  background: #dbeafe;
  color: #1e40af;
  font-size: 0.9rem;
  vertical-align: middle;
}
.schedule-form { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border); }
.schedule-form label { display: block; margin-bottom: 6px; }
.schedule-form input[type="datetime-local"] { font: inherit; }
#set-title[data-state="draft"]::after {
  content: "Draft";
  margin-left: 12px;
  padding: 2px 10px;
  border-radius: 999px;
  background: #fef3c7;
  color: #92400e;
  font-size: 0.9rem;
  vertical-align: middle;
}

/* ---------- Not found page ---------- */
.not-found { text-align: center; padding-top: 10vh; min-height: 50vh; }
.not-found-code { font-size: 4rem; font-weight: 700; margin: 0; color: var(--border); }
/* Decoration only ("Page not found" below says it), so it is drawn here and
   not part of the page text. The second line hides it from screen readers. */
.not-found-code::before { content: "404"; content: "404" / ""; }
.not-found h1 { margin-top: 0; }
.button-link {
  display: inline-block;
  padding: 10px 18px;
  border-radius: 6px;
  background: var(--text);
  color: var(--bg);
  text-decoration: none;
}

#queue-summary[data-state="error"] { color: #dc2626; font-weight: 600; }

/* ---------- Site navigation (Work / About) ---------- */
.site-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; }

/* ---------- About page ---------- */
.about {
  display: grid;
  grid-template-columns: minmax(0, 420px) minmax(24em, 1fr); /* text never squeezed narrow */
  gap: 48px;
  align-items: start;
  padding-top: 16px;
}
.about-portrait img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 8px;
}
.about h1 { margin-top: 0; }
.about-bio { max-width: 62ch; }
.about-bio p { line-height: 1.7; }
.about h2 { font-size: 1.1rem; margin-top: 32px; }
.about-contact { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.about-contact li { display: flex; gap: 12px; flex-wrap: wrap; }
.contact-label { min-width: 90px; color: var(--muted); }
/* The text-only layout when there is no portrait yet. */
.about:not(:has(.about-portrait)) { grid-template-columns: minmax(0, 1fr); }
@media (max-width: 700px) {
  .about { grid-template-columns: minmax(0, 1fr); gap: 24px; }
}

/* ---------- Admin: About page editor ---------- */
.portrait-editor { display: flex; gap: 20px; align-items: center; flex-wrap: wrap; }
.portrait-editor img { width: 140px; height: 140px; object-fit: cover; border-radius: 8px; }
.portrait-editor .button-secondary { margin-left: 8px; }
.button-like {
  display: inline-block;
  padding: 10px 16px;
  border-radius: 6px;
  background: var(--text);
  color: var(--bg);
  cursor: pointer;
}
.panel input[type="email"],
.panel input[type="url"] { width: 100%; max-width: 360px; margin-bottom: 12px; }
.panel h2 + label { margin-top: 0; }
.button-like:has(+ input:focus-visible) { outline: 3px solid var(--focus); outline-offset: 2px; }

/* ---------- Home page tagline ---------- */
.site-tagline { margin: 0 0 24px; color: var(--muted); }

/* ---------- Categories (home page filter) ---------- */
.category-nav { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 24px; }
.category-nav a {
  padding: 6px 14px;
  border: 1px solid var(--border);
  border-radius: 999px;
  text-decoration: none;
  color: var(--muted);
  font-size: 0.875rem;
}
.category-nav a:hover { color: var(--text); border-color: var(--text); }
.category-nav a[aria-current="page"] { background: var(--text); border-color: var(--text); color: var(--bg); }

/* ---------- Licensed downloads ---------- */
.license-panel {
  margin: 32px 0 0;
  padding: 16px 20px;
  border: 1px solid var(--border);
  border-radius: 8px;
  max-width: 560px;
}
.license-panel h2 { margin: 0 0 4px; font-size: 1rem; }
.license-panel.unlocked { border-color: #16a34a; }
.license-panel input { text-transform: uppercase; letter-spacing: 0.05em; }
.lightbox-download {
  position: absolute;
  top: 8px;
  right: 64px;
  padding: 12px 16px;
  border-radius: 999px;
  background: #fff;
  color: #111;
  font-weight: 600;
  font-size: 0.9rem;
  text-decoration: none;
}
.lightbox-download:hover { background: #e5e5e5; }

/* ---------- Admin: downloads and categories ---------- */
.download-badge {
  position: absolute;
  bottom: 6px;
  left: 6px;
  padding: 2px 8px;
  border-radius: 4px;
  background: #16a34a;
  color: #fff;
  font-size: 0.75rem;
  font-weight: 600;
}
.category-picker { border: 0; padding: 0; margin: 0 0 12px; }
.category-picker legend { margin-bottom: 6px; }
.category-picker .checkbox { display: inline-flex !important; margin: 0 16px 6px 0 !important; }
.admin-tags { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 8px 0 0; padding: 0; }
.admin-tags li {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 4px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
}
.admin-tags button { width: 24px; height: 24px; padding: 0; border-radius: 50%; }
.license-table { width: 100%; border-collapse: collapse; margin-top: 12px; font-size: 0.9rem; }
.license-table th, .license-table td { text-align: left; padding: 10px 8px; border-bottom: 1px solid var(--border); vertical-align: top; }
.license-table code { font-size: 1rem; font-weight: 600; letter-spacing: 0.04em; }
.license-table tr.is-off { opacity: 0.55; }
.license-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.license-actions button { padding: 4px 10px; font-size: 0.85rem; }
.set-checklist { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 4px 16px; margin: 0 0 12px; }
.new-code { margin: 12px 0 0; padding: 12px 16px; border-radius: 8px; background: #dcfce7; color: #14532d; }
.new-code code { font-size: 1.2rem; font-weight: 700; letter-spacing: 0.06em; }
@media (max-width: 700px) { .license-table .hide-small { display: none; } }
.panel input[type="date"],
.panel input[type="number"] { display: block; max-width: 220px; margin-bottom: 12px; }

/* ---------- Light / dark switch (see js/theme.js) ---------- */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
}
.theme-toggle:hover { color: var(--text); border-color: var(--text); }
.theme-toggle svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
}
.theme-toggle .icon-moon { fill: currentColor; stroke: none; }
/* Show the moon in light mode (tap for dark), the sun in dark mode. */
.theme-toggle[data-showing="light"] .icon-sun,
.theme-toggle[data-showing="dark"] .icon-moon { display: none; }

@media (prefers-reduced-motion: reduce) {
  .set-card img { transition: none; }
}

/* ---------- Admin: watermark style ---------- */
.wm-style { border: 0; padding: 0; margin: 0 0 12px; }
.wm-style legend { margin-bottom: 6px; }

/* ---------- Share buttons and menu (see js/share.js) ---------- */
.share-button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
}
.share-button:hover { border-color: var(--text); }
.share-button svg,
.share-item svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex-shrink: 0;
}
/* The round icon-only version in the header, matching the light / dark switch. */
.share-button-icon {
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 50%;
  color: var(--muted);
}
.share-button-icon:hover { color: var(--text); }

.set-heading {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
}
/* The title's usual space above (0.67em of the 2em heading) belongs to the
   whole row, so the Share button stays centered on the title. */
.set-heading { margin: 1.34rem 0 8px; }
.set-heading h1 { margin: 0 auto 0 0; }
/* The description belongs to the title above it, so it sits close to it. */
.set-heading + .set-description,
.set-location + .set-description { margin-top: 0; }
/* No location or description: the photos keep their usual distance. */
.set-heading + .photo-grid { margin-top: 45px; }

.share-menu {
  position: absolute;
  z-index: 50;
  min-width: 200px;
  padding: 6px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
}
.share-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 12px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 0.95rem;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.share-item:hover,
.share-item:focus-visible { background: var(--border); outline: none; }
/* After a click or tap the first item gets focus too; no highlight for that
   (see "data-pointer" near the top). Hover still highlights. */
html[data-pointer] .share-item:focus-visible:not(:hover) { background: none; }

/* ---------- Admin: watermark toolkit ---------- */
.panel select {
  display: block;
  font: inherit;
  max-width: 360px;
  width: 100%;
  margin-bottom: 12px;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card);
  color: var(--text);
}
#tk-list .button-link { margin-top: 6px; padding: 6px 14px; font-size: 0.85rem; }

/* ---------- Upload page: duplicate warnings ---------- */
.queue-warning {
  margin-top: 6px;
  padding: 8px 10px;
  border-radius: 6px;
  background: #fef3c7;
  color: #78350f;
  font-size: 0.8rem;
}
.queue-warning[data-strength="weak"] { background: var(--border); color: var(--text); }
.queue-warning ul { margin: 4px 0 6px; padding-left: 18px; }
.queue-warning a { color: inherit; }
.queue-warning button { padding: 4px 10px; font-size: 0.8rem; }

/* ---------- Admin: activity log ---------- */
.activity-filters { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; }
.activity-filters button { padding: 6px 14px; font-size: 0.9rem; }
.activity-filters button[aria-pressed="false"] { background: transparent; color: var(--text); border: 1px solid var(--border); }
.activity-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.activity-table th,
.activity-table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
.activity-table th { color: var(--muted); font-weight: 500; }
.activity-table td:first-child { white-space: nowrap; color: var(--muted); }
.activity-group {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--border);
  font-size: 0.75rem;
  white-space: nowrap;
}
.activity-group[data-group="Deletion"] { background: #fee2e2; color: #991b1b; }
.activity-group[data-group="Download"] { background: #dcfce7; color: #166534; }
.activity-group[data-group="License"] { background: #ede9fe; color: #5b21b6; }
.activity-group[data-group="Publishing"] { background: #dbeafe; color: #1e40af; }
@media (max-width: 600px) {
  .activity-table thead { display: none; }
  .activity-table tr { display: block; padding: 8px 0; border-bottom: 1px solid var(--border); }
  .activity-table td { display: block; border: 0; padding: 2px 0; }
}

/* ---------- Set page: download all ---------- */
.license-actions-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-top: 10px; }

/* ---------- Hero choices, location, related sets ---------- */
.featured-badge { background: #ede9fe; color: #5b21b6; }
.month-boxes { display: flex; flex-wrap: wrap; gap: 4px 14px; }
.month-boxes .checkbox { display: inline-flex !important; margin: 0 !important; }
.set-location { margin: 0 0 8px; color: var(--muted); font-size: 0.875rem; }
.related-sets { margin: 56px 0 0; padding-top: 32px; border-top: 1px solid var(--border); }
.related-sets h2 { margin: 0 0 16px; font-size: 1.2rem; }
.related-grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr)); gap: 16px; }
.related-grid .set-card h3 { margin: 0; font-size: 1.1rem; line-height: 1.25; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); text-wrap: balance; }
.related-grid .set-card-text { padding: 36px 16px 14px; }
#hero-mode .checkbox,
#hero-rotate-field .checkbox { display: flex; margin-bottom: 6px; }
section.panel[aria-labelledby="hero-heading"] { max-width: 720px; }

/* ---------- Photo protection (see js/protect.js) ----------
   iPhones and iPads ignore the script for long presses, so this does it:
   -webkit-touch-callout: none   no "Save to Photos / Copy / Share" pop-up
   user-select: none             a long press does not select the picture
   -webkit-user-drag: none       the picture cannot be dragged out (Safari)
   For pictures inside a link or button (grid thumbnails, cards), the picture
   also ignores touches entirely (pointer-events: none): a long press then
   lands on the button or link around it, which has no "Save Image" option.
   Tapping still works, because the tap reaches the button or link instead. */
main img,
.lightbox img {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
}
.thumb-button img,
.set-card img,
.hero img {
  pointer-events: none;
}
.thumb-button,
.set-card,
.hero {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* ---------- Header that stays at the top ---------- */
/* Public pages: the header stays in view while scrolling, so the menu is
   always one tap away. The shadow trick paints its background the full width
   of the window, even though the header itself is at most 1200 px wide. */
.site-header:not(.admin-header) {
  position: sticky;
  top: 0;
  z-index: 20;
  padding-top: 14px;
  padding-bottom: 14px;
  background: var(--bg);
  box-shadow: 0 0 0 100vmax var(--bg);
  clip-path: inset(0 -100vmax);
  /* Stays in place while the rest of the page fades (see "Smooth page changes"). */
  view-transition-name: site-header;
}

/* ---------- The menu button (top right) ---------- */
.menu-button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--text);
}
.menu-button:hover { border-color: var(--text); }
.menu-button svg,
.menu-close svg,
.menu-action svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex-shrink: 0;
}
.menu-action .icon-moon { fill: currentColor; stroke: none; }
.menu-action[data-showing="light"] .icon-sun,
.menu-action[data-showing="dark"] .icon-moon { display: none; }

/* ---------- Menu: a panel that slides in from the right ---------- */
.site-menu {
  /* <dialog> starts as a box in the middle; make it a full-height side panel. */
  position: fixed;
  inset: 0 0 0 auto;
  width: min(380px, 88vw);
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: var(--bg);
  color: var(--text);
  box-shadow: -12px 0 40px rgba(0, 0, 0, 0.18);
  /* Stays above the header while it slides out (Safari lets go of a closing
     dialog's "always on top" place at once; Chrome keeps it until the end). */
  z-index: 100;
  /* Sliding: from off-screen to its place, and back when closing. */
  transform: translateX(100%);
  transition: transform 0.32s cubic-bezier(0.2, 0.8, 0.2, 1), overlay 0.32s allow-discrete, display 0.32s allow-discrete;
}
.site-menu[open] { transform: translateX(0); }
@starting-style { .site-menu[open] { transform: translateX(100%); } }
.site-menu::backdrop {
  background: rgba(0, 0, 0, 0);
  transition: background-color 0.32s, overlay 0.32s allow-discrete, display 0.32s allow-discrete;
}
.site-menu[open]::backdrop { background: rgba(0, 0, 0, 0.4); }
@starting-style { .site-menu[open]::backdrop { background: rgba(0, 0, 0, 0); } }

.menu-panel { display: flex; flex-direction: column; height: 100%; padding: 14px 24px 24px; overflow-y: auto; }
.menu-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.menu-title { font-weight: 600; letter-spacing: 0.04em; }
.menu-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--text);
}
.menu-close:hover { background: var(--border); }
.menu-nav ul { list-style: none; margin: 0; padding: 0; }
.menu-main a {
  display: block;
  padding: 8px 0;
  font-size: 1.6rem;
  font-weight: 600;
  text-decoration: none;
}
.menu-main a:hover,
.menu-list a:hover { text-decoration: underline; text-underline-offset: 5px; }
.menu-nav a[aria-current="page"] { text-decoration: underline; text-underline-offset: 5px; text-decoration-thickness: 2px; }
.menu-heading {
  margin: 24px 0 6px;
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.menu-list a { display: block; padding: 7px 0; text-decoration: none; color: var(--text); }
.menu-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.menu-chips a { padding: 6px 14px; border: 1px solid var(--border); border-radius: 999px; font-size: 0.875rem; }
/* Same look as the category buttons on the home page. */
.menu-chips a:hover { text-decoration: none; border-color: var(--text); }
.menu-chips a[aria-current="page"] { text-decoration: none; background: var(--text); border-color: var(--text); color: var(--bg); }
.menu-actions { display: grid; gap: 8px; margin-top: auto; padding-top: 24px; }
.menu-action {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: transparent;
  color: var(--text);
  text-align: left;
}
.menu-action:hover { border-color: var(--text); }

/* ---------- Footer: contact details on every page ---------- */
.site-footer:not(:empty) { display: grid; gap: 24px; padding-top: 40px; }
.footer-heading { margin: 0 0 10px; color: var(--text); font-size: 1.1rem; }
.footer-links { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 10px 32px; font-size: 0.875rem; }
.footer-links li { display: flex; gap: 10px; }
.footer-links a { color: var(--text); padding: 5px 0; margin: -5px 0; } /* taller tap area, same layout */
.footer-bottom { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px 24px; }
.footer-copy { margin: 0; }
.footer-top { text-decoration: none; display: inline-block; padding: 11px 0; margin: -11px 0; }
.footer-top:hover { color: var(--text); text-decoration: underline; text-underline-offset: 4px; }

/* ---------- Smooth page changes ---------- */
/* Browsers that support it (Chrome, Edge, Safari 18) fade from one page to the
   next instead of flashing white, while the header stays still. Others just
   change pages as before. Together with the Speculation-Rules header (pages
   load early when you point at a link), moving around feels instant. */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: page-out 0.18s ease both; }
::view-transition-new(root) { animation: page-in 0.28s ease both; }
@keyframes page-out { to { opacity: 0; } }
@keyframes page-in { from { opacity: 0; transform: translateY(8px); } }

/* Links to a place on the same page ("Contact", "Back to top") glide there. */
html { scroll-behavior: smooth; scroll-padding-top: 80px; }

/* Photos show a soft color while they load, instead of a white gap. */
.thumb-button,
.set-card,
.hero { background-color: var(--card); }

/* The photo viewer fades in and out instead of appearing at once. While it
   fades out, it keeps covering the screen, in Safari too (see .site-menu). */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  opacity: 0;
  transition: opacity 0.25s ease, overlay 0.25s allow-discrete, display 0.25s allow-discrete;
}
.lightbox[open] { opacity: 1; }
@starting-style { .lightbox[open] { opacity: 0; } }
/* The dark background behind it fades along with it. */
.lightbox::backdrop {
  opacity: 0;
  transition: opacity 0.25s ease, overlay 0.25s allow-discrete, display 0.25s allow-discrete;
}
.lightbox[open]::backdrop { opacity: 1; }
@starting-style { .lightbox[open]::backdrop { opacity: 0; } }

/* Visitors who ask their device for less motion get none of this movement. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
  .site-menu, .site-menu::backdrop, .lightbox, .lightbox::backdrop, .lightbox-stage img { transition: none; }
}
/* The light / dark button inside the menu: a full row, not the small round icon. */
.menu-action.theme-toggle { justify-content: flex-start; width: 100%; height: auto; border-radius: 10px; color: var(--text); }
.menu-action.theme-toggle svg { width: 20px; height: 20px; }
.footer-links .contact-label { min-width: 0; color: var(--muted); }

/* ---------- Gentle hover ---------- */
/* Buttons and chips fade to their hover color instead of switching at once,
   like the photos do. Only where there is a mouse. */
@media (hover: hover) {
  .menu-button, .share-button, .category-nav a, .menu-chips a, .menu-close,
  .menu-action, .lightbox-button, .lightbox-download {
    transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
  }
}
/* Switching light and dark mode changes every color at once (theme.js). */
.theme-switching * { transition: none !important; }

/* The Share button shows before the page is first drawn, so the photos below
   it never jump down. Without scripts it stays hidden (theme.js sets data-js). */
html:not([data-js]) [data-share] { display: none; }
