/* Switch-style option card (e.g. the day-end map transition). The real checkbox stays in the
   DOM for keyboard/screen readers; the track and thumb are drawn from its :checked state. */
.toggle-card {
  position: relative; display: flex; align-items: center; gap: 16px; cursor: pointer; margin: 6px 0 14px;
  padding: 16px 18px; border-radius: var(--r-md); border: 1px solid var(--line-strong);
  background: linear-gradient(120deg, rgba(var(--wash-rgb), .03), rgba(var(--wash-rgb), .01));
  transition: border-color .25s, box-shadow .25s, background .25s;
}
.toggle-card:hover { border-color: rgba(240, 177, 85, .55); }
.toggle-card input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.toggle-track {
  flex: none; width: 52px; height: 30px; border-radius: var(--r-pill); padding: 3px;
  background: rgba(var(--wash-rgb), .12); box-shadow: inset 0 0 0 1px var(--line-strong);
  transition: background .3s, box-shadow .3s;
}
.toggle-thumb {
  display: block; width: 24px; height: 24px; border-radius: 50%; background: var(--ink);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .5); transition: transform .3s cubic-bezier(.3, 1.4, .5, 1), background .3s;
}
.toggle-text { display: grid; gap: 3px; }
.toggle-text strong { font-weight: 700; color: var(--ink); }
.toggle-card-big { padding: 20px 22px; }
.toggle-card-big .toggle-text strong { font-size: 1.15rem; }
.review-maps-toggle { margin: 16px 0; }
.toggle-text small { color: var(--muted); font-size: .82rem; line-height: 1.4; }
.toggle-card:has(input:checked) {
  border-color: rgba(240, 177, 85, .7);
  background: radial-gradient(120% 220% at 0% 50%, rgba(240, 177, 85, .16), rgba(141, 123, 245, .10) 55%, transparent 80%);
  box-shadow: 0 0 0 1px rgba(240, 177, 85, .25), 0 14px 40px -18px rgba(240, 177, 85, .6);
}
.toggle-card input:checked + .toggle-track {
  background: linear-gradient(100deg, var(--gold), var(--violet)); box-shadow: 0 0 18px rgba(240, 177, 85, .55);
}
.toggle-card input:checked + .toggle-track .toggle-thumb { transform: translateX(22px); background: #fff; }
.toggle-card input:focus-visible + .toggle-track { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .toggle-card, .toggle-track, .toggle-thumb { transition: none; } }
.controls .toggle-card .toggle-text { font-size: 1rem; color: var(--ink); font-weight: 400; }
/* Greyed out (checkPhotoDumpAvailability, app-review-photo-dump.js): this album/shape doesn't have enough eligible
   photos for a fast photo dump at all -- explained by #review-dump-unavailable right below the switches, not left
   to fail silently or generically once tapped. */
.toggle-card:has(input:disabled) { cursor: not-allowed; opacity: .5; filter: grayscale(.4); }
.toggle-card input:disabled + .toggle-track { cursor: not-allowed; }
.dump-unavailable { color: var(--warning); }

.look-card-thumb { grid-column: 1 / -1; width: 100%; aspect-ratio: 16 / 7; object-fit: cover; border-radius: 10px; margin-bottom: 8px; background: #0a0c10; }
.look-card-thumb[hidden] { display: none; }

/* Property sheet tabs (Style / Material / Strength) and the option captions. */
.look-card { grid-column: 1 / -1; margin: 6px 0 14px; }
.look-card-thumb { max-height: 240px; }
.look-tabs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.look-tab { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; text-align: left; padding: 6px 12px; cursor: pointer; color: var(--ink);
  background: rgba(var(--wash-rgb), .03); border: 1px solid var(--line-strong); border-radius: var(--r-md, 14px);
  transition: border-color .2s, box-shadow .2s, background .2s; }
.look-tab:hover { border-color: rgba(240, 177, 85, .6); }
.look-tab.active { border-color: var(--accent); background: rgba(240, 177, 85, .12);
  box-shadow: 0 0 0 1px rgba(240, 177, 85, .5), 0 0 24px rgba(240, 177, 85, .45); }
.look-tab-title { font-size: .78rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.look-tab.active .look-tab-title { color: var(--accent); }
.look-tab-value { font-weight: 700; font-size: .95rem; }
.look-hint { margin: 0; color: var(--ink-dim); font-size: 1rem; }
.look-option-name { font-weight: 700; padding: 0 4px; }
.look-option-note { color: var(--muted); font-size: .85rem; padding: 0 4px 4px; }
@media (max-width: 700px) { .look-tabs { grid-template-columns: repeat(3, minmax(0, 1fr)); } .look-tab { display: grid; gap: 0; padding: 6px 8px; } .look-header .look-hint { display: none; } }

/* Wizard sub-steps: short pages with a clear Back / Next and a skip-ahead escape. */
.step-nav { display: flex; align-items: center; gap: 14px; margin-top: 26px; }
.back { padding: 14px 24px; cursor: pointer; color: var(--ink-dim); background: none;
  border: 1px solid var(--line-strong); border-radius: var(--r-pill); font-weight: 700; }
.back:hover { border-color: var(--accent); color: var(--ink); }
/* Back at the top left of every step after the first (macros.step_back): an arrow and the word, a finger's size. */
.step-back-bar { display: flex; justify-content: flex-start; margin: 0 0 8px; min-width: 0; }
.step > .step-back-bar { grid-column: 1 / -1; }   /* a wizard page is a two-column grid: the bar spans both */
.review-step > .step-back-bar { margin-top: 16px; }   /* clear of the header's Create above it */
.step-back { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; min-width: 44px; padding: 8px 18px 8px 12px; }
.step-back svg { width: 20px; height: 20px; flex: none; }
/* On phones the Back / Next row stays pinned to the bottom of the screen so nobody has to scroll to move on. */
@media (max-width: 700px) {
  .step-nav { position: sticky; bottom: 0; z-index: 5; margin-inline: -20px; padding: 26px 20px calc(14px + env(safe-area-inset-bottom));
    background: linear-gradient(to bottom, transparent, var(--surface) 40%); }
  .step-nav .next { flex: 1; }
}
.controls .wide { grid-column: 1 / -1; }
.stepper-dot .dot-label { display: none; }
.stepper-dot.active .dot-label { display: inline; }

.stepper-line[hidden], .stepper-dot[hidden] { display: none; }

/* A row of pill tabs, role="tablist"/role="tab"/aria-selected: the music picker's genre filter
   (music-picker.js) and the dump/teaser sheets' Settings/Photos pages (app-review-photo-dump.js). */
.pill-tabs { display: flex; flex-wrap: wrap; gap: 8px; }
.pill-tabs button { background: var(--surface-2); color: var(--ink); border: 1px solid var(--line-strong); border-radius: var(--r-pill); padding: 7px 14px; font-size: .82rem; cursor: pointer; }
.pill-tabs button[aria-selected="true"] { border-color: var(--accent); color: var(--accent); }
.music-picker { width: min(640px, 94vw); height: min(640px, 88vh); }
/* Phones: one swipeable row of genres and a taller dialog, so the track list keeps most of the screen. */
@media (max-width: 640px) {
  .music-picker { height: 96dvh; max-height: 96dvh; }
  .music-picker .pill-tabs { flex-wrap: nowrap; overflow-x: auto; flex: none; scrollbar-width: none; padding-bottom: 4px; }
  .music-picker .pill-tabs button { flex: none; white-space: nowrap; }
}
.music-status { margin: 0; color: var(--muted); min-height: 1.2em; }
.music-list { flex: 1; min-height: 0; overflow-y: auto; display: grid; gap: 8px; align-content: start; }
.music-track { display: flex; align-items: center; gap: 12px; padding: 10px 12px; cursor: pointer; background: var(--surface); border: 1px solid var(--line); border-radius: 12px; }
.music-track:has(input:checked) { border-color: var(--accent); }
.music-play { flex: none; width: 38px; height: 38px; border-radius: 50%; cursor: pointer; color: var(--ink); background: var(--surface-2); border: 1px solid var(--line-strong); }
.music-track-text { display: grid; min-width: 0; }
.music-track-text strong, .music-track-text small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.music-track-text small { color: var(--muted); }
.music-picker-footer { display: flex; justify-content: flex-end; gap: 10px; }
.music-picked { margin: 6px 0; color: var(--accent); font-size: .9rem; }
.review-music { margin: 12px 0; }
/* Override the global text-input look for the picker's radios so rows stay compact. */
.music-track input[type="radio"] { flex: none; width: 18px; height: 18px; min-height: 0; padding: 0; margin: 0; accent-color: var(--gold); }
.music-track { min-height: 0; }
.music-list { grid-auto-rows: min-content; }
.music-picker .music-status { min-height: 0; }
.music-track { margin: 0; display: flex; font-weight: 400; color: var(--ink); }

/* The film's "Time each photo stays on screen": slider plus a live preview that cuts between photos at the
   chosen pace (still-pace.js), reflecting the "Soft crossfade" and "Gentle motion" toggles. */
.still-nudge-row { display: flex; align-items: center; gap: 10px; }
.still-nudge-row input[type=range] { flex: 1; min-width: 0; }
.still-nudge { width: 44px; height: 44px; flex: none; border-radius: 50%; border: 1px solid var(--accent); background: transparent; color: var(--accent); font-size: 1.4rem; line-height: 1; cursor: pointer; }
.still-nudge:active { background: var(--gold); color: #000; }
.still-pace-preview { margin-top: 12px; max-width: 320px; }
.still-pace-frame { position: relative; aspect-ratio: 16 / 9; border-radius: 8px; overflow: hidden; border: 1px solid var(--line-strong); }
.still-pace-frame img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
@keyframes still-pace-zoom { from { transform: scale(1); } to { transform: scale(1.09); } }
.still-pace-num { position: absolute; right: 10px; bottom: 6px; font-size: 1.6rem; font-weight: 700; color: rgba(255, 255, 255, .85); text-shadow: 0 1px 6px rgba(0, 0, 0, .6); }
.still-pace-bar { height: 4px; margin-top: 6px; background: var(--line); border-radius: 2px; overflow: hidden; }
.still-pace-bar i { display: block; height: 100%; width: 0; background: var(--gold); }

/* Review screen: the two sliders live in one card with identical rows -- what each controls, its
   current value, its two ends -- and the pace preview beside them. */
.review-shape { display: flex; gap: 20px; align-items: center; margin-top: 12px; padding: 14px 16px; background: var(--surface); border: 1px solid var(--line); border-radius: 12px; }
.shape-head { display: flex; justify-content: space-between; align-items: baseline; }
.shape-head label { font-size: .9rem; font-weight: 600; color: var(--ink); }
.shape-head { gap: 12px; }
.shape-head output { font-size: .9rem; font-weight: 650; color: var(--accent); white-space: nowrap; flex: none; }  /* "2.2 s" never wraps onto a second line */
.shape-row input[type=range] { width: 100%; margin: 6px 0 2px; accent-color: var(--gold); cursor: pointer; }
/* A fewer/more slider's row takes its touches (fewer-more-slider.js accelerateSlider): up and down scroll the page, sideways drags.
   The browser's own slider under it never sees a finger or the mouse -- it jumps to wherever one lands. */
.slider-pad { touch-action: pan-y; cursor: pointer; user-select: none; -webkit-user-select: none; }
.slider-pad input[type=range] { pointer-events: none; }
.shape-ends { display: flex; justify-content: space-between; font-size: .75rem; color: var(--muted); }
.review-shape .still-pace-preview { margin: 0; width: 240px; flex: none; }
@media (max-width: 640px) { .review-shape { flex-direction: column; align-items: stretch; } .review-shape .still-pace-preview { width: 100%; max-width: 320px; } }
/* Never crop a photo in the preview: show it whole on black. */
.still-pace-frame img { object-fit: contain; background: #000; }

/* Review screen: each job gets its own card and heading -- choosing photos, and setting the pace. */
.review-card { margin-top: 14px; padding: 14px 16px 16px; background: var(--surface); border: 1px solid var(--line); border-radius: 12px; }
.review-card h3 { margin: 0 0 6px; font-size: 1.05rem; }
.review-card .review-toolbar { margin-top: 8px; }
.review-card .shape-row { margin-top: 14px; }
.review-card .review-shape { margin: 8px 0 0; padding: 0; background: none; border: 0; }
.review-card .review-shape .shape-row { flex: 1; min-width: 0; margin-top: 0; }

/* Review screen wizard pages */
.review-step[hidden] { display: none; }
.review-step-nav { display: flex; justify-content: space-between; gap: 12px; margin: 20px 0 8px; }
.review-step-nav .next { margin-left: auto; }

/* Selecting many photos at once on the review grid: a mouse's rectangle (app-review-marquee.js). A finger ticks one
   photo at a time: a slide along the tick boxes ticked a run, set off by accident and of no use (the owner). */
#review-grid { user-select: none; -webkit-user-select: none; }
#review-grid img { -webkit-user-drag: none; user-drag: none; }
#review-grid.tick-sweep, #review-grid.tick-sweep * { cursor: crosshair; }
#review-grid .review-tile { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.marquee-box { position: fixed; z-index: 45; pointer-events: none; border: 1.5px solid var(--accent); background: rgba(240, 177, 85, .18); border-radius: 3px; }
.review-hint { font-size: .8rem; color: var(--muted); margin: 14px 0 -12px; }
@media (pointer: coarse) { .review-hint { display: none; } }

/* The map look is tuned once, on the review screen, against the customer's own route -- not in the wizard. */
#wizard .look-card, #wizard .look-dialog { display: none; }

/* The finished-film "change my photo selection" call to action: a redo of the whole film, so it must stand out. */
#rereview { margin: 24px 0; padding: 22px 22px 24px; border-radius: var(--r-md, 14px); border: 2px solid var(--gold);
  background: radial-gradient(120% 220% at 0% 0%, rgba(240, 177, 85, .18), rgba(141, 123, 245, .10) 60%, transparent 85%);
  box-shadow: 0 0 34px -8px rgba(240, 177, 85, .55); }
#rereview[hidden] { display: none; }
.rereview-title { margin: 0 0 6px; font-size: 1.35rem; }
#rereview p { font-size: 1.05rem; line-height: 1.5; }
#rereview strong { color: var(--accent); }
.rereview-btn { width: 100%; margin-top: 8px; font-size: 1.2rem; padding: 18px 24px; }

/* Finished-film screen: title, cover-poster video, Download|Share side by side, then two option cards. */
#progress.done .progress-track, #progress.done #status-detail, #progress.done #status-log, #progress.done #status-message { display: none; }
#progress.done #status-title { margin-bottom: 4px; }
#downloads video { max-width: 100%; }
.film-actions { display: flex; gap: 10px; margin-top: 12px; }
#downloads .film-actions a, #downloads .film-actions .share-btn { flex: 1; margin: 0; text-align: center; font-size: 1rem; box-sizing: border-box; }
#downloads .film-actions a { background: transparent; }
/* Open Google Photos / Save to Photos, once the film is saved (app-share.js offerGallery()): its own row under the others. */
#downloads .film-actions { flex-wrap: wrap; }
#downloads .film-actions .gallery-btn { flex: 1 1 100%; display: inline-flex; flex-direction: column; align-items: center;
  gap: 2px; padding: 12px 20px; border-radius: var(--r-pill); font: inherit; font-size: 1rem; cursor: pointer;
  color: var(--ink); background: rgba(141, 123, 245, .16); border: 1px solid var(--violet); text-decoration: none; }
#downloads .film-actions .gallery-note { margin: 0; padding: 4px 0 0; cursor: default; background: none; border: 0;
  font-size: .88rem; color: var(--ink-dim); text-align: center; }
#remix-music { margin-top: 0; padding: 20px; border: 1px solid var(--line); border-radius: var(--r-md, 14px); background: var(--surface-2); }
.remix-title { margin: 0 0 4px; font-size: 1.2rem; }
.remix-lead { margin: 0 0 14px; color: var(--ink-dim); }
#remix-music select { display: block; width: 100%; margin: 0 0 10px; box-sizing: border-box; }
#remix-music > button { display: block; width: 100%; margin-top: 10px; }
#remix-music #remix-continue { background: rgba(240, 177, 85, .18); }
#remix-music .music-source-page > button { display: block; width: 100%; }
#remix-music .music-source-page > button[hidden] { display: none; }
#remix-music #remix-continue, #remix-music #remix-upload-continue { margin-top: 6px; padding: 12px; }

/* Trimming a video (app-review-clips.js): a chip on the tile, and the dialog -- a looping preview, length buttons, a frame timeline with a draggable box. */
/* Small and icon-only, like the photo's motion chip: the rest of the tile is for ticking it. */
.review-tile .clip-trim-chip {
  position: absolute; right: 8px; bottom: 8px; z-index: 2; width: 30px; height: 30px; padding: 0; border-radius: var(--r-pill);
  border: 1px solid var(--line-strong); background: rgba(10, 10, 14, .85); color: #fff; font-size: 14px; line-height: 1; cursor: pointer;
}
.review-tile:has(.clip-trim-chip) .tile-number { right: 44px; }
.review-tile .clip-trim-chip.customized { border-color: var(--gold); color: var(--gold); }   /* a chip over the photo: fixed */
.clip-dialog { width: min(560px, 96vw); max-height: 96vh; max-height: 96dvh; overflow-y: auto; background: var(--surface); color: var(--ink); border: 1px solid var(--line-strong); border-radius: 14px; padding: 16px; }
.clip-dialog::backdrop { background: rgba(4, 6, 10, .72); }
.clip-title { margin: 0 0 10px; font-size: 1.1rem; }
.clip-player { position: relative; background: #000; border-radius: 10px; overflow: hidden; }
.clip-preview { display: block; width: 100%; max-height: 44vh; object-fit: contain; background: #000; }
/* A phone's screen, not the page behind its address bar (dvh): a tall portrait clip pushed Use off the bottom. */
@media (max-width: 600px) { .clip-preview { max-height: 30vh; max-height: 30dvh; } }
.clip-loading { position: absolute; left: 0; right: 0; bottom: 0; margin: 0; padding: 6px 10px; font-size: .8rem; color: #f4f1ea; background: rgba(0, 0, 0, .6); }   /* over the video: fixed */
.clip-loading:empty { display: none; }
.clip-step { margin: 14px 0 6px; font-size: .9rem; font-weight: 700; color: var(--accent); }
.clip-lengths { display: flex; flex-wrap: wrap; gap: 8px; }
.clip-lengths button { flex: 1 1 0; min-width: 64px; padding: 12px 8px; font-size: 1rem; font-weight: 700; border-radius: var(--r-pill); border: 1.5px solid var(--line-strong); background: var(--surface-2); color: var(--ink); cursor: pointer;
  white-space: nowrap; }
.clip-lengths button.active { background: var(--gold); border-color: var(--gold); color: #1a1206; }
.clip-track { position: relative; height: 72px; border-radius: 8px; overflow: hidden; background: #000; touch-action: none; cursor: ew-resize; user-select: none; }
.clip-strip { display: flex; height: 100%; pointer-events: none; }
.clip-strip img { flex: 1 1 0; min-width: 0; height: 100%; object-fit: cover; }
/* The box is the part that plays; the huge shadow dims everything outside it. */
.clip-band { position: absolute; top: 0; bottom: 0; min-width: 14px; border: 3px solid var(--gold); border-radius: 6px; box-sizing: border-box; box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .62); pointer-events: none; }
/* The box's two ends, dragged to set where the part starts and stops: 44 px wide, centred on the end (the strip finds
   which end a finger grabbed, clipEdgeAt); the grip is drawn just inside the box so the strip's edge never cuts it. */
.clip-handle { position: absolute; top: 0; bottom: 0; width: 44px; margin-left: -22px; pointer-events: none; }
.clip-handle::before { content: ""; position: absolute; top: 50%; width: 12px; height: 34px; margin-top: -17px; border-radius: 4px;
  background: linear-gradient(90deg, var(--gold) 30%, rgba(0, 0, 0, .45) 30% 42%, var(--gold) 42% 58%, rgba(0, 0, 0, .45) 58% 70%, var(--gold) 70%);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .55); }
.clip-handle-start::before { left: 22px; }
.clip-handle-end::before { right: 22px; }
.clip-handle.active::before { height: 50px; margin-top: -25px; }
.clip-nudge { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 8px; }
.clip-nudge button { padding: 10px 12px; font-size: .85rem; border-radius: var(--r-pill); border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink); cursor: pointer; }
/* The readout has a row of its own, on one line with digits of one width: squeezed between the buttons it wrapped to
   3 or 4 lines as its length changed while a finger dragged, and the dialog jumped with it. */
.clip-nudge { flex-wrap: wrap; }
.clip-readout { order: -1; flex: 1 0 100%; text-align: center; font-size: .95rem; font-weight: 700; white-space: nowrap;
                font-variant-numeric: tabular-nums; }
.clip-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; margin-top: 16px; }
/* Always on screen: the dialog scrolls under its buttons rather than hiding them below the fold. */
.clip-dialog .clip-actions { position: sticky; bottom: 0; z-index: 1; margin: 16px -16px -16px; padding: 12px 16px 16px;
  background: var(--surface); border-top: 1px solid var(--line-strong); }
.clip-actions .clip-done { padding: 12px 20px; font-size: 1rem; font-weight: 700; }

/* photo dump review: no maps page, so rstep 1 is the last and its Next gives way to Create in the photo page's bar
   (app-review-bar.js). Its pace is on the film sheet's Pace page (app-review-sheets.js). */


/* The Film photos / Photo dump tab strip (review/step1.html, app-review-photo-dump.js): only + Teaser needs
   two photo selections at once -- the film's own grid (untouched by the teaser) and the teaser's own picks --
   so only it gets this outer tab bar; Normal stays flat, always showing the "film" page with no border and no
   tab strip -- "as before". Inline in the DOM, not a popup: a classic notebook/property-sheet tab pair (raised,
   square-topped, sitting on a bordered page it visually merges into), deliberately NOT the .mode-switch pill
   used for the mode picker and shape picker above: those pick a value and leave the rest of the screen alone;
   this switches which page is showing, a different kind of control that needs to look different, not like one
   more pill that behaves differently from the others.
   Every strip on the page (Film photos | Photo dump, each sheet's Photos | Pace, Look | Music) is one component:
   .property-tabs[data-sheet] and the [data-sheet-page] pages after it (app-review-sheets.js bindSheet()), so the
   rules below are written generically and every nesting level shares the same look. */
#review-photo-tabs { margin: 18px 0 0; }   /* an inner strip needs none -- it's already the first thing inside its own padded .property-page */
.property-tabs { display: flex; gap: 4px; position: relative; z-index: 1; }
.property-tabs[hidden] { display: none; }   /* its own display would otherwise win over [hidden] */
.property-tab {
  position: relative; flex: 1; min-width: 0; text-align: center; padding: 9px 18px; margin-bottom: -1px;
  font-size: .85rem; font-weight: 700; color: var(--muted); cursor: pointer; background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: 8px 8px 0 0;
}
.property-tab input { position: absolute; opacity: 0; width: 1px; height: 1px; }
/* The open tab: its page's colour and a gold top edge, so which one is open reads at a glance. */
.property-tab:has(input:checked) { background: var(--surface-2); color: var(--ink); border-bottom-color: var(--surface-2); box-shadow: inset 0 3px 0 var(--accent); z-index: 1; }
.property-tab:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The page is its own card by default -- same look as .review-card just above it (Normal/Photo dump's single
   grid this way still reads the same as it always did, just as its own block instead of packed into the one
   card with the mode picker). Only when its own .property-tabs strip is visible does it switch to the bordered
   page the active tab appears to grow out of -- true independently at each nesting level, since :not([hidden])
   ~ only matches a .property-tabs and .property-page that share the same immediate parent. */
[data-sheet-page][hidden] { display: none; }   /* their own display would otherwise win over [hidden] */
.property-page { margin-top: 14px; padding: 14px 16px 16px; background: var(--surface); border: 1px solid var(--line); border-radius: 12px; }
.property-tabs:not([hidden]) ~ .property-page {
  background: var(--surface-2); border-color: var(--line-strong); border-radius: 0 8px 8px 8px; margin-top: 0;
}
/* The outer Film photos/Photo dump strip (not the nested Pace/Photos one inside it, which already fills
   whatever width this gives it) cancels #review-panel's own side padding with a negative margin, so the
   picker gets the panel's full width to work with instead of shrinking by 2x48px (2x22px on a phone) for no
   reason .review-card above it still needs padding for -- .review-card stays inset; this is deliberately wider. */
#review-photo-tabs, .property-page[data-sheet-page="film"], .property-page[data-sheet-page="dump"] { margin-left: -48px; margin-right: -48px; }
@media (max-width: 700px) {
  #review-photo-tabs, .property-page[data-sheet-page="film"], .property-page[data-sheet-page="dump"] { margin-left: -22px; margin-right: -22px; }
}
/* The Photo dump tab nests a second .property-page inside the first (its own Pace/Photos sheet) -- full
   padding at both levels stacks up to 32px lost off each side on a phone, on top of what .review-card and the
   page's own gutter already take. The inner one only needs enough padding to clear its own tabs; it already
   sits inside the outer page's own horizontal inset. */
.property-page .property-page { padding: 10px 0 0; }
/* That zeroing is right for the grids themselves (#review-grid, .teaser-picker-grid read fine edge to edge,
   same as any other grid) but wrong for readable text and controls around them -- legends, the toolbar, hints,
   the teaser's quick-select row, and the length slider(s) -- which still want their own small gutter from the
   true edge or they read as cut off right against it. Given back here, to everything at this nesting depth
   except the grids. */
.property-page .property-page .legend,
.property-page .property-page .review-toolbar,
.property-page .property-page .review-hint,
.property-page .property-page .shape-row,
.property-page .property-page .teaser-quick-select { padding-left: 12px; padding-right: 12px; }
@media (max-width: 480px) {
  .property-page { padding: 12px 10px 12px; }
  .teaser-quick-select { flex-wrap: wrap; }
}
/* Quick-select for the teaser's own picker grid (app-review-photo-dump.js): one-tap icon buttons -- and for the Film photos
   grid too ([data-film-select]), in place of its old text menu, so both pages look and work alike. A glyph reads
   faster once learned. Each gets its own accent color so they're not just five identical
   grey buttons the customer has to read the tooltip on every time. */
.teaser-quick-select { display: flex; gap: 8px; margin: 10px 0; }
.teaser-quick-select button {
  width: 38px; height: 38px; flex: none; display: grid; place-items: center; padding: 0;
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: 10px; cursor: pointer;
  color: var(--muted); transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.teaser-quick-select button[hidden] { display: none; }   /* Faces / No faces in an album with no faces: hidden, and drawn */
.teaser-quick-select svg { width: 20px; height: 20px; }
.teaser-quick-select button:hover { background: var(--surface-2); color: var(--ink); }
.teaser-select-faces:hover, .teaser-select-faces:focus-visible { color: var(--accent); border-color: var(--accent); }
.teaser-select-no-faces:hover, .teaser-select-no-faces:focus-visible { color: #e0725f; border-color: #e0725f; }
.teaser-select-no-faces .no-badge { color: #e0725f; }
.teaser-select-all:hover, .teaser-select-all:focus-visible { color: var(--violet); border-color: var(--violet); }
.teaser-select-auto:hover, .teaser-select-auto:focus-visible { color: var(--accent); border-color: var(--accent); }
.teaser-select-none:hover, .teaser-select-none:focus-visible { color: var(--muted); border-color: var(--line-strong); }
.teaser-quick-select button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A word under each tool's icon while the customer makes a first film (app-review-tool-labels.js); then the icons alone.
   Six icons in a row were six mysteries (the owner). */
.tool-label { display: none; }
body.tools-named .tool-label { display: block; font-size: clamp(.56rem, 2.6vw, .7rem); font-weight: 600; line-height: 1.1;
  color: var(--muted); white-space: nowrap; }
body.tools-named .teaser-quick-select button, body.tools-named .review-dice, body.tools-named .review-zoom .review-toolbar-btn {
  width: auto; height: auto; min-height: 52px; padding: 6px 2px 4px; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 3px; }
body.tools-named .review-zoom .review-toolbar-btn { min-width: 48px; padding: 6px 6px 4px; }
body.tools-named .teaser-quick-select button[hidden] { display: none; }   /* the display above would draw a hidden one */
/* The pick buttons and the dice: one row, never two (the owner), each button an equal share of the toolbar's width up
   to 64 px, shrinking together on a narrow phone, its icon and word sized to the screen. The group of five lays its
   buttons out in the row itself (display: contents): as a box of its own it took its words' width, not the row's
   share, and its buttons came out narrower than the dice. A word too long for its share goes on two lines. */
.review-quick-row { display: flex; flex: 1 1 100%; align-items: stretch; gap: 6px; min-width: 0; }
.review-quick-row .teaser-quick-select { display: contents; }
body.tools-named .review-quick-row .tool-label { white-space: normal; text-align: center; overflow-wrap: normal; }
.review-quick-row .teaser-quick-select button, .review-quick-row .review-dice { flex: 0 1 64px; width: auto; min-width: 0;
  height: auto; min-height: 44px; }
.review-quick-row .review-dice { min-height: 44px; padding: 0 2px; border-radius: 10px; }
.review-quick-row .teaser-quick-select svg { width: clamp(18px, 5.5vw, 22px); height: clamp(18px, 5.5vw, 22px); }
.review-quick-row .review-dice svg { width: clamp(24px, 7.5vw, 30px); height: clamp(24px, 7.5vw, 30px); }
/* A real CSS grid, not CSS column-count masonry any more (column-count fills each
   column top to bottom before starting the next, so tile order read left-to-right, top-to-bottom in the DOM
   -- e.g. 1,2,3 -- but appeared 1 under 4 under 7 down the first column, then 2,5,8 down the second, reading
   as scrambled). Grid's default row-major auto-flow puts them back in DOM order, left to right, same as
   #review-grid. First tried letting each thumb keep its own original aspect ratio (a row just as tall as its
   tallest tile) rather than cropping to a uniform box, on the theory that the point of this picker is judging
   what the photo actually looks like before it gets trimmed into the reel -- confirmed live the same day: a
   shorter tile in a tall row just reads as broken, a block of plain black where the tile's own background
   shows through empty space, not "the real framing". Cropped to a fixed box now instead, exactly like the
   main grid already does for these same orientation-locked photos (.review-tile's own aspect-ratio/object-fit
   pair) -- no gaps, and no less consistent than what customers already see everywhere else on this screen. A
   16:9 photo at a 9:16-tile's width would come out short next to portrait ones, same reasoning as before --
   fewer, wider columns for landscape. */
.teaser-picker-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; padding: 2px; margin: 10px 0; }
.review-dump-landscape .teaser-picker-grid { grid-template-columns: repeat(2, 1fr); }
.teaser-tile { position: relative; display: block; border-radius: 8px; overflow: hidden; cursor: pointer; border: 2px solid transparent; opacity: .35; }
.teaser-tile img { width: 100%; height: auto; aspect-ratio: 3 / 4; object-fit: cover; display: block; }
.review-dump-landscape .teaser-tile img { aspect-ratio: 3 / 2; }
.teaser-tile input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.teaser-tile:has(input:checked) { opacity: 1; border-color: var(--accent); }
/* Shown in place of the grid while its picks are invalidated (orientation just changed) or still loading
   (app-review-photo-dump.js: showTeaserLoading()) -- an empty grid read as broken, not busy. */
.teaser-loading { display: flex; align-items: center; justify-content: center; padding: 48px 0; grid-column: 1 / -1; }   /* column-span: all was the column-count-era version of this; grid-column spans the row now */
.teaser-loading .spinner {
  width: 26px; height: 26px; border-radius: 50%; border: 3px solid var(--line-strong); border-top-color: var(--accent);
  animation: teaser-spin .8s linear infinite;
}
@keyframes teaser-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .teaser-loading .spinner { animation: none; } }

/* A finished film made from the automatic selection (the review wasn't completed): said above the film, with Change
   photos right there (app-status.js, #auto-approved). No display rule, so [hidden] still hides it. */
.auto-approved { margin: 18px 0; padding: 16px 18px; border: 1.5px solid var(--accent); border-radius: 14px;
  background: rgba(240, 177, 85, .08); }
.auto-approved p { margin: 0 0 12px; font-weight: 600; }
.auto-approved .rereview-btn { width: 100%; }
/* Snaps (app-review-snaps.js): photos only, and nothing of a film's -- no shape, look or music, pace, notes, day cards
   or maps page, and no "how like the one before" marker (the film's look-alikes: on Snaps' small thumbnails it covered
   the photo's middle and took the tap meant for the photo). */
.review-snaps .review-tile .alike-chip,
.review-snaps #review-video-shape, .review-snaps #review-look-tabs, .review-snaps [data-sheet-page="look"],
.review-snaps [data-sheet-page="music"],
.review-snaps .review-note, .review-snaps .review-day, .review-snaps .review-tile[data-type="video"], .review-snaps #review-film-sheet,
.review-snaps .review-groups-hint, .review-snaps #review-order-open, .review-snaps .motion-chip,
.review-snaps #review-dump-unavailable { display: none; }
