/* ===========================================================================
   PhotoGalaxy · public shared gallery
   Loads after theme.css. Uses the same tokens as the rest of the app.
   =========================================================================== */

.shared-main { padding: 0; min-height: 100vh; }

/* ---------- COVER ---------- */
.cover {
  position: relative;
  width: 100%;
  height: min(78vh, 680px);
  background: var(--surface-alt) center/cover no-repeat;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
/* Empty-state cover (no image uploaded). Used in two places:
   - Public gallery view at tenant subdomain (visitors land here)
   - Public gallery view at legacy share-link route
   In both, we render the cover-style design without a background photo — solid black
   with the gallery name and byline in white, plus a subtle gradient for depth. Matches
   the dashboard preview's empty state so what the photographer sees in editing matches
   what visitors see. */
.cover.is-empty {
  background: #0f0f10;
  color: #fff;
}
.cover.is-empty .cover-overlay {
  background: linear-gradient(180deg, rgba(0,0,0,.0) 0%, rgba(0,0,0,.45) 100%);
}
.cover.is-empty .cover-title,
.cover.is-empty .cover-byline,
.cover.is-empty .cover-description {
  color: #fff;
}
.cover-overlay {
  position: absolute; inset: 0;
  pointer-events: none;
  transition: background var(--dur) var(--ease);
}
.cover-content {
  position: relative; z-index: 2;
  text-align: center;
  color: var(--text-on-accent);
  padding: 0 24px;
  margin-top: -32px;
  transition: all var(--dur) var(--ease);
}
.cover-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(40px, 6.5vw, 78px);
  line-height: 1;
  letter-spacing: -.01em;
  margin: 0;
  text-shadow: 0 2px 20px rgba(0, 0, 0, .25);
}
.cover-byline {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: 13px;
  letter-spacing: .2em;
  margin: 14px 0 0;
  opacity: .95;
  text-shadow: 0 1px 6px rgba(0, 0, 0, .3);
}
/* Optional photographer-written caption shown below the byline on the cover.
   Sits in the same white-on-cover-image context, so we use the same shadow trick
   for legibility regardless of what the cover image looks like underneath. */
.cover-description {
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  letter-spacing: 0;
  font-style: normal;
  max-width: 580px;
  margin: 16px auto 0;
  opacity: .95;
  text-shadow: 0 1px 8px rgba(0, 0, 0, .35);
}
.cover-scroll {
  position: absolute;
  bottom: 28px; left: 50%;
  transform: translateX(-50%);
  color: var(--text-on-accent);
  opacity: .8;
  z-index: 3;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.cover-scroll:hover {
  opacity: 1;
  transform: translateX(-50%) translateY(2px);
}

/* ---------- COVER VARIANTS ---------- */

/* Classic — full image with soft top/bottom gradient, centered title */
.cover-classic .cover-overlay,
.cover-overlay .cover-overlay {
  background: linear-gradient(180deg,
    rgba(0,0,0,.25) 0%,
    rgba(0,0,0,0) 32%,
    rgba(0,0,0,0) 68%,
    rgba(0,0,0,.28) 100%);
}

/* Framed — title inside a thin centered rectangle */
.cover-framed .cover-overlay {
  background: linear-gradient(180deg,
    rgba(0,0,0,.15) 0%,
    rgba(0,0,0,.05) 100%);
}
.cover-framed .cover-content {
  padding: 56px 80px;
  border: 1px solid rgba(255,255,255,.85);
  margin-top: 0;
  max-width: min(680px, 80%);
}
.cover-framed .cover-title {
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: clamp(32px, 5vw, 56px);
  letter-spacing: .04em;
  text-transform: uppercase;
}

/* Split — image covers right 7/12, sand-tinted panel on left 5/12 holds title */
.cover-split {
  display: grid;
  grid-template-columns: 5fr 7fr;
  background-position: right center;   /* anchor the image to the right column */
}
.cover-split .cover-overlay {
  left: 0;
  right: auto;
  width: calc(5 / 12 * 100%);
  background: linear-gradient(135deg, rgba(0,0,0,.45), rgba(0,0,0,.65));
}
.cover-split .cover-content {
  grid-column: 1;
  text-align: left;
  align-self: center;
  padding: 0 64px;
  margin: 0;
  z-index: 3;
}
.cover-split .cover-title {
  font-size: clamp(32px, 4.5vw, 58px);
}
.cover-split .cover-title::after {
  content: "";
  display: block;
  width: 48px;
  height: 1px;
  background: rgba(255,255,255,.85);
  margin: 16px 0 0;
}
.cover-split .cover-byline {
  text-align: left;
  margin-top: 18px;
  letter-spacing: .18em;
  font-style: normal;
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 11px;
  text-transform: uppercase;
}

/* Minimal — no overlay, small caps title, image stays bright */
.cover-minimal .cover-overlay {
  background: rgba(0,0,0,.1);
}
.cover-minimal .cover-title {
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: clamp(20px, 2.4vw, 30px);
  letter-spacing: .32em;
  text-transform: uppercase;
}
.cover-minimal .cover-byline {
  font-size: 11px;
  letter-spacing: .28em;
}

/* Editorial — left-aligned title with left-aligned byline under a rule */
.cover-editorial {
  align-items: flex-end;
  justify-content: flex-start;
}
.cover-editorial .cover-overlay {
  background: linear-gradient(180deg,
    rgba(0,0,0,.0) 40%,
    rgba(0,0,0,.45) 100%);
}
.cover-editorial .cover-content {
  text-align: left;
  padding: 0 64px 80px;
  margin: 0;
  max-width: 900px;
}
.cover-editorial .cover-title {
  font-size: clamp(40px, 5.5vw, 72px);
}
.cover-editorial .cover-byline {
  text-align: left;
  padding-top: 16px;
  margin-top: 18px;
  border-top: 1px solid rgba(255,255,255,.7);
  display: inline-block;
  padding-right: 40px;
}

/* Duotone — dark monochrome wash across the image, centered title */
.cover-duotone .cover-overlay {
  background:
    linear-gradient(180deg, rgba(44,43,46,.35), rgba(44,43,46,.5)),
    /* Slight grey-tint blend so any image reads darker and unified */
    rgba(44, 43, 46, .2);
  mix-blend-mode: normal;
}
.cover-duotone .cover-title {
  letter-spacing: .04em;
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: clamp(36px, 5vw, 64px);
  text-transform: uppercase;
}

/* Band — horizontal translucent dark band centered on the image */
.cover-band .cover-overlay {
  background:
    linear-gradient(180deg, transparent 0%, transparent 38%, rgba(0,0,0,.55) 38%, rgba(0,0,0,.55) 62%, transparent 62%, transparent 100%);
}
.cover-band .cover-content {
  padding: 22px 24px;
  margin: 0;
}
.cover-band .cover-title {
  font-size: clamp(28px, 4vw, 48px);
  letter-spacing: .08em;
  text-transform: uppercase;
  font-family: var(--font-ui);
  font-weight: 400;
}

/* Corner — small card anchored to lower-left with title + byline */
.cover-corner {
  align-items: flex-end;
  justify-content: flex-start;
}
.cover-corner .cover-overlay {
  background: linear-gradient(135deg, rgba(0,0,0,.25), rgba(0,0,0,0) 50%);
}
.cover-corner .cover-content {
  margin: 0 0 48px 48px;
  padding: 22px 28px;
  background: rgba(44, 43, 46, .72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border-radius: var(--radius-sm);
  text-align: left;
  max-width: 420px;
}
.cover-corner .cover-title {
  font-size: clamp(26px, 3.2vw, 42px);
  letter-spacing: -.01em;
}
.cover-corner .cover-byline {
  text-align: left;
  margin-top: 8px;
  letter-spacing: .16em;
  font-style: normal;
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 10px;
  text-transform: uppercase;
  opacity: .85;
}

/* Vintage — cream paper panel floating on the image, serif title */
.cover-vintage .cover-overlay {
  background: linear-gradient(180deg, rgba(0,0,0,.2), rgba(0,0,0,.3));
}
.cover-vintage .cover-content {
  background: #f4ece0;
  color: #3a2f24;
  padding: 40px 56px;
  border: 1px solid rgba(58, 47, 36, .18);
  max-width: 560px;
  margin-top: 0;
}
.cover-vintage .cover-title {
  color: #3a2f24;
  text-shadow: none;
  font-style: italic;
  font-size: clamp(40px, 5.5vw, 60px);
}
.cover-vintage .cover-byline {
  color: #6b5a46;
  text-shadow: none;
  margin-top: 10px;
}

/* ---------- STICKY TOOLBAR ---------- */
.section-bar {
  position: sticky; top: 0; z-index: 10;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: saturate(1.4) blur(14px);
  -webkit-backdrop-filter: saturate(1.4) blur(14px);
  border-bottom: 1px solid var(--line);
}
.section-bar-inner {
  max-width: 1400px; margin: 0 auto;
  padding: 16px 28px;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 24px;
}

/* Left: "PHOTOS BY / NAME" stacked */
.section-bar-brand {
  display: flex; flex-direction: column;
  line-height: 1.15;
}
.brand-label {
  font-size: 10px;
  letter-spacing: .22em;
  color: var(--text-muted);
  font-weight: 500;
}
.brand-name {
  font-size: 13px;
  letter-spacing: .12em;
  color: var(--text);
  font-weight: 500;
  margin-top: 2px;
}

/* Center: section links */
.section-bar-links {
  display: flex;
  justify-content: center;
  gap: 40px;
  overflow-x: auto;
  scrollbar-width: none;
}
.section-bar-links::-webkit-scrollbar { display: none; }
.section-link {
  position: relative;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .18em;
  color: var(--text-secondary);
  padding: 10px 2px;
  white-space: nowrap;
  transition: color var(--dur) var(--ease);
}
.section-link:hover { color: var(--text); }
.section-link.active { color: var(--text); }
.section-link.active::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1.5px;
  background: var(--accent);
}

/* Right: tool icons + user */
.section-bar-tools {
  display: flex; align-items: center; gap: 12px;
  justify-content: flex-end;
}
.tool-btn {
  background: none; border: none; cursor: pointer;
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-secondary);
  border-radius: 50%;
  transition:
    background var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}
.tool-btn:hover {
  background: var(--surface-alt);
  color: var(--text);
}
.tool-sep {
  width: 1px; height: 20px;
  background: var(--line);
  margin: 0 4px;
}
.tool-user {
  display: inline-flex; align-items: center; gap: 8px;
}
.tool-user-avatar {
  width: 30px; height: 30px;
  border-radius: 50%;
  background: var(--surface-sunken);
  color: var(--text-secondary);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 600;
}
.tool-user-name {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 13px;
  color: var(--text-secondary);
}

/* ---------- FACE FILTER ----------
   Strip of circular avatar chips above the masonry. Selecting one or more filters
   the photos below to those containing the selected person(s). AND semantics —
   multiple chips show photos containing ALL of them together.
*/
.face-filter-bar {
  max-width: 1680px; margin: 0 auto;
  padding: 24px 32px 0;
}
.face-filter-inner {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 18px;
  display: flex;
  align-items: center;
  gap: 18px;
  box-shadow: var(--shadow-xs);
}
.face-filter-label {
  font-family: var(--font-ui);
  font-size: 11px;
  letter-spacing: .12em;
  font-weight: 600;
  color: var(--text-muted);
  flex-shrink: 0;
}
.face-chips {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  scrollbar-width: thin;
  padding: 2px;          /* room for the selection ring to render without clipping */
  flex: 1;
}
.face-chips::-webkit-scrollbar { height: 6px; }
.face-chips::-webkit-scrollbar-thumb { background: var(--line); border-radius: 3px; }

.face-chip {
  flex-shrink: 0;
  position: relative;
  width: 44px; height: 44px;
  border-radius: 50%;
  border: 2px solid transparent;
  padding: 0;
  background: var(--surface-alt);
  cursor: pointer;
  overflow: visible;     /* count badge can hang slightly outside the circle */
  transition:
    transform var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
.face-chip img {
  width: 100%; height: 100%;
  border-radius: 50%;
  object-fit: cover;
  display: block;
}
.face-chip-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%; height: 100%;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--text-secondary);
  font-weight: 600;
  font-size: 16px;
}
.face-chip:hover {
  transform: scale(1.06);
  border-color: var(--line-strong);
}
.face-chip.selected {
  border-color: var(--text);
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--text);
}
/* Small face-count badge sitting bottom-right of the chip. */
.face-chip-count {
  position: absolute;
  bottom: -3px; right: -3px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--surface);
  border: 1.5px solid var(--line);
  color: var(--text-secondary);
  font-size: 10px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-variant-numeric: tabular-nums;
}
.face-chip.selected .face-chip-count {
  background: var(--text);
  color: var(--surface);
  border-color: var(--text);
}

.face-filter-clear {
  flex-shrink: 0;
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .04em;
  cursor: pointer;
  padding: 6px 10px;
  border-radius: var(--radius-xs);
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.face-filter-clear:hover {
  color: var(--text);
  background: var(--surface-alt);
}

/* Items hidden by the face filter — also respected by masonry.js to recompute layout. */
.masonry-item.hidden { display: none; }

@media (max-width: 640px) {
  .face-filter-bar { padding: 16px 16px 0; }
  .face-filter-inner { padding: 10px 12px; gap: 12px; }
  .face-filter-label { display: none; }     /* save room on small screens */
}

/* ---------- SECTIONS + MASONRY ---------- */
.sections-host {
  /* Wider than dashboard pages — gallery viewers benefit from edge-to-edge use
     of the screen. 1680px caps it at very large displays so individual tiles
     don't grow unreasonably; below that the container shrinks with the viewport. */
  max-width: 1680px; margin: 0 auto;
  padding: 40px 32px 80px;
}
.gallery-section { padding: 40px 0 60px; }
.gallery-section-head {
  text-align: center;
  margin-bottom: 40px;
}
.gallery-section-head h2 {
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 16px;
  letter-spacing: .4em;
  color: var(--text);
  margin: 0;
  padding-left: .4em;
}
/* Subtitle below the section title — renders the section's optional description.
   Sits centered with the heading and uses muted body text so it reads as
   supporting context rather than competing with the section name. */
.gallery-section-sub {
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-secondary);
  margin: 14px auto 0;
  max-width: 560px;
  text-align: center;
}

/* Masonry layout — true 2D packing done in JavaScript (wwwroot/js/masonry.js).
   The container is positioned and gets its height set explicitly by the packer.
   Each .masonry-item is absolutely positioned; its top/left are written by JS
   based on the natural rendered height of the image inside.

   Why not CSS columns? CSS columns fill top-to-bottom per column, so when the
   shortest column happens to also be the leftmost, gaps appear in other columns
   (e.g., column 1 has 3 photos totaling 1200px, columns 2-3 have 2 photos
   totaling 700px → a 500px void at the bottom of cols 2-3). True 2D masonry
   solves this by placing each tile into the currently-shortest column.

   While JS lays out the page, items render in flow with `data-masonry-pending`
   to avoid a flash of broken layout — they fade in once positioned. */
.masonry {
  position: relative;
  width: 100%;
  /* Clip absolutely-positioned children to the container's box. If JS ever measures
     the container width wrong and positions an item slightly past the right edge,
     it gets clipped here rather than pushing the page wider. */
  overflow: hidden;
}
.masonry-item {
  position: absolute;
  top: 0; left: 0;
  /* box-sizing: border-box so the JS-set width INCLUDES the 1px border on each side.
     Without this, an item with width:343px renders at 345px (content + 2*1px border),
     overflowing the container on the right and creating visible left-bias on a single-
     column phone layout. */
  box-sizing: border-box;
  border-radius: var(--radius-xs);
  overflow: hidden;
  background: var(--surface-alt);
  cursor: zoom-in;
  border: 1px solid var(--line-soft);
  box-shadow: var(--shadow-xs);
  /* Smooth re-flow when columns recompute on window resize. */
  transition:
    top var(--dur-slow) var(--ease),
    left var(--dur-slow) var(--ease),
    width var(--dur-slow) var(--ease),
    transform var(--dur-slow) var(--ease),
    box-shadow var(--dur-slow) var(--ease);
}
/* Before JS positions an item we hide it to avoid a flash of stacked items
   piled at (0,0). The masonry script removes this class after layout completes. */
.masonry-item[data-masonry-pending] {
  visibility: hidden;
}
.masonry-item img {
  width: 100%; height: auto; display: block;
  transition: transform var(--dur-slow) var(--ease);
}
.masonry-item:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-sm);
}
.masonry-item.hidden { display: none; }

/*
  Description overlay — slides up from bottom on hover. Kept lightweight so
  it doesn't compete with the photo. Falls back to native title tooltip on
  touch devices that have no hover state.
*/
.masonry-caption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 14px 16px;
  background: linear-gradient(to top, rgba(20, 18, 15, .85), rgba(20, 18, 15, 0));
  color: #fafaf8;
  font-size: 13px;
  line-height: 1.4;
  letter-spacing: .01em;
  opacity: 0;
  transform: translateY(8px);
  transition:
    opacity var(--dur) var(--ease),
    transform var(--dur) var(--ease);
  pointer-events: none;
  /* Limit to 3 lines so very long descriptions don't blanket the photo */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.masonry-item:hover .masonry-caption,
.masonry-item:focus-within .masonry-caption {
  opacity: 1;
  transform: translateY(0);
}

/* ---------- SLIDESHOW LIGHTBOX ---------- */
.lightbox {
  position: fixed; inset: 0;
  background: rgba(15, 15, 16, .96);
  display: none;
  align-items: center; justify-content: center;
  z-index: 100;
  cursor: zoom-out;
  padding: 40px 80px;
  animation: lightboxIn var(--dur) var(--ease);
}
.lightbox.open { display: flex; }

/* In OS fullscreen the lightbox IS the screen — drop the chrome padding so the photo
   takes full advantage of the available space. Solid background so no host page
   bleed-through at the edges. */
.lightbox:fullscreen,
.lightbox:-webkit-full-screen {
  padding: 0;
  background: #0f0f10;
}
.lightbox:fullscreen .lightbox-img,
.lightbox:-webkit-full-screen .lightbox-img {
  /* Allow image to use the entire screen minus a small breathing margin. */
  max-height: 100vh;
  border-radius: 0;
}

/* ---------- AUTO-HIDE CONTROLS ON IDLE ----------
   `.lightbox.is-idle` is toggled by slideshow.js after 5s of no user activity. It fades
   out all chrome — close button, nav arrows, play/stop pill, index pill, caption —
   leaving only the photo. Cleared on any mouse / key / touch activity.

   Why pointer-events:none alongside opacity:0 — without it, the (invisible) buttons
   would still consume clicks. The image area becomes one big tap target during idle,
   which is what we want for the "single tap to wake" behavior on mobile.

   The cursor is hidden too because in cinematic mode an idle cursor is visual noise.
   Moving the mouse re-shows it (and the controls) automatically. */
.lightbox.is-idle { cursor: none; }
.lightbox.is-idle .lightbox-close,
.lightbox.is-idle .lightbox-nav,
.lightbox.is-idle .lightbox-controls,
.lightbox.is-idle .lightbox-index,
.lightbox.is-idle .lightbox-section,
.lightbox.is-idle .lightbox-caption {
  opacity: 0;
  pointer-events: none;
  transition: opacity 600ms ease;
}
/* Fade IN transition (default state) — slightly faster than the fade-out so the chrome
   feels responsive when activity resumes. */
.lightbox-close,
.lightbox-nav,
.lightbox-controls,
.lightbox-index,
.lightbox-section,
.lightbox-caption {
  transition: opacity 200ms ease;
}

/* Image stage — vertical stack of image + caption, centered. Stops click propagation
   so clicking the image area doesn't close the slideshow. */
.lightbox-stage {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  max-width: 100%;
  max-height: 100%;
  cursor: default;
}
.lightbox-img {
  max-width: 100%;
  /* Pills now overlay the image rather than sit below it, so we don't need to reserve
     room at the bottom of the viewport. Slight padding so the image doesn't hit the
     absolute viewport edges in fullscreen. */
  max-height: calc(100vh - 40px);
  object-fit: contain;
  border-radius: var(--radius-xs);
  box-shadow: 0 30px 80px rgba(0, 0, 0, .4);
  animation: slideIn var(--dur) var(--ease);
  /* Explicit z-index so the image sits BELOW the overlay chrome (caption, nav arrows,
     pills). Without this, very tall portraits or panoramas could appear to "swallow" the
     nav buttons because the image element renders in normal flow above absolutely-positioned
     siblings in some stacking contexts. */
  position: relative;
  z-index: 1;
}

/* Caption — overlays the BOTTOM of the image (was a flow-element sibling sitting below it,
   which got clipped off the bottom of the viewport on tall photos). Now an absolute
   overlay so it's always readable on the image itself. Bottom-center placement leaves the
   top-left (section pill) and top-right (index pill) clear and matches the convention of
   most professional photo viewers (Lightroom, Aperture, Pic-Time). */
.lightbox-caption {
  position: absolute;
  left: 50%;
  bottom: 72px;                /* sit just above the play/stop pill which is bottom: 16px */
  transform: translateX(-50%);
  max-width: min(720px, 90%);
  padding: 8px 16px;
  background: rgba(0, 0, 0, .55);
  color: rgba(255, 255, 255, .95);
  font-size: 13px;
  line-height: 1.5;
  text-align: center;
  letter-spacing: .01em;
  border-radius: var(--radius-pill);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  z-index: 3;
  pointer-events: none;
  /* Truncate very long descriptions to two lines so a paragraph caption doesn't
     dominate the photo. Users with long captions can still read the full text in the
     gallery view; the lightbox is for the photo. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Top-right close */
.lightbox-close {
  position: absolute;
  top: 20px; right: 24px;
  background: rgba(255, 255, 255, .08);
  border: none;
  color: #fff;
  font-size: 28px;
  width: 44px; height: 44px;
  border-radius: 50%;
  cursor: pointer;
  opacity: .85;
  display: inline-flex; align-items: center; justify-content: center;
  transition: opacity var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
  z-index: 4;                  /* above caption (3) and image (1) */
}
.lightbox-close:hover {
  opacity: 1;
  background: rgba(255, 255, 255, .15);
}

/* Prev/next nav buttons — flank the image vertically centered.
   Explicit z-index so they ride above the image (z:1) even when the image fills the
   entire stage. Without this, very large photos appeared to "cover" the nav arrows. */
.lightbox-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background: rgba(0, 0, 0, .35);
  border: none;
  color: #fff;
  width: 52px; height: 52px;
  border-radius: 50%;
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: .85;
  transition: opacity var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  z-index: 4;
}
.lightbox-nav:hover {
  opacity: 1;
  background: rgba(0, 0, 0, .55);
}
.lightbox-prev { left: 24px; }
.lightbox-next { right: 24px; }

/* Index counter — bottom-center pill */
/* Photo index pill — sits at the TOP-RIGHT of the stage. Paired with .lightbox-section
   (top-left) so the two pills bracket the photo: section on the left tells you "where
   am I", index on the right tells you "which one am I on". */
.lightbox-index {
  position: absolute;
  top: 16px; right: 16px;
  background: rgba(0, 0, 0, .55);
  color: rgba(255, 255, 255, .95);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .08em;
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  font-variant-numeric: tabular-nums;
  z-index: 3;
  pointer-events: none;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

/* Section name pill — TOP-LEFT of the stage. Same visual treatment as the index pill
   for symmetry. Shows which section the current photo belongs to; updated by
   slideshow.js on every render() call so it tracks the photo as you navigate. */
.lightbox-section {
  position: absolute;
  top: 16px; left: 16px;
  max-width: calc(50% - 40px);    /* don't grow into the index pill on tiny viewports */
  background: rgba(0, 0, 0, .55);
  color: rgba(255, 255, 255, .95);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;       /* matches the section heading style on the gallery */
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  z-index: 3;
  pointer-events: none;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  /* Single-line truncation — long section names get ellipsis rather than wrapping
     into a multi-line block that competes visually with the image. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Slideshow controls — Play/Pause + Stop. Overlay the BOTTOM-CENTER of the image.
   In fullscreen mode there's no other chrome, so the controls need to be obvious
   AND ON the image (not floating at viewport edges where they'd be far from the
   eye-line) — this is what users expect from photo viewers like macOS Preview,
   Google Photos, Lightroom, etc. */
.lightbox-controls {
  position: absolute;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 8px;
  z-index: 3;
  /* Subtle dark pill behind the buttons so they read clearly on bright photos
     (white birds on white sky, snow scenes, etc.) where translucent buttons alone
     would disappear. */
  padding: 6px;
  background: rgba(0, 0, 0, .35);
  border-radius: calc(var(--radius-pill) + 6px);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.lightbox-ctrl {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, .12);
  color: rgba(255, 255, 255, .95);
  border: 1px solid rgba(255, 255, 255, .22);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .06em;
  cursor: pointer;
  font-family: inherit;
  transition:
    background var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    opacity var(--dur-fast) var(--ease);
}
.lightbox-ctrl:hover:not([disabled]) {
  background: rgba(255, 255, 255, .25);
  border-color: rgba(255, 255, 255, .4);
}
.lightbox-ctrl[disabled] {
  opacity: .4;
  cursor: not-allowed;
}
.lightbox-ctrl svg { flex-shrink: 0; }
.lightbox-ctrl .ctrl-label { line-height: 1; }

/* Slide duration selector — looks like a lightbox-ctrl pill but wraps a native <select>.
   Native rather than custom dropdown because:
     - iOS/Android get their familiar OS picker for free (touch-friendly)
     - <select> handles focus + keyboard nav correctly out of the box
     - One less component for us to maintain
   The label is the visual pill; the select inside is mostly transparent except for its
   text content (the chosen "2s" / "5s" etc), which we restyle to fit the dark glass pill.
*/
.lightbox-duration {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px 5px 12px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, .12);
  color: rgba(255, 255, 255, .95);
  border: 1px solid rgba(255, 255, 255, .22);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
  position: relative;
}
.lightbox-duration:hover {
  background: rgba(255, 255, 255, .25);
  border-color: rgba(255, 255, 255, .4);
}
.lightbox-duration svg {
  flex-shrink: 0;
  opacity: .85;
}
.lightbox-duration-select {
  /* Strip native chrome — we let the surrounding pill provide the look. */
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  background: transparent;
  border: none;
  color: inherit;
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .06em;
  padding: 2px 14px 2px 0;
  cursor: pointer;
  outline: none;
  font-family: inherit;
  /* Custom dropdown chevron drawn via background-image — native arrow is removed by
     appearance:none above. White SVG so it shows on the dark pill background. */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' stroke='white' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right center;
  background-size: 10px 6px;
}
/* Option list is rendered by the OS — we can't fully style it, but at least the dropdown
   itself should be readable. Most browsers honor color/background on option elements. */
.lightbox-duration-select option {
  background: #1a1a1c;
  color: #fff;
}
.lightbox-duration-select:focus-visible {
  outline: 2px solid rgba(255, 255, 255, .55);
  outline-offset: 2px;
  border-radius: 4px;
}

@media (max-width: 640px) {
  .lightbox-duration { padding: 6px 8px 6px 10px; }
  .lightbox-duration-select { font-size: 11px; padding-right: 12px; }
}

@media (max-width: 640px) {
  /* Keep the controls visible but tighter on phones. */
  .lightbox-ctrl .ctrl-label { display: none; }
  .lightbox-ctrl { padding: 8px 10px; }
  .lightbox-controls { bottom: 12px; padding: 5px; }
  .lightbox-index { top: 12px; right: 12px; font-size: 11px; padding: 5px 12px; }
  .lightbox-section { top: 12px; left: 12px; font-size: 10px; padding: 5px 10px; max-width: calc(60% - 24px); }
}

@keyframes lightboxIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes slideIn {
  from { opacity: 0; transform: scale(.98); }
  to { opacity: 1; transform: scale(1); }
}

/* Touch/small-screen tweaks — tighter padding, smaller nav buttons */
@media (max-width: 640px) {
  .lightbox { padding: 16px; }
  .lightbox-nav {
    width: 40px; height: 40px;
    background: rgba(255, 255, 255, .14);
  }
  .lightbox-prev { left: 8px; }
  .lightbox-next { right: 8px; }
  .lightbox-close { top: 12px; right: 12px; width: 36px; height: 36px; font-size: 22px; }
  .lightbox-img { max-height: calc(100vh - 130px); }
}

/* ---------- RESPONSIVE ---------- */
@media (max-width: 960px) {
  .section-bar-inner {
    grid-template-columns: 1fr auto;
    gap: 14px;
  }
  .section-bar-tools { grid-column: 2; }
  .section-bar-links {
    grid-column: 1 / -1;
    order: 3;
    justify-content: flex-start;
    gap: 28px;
    padding-top: 6px;
    border-top: 1px solid var(--line-soft);
  }
}
@media (max-width: 560px) {
  .cover { height: 62vh; }
  .cover-title { font-size: clamp(32px, 10vw, 48px); }
  .section-bar-inner { padding: 12px 16px; }
  .brand-name { font-size: 12px; }
  .tool-user-name { display: none; }
  .sections-host { padding: 24px 16px 60px; }
  .gallery-section { padding: 24px 0 40px; }
  .gallery-section-head h2 { font-size: 13px; letter-spacing: .35em; }
  .face-filter { padding: 16px 16px 0; }
  .face-filter-inner { flex-direction: column; align-items: flex-start; }
}

/* ---------- PUBLIC SINGLE-PHOTO SHARE VIEW ----------
   Minimal centered layout for /photo/{token}. Echoes the gallery aesthetic without
   the navigation chrome — visitor is here for this one photo.
*/
.photo-share-shell {
  max-width: 1280px;
  margin: 0 auto;
  padding: 32px 24px 80px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.photo-share-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.photo-share-eyebrow {
  font-family: var(--font-ui);
  font-size: 11px;
  letter-spacing: .18em;
  font-weight: 600;
  color: var(--text-muted);
}
.photo-share-section {
  font-family: var(--font-display);
  font-size: 22px;
  color: var(--text);
  margin-top: 4px;
  line-height: 1.1;
}
.photo-share-download {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-radius: var(--radius-pill);
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--text-secondary);
  font-size: 13px;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.photo-share-download:hover {
  background: var(--surface-alt);
  color: var(--text);
}
.photo-share-stage {
  background: var(--surface-alt);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Cap height so very tall portraits don't push the caption off the page; visitor can
     click the image to view at full resolution if they need to. */
  max-height: 86vh;
}
.photo-share-stage img {
  width: 100%;
  height: auto;
  max-height: 86vh;
  object-fit: contain;
  display: block;
}
.photo-share-caption {
  font-size: 15px;
  color: var(--text-secondary);
  line-height: 1.5;
  text-align: center;
  margin: 0;
  max-width: 720px;
  align-self: center;
}
.photo-share-missing {
  max-width: 480px;
  margin: 80px auto;
  padding: 40px;
  text-align: center;
}
.photo-share-missing h1 {
  font-family: var(--font-display);
  font-size: 28px;
  margin: 0 0 12px;
}
.photo-share-missing p {
  color: var(--text-secondary);
  margin: 0 0 24px;
}

/* ---------- DASHBOARD: PHOTO GRID HOVER ACTIONS ----------
   Share + Edit buttons that appear on the thumbnail in the upload management page.
*/
.thumb-actions {
  position: absolute;
  top: 8px; right: 8px;
  display: flex;
  gap: 6px;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease);
  z-index: 2;
}
.thumb:hover .thumb-actions,
.thumb:focus-within .thumb-actions { opacity: 1; }
.thumb-action-btn {
  width: 30px; height: 30px;
  border-radius: 50%;
  background: rgba(0, 0, 0, .55);
  color: #fff;
  border: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.thumb-action-btn:hover { background: rgba(0, 0, 0, .75); }

/* Drag handle / dragging visual feedback when SortableJS picks up a tile. */
.thumb { cursor: grab; }
.thumb.sortable-chosen { cursor: grabbing; }
.thumb.sortable-ghost {
  opacity: .35;
  background: var(--accent-soft);
}
.thumb.sortable-drag {
  cursor: grabbing;
  box-shadow: 0 12px 32px rgba(0, 0, 0, .25);
  transform: scale(1.02);
}

/* ---------- PHOTO EDITOR MODAL ---------- */
.photo-editor-modal {
  position: fixed; inset: 0;
  z-index: 200;
  background: rgba(15, 15, 16, .94);
  display: none;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.photo-editor-modal.open { display: flex; }

/* ---------- Three-column editor shell ---------- */
/* The shell takes ~95% of the viewport so the editor feels like a dedicated workspace
   without being literally fullscreen (which would lose the modal context). At very small
   viewports we fill the screen edge-to-edge and let the columns stack. */
.photo-editor-shell {
  background: var(--surface);
  border-radius: var(--radius);
  width: min(1280px, 100%);
  height: min(840px, 92vh);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .35);
  /* Position context for the absolutely-positioned loader overlay (showLoader injects
     a child with position:absolute inset:0). Without this, the loader would anchor to
     the viewport and could escape the modal boundaries. */
  position: relative;
}

.photo-editor-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 20px;
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
.photo-editor-title {
  font-family: var(--font-display);
  font-size: 18px;
  margin: 0;
  color: var(--text);
}
.photo-editor-close {
  width: 36px; height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  border-radius: var(--radius-xs);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.photo-editor-close:hover { background: var(--surface-alt); color: var(--text); }

/* Body = three-column grid: tool rail | preview stage | settings pane.
   Settings rail is sized for slider labels + readouts; preview takes everything else. */
.photo-editor-body {
  display: grid;
  grid-template-columns: 140px 1fr 300px;
  flex: 1;
  min-height: 0;       /* allow grid children to overflow / scroll properly */
}

/* ---------- LEFT RAIL (tool nav) ---------- */
/* Background tinted slightly darker than the surface to distinguish navigation from
   content — matches the convention of sidebars elsewhere in the app. */
.photo-editor-rail {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px 12px;
  background: var(--surface-alt);
  border-right: 1px solid var(--line);
  overflow-y: auto;
}
.photo-editor-tool {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 14px 8px;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  border-radius: var(--radius-xs);
  font-size: 12px;
  font-family: inherit;
  font-weight: 500;
  letter-spacing: .02em;
  transition:
    background var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}
.photo-editor-tool svg { transition: transform var(--dur-fast) var(--ease); }
.photo-editor-tool:hover {
  background: var(--surface);
  color: var(--text);
}
/* Active tool: prominent fill against the rail background so the user always knows
   which mode they're in. Color chosen from the existing theme (text/surface inversion). */
.photo-editor-tool.is-active {
  background: var(--text);
  color: var(--surface);
}
.photo-editor-tool.is-active:hover {
  background: var(--text);
  color: var(--surface);
}
.photo-editor-rail-spacer { flex: 1; }
.photo-editor-tool-revert {
  /* Revert button has a subtle danger affordance — different from the regular tools
     because it discards work, but not so loud it competes with primary actions. */
  color: var(--text-muted);
  border-top: 1px solid var(--line);
  padding-top: 12px;
  margin-top: 8px;
}
.photo-editor-tool-revert:hover { color: rgba(220, 53, 69, .9); }

/* ---------- CENTER STAGE ---------- */
.photo-editor-stage {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background:
    linear-gradient(45deg, var(--surface-alt) 25%, transparent 25%),
    linear-gradient(-45deg, var(--surface-alt) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--surface-alt) 75%),
    linear-gradient(-45deg, transparent 75%, var(--surface-alt) 75%);
  background-size: 16px 16px;
  background-position: 0 0, 0 8px, 8px -8px, -8px 0;
  background-color: var(--surface);
  /* Checkerboard pattern subtly indicates "canvas area" without competing with the photo —
     standard convention in photo editors. Surface and surface-alt are theme tokens. */
  min-width: 0;
  overflow: hidden;
}
.photo-editor-canvas-wrap {
  position: relative;
  max-width: 100%;
  max-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* The image (and Cropper.js attached to it) sits inside this wrap. We give the image
   an explicit max so it fits the stage without overflowing when very tall or wide. */
.photo-editor-canvas-wrap > img,
.photo-editor-canvas-wrap > img#photoEditorImage {
  max-width: 100%;
  max-height: calc(100vh - 280px);   /* viewport minus header/footer/padding */
  display: block;
  transition: transform var(--dur) var(--ease), filter var(--dur-fast) var(--ease);
}

/* Cropper.js renders its UI inside the bound <img>'s slot using its own container.
   These overrides bring its visual style in line with our design system — same line
   colors, slightly more subtle dashed border so the photo is the visual focus rather
   than the crop chrome. */
.cropper-line, .cropper-point {
  background-color: rgba(255, 255, 255, .9);
}
.cropper-view-box {
  outline: 1px solid rgba(255, 255, 255, .9);
}
.cropper-dashed {
  border-color: rgba(255, 255, 255, .55);
}

/* ---------- RIGHT RAIL (settings) ---------- */
.photo-editor-settings {
  padding: 20px;
  border-left: 1px solid var(--line);
  overflow-y: auto;
  background: var(--surface);
}
.photo-editor-pane {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* The [hidden] HTML attribute's default `display: none` would normally suffice, but the
   .photo-editor-pane class rule above declares `display: flex`. Class selectors and the
   browser's UA stylesheet for [hidden] have similar effective specificity in this context,
   and class rules from author CSS win. Explicit override ensures hidden panes stay hidden. */
.photo-editor-pane[hidden] {
  display: none;
}
.photo-editor-pane-title {
  font-family: var(--font-display);
  font-size: 16px;
  margin: 0 0 4px;
  color: var(--text);
}
.photo-editor-group-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: -4px;
}

.photo-editor-aspect-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.photo-editor-aspect {
  flex: 1 1 auto;
  min-width: 48px;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text-secondary);
  font-size: 12px;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  transition:
    background var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}
.photo-editor-aspect:hover {
  background: var(--surface-alt);
  color: var(--text);
}
.photo-editor-aspect.is-active {
  background: var(--text);
  border-color: var(--text);
  color: var(--surface);
}

.photo-editor-control {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.photo-editor-control label {
  font-size: 12px;
  color: var(--text-secondary);
  letter-spacing: .04em;
  display: flex;
  justify-content: space-between;
}
.photo-editor-control input[type="range"] {
  width: 100%;
  accent-color: var(--text);
}
.photo-editor-divider {
  height: 1px;
  background: var(--line);
  margin: 4px 0;
}

.photo-editor-rotate {
  display: flex;
  gap: 8px;
}
.photo-editor-rotate button {
  flex: 1;
  padding: 10px;
  border-radius: var(--radius-xs);
  border: 1px solid var(--line);
  background: var(--surface);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: var(--text-secondary);
  font-size: 13px;
  font-family: inherit;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.photo-editor-rotate button:hover { background: var(--surface-alt); color: var(--text); }

/* Flip buttons — full-width pill-ish that toggles between unfilled (inactive) and
   filled (flip applied). Visual state is everything because flip is a binary toggle. */
.photo-editor-flip-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text-secondary);
  cursor: pointer;
  border-radius: var(--radius-xs);
  font-size: 13px;
  font-family: inherit;
  width: 100%;
  text-align: left;
  transition:
    background var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}
.photo-editor-flip-btn:hover {
  background: var(--surface-alt);
  color: var(--text);
}
.photo-editor-flip-btn.is-active {
  background: var(--text);
  border-color: var(--text);
  color: var(--surface);
}

.photo-editor-hint {
  margin: 0;
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.5;
}
.photo-editor-hint strong { color: var(--text-secondary); }

/* ---------- FOOTER ACTION BAR ---------- */
.photo-editor-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 14px 20px;
  border-top: 1px solid var(--line);
  background: var(--surface-alt);
  gap: 12px;
  flex-shrink: 0;
}
.photo-editor-foot .editor-actions {
  display: flex;
  gap: 8px;
}

/* ---------- LOADER OVERLAY ---------- */
/* Shown while the image is fetching from blob storage. Covers the body grid only
   (header + footer remain visible so the user can still cancel during load). The
   semi-transparent fill matches the modal's surface so the transition feels
   continuous when the loader hides. */
.photo-editor-loader {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, .82);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  z-index: 5;
}
.photo-editor-loader[hidden] { display: none; }
.photo-editor-spinner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  color: var(--text-secondary);
  font-size: 13px;
  letter-spacing: .04em;
}

/* ---------- RESPONSIVE ---------- */
@media (max-width: 960px) {
  /* On tablets, narrow the rails so the canvas still gets meaningful room. */
  .photo-editor-body { grid-template-columns: 110px 1fr 260px; }
}
@media (max-width: 720px) {
  /* On phones, collapse the rails: tools become a horizontal strip at the top,
     settings become a horizontal scrollable strip at the bottom. The center stage
     is still the dominant area. */
  .photo-editor-modal { padding: 0; }
  .photo-editor-shell {
    width: 100%;
    height: 100vh;
    max-height: 100vh;
    border-radius: 0;
  }
  .photo-editor-body {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr auto;
  }
  .photo-editor-rail {
    flex-direction: row;
    border-right: none;
    border-bottom: 1px solid var(--line);
    padding: 10px;
    gap: 6px;
    overflow-x: auto;
  }
  .photo-editor-tool { flex-direction: row; gap: 8px; padding: 8px 12px; }
  .photo-editor-rail-spacer { display: none; }
  .photo-editor-tool-revert { border-top: none; padding-top: 8px; margin-top: 0; margin-left: auto; }
  .photo-editor-settings {
    border-left: none;
    border-top: 1px solid var(--line);
    max-height: 40vh;
  }
}

/* ---------- SECTION DRAG HANDLE + SECTION DRAG VISUALS ---------- */

/* Drag handle sits left of the section heading. Subtle until hovered — section content
   gets visual priority, the handle is a "second-level" affordance. */
.section-drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px; height: 26px;
  margin-right: 6px;
  border: none;
  background: transparent;
  color: var(--text-muted);
  cursor: grab;
  border-radius: var(--radius-xs);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
  /* iOS Safari needs this hint to prevent text selection during drag */
  -webkit-user-select: none;
  user-select: none;
  touch-action: none;
}
.section-drag-handle:hover {
  background: var(--surface-alt);
  color: var(--text-secondary);
}
.section-drag-handle:active { cursor: grabbing; }

/* While dragging a section, the placeholder. */
.section-sortable-ghost {
  opacity: .35;
  background: var(--accent-soft);
  border-radius: var(--radius);
}
.section-sortable-chosen { cursor: grabbing; }
.section-sortable-drag {
  cursor: grabbing;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .18);
  transform: scale(1.005);
}

/* When dragging a photo, every other thumb-grid should subtly highlight as a valid
   drop target. SortableJS doesn't add a class to "the body during a drag" by default,
   so we listen via class on <body> applied during drag (see photo-reorder.js).
   Instead we use SortableJS's built-in approach: on the destination grid being-dragged-over,
   it adds the sortable-drag class to the moving item — we style the GHOST that
   represents where the drop would land. */
.thumb-grid {
  /* Soft dashed border on hover when empty so an empty section is clearly a drop target. */
  min-height: 60px;
  border-radius: var(--radius-xs);
  transition: background var(--dur-fast) var(--ease), outline var(--dur-fast) var(--ease);
}
.thumb-grid:empty::after {
  content: 'Drop photos here';
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100px;
  width: 100%;
  color: var(--text-muted);
  font-size: 13px;
  border: 1.5px dashed var(--line-strong);
  border-radius: var(--radius-xs);
}

@media (max-width: 640px) {
  /* On phones, dragging sections is fiddly. We keep the handle visible but make it
     slightly larger so it's tappable, and give it more breathing room. */
  .section-drag-handle { width: 32px; height: 32px; }
}
