/* DarkRoom - "Gallery" theme.
   iOS-native dark gallery: bottom tab bar, large titles, hairlines,
   -apple-system UI type with a serif display face for placards only.
   No gradients on chrome; photos are the hero. */

:root {
  --font-ui: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display",
    system-ui, "Segoe UI", sans-serif;
  --font-display: ui-serif, "New York", "Iowan Old Style", Georgia, serif;

  --bg: #000;
  --bg-soft: #0a0a0d;
  --elev: #131318;
  --elev-2: #1c1c21;
  --text: #f5f5f7;
  --text-2: rgba(235, 235, 245, 0.6);
  --text-3: rgba(235, 235, 245, 0.32);
  --hairline: rgba(255, 255, 255, 0.14);
  --hairline-soft: rgba(255, 255, 255, 0.07);
  --accent: #0a84ff;
  --accent-dim: rgba(10, 132, 255, 0.16);
  --danger: #ff453a;
  --ok: #30d158;

  --spring: cubic-bezier(0.32, 0.72, 0, 1);
  --tabbar-h: calc(52px + env(safe-area-inset-bottom, 0px));
  --radius: 14px;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100vh;
  padding-bottom: var(--tabbar-h);
}

/* Whisper of film grain over everything. Opacity stays barely-there. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
  opacity: 0.028;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='1'/%3E%3C/svg%3E");
}

.ambient { display: none; }
.hidden, .view.hidden { display: none !important; }

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -0.01em; }
p { margin: 0; }
button { font-family: var(--font-ui); touch-action: manipulation; }
/* Button theme safety net (Build 39 part 5): every button in the app
   defaults to the themed look (dark translucent surface, hairline
   border, light text), so a button whose class carries no background
   rule of its own can never fall back to the raw OS white chrome
   again. More specific class rules (.primary, .ghost, .pp-tab,
   swatches, tiles) and inline styles override this by specificity;
   elements that deliberately want no chrome set background: none in
   their own rule (e.g. .pp-tileapply). */
button {
  appearance: none; -webkit-appearance: none;
  background: rgba(255, 255, 255, 0.05);
  border: 0.5px solid var(--hairline);
  border-radius: 10px;
  color: var(--text);
  font-size: 14px; font-weight: 500;
  padding: 8px 14px;
  cursor: pointer;
}
button:disabled { opacity: 0.45; cursor: default; }
button, .photo-cell, .cluster-card, .face-card { -webkit-tap-highlight-color: transparent; }
img { display: block; }

.meta { color: var(--text-2); font-size: 13px; }
.fineprint { color: var(--text-3); font-size: 12px; }

/* ---------- top bar ---------- */
#topbar {
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: calc(10px + env(safe-area-inset-top, 0px)) 16px 10px;
  background: rgba(0, 0, 0, 0.72);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 0.5px solid var(--hairline-soft);
}
.brand { display: flex; align-items: center; gap: 9px; min-width: 0; }
.brand .mark { width: 30px; height: 30px; flex: none; border-radius: 8px; }
.brand-text h1 { font-size: 17px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.15; }
.brand-text .tagline {
  display: block;
  font-size: 10.5px;
  font-weight: 400;
  letter-spacing: 0.01em;
  color: var(--text-3);
  line-height: 1.25;
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- buttons ---------- */
.primary {
  appearance: none;
  border: none;
  border-radius: 12px;
  background: var(--accent);
  color: #fff;
  font-size: 15px;
  font-weight: 600;
  padding: 10px 18px;
  cursor: pointer;
  transition: transform 0.18s var(--spring), filter 0.18s var(--spring), opacity 0.18s;
}
.primary:hover { filter: brightness(1.1); }
.primary:active { transform: scale(0.97); }
.primary:disabled { opacity: 0.4; cursor: default; transform: none; }
.primary.big { font-size: 16px; padding: 14px 30px; border-radius: 14px; width: 100%; }

.ghost {
  appearance: none;
  border: 0.5px solid var(--hairline);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.05);
  color: var(--text);
  font-size: 14px;
  font-weight: 500;
  padding: 8px 14px;
  cursor: pointer;
  transition: transform 0.18s var(--spring), background 0.18s;
}
.ghost:hover { background: rgba(255, 255, 255, 0.1); }
.ghost:active { transform: scale(0.97); }
.ghost.danger { color: var(--danger); border-color: rgba(255, 69, 58, 0.4); }
.ghost:disabled { opacity: 0.4; }

#topbar .primary { padding: 7px 14px; font-size: 14px; border-radius: 999px; }

/* ---------- bottom tab bar ---------- */
#tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 70;
  display: flex;
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: rgba(10, 10, 13, 0.78);
  -webkit-backdrop-filter: saturate(180%) blur(22px);
  backdrop-filter: saturate(180%) blur(22px);
  border-top: 0.5px solid var(--hairline-soft);
}
#tabbar .navbtn {
  flex: 1 1 0;
  min-width: 0;
  appearance: none;
  border: none;
  background: none;
  color: var(--text-3);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 8px 0 6px;
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.2s var(--spring), transform 0.2s var(--spring);
}
#tabbar .navbtn svg { width: 24px; height: 24px; }
#tabbar .navbtn.active { color: var(--accent); }
#tabbar .navbtn:active { transform: scale(0.92); }
/* Active-tab indicator pill: glides between tabs on transform only.
   Positioned from JS (count-agnostic: measured off the live buttons). */
#tab-indicator {
  position: absolute;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 3px);
  left: 0;
  width: 30px;
  height: 3.5px;
  border-radius: 999px;
  background: var(--accent);
  pointer-events: none;
  transition: transform 0.34s var(--spring);
  will-change: transform;
}

/* ---------- views ---------- */
main {
  max-width: 1120px;
  margin: 0 auto;
}
.view { padding: 0 0 24px; }
.view:not(.hidden) { animation: viewIn 0.38s var(--spring); }
@keyframes viewIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
/* Direction-aware tab switches: incoming view slides from the side the
   user came from. JS adds slide-fwd/slide-back before unhiding. */
.view.slide-fwd:not(.hidden) { animation: viewFwd 0.32s var(--spring); }
.view.slide-back:not(.hidden) { animation: viewBack 0.32s var(--spring); }
@keyframes viewFwd {
  from { opacity: 0; transform: translateX(30px); }
  to { opacity: 1; transform: none; }
}
@keyframes viewBack {
  from { opacity: 0; transform: translateX(-30px); }
  to { opacity: 1; transform: none; }
}

.pagetitle { padding: 18px 16px 6px; }
.pagetitle h1 {
  font-size: 34px;
  font-weight: 700;
  letter-spacing: -0.02em;
}
.pagetitle .sub { color: var(--text-2); font-size: 13px; margin-top: 2px; }

/* iOS-style grouped card */
.card {
  background: var(--elev);
  border: 0.5px solid var(--hairline-soft);
  border-radius: var(--radius);
  padding: 16px;
  margin: 12px 16px;
}
.card h2 { font-size: 17px; font-weight: 600; margin-bottom: 6px; }
.card p { color: var(--text-2); font-size: 14px; }
.card p + p { margin-top: 8px; }
.card .primary { margin-top: 12px; }

/* ---------- empty state: full-bleed gallery wall ---------- */
#emptystate.hero {
  position: relative;
  overflow: hidden;
  border-radius: 0;
  margin: 0;
  border: none;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(20, 22, 34, 0.9) 0%, rgba(0, 0, 0, 0) 60%),
    radial-gradient(140% 120% at 50% 115%, rgba(38, 30, 52, 0.55) 0%, rgba(0, 0, 0, 0) 55%),
    #000;
  padding: 64px 28px 56px;
  text-align: center;
}
#emptystate.hero::after {
  /* vignette */
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(90% 75% at 50% 45%, rgba(0, 0, 0, 0) 55%, rgba(0, 0, 0, 0.55) 100%);
}
#emptystate .hero-mark { width: 54px; height: 54px; margin: 0 auto 22px; opacity: 0.95; }
#emptystate h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 40px;
  line-height: 1.08;
  letter-spacing: 0;
  margin: 0 0 14px;
}
#emptystate .lede {
  color: var(--text-2);
  font-size: 16px;
  line-height: 1.55;
  max-width: 30em;
  margin: 0 auto;
}
#emptystate .primary.big { max-width: 340px; margin: 26px auto 0; }
#emptystate .trust {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  margin-top: 26px;
}
#emptystate .fineprint { margin-top: 22px; max-width: 34em; margin-left: auto; margin-right: auto; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-2);
  border: 0.5px solid var(--hairline);
  border-radius: 999px;
  padding: 6px 12px;
  background: rgba(255, 255, 255, 0.03);
}
.chip svg { width: 13px; height: 13px; }

/* ---------- workstream H: tag cloud ---------- */
.tagchips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.tagchip {
  display: inline-flex;
  align-items: center;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-2);
  border: 0.5px solid var(--hairline);
  border-radius: 999px;
  padding: 6px 12px;
  background: rgba(255, 255, 255, 0.03);
  cursor: pointer;
}
.tagchip:hover { border-color: var(--accent); color: var(--text); }

/* ---------- iOS import + storage cards ---------- */
#ios-card { border-color: rgba(10, 132, 255, 0.25); }
#ios-card .honest {
  border-left: 2px solid var(--accent);
  padding-left: 12px;
  margin: 10px 0 4px;
}
#ios-card .primary.big { margin-top: 14px; }
.storage-meter { margin-top: 12px; }
.storage-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
}
#storage-card .ghost.danger { margin-top: 12px; }
.bar.big {
  height: 6px;
  background: rgba(255, 255, 255, 0.09);
  border-radius: 999px;
  overflow: hidden;
}
.bar.big > div {
  height: 100%;
  width: 100%;
  background: linear-gradient(90deg, #c99734, #e8b93c);
  border-radius: 999px;
  /* Compositor-only fill: scaleX instead of width (see progress pill). */
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.5s var(--spring);
}

/* ---------- library tools row (captions toggle) ---------- */
#lib-tools {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 2px 16px 8px;
}
.switch-wrap {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 13px;
  color: var(--text-2);
  cursor: pointer;
  user-select: none;
}
.switch { position: relative; display: inline-block; width: 51px; height: 31px; flex: none; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch .knob {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.16);
  transition: background 0.25s var(--spring);
}
.switch .knob::before {
  content: "";
  position: absolute;
  width: 27px;
  height: 27px;
  left: 2px;
  top: 2px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
  transition: transform 0.25s var(--spring);
}
.switch input:checked + .knob { background: var(--ok); }
.switch input:checked + .knob::before { transform: translateX(20px); }

/* ---------- photo grid: edge-to-edge gallery wall ---------- */
.gridwrap {
  position: relative;
  height: calc(100vh - 260px);
  min-height: 320px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  outline: none;
}
#searchgrid { height: 52vh; }
.grid-spacer { position: relative; }
.grid-row { position: absolute; left: 0; right: 0; display: flex; gap: 2px; padding: 0 2px; }
.photo-cell {
  position: relative;
  flex: none;
  min-width: 0;
  cursor: pointer;
  overflow: hidden;
  background: var(--bg-soft);
}
.photo-cell img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Thumbnails fade in once decoded: opacity-only, staggered by position
     via --cd (set from JS for newly appearing cells). transform is reserved
     for the hover zoom. */
  opacity: 0;
  transition: transform 0.45s var(--spring), opacity 0.28s ease var(--cd, 0ms);
}
.photo-cell img.ld { opacity: 1; }
.photo-cell:hover img { transform: scale(1.04); }
.photo-cell .cap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 14px 6px 5px;
  font-size: 10.5px;
  color: rgba(255, 255, 255, 0.92);
  background: linear-gradient(to top, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0));
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: none;
}
body.show-caps .photo-cell .cap { display: block; }
.score-badge {
  position: absolute;
  top: 6px;
  right: 6px;
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: #fff;
  background: rgba(0, 0, 0, 0.62);
  border: 0.5px solid var(--hairline);
  padding: 2px 7px;
  border-radius: 999px;
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.grid-empty { padding: 40px 20px; text-align: center; color: var(--text-3); }

/* timeline placards */
.date-header {
  position: absolute;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-end;
  padding: 26px 16px 10px;
  background: linear-gradient(to bottom, var(--bg) 30%, rgba(0, 0, 0, 0));
}
.date-header .placard-month {
  font-family: var(--font-display);
  font-size: 27px;
  font-weight: 500;
  letter-spacing: 0.005em;
  line-height: 1.1;
}
.date-header .placard-day {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-2);
  margin-top: 5px;
}
.date-header .placard-day.compact {
  text-transform: none;
  letter-spacing: 0.01em;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}
.date-header .count {
  color: var(--text-3);
  font-weight: 400;
  letter-spacing: 0.02em;
  margin-left: 8px;
  text-transform: none;
}

/* ---------- search view ---------- */
.searchform { padding: 14px 16px; }
.srow {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: flex-end;
}
.srow label {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-3);
}
.srow label.grow { flex: 1 1 160px; }
input[type="text"], input[type="date"], select {
  font-family: var(--font-ui);
  font-size: 15px;
  color: var(--text);
  background: rgba(255, 255, 255, 0.06);
  border: 0.5px solid var(--hairline-soft);
  border-radius: 10px;
  padding: 9px 12px;
  outline: none;
  transition: border-color 0.2s;
  max-width: 100%;
}
input:focus, select:focus { border-color: var(--accent); }
select option { background: var(--elev); }
#searchmeta { margin: 10px 2px 0; }

.smartsearch { margin-top: 12px; }
.ss-query { display: flex; gap: 10px; margin-top: 12px; }
.ss-query input { flex: 1; }
.dl-row { margin-top: 12px; }
.dl-text {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 8px;
}
.dl-btns { display: flex; gap: 8px; flex: none; }

/* ---------- duplicates ---------- */
.dup-intro {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}
.dup-intro .primary { margin-top: 0; flex: none; }
#dupmeta { padding: 0 16px; }
#duptabs { margin: 10px 16px 0; }
.dup-group { padding: 6px 16px 18px; }
.dup-group h3 { font-size: 16px; font-weight: 600; margin-bottom: 4px; }
.dup-group .meta { margin: 0 0 10px; }
.dup-members { display: flex; gap: 2px; overflow-x: auto; padding-bottom: 4px; }
.dup-members > div { flex: 0 0 150px; }
.dup-members .photo-cell { aspect-ratio: 1; }
.dup-members .cap {
  position: static;
  display: block;
  background: none;
  padding: 5px 2px 0;
  color: var(--text-2);
  font-size: 11px;
}

/* ---------- faces ---------- */
.faces-intro {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}
.faces-intro .primary { margin-top: 0; flex: none; }
#faces-meta { padding: 0 16px; }
.cluster-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 2px;
  padding: 12px 2px 0;
}
.cluster-card {
  position: relative;
  background: var(--bg-soft);
  cursor: pointer;
  overflow: hidden;
  transition: transform 0.3s var(--spring);
}
.cluster-card:active { transform: scale(0.97); }
.cluster-card.selected { outline: 2px solid var(--accent); outline-offset: -2px; }
.cluster-card .face-frame { aspect-ratio: 1; }
.cluster-check {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 2;
  width: 22px;
  height: 22px;
  accent-color: var(--accent);
}
.cluster-name {
  font-size: 14px;
  font-weight: 600;
  padding: 8px 10px 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cluster-name input {
  width: 100%;
  font-size: 14px;
  font-weight: 600;
}
.cluster-sub { padding: 0 10px 10px; }
.mergebar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 10px 16px 0;
  padding: 10px 12px;
  background: var(--elev);
  border: 0.5px solid var(--hairline-soft);
  border-radius: 12px;
}
.detail-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 4px 16px 10px;
}
.detail-head h3 { font-family: var(--font-display); font-size: 24px; font-weight: 500; }
.moverow {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 0 16px 10px;
  padding: 10px 12px;
  background: var(--elev);
  border: 0.5px solid var(--hairline-soft);
  border-radius: 12px;
}
.face-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  padding: 0 2px;
}
.face-card { position: relative; cursor: pointer; background: var(--bg-soft); }
.face-card.selected { outline: 2px solid var(--accent); outline-offset: -2px; }
.face-card .cap {
  font-size: 10.5px;
  color: var(--text-2);
  padding: 5px 6px 7px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.face-frame { position: relative; overflow: hidden; background: var(--bg-soft); }
.face-frame img { width: 100%; height: 100%; object-fit: contain; }
.face-box {
  position: absolute;
  border: 1.5px solid rgba(255, 255, 255, 0.85);
  border-radius: 6px;
  box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.5);
  pointer-events: none;
}

/* ---------- growth lines ---------- */
.growth-section { margin: 18px 16px 6px; max-width: 420px; }
.growth-section h4 { margin: 0 0 4px; font-size: 16px; font-weight: 650; }
.growth-section .growth-note { margin: 0 0 10px; }
.growth-stage {
  display: block;
  width: 100%;
  max-width: 360px;
  aspect-ratio: 1;
  border-radius: 14px;
  background: #14100c;
}
.growth-date { margin-top: 8px; font-size: 13px; color: var(--text-2); }
.growth-controls {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 8px;
  max-width: 360px;
}
.growth-controls .growth-play { flex: none; }
.growth-controls .growth-scrub { flex: 1; min-width: 0; }
.growth-readout { margin: 10px 0 0; }
.growth-actions { display: flex; align-items: center; gap: 12px; margin-top: 10px; flex-wrap: wrap; }

/* ---------- map ---------- */
.map-intro {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}
.map-actions { display: flex; gap: 8px; flex: none; flex-wrap: wrap; }
.map-actions .ghost { padding: 7px 11px; font-size: 15px; }
#mapwrap {
  margin: 12px 16px 0;
  border-radius: var(--radius);
  overflow: hidden;
  border: 0.5px solid var(--hairline-soft);
  background: var(--bg-soft);
  min-height: 420px;
}
#mapwrap canvas { width: 100%; display: block; }

/* ---------- lightbox: immersive viewer + bottom sheet ---------- */
#lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: #000;
  animation: lbIn 0.38s var(--spring);
  touch-action: pan-y;
}
@keyframes lbIn {
  from { opacity: 0; transform: scale(0.96); }
  to { opacity: 1; transform: none; }
}
#lightbox.hidden { display: none; }
.lb-top {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: nowrap;
  gap: 10px;
  padding: calc(10px + env(safe-area-inset-top, 0px)) 16px 24px;
  z-index: 3;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.65), transparent);
  pointer-events: none;
}
.lb-top > * { pointer-events: auto; }
.lb-top.lb-top-hidden {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
}
.lb-top { transition: opacity 0.25s ease; }
/* The counter never wraps or shrinks: one muted line, clear of the
   heart (the bar gap and the actions' own flex spacing see to it). */
#lb-count {
  font-size: 13px; font-weight: 600; color: var(--text-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap; flex: none;
}
#lb-close {
  appearance: none;
  border: none;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
.lb-stage {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
#lb-img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  transition: transform 0.3s var(--spring);
  user-select: none;
  -webkit-user-drag: none;
}
/* Swipe track: three full-stage panels (prev/current/next) translated as
   one compositor layer. Built by JS on first open; keeps #lb-img's id. */
.lb-stage { touch-action: none; }
#lb-track {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 300%;
  display: flex;
  will-change: transform;
}
.lb-slide {
  flex: 0 0 33.3333%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.lb-slide img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
}
/* Photo roll: iOS-style filmstrip along the bottom of the lightbox. It
   overlays the photo (the stage never resizes). z-index 1 keeps it under
   the info sheet (2), the top bar (3) and the nav arrows (4). */
#lb-strip {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-x;
  padding: 26px 0 calc(10px + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(to top, rgba(0, 0, 0, 0.55), transparent);
  cursor: grab;
  transition: opacity 0.25s ease, transform 0.25s ease;
}
#lb-strip::-webkit-scrollbar { display: none; }
#lb-strip.hidden { display: none; }
#lb-strip.lb-strip-hidden {
  opacity: 0;
  pointer-events: none;
  transform: translateY(8px);
}
#lb-strip-track {
  display: flex;
  align-items: center;
  width: max-content;
  padding: 0 calc(50% - 28px);
}
.lb-thumb {
  flex: none;
  position: relative;
  width: 56px;
  height: 56px;
  margin-right: 6px;
  border: none;
  border-radius: 8px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.08);
  padding: 0;
  cursor: pointer;
  opacity: 0.5;
  transition: opacity 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.lb-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}
.lb-thumb.cur {
  opacity: 1;
  transform: scale(1.1);
  box-shadow: 0 0 0 2px #e8b93c;
}
.lb-thumb.peek {
  opacity: 0.95;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.7);
}
.lb-thumb .video-badge {
  font-size: 9px;
  padding: 1px 5px;
  right: 3px;
  bottom: 3px;
  border-radius: 5px;
}
.lb-thumb-spacer { flex: none; height: 1px; }
.lb-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 4;
  appearance: none;
  border: none;
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  font-size: 20px;
  cursor: pointer;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  opacity: 0.85;
}
#lb-prev { left: 12px; }
#lb-next { right: 12px; }
@media (hover: none) { .lb-nav { display: none; } }

/* bottom sheet */
#lb-sheet {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 2;
  background: rgba(16, 16, 20, 0.92);
  -webkit-backdrop-filter: saturate(180%) blur(24px);
  backdrop-filter: saturate(180%) blur(24px);
  border-top: 0.5px solid var(--hairline-soft);
  border-radius: 18px 18px 0 0;
  padding: 10px 16px calc(14px + env(safe-area-inset-bottom, 0px));
  max-height: 70vh;
  overflow-y: auto;
  transform: translateY(105%);
  transition: transform 0.38s var(--spring);
}
#lb-sheet.open {
  transform: translateY(0);
  animation: none;
}
@keyframes sheetUp {
  from { transform: translateY(40px); opacity: 0; }
  to { transform: none; opacity: 1; }
}
/* Grab handle: a real element (not a ::before) so it can own the
   drag-to-dismiss gesture. The strip is a fat hit target; the visible
   pill is drawn by its ::before, same geometry as the old sheet pill. */
#lb-sheet .lb-grab {
  height: 26px;
  margin: -4px -16px 2px;
  touch-action: none;
  cursor: grab;
}
#lb-sheet .lb-grab::before {
  content: "";
  display: block;
  width: 36px;
  height: 5px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.28);
  margin: 6px auto 0;
}
.segmented {
  display: flex;
  background: rgba(255, 255, 255, 0.08);
  border-radius: 10px;
  padding: 2px;
  margin-bottom: 12px;
}
.segmented button {
  flex: 1;
  appearance: none;
  border: none;
  background: none;
  color: var(--text-2);
  font-size: 14px;
  font-weight: 600;
  padding: 8px;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.25s var(--spring);
}
.segmented button.active {
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.4);
}
#lb-meta { background: none; border: none; padding: 0; margin: 0; }
#lb-meta dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 7px 14px;
  margin: 0;
  font-size: 13.5px;
}
#lb-meta dt { color: var(--text-3); font-weight: 500; }
#lb-meta dd { margin: 0; color: var(--text); overflow-wrap: anywhere; }
#lb-sheet .ghost { width: 100%; margin-top: 4px; }

/* ---------- progress pill ---------- */
#progress {
  position: fixed;
  top: calc(8px + env(safe-area-inset-top, 0px));
  left: 50%;
  transform: translateX(-50%);
  z-index: 120;
  pointer-events: auto;
  max-width: calc(100vw - 32px);
}
#progress.hidden { display: none; }
.progress-pill {
  display: flex;
  align-items: center;
  gap: 10px;
  background: rgba(20, 20, 26, 0.88);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border: 0.5px solid var(--hairline);
  border-radius: 999px;
  padding: 9px 9px 9px 16px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
  animation: pillIn 0.35s var(--spring);
}
@keyframes pillIn {
  from { opacity: 0; transform: translateY(-12px) scale(0.96); }
  to { opacity: 1; transform: none; }
}
#progresstext {
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 52vw;
}
.progress-pill .bar {
  width: 110px;
  height: 5px;
  background: rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  overflow: hidden;
  flex: none;
}
.progress-pill .bar > div, #progressfill {
  height: 100%;
  width: 100%;
  background: var(--accent);
  border-radius: 999px;
  /* Compositor-only fill: scaleX instead of width, so progress updates
     never trigger layout. The rest state is scaleX(0). */
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.25s var(--spring);
}
.progress-pill .bar.indet > div { transform: none; }
.progress-pill .bar.indet > div { width: 40% !important; animation: indet 1.1s ease-in-out infinite alternate; }
@keyframes indet { from { margin-left: -40%; } to { margin-left: 100%; } }
#cancelBtn { border-radius: 999px; padding: 6px 12px; font-size: 13px; }

/* ---------- status toast + notice + footer ---------- */
#statusbar {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(var(--tabbar-h) + 12px);
  z-index: 110;
  max-width: calc(100vw - 40px);
  background: rgba(22, 22, 28, 0.92);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border: 0.5px solid var(--hairline);
  color: var(--text);
  font-size: 13.5px;
  font-weight: 500;
  padding: 11px 18px;
  border-radius: 999px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
  animation: pillIn 0.35s var(--spring);
  text-align: center;
}
#statusbar.hidden { display: none; }
#statusbar.err { border-color: rgba(255, 69, 58, 0.5); }
#statusbar.ok { border-color: rgba(48, 209, 88, 0.4); }

.notice {
  margin: 12px 16px;
  padding: 14px 16px;
  background: var(--elev);
  border: 0.5px solid var(--hairline-soft);
  border-radius: var(--radius);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-size: 14px;
}
.notice .primary { margin-top: 0; flex: none; }

footer {
  text-align: center;
  padding: 26px 20px 10px;
  color: var(--text-3);
  font-size: 12px;
}

/* Compact cards once the library has photos: the wall is the star. */
body.has-photos #ios-card .honest,
body.has-photos #ios-card #ios-note,
body.has-photos #storage-card #storage-note { display: none; }
body.has-photos #ios-card { padding: 14px 16px; }
body.has-photos #ios-card h2 { font-size: 15px; margin-bottom: 0; }
body.has-photos #ios-card .primary.big {
  width: auto;
  margin-top: 10px;
  padding: 10px 20px;
  font-size: 15px;
}
body.has-photos #storage-card { padding: 12px 16px; }
body.has-photos #storage-card h2 { font-size: 15px; margin-bottom: 0; }
body.has-photos #storage-card .storage-top { margin-bottom: 6px; }
body.has-photos #storage-card .ghost.danger { margin-top: 0; padding: 6px 12px; font-size: 13px; }

/* ---------- wider screens ---------- */
@media (min-width: 720px) {
  .pagetitle { padding: 26px 24px 8px; }
  .pagetitle h1 { font-size: 40px; }
  .card { margin: 14px 24px; padding: 20px 22px; }
  #lib-tools { padding: 2px 24px 10px; }
  .notice { margin: 14px 24px; }
  #dupmeta, #faces-meta { padding: 0 24px; }
  #duptabs { margin-left: 24px; margin-right: 24px; }
  .dup-group { padding: 6px 24px 22px; }
  .detail-head { padding: 4px 24px 12px; }
  .mergebar, .moverow { margin-left: 24px; margin-right: 24px; }
  #mapwrap { margin: 14px 24px 0; }
  #emptystate h2 { font-size: 52px; }
  #tabbar { justify-content: center; }
  #tabbar .navbtn { flex: 0 1 120px; }
  .face-grid { grid-template-columns: repeat(4, 1fr); }
}
@media (min-width: 1100px) {
  .face-grid { grid-template-columns: repeat(5, 1fr); }
}

/* ---------- motion: reduced-motion support ----------
   Every animation in this file is transform/opacity-only, and JS-driven
   motion (lightbox springs, tab indicator, staggered fades) checks
   matchMedia('(prefers-reduced-motion: reduce)') and snaps instantly.
   This block kills the CSS side for users who ask for reduced motion. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  #tab-indicator { transition: none !important; }
  .photo-cell img { opacity: 1 !important; }
}

/* ---------- Cleanup assistant (workstream I) ---------- */
.cl-grid { display: flex; flex-wrap: wrap; gap: 2px; }
.cl-cell { flex: 0 0 150px; min-width: 0; }
.cl-cell .photo-cell { aspect-ratio: 1; }
.cl-box { position: relative; display: block; cursor: pointer; }
.cl-check {
  position: absolute; top: 8px; left: 8px; z-index: 2;
  width: 22px; height: 22px; margin: 0; cursor: pointer;
  accent-color: var(--accent);
}
.cl-reason {
  display: inline-block; font-size: 11px; font-weight: 600;
  padding: 2px 8px; border-radius: 999px;
  background: var(--accent-dim); color: var(--accent);
  border: 1px solid var(--hairline);
}
hr.hairline { border: none; border-top: 0.5px solid var(--hairline); margin: 16px 0; }

/* ---------- workstream J: Locked folder + blur tools ---------- */
#locked-body { padding: 0 24px 48px; }
#locked-grid { height: 62vh; }
.lock-card { max-width: 560px; margin: 16px auto; }
.lock-card .pin-label { display: block; margin: 14px 0 4px; font-weight: 600; }
.lock-card input[type="password"] {
  display: block; margin-top: 6px; width: 220px;
  font-size: 20px; letter-spacing: 6px;
}
.lock-card .primary { margin-top: 14px; }
.lock-card .meta.err { color: #e08080; }
.lock-bar { display: flex; justify-content: flex-end; margin: 12px 0; }
/* Blur brush overlay: fixed over the displayed photo, above the stage. */
#lb-blur-overlay {
  position: fixed; z-index: 60; touch-action: none; cursor: crosshair;
}
#lb-blurbar {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 120px; z-index: 61;
  display: flex; gap: 10px; align-items: center;
  background: rgba(10, 10, 16, 0.92); border-radius: 12px; padding: 10px 14px;
  border: 1px solid rgba(255, 255, 255, 0.14);
}
/* EXIF strip checkbox on the Settings export card (workstream C). */
.set-check { display: flex; gap: 8px; align-items: flex-start; margin: 10px 0; }
.set-check input { margin-top: 3px; }
#cleanup-actionbar {
  position: sticky; bottom: 76px; z-index: 5;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 16px; padding: 12px 16px;
  background: var(--elev); border: 1px solid var(--hairline);
  border-radius: 14px;
}

/* ---------- workstream G: albums, favorites, memories ---------- */

/* Library Timeline|Albums segmented control. */
#lib-seg { margin: 12px 16px 0; max-width: 320px; }

/* Favorite heart on grid cells: small, quiet, top-right. */
.fav-btn {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 30px;
  height: 30px;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0.85;
  z-index: 2;
  padding: 0;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.fav-btn svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
}
.fav-btn.on { color: #ff5a6e; opacity: 1; }
.fav-btn.on svg { fill: currentColor; }

/* Lightbox top bar: heart and Edit next to the close button. */
.lb-top-actions {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  flex: none;
  gap: 8px;
}
#lb-fav {
  appearance: none;
  border: none;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
#lb-fav svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
}
#lb-fav.on { color: #ff5a6e; }
#lb-fav.on svg { fill: currentColor; }

/* Lightbox top bar control system (Build 39 fix pass): the bar is a
   single fixed-height strip. Every control shares one height, the
   text buttons share one text size and one pill radius, labels never
   wrap, and the icon buttons (favorite, play, close) are round at
   the same height. Narrow screens shrink type and padding instead
   of wrapping. */
.lb-top-actions > button {
  height: 36px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  line-height: 1;
}
.lb-top-actions > button.ghost {
  padding: 0 13px;
  font-size: 13.5px;
  border-radius: 999px;
}
.lb-top .lb-play {
  width: 36px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
.lb-top .lb-play svg { width: 16px; height: 16px; display: block; }
@media (max-width: 560px) {
  /* The full control set (counter, heart, four text buttons, play,
     close) only fits a phone width with compact type and padding;
     the alternative the old bar chose was wrapping, which is worse. */
  .lb-top { padding-left: 10px; padding-right: 10px; gap: 6px; }
  #lb-count { font-size: 11.5px; }
  .lb-top-actions { gap: 3px; }
  .lb-top-actions > button { height: 30px; }
  .lb-top-actions > button.ghost { padding: 0 6px; font-size: 11px; }
  #lb-fav, #lb-close, .lb-top .lb-play { width: 30px; height: 30px; }
  #lb-fav svg { width: 14px; height: 14px; }
  .lb-top .lb-play svg { width: 14px; height: 14px; }
  #lb-close { font-size: 17px; }
}

/* Memories card: quiet, serif, at the top of the Library. */
.memories { margin: 12px 16px 0; }
.memories-title {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 500;
  margin-bottom: 2px;
}
.memory-group { margin-top: 14px; }
.memory-group:first-of-type { margin-top: 10px; }
.memory-label {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 8px;
}
.memory-when {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 17px;
}
.memory-year {
  color: var(--text-3);
  font-size: 13px;
}
.memory-strip {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 4px;
  -webkit-overflow-scrolling: touch;
}
.memory-thumb {
  flex: none;
  border: none;
  padding: 0;
  background: var(--bg-soft);
  border-radius: 10px;
  overflow: hidden;
  cursor: pointer;
  width: 96px;
  height: 96px;
}
.memory-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Albums pane inside Library. */
.albums-intro {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  margin: 12px 16px 0;
}
.album-actions {
  display: flex;
  gap: 8px;
  flex: none;
  flex-wrap: wrap;
  margin-top: 12px;
}
.albums-intro .album-actions { margin-top: 0; }
#smart-form { margin: 12px 16px 0; }
#albums-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 16px 14px;
  padding: 12px 16px 0;
}
.albums-section-head { grid-column: 1 / -1; margin-top: 14px; }
.albums-section-head h3 { margin: 0 0 2px; font-size: 17px; }
.album-card { cursor: pointer; }
.album-card:active { transform: scale(0.97); }
.album-cover {
  position: relative;
  aspect-ratio: 1;
  border-radius: 12px;
  overflow: hidden;
  background: var(--bg-soft);
  border: 0.5px solid var(--hairline-soft);
  display: flex;
  align-items: center;
  justify-content: center;
}
.album-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.album-cover-empty { color: var(--text-3); }
.album-cover-empty svg { width: 34px; height: 34px; display: block; }
.album-badge {
  position: absolute;
  left: 8px;
  bottom: 8px;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.55);
  color: var(--text);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.album-name {
  font-weight: 600;
  margin-top: 8px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.album-sub {
  color: var(--text-2);
  font-size: 13px;
  margin-top: 1px;
}
#smart-form .srow { margin-top: 10px; }
#smart-hint { margin-top: 8px; }
#album-meta { padding: 0 16px; }
#album-detail .album-actions { padding: 0 16px; margin-bottom: 12px; }
#album-detail .detail-head { align-items: center; }
#album-name input {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 500;
  background: var(--elev);
  color: var(--text);
  border: 1px solid var(--accent);
  border-radius: 8px;
  padding: 4px 10px;
  width: 100%;
  max-width: 320px;
}

/* Album picker bottom sheet. */
.sheet-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.sheet {
  background: var(--elev);
  width: 100%;
  max-width: 520px;
  max-height: 72vh;
  overflow-y: auto;
  border-radius: 18px 18px 0 0;
  padding: 16px 16px calc(16px + env(safe-area-inset-bottom, 0px));
  border-top: 0.5px solid var(--hairline);
}
.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
}
.sheet-head h3 {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 500;
}
.album-pick-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 4px;
  cursor: pointer;
}
.album-pick-row input[type="checkbox"] {
  width: 20px;
  height: 20px;
  accent-color: var(--accent);
  flex: none;
}
.album-pick-name { flex: 1; font-weight: 500; }
#album-picker-newform {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}
#album-picker-newform input { flex: 1; min-width: 0; }

/* Pre-share scrub report sheet (Wave E4). */
#scrub-findings { margin: 8px 0 2px; }
.scrub-row {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 9px 2px;
  border-bottom: 0.5px solid var(--hairline);
}
.scrub-row:last-child { border-bottom: none; }
.scrub-dot {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin-top: 5px;
  background: var(--ok);
}
.scrub-dot.warn { background: #ff9f0a; }
.scrub-dot.info { background: var(--accent); }
.scrub-dot.unknown { background: rgba(255, 255, 255, 0.35); }
.scrub-row-text { flex: 1; min-width: 0; font-size: 14px; line-height: 1.45; }
.scrub-row-text b { font-weight: 600; }
.scrub-toggles {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 14px 0 2px;
}
#scrub-create { width: 100%; margin-top: 12px; }
#scrub-note.warn { color: #ff9f0a; }
/* Settings view (workstream C). Uses the gallery theme tokens from
   styles.css: --bg, --elev, --elev-2, --text, --text-2, --hairline-soft,
   --accent, --danger, --radius, --spring. Reuses .card, .ghost, .primary,
   .meta from styles.css; nothing here redefines them. */

/* Storage / export meter bars */
.set-meter {
  height: 8px;
  border-radius: 99px;
  background: var(--elev-2);
  overflow: hidden;
  margin: 10px 0 4px;
}
.set-meter-fill {
  height: 100%;
  width: 0%;
  border-radius: 99px;
  background: var(--accent);
  transition: width 0.4s var(--spring);
}
.set-meter-fill.warn { background: #ff9f0a; }
.set-meter-fill.full { background: var(--danger); }

/* Label + action rows inside cards */
.set-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
  border-top: 0.5px solid var(--hairline-soft);
}
.set-row:first-child { border-top: 0; }
.set-row .grow { flex: 1; min-width: 0; }
.set-row .t { font-size: 14px; font-weight: 500; }
.set-row .s { font-size: 12.5px; color: var(--text-2); margin-top: 2px; }
.set-row .ghost { flex-shrink: 0; }

.set-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 12px;
}

.set-status {
  font-size: 13px;
  color: var(--text-2);
  margin-top: 8px;
  min-height: 1.2em;
}

/* Connected folder rows */
.set-folder {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 8px 0;
  border-top: 0.5px solid var(--hairline-soft);
  font-size: 14px;
}
.set-folder:first-child { border-top: 0; }
.set-folder .count {
  color: var(--text-2);
  font-size: 12px;
  text-align: right;
  flex-shrink: 0;
}

/* Danger zone: hairline tint; armed confirm buttons go solid red */
.card.danger-zone { border-color: rgba(255, 69, 58, 0.35); }
button.ghost.danger.armed {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}
/* edit.css (workstream F): styles for the photo/video editor overlay,
   batch select mode, and video badges. The integrator merges this into
   styles.css (motion workstream owns that file). Dark, gallery-like. */

/* ---- editor overlay ---- */
.pp-editor {
  position: fixed; inset: 0; z-index: 150;
  background: #000; color: #f2efe6;
  font-family: inherit;
}
.pp-ed-top {
  position: absolute; top: 0; left: 0; right: 0; z-index: 12;
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px;
  background: transparent; border-bottom: none;
}
/* Top scrim: keeps the floating top bar readable over any
   photo. Its height is set by JS (layoutChrome). */
.pp-ed-scrim {
  position: absolute; top: 0; left: 0; right: 0; z-index: 11;
  height: 150px;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0));
  pointer-events: none;
}
.pp-ed-title {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-weight: 600;
}
/* Action buttons group (single element, permanent child of the panel
   footer .pp-ed-foot in both layouts). */
.pp-ed-actions { display: flex; align-items: center; gap: 8px; }
/* Stage: fills the editor root. On desktop it yields the panel's width
   on the right (see the layout block below), so the photo is fitted into
   the stage area only and controls never overlap it. On mobile it is the
   whole viewport. */
.pp-ed-stage {
  position: absolute; inset: 0;
  overflow: hidden; background: #000;
  /* Direct manipulation: the editor owns all touch gestures on the stage. */
  touch-action: none;
}
.pp-ed-canvaswrap { position: absolute; inset: 0; line-height: 0; display: flex; align-items: center; justify-content: center; }
/* Zoom viewport: JS sizes this to the fitted image box, so the canvas
   element box always matches the image content box exactly (no letterbox).
   The zoom transform (translate + scale about center) is applied here. */
.pp-ed-viewport { position: relative; transform-origin: center; will-change: transform; line-height: 0; }
.pp-ed-viewport.anim { transition: transform 0.35s cubic-bezier(0.32, 0.72, 0, 1); }
.pp-ed-canvas { width: 100%; height: 100%; display: block; }
.pp-ed-video { width: 100%; height: 100%; object-fit: contain; background: #000; }
/* Compare (before/after) host fills the full-bleed stage. */
.pp-ba-host { position: absolute; inset: 0; z-index: 5; }
/* Zoom controls: a subtle pill that lives INSIDE the panel (edit.js
   appends it there). On desktop CSS parks it at the bottom-right of
   the stage (offset from the panel's right edge, the viewport edge);
   on mobile it is a slim row in the panel header. */
.pp-ed-zoombar {
  position: absolute; right: 14px; bottom: 14px; z-index: 13;
  display: flex; align-items: center; gap: 2px;
  background: rgba(12, 12, 16, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px; padding: 3px;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.pp-ed-zoombar.hidden { display: none; }
.pp-zoom-btn, .pp-zoom-pct {
  appearance: none; border: none; background: transparent; color: #fff;
  font-size: 15px; font-weight: 600; line-height: 1;
  min-width: 30px; height: 30px; border-radius: 999px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  touch-action: manipulation;
}
.pp-zoom-btn:hover, .pp-zoom-pct:hover { background: rgba(255, 255, 255, 0.12); }
.pp-zoom-pct { font-size: 11px; font-weight: 600; padding: 0 8px; min-width: 46px; opacity: 0.85; }
/* Control panel container. On desktop it is a solid docked panel on the
   right that owns its space (the stage yields its width); on mobile it is
   docked below the stage in the layout flow (rules in the layout blocks
   below). */
.pp-ed-panel {
  position: absolute; z-index: 10;
  display: flex; flex-direction: column; min-height: 0;
}
/* Panel zones (photo editor): flattened by default so the mobile panel
   flows grab, zoom, tabs, body, footer in one column; the desktop layout block
   turns .pp-ed-cols into a row with the rail beside the controls column.
   The video editor's simpler panel has none of these children. */
.pp-ed-cols { display: contents; }
.pp-ed-rail { display: contents; }
.pp-ed-main { display: contents; }
/* Action footer: pinned to the panel bottom in both layouts, never
   scrolls with the controls. */
.pp-ed-foot {
  flex: none;
  display: flex;
  padding: 8px 10px;
  background: #0d0d11;
  border-top: 0.5px solid var(--hairline-soft);
}
.pp-ed-foot .pp-ed-actions { display: flex; flex: 1; gap: 6px; }
.pp-ed-foot .pp-ed-actions > button {
  flex: 1; min-height: 38px; padding: 6px 2px;
  font-size: 12.5px; white-space: nowrap;
  display: flex; align-items: center; justify-content: center;
}
.pp-ed-foot .pp-ed-actions > button.primary { flex: 1.35; }
/* Reset (Fix F2): danger-tinted so it stands apart from the neutral
   footer actions without reading as disabled: soft red text and
   border over a faint red wash on the dark footer surface. */
.pp-ed-foot .pp-ed-actions > button.pp-ed-reset {
  color: #ff9d94;
  border-color: rgba(255, 69, 58, 0.45);
  background: rgba(255, 69, 58, 0.10);
}
.pp-ed-foot .pp-ed-actions > button.pp-ed-reset:hover { background: rgba(255, 69, 58, 0.20); }
.pp-ed-tabs { display: flex; gap: 4px; padding: 8px 12px 0; overflow-x: auto; scrollbar-width: none; position: relative; }
.pp-ed-tabs::-webkit-scrollbar { display: none; }
/* Fade indicators show the rail scrolls: more tabs off-screen. */
.pp-ed-tabs::after {
  content: ""; position: sticky; right: 0; flex: none; width: 28px; margin-left: -28px;
  background: linear-gradient(to right, transparent, #0a0a0d 80%);
  pointer-events: none; align-self: stretch;
}
.pp-tab {
  background: none; border: none; color: #b9b4a6; padding: 8px 12px;
  border-bottom: 2px solid transparent; cursor: pointer; white-space: nowrap;
}
.pp-tab.active { color: #f2efe6; border-bottom-color: #e8b93c; }
.pp-ed-body { overflow-y: auto; padding: 8px 12px 20px; }
.pp-ed-body h3 { margin: 14px 0 6px; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: #b9b4a6; }
.pp-row { display: flex; align-items: center; gap: 10px; margin: 8px 0; flex-wrap: wrap; }
.pp-lab { min-width: 92px; font-size: 14px; }
.pp-val { min-width: 52px; text-align: right; font-size: 13px; color: #b9b4a6; font-variant-numeric: tabular-nums; }
/* Editor sliders: custom track painted by js/range.js. The fill spans
   --lo..--hi (neutral point to knob), a tick marks the neutral point
   --o on bipolar sliders, and the knob is gold only when off neutral
   (.is-active). One language in every tab: neutral always looks
   neutral, whatever the slider's range. */
.pp-slider {
  flex: 1; min-width: 120px; height: 22px; margin: 0; padding: 0;
  -webkit-appearance: none; appearance: none;
  background: transparent; cursor: pointer;
  --lo: 0%; --hi: 0%; --o: 0%; --tickc: transparent;
}
.pp-slider.is-bipolar { --tickc: rgba(242, 239, 230, 0.45); }
.pp-slider::-webkit-slider-runnable-track {
  height: 4px; border-radius: 2px;
  background:
    linear-gradient(90deg, transparent 0, transparent calc(var(--o) - 1px), var(--tickc) calc(var(--o) - 1px), var(--tickc) calc(var(--o) + 1px), transparent calc(var(--o) + 1px)),
    linear-gradient(90deg, rgba(242, 239, 230, 0.16) 0, rgba(242, 239, 230, 0.16) var(--lo), #e8b93c var(--lo), #e8b93c var(--hi), rgba(242, 239, 230, 0.16) var(--hi));
}
.pp-slider::-moz-range-track {
  height: 4px; border-radius: 2px;
  background:
    linear-gradient(90deg, transparent 0, transparent calc(var(--o) - 1px), var(--tickc) calc(var(--o) - 1px), var(--tickc) calc(var(--o) + 1px), transparent calc(var(--o) + 1px)),
    linear-gradient(90deg, rgba(242, 239, 230, 0.16) 0, rgba(242, 239, 230, 0.16) var(--lo), #e8b93c var(--lo), #e8b93c var(--hi), rgba(242, 239, 230, 0.16) var(--hi));
}
.pp-slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px; border-radius: 50%; border: none;
  background: #d8d3c3; margin-top: -7px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.55);
}
.pp-slider::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: 50%; border: none;
  background: #d8d3c3;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.55);
}
.pp-slider.is-active::-webkit-slider-thumb { background: #e8b93c; }
.pp-slider.is-active::-moz-range-thumb { background: #e8b93c; }
.pp-slider.is-active + .pp-val { color: #e8b93c; }
.pp-slider:disabled { cursor: default; }
/* Type-in slider values: the number input that replaces a .pp-val on click. */
.pp-valinput {
  width: 64px; text-align: right; font-size: 13px; color: #f2efe6;
  background: #1c1c21; border: 1px solid #e8b93c; border-radius: 6px; padding: 2px 4px;
  font-variant-numeric: tabular-nums;
}
.pp-textinput {
  flex: 1; min-width: 140px; font-size: 14px; color: #f2efe6;
  background: #1c1c21; border: 1px solid #33333a; border-radius: 8px; padding: 7px 10px;
}
.pp-textinput:focus { border-color: #e8b93c; outline: none; }
.pp-note { font-size: 13px; color: #8f8a7c; margin: 6px 0; line-height: 1.45; }
.pp-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0; }
/* Beauty face picker (B3): one chip per detected face with a small
   crop of that face, plus a text-only All faces chip. Same card
   language as .pp-filtercard. */
.pp-facechips { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0; }
.pp-facechip {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  background: #17171c; border: 1px solid #2c2c33; border-radius: 10px;
  padding: 6px 8px; color: #f2efe6; font: inherit; font-size: 12px;
  cursor: pointer;
}
.pp-facechip canvas { display: block; border-radius: 6px; }
.pp-facechip.all { justify-content: center; min-height: 62px; padding: 6px 14px; font-size: 13px; }
.pp-facechip.active { border-color: #e8b93c; outline: 1px solid #e8b93c; }
.pp-ed-body .pp-row.is-disabled { opacity: .5; }
.pp-ed-body .pp-row.is-disabled .pp-slider { pointer-events: none; }
.pp-sub { font-size: 12px; color: #8f8a7c; }
/* Editor tool buttons. A bare .pp-toolbtn is the tab's primary action
   (Find face, Find subject, and the one-time model-download buttons in
   Reshape, Background and Beauty): gold pill, same language as
   .mk-btn.primary in the Masks tab. Without this base rule those buttons
   fell back to the raw OS default chrome. With .ghost they stay quiet
   secondaries; the .ghost rules supply that chrome. */
.pp-toolbtn:not(.ghost) {
  appearance: none;
  border: 1px solid #e8b93c;
  border-radius: 999px;
  background: #e8b93c;
  color: #141414;
  font-size: 14px;
  font-weight: 600;
  padding: 9px 18px;
  cursor: pointer;
  transition: transform 0.18s var(--spring), filter 0.18s var(--spring), opacity 0.18s;
}
.pp-toolbtn:not(.ghost):hover { filter: brightness(1.07); }
.pp-toolbtn:not(.ghost):active { transform: scale(0.97); }
.pp-toolbtn:not(.ghost):disabled { opacity: 0.45; cursor: default; transform: none; filter: none; }
.pp-toolbtn.active { outline: 2px solid #e8b93c; outline-offset: 1px; }

/* ---- filter picker ---- */
.pp-filtergrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 8px; margin: 8px 0; }
.pp-filtergrid.small { max-height: 40vh; overflow-y: auto; }
.pp-filtercard {
  background: #17171c; border: 1px solid #2c2c33; border-radius: 10px;
  padding: 10px; text-align: left; color: #f2efe6; cursor: pointer;
}
.pp-filtercard.active { border-color: #e8b93c; outline: 1px solid #e8b93c; }
.pp-fname { font-weight: 600; font-size: 14px; margin-bottom: 4px; }
.pp-fdesc { font-size: 12px; color: #a09a8a; line-height: 1.4; }
.pp-famhead {
  grid-column: 1 / -1;
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em;
  color: #b9b4a6; margin: 10px 0 2px;
}
.pp-famhead:first-child { margin-top: 0; }

/* ---- filter tiles: live thumbnails rendered on the user's own photo.
   Used by the photo editor's Filters tab (presets, My Looks, LUTs). The
   video editor's text cards use .pp-filtergrid/.pp-filtercard alone and
   are unaffected. ---- */
.pp-tiles { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin: 8px 0; }
.pp-filtercard.pp-tile {
  padding: 0; overflow: hidden; position: relative;
  display: flex; flex-direction: column;
}
.pp-tileapply {
  appearance: none; -webkit-appearance: none;
  background: none; border: none; padding: 0; margin: 0;
  display: block; width: 100%; text-align: left; cursor: pointer;
  color: inherit; font: inherit;
}
.pp-tilecv {
  display: block; width: 100%; aspect-ratio: 3 / 2;
  background: #232329;
}
.pp-tname {
  padding: 5px 8px 7px; font-size: 11px; font-weight: 600; color: #f2efe6;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Match Look picker (Filters tab sub-view): recent library photos. */
.pp-matchgrid { grid-template-columns: repeat(3, 1fr); }
.pp-matchtile {
  appearance: none; -webkit-appearance: none;
  padding: 0; overflow: hidden; cursor: pointer;
  display: flex; flex-direction: column; text-align: left;
  background: #232329; border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 8px; color: inherit; font: inherit;
}
.pp-matchtile:disabled { cursor: default; }
.pp-matchtile.busy { outline: 2px solid #e8b93c; outline-offset: -2px; }
.pp-matchtile img {
  display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  background: #1a1a1f;
}
.pp-matchname {
  padding: 4px 6px 5px; font-size: 10px; font-weight: 600; color: #f2efe6;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pp-favbtn {
  position: absolute; top: 4px; right: 4px; z-index: 2;
  width: 24px; height: 24px; border-radius: 50%;
  border: none; cursor: pointer;
  background: rgba(10, 10, 14, 0.55); color: #d8d2c2;
  display: flex; align-items: center; justify-content: center;
  padding: 0;
}
.pp-favbtn svg {
  width: 13px; height: 13px;
  fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linejoin: round;
}
.pp-favbtn.on { color: #e8b93c; }
.pp-favbtn.on svg { fill: currentColor; }
.pp-loaddash {
  border: 1.5px dashed #45454f; background: transparent; color: #cfcabb;
  border-radius: 10px; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; min-height: 96px; font-size: 12px; font-weight: 600;
  padding: 10px 6px; text-align: center; width: 100%;
}
.pp-loadplus { font-size: 22px; line-height: 1; font-weight: 400; }
.pp-fsearchrow { flex: 1 1 100%; }
.pp-fsearch {
  width: 100%; box-sizing: border-box;
  font-size: 13px; color: #f2efe6;
  background: #1c1c21; border: 1px solid #33333a; border-radius: 8px;
  padding: 6px 10px;
}
.pp-fsearch:focus { border-color: #e8b93c; outline: none; }

/* ---- crop overlay ---- */
.pp-cropov { position: absolute; inset: 0; touch-action: none; cursor: move; }
.pp-cropov.hidden { display: none; }
.pp-cropbox {
  position: absolute; border: 2px solid #e8b93c;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.55);
}
.pp-crophandle {
  position: absolute; width: 22px; height: 22px; background: #e8b93c; border-radius: 50%;
  transform: translate(-50%, -50%);
}
.pp-crophandle.nw { left: 0; top: 0; } .pp-crophandle.ne { left: 100%; top: 0; }
.pp-crophandle.sw { left: 0; top: 100%; } .pp-crophandle.se { left: 100%; top: 100%; }

/* ---- retouch spots ---- */
.pp-spots { position: absolute; inset: 0; pointer-events: none; }
.pp-spots.hidden { display: none; }
.pp-spot {
  position: absolute; width: 26px; height: 26px; border: 2px solid #e8b93c; border-radius: 50%;
  transform: translate(-50%, -50%); background: rgba(232, 185, 60, 0.15);
}
/* Clone stamp source point: distinct from the gold destination markers. */
.pp-spot.pp-src { border-color: #7cc7ff; border-style: dashed; background: rgba(124, 199, 255, 0.15); }

/* ---- targeted adjustment (drag-on-photo) ---- */
.pp-tat-on .pp-ed-canvaswrap { cursor: crosshair; }
.pp-tat-btn svg { width: 14px; height: 14px; vertical-align: -2px; margin-right: 6px; }
.pp-tat-chip {
  position: absolute; z-index: 14; pointer-events: none;
  transform: translate(-50%, -130%);
  background: rgba(12, 12, 16, 0.8);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 999px; padding: 6px 12px;
  color: #fff; font-size: 12px; font-weight: 600; white-space: nowrap;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.pp-tat-chip.hidden { display: none; }

/* ---- frame and text overlay controls (Effects tab) ---- */
.pp-swatchrow { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 8px 0; }
.pp-swatch {
  width: 30px; height: 30px; border-radius: 50%; padding: 0; cursor: pointer;
  border: 2px solid rgba(255, 255, 255, 0.25); background: transparent;
}
.pp-swatch.active { border-color: #e8b93c; }
.pp-colorin { width: 34px; height: 30px; border: none; background: none; padding: 0; cursor: pointer; }
.pp-textin {
  width: 100%; box-sizing: border-box; margin: 4px 0 8px;
  background: #141418; color: #f2efe6; border: 1px solid #33333a; border-radius: 8px;
  padding: 9px 10px; font-size: 13px;
}
.pp-posgrid { display: grid; grid-template-columns: repeat(3, 26px); gap: 4px; }
.pp-posbtn {
  width: 26px; height: 26px; border-radius: 6px; padding: 0; cursor: pointer; position: relative;
  background: #1c1c21; border: 1px solid #33333a;
}
.pp-posbtn .pp-posdot { position: absolute; width: 6px; height: 6px; border-radius: 50%; background: #8a8577; }
.pp-posbtn.active { border-color: #e8b93c; }
.pp-posbtn.active .pp-posdot { background: #e8b93c; }

/* ---- curves ---- */
.pp-curves { margin: 8px 0; }
.pp-curve-canvas { background: #0a0a0d; border: 1px solid #2c2c33; border-radius: 8px; touch-action: none; }
.pp-hslgrp { margin: 4px 0; border: 1px solid #232329; border-radius: 8px; padding: 4px 8px; }
.pp-hslgrp summary { cursor: pointer; padding: 6px 0; color: #d8d2c2; }

/* ---- toast ---- */
.pp-toast {
  position: fixed; left: 50%; bottom: 88px; transform: translateX(-50%) translateY(20px);
  background: #1c1c21; color: #f2efe6; border: 1px solid #33333a; border-radius: 10px;
  padding: 10px 16px; font-size: 14px; z-index: 200; opacity: 0; pointer-events: none;
  transition: opacity 0.2s, transform 0.2s; max-width: 86vw; text-align: center;
}
.pp-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.pp-toast.actionable { pointer-events: auto; }
.pp-toast .toast-action {
  margin-left: 10px; background: none; border: none; padding: 2px 4px;
  color: #e8b93c; font: inherit; font-weight: 600; cursor: pointer;
}

/* ---- batch select mode ---- */
.pp-selectbar {
  /* Above the sticky topbar (z 60) so the bar's buttons stay
     clickable during select mode; below dialogs (z 80). */
  position: fixed; top: 0; left: 0; right: 0; z-index: 70;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  background: rgba(12, 12, 15, 0.96); border-bottom: 1px solid #2c2c33;
  padding: 10px 12px;
}
.pp-selcount { font-weight: 600; margin-right: auto; }
.pp-check {
  position: absolute; top: 8px; left: 8px; width: 26px; height: 26px;
  border-radius: 50%; border: 2px solid #fff; background: rgba(0, 0, 0, 0.45);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}
.pp-check.on { background: #e8b93c; border-color: #e8b93c; }
.pp-check.on::after {
  content: ""; position: absolute; left: 8px; top: 4px; width: 6px; height: 11px;
  border: solid #111; border-width: 0 3px 3px 0; transform: rotate(42deg);
}
.photo-cell { position: relative; }
.pp-select-toggle { margin-left: 8px; }

/* ---- dialogs and progress ---- */
.pp-dialog-ov, .pp-progress-ov {
  position: fixed; inset: 0; z-index: 80; background: rgba(0, 0, 0, 0.7);
  display: flex; align-items: center; justify-content: center; padding: 16px;
}
.pp-dialog, .pp-progress {
  background: #141419; border: 1px solid #2c2c33; border-radius: 14px;
  padding: 18px; width: min(560px, 94vw); max-height: 86vh; overflow-y: auto; color: #f2efe6;
}
.pp-dialog h2 { margin: 0 0 10px; font-size: 17px; }
.pp-proglabel { margin-bottom: 10px; font-size: 14px; }
.pp-progfill { height: 100%; width: 0%; background: #e8b93c; border-radius: 4px; transition: width 0.15s; }

/* ---- video badges in the grid ---- */
.video-badge {
  position: absolute; right: 6px; bottom: 6px;
  background: rgba(0, 0, 0, 0.72); color: #fff;
  font-size: 12px; font-variant-numeric: tabular-nums;
  padding: 2px 7px; border-radius: 8px;
}
.video-play {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 0; height: 0; opacity: 0.92;
  border-left: 26px solid #fff; border-top: 16px solid transparent; border-bottom: 16px solid transparent;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.7));
  pointer-events: none;
}

/* ---- lightbox video ---- */
#lb-video { max-width: 100%; max-height: 100%; background: #000; }
/* Build 42 Part 1: lightbox video tools (Save frame + preview speed)
   and the shared speed picker used by the video editor. */
.pp-vid-tools {
  position: absolute;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  display: flex;
  gap: 8px;
  align-items: center;
  z-index: 6;
}
.pp-vid-tools button {
  background: rgba(28, 28, 30, 0.82);
  color: #f2f2f5;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  padding: 7px 14px;
  font-size: 13.5px;
  backdrop-filter: blur(10px);
  cursor: pointer;
}
.pp-vid-tools button:disabled { opacity: 0.55; }
.pp-vid-rate {
  background: rgba(28, 28, 30, 0.82);
  color: #f2f2f5;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  padding: 6px 10px;
  font-size: 13.5px;
}
.pp-ed-body .pp-vid-rate {
  background: var(--surface2, #26262b);
  backdrop-filter: none;
}
/* (lb-stage positioning is defined in the main lightbox rules above) */
/* ---------- Year in photos view (workstream L) ----------
   Append to the end of styles.css. Gallery theme: serif display for the
   year placard, iOS grouped cards, stat tiles, month rhythm bars,
   horizontal standout strip, place rows. */

/* Serif year placard, matching the theme's display face. */
.yr-hero { text-align: center; padding: 28px 16px 22px; }
.yr-hero-year {
  font-family: var(--font-display);
  font-size: 64px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1;
}

/* Stat tiles: 2x2 grid of small cards. */
.yr-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin: 0 16px;
}
.yr-stat {
  background: var(--elev);
  border: 0.5px solid var(--hairline-soft);
  border-radius: var(--radius);
  padding: 14px 12px;
  text-align: center;
}
.yr-stat-num { font-size: 24px; font-weight: 700; letter-spacing: -0.01em; }
.yr-stat-label { color: var(--text-2); font-size: 13px; margin-top: 2px; }

/* Month rhythm: 12 vertical bars, compositor-only scaleY. */
.yr-bars {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  height: 110px;
  margin-top: 12px;
}
.yr-bar-col {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  height: 100%;
}
.yr-bar {
  width: 100%;
  max-width: 26px;
  height: 86px;
  background: var(--accent);
  border-radius: 4px;
  transform-origin: bottom;
  transform: scaleY(0);
}
.yr-bar-abbr { color: var(--text-3); font-size: 10px; margin-top: 4px; }

/* Standout strip + place thumbnails: horizontal scroll, square tiles. */
.yr-strip, .yr-place-thumbs {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 4px 0 2px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.yr-strip::-webkit-scrollbar, .yr-place-thumbs::-webkit-scrollbar { display: none; }
.yr-thumb {
  width: 112px;
  height: 112px;
  flex: 0 0 112px;
  object-fit: cover;
  border-radius: 10px;
  background: var(--elev-2);
}
div.yr-thumb { min-height: 112px; } /* placeholder when no thumbnail yet */

/* Places list. */
.yr-places { display: flex; flex-direction: column; gap: 14px; margin-top: 12px; }
.yr-place { display: flex; flex-direction: column; gap: 8px; }
.yr-place-label { font-size: 15px; font-weight: 600; }
.yr-places-note { margin-top: 4px; }

/* Camera ranking. */
.yr-camlist { margin: 10px 0 0; padding-left: 20px; }
.yr-camlist li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 6px 0;
  border-bottom: 0.5px solid var(--hairline-soft);
}
.yr-camlist li:last-child { border-bottom: none; }

/* Year picker row. */
.year-picker-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px 2px;
}
.year-picker-row select {
  flex: 1;
  background: var(--elev);
  color: var(--text);
  border: 0.5px solid var(--hairline);
  border-radius: 10px;
  padding: 9px 12px;
  font-size: 16px;
}

@media (min-width: 760px) {
  .yr-stats { margin: 0 24px; gap: 12px; }
  .yr-hero-year { font-size: 84px; }
  .yr-thumb { width: 140px; height: 140px; flex-basis: 140px; }
}
/* Workstream N additions: date-tools card, export format rows, EXIF table.
   Append to styles.css (after workstream C's settings.css). Gallery-theme
   tokens only; reuses .card, .set-row, .ghost, .primary, .meta. */

.set-select {
  appearance: none;
  -webkit-appearance: none;
  background-color: var(--card-bg, #1c1c1e);
  color: inherit;
  border: 1px solid var(--border, #38383a);
  border-radius: 10px;
  padding: 8px 28px 8px 10px;
  font-size: 0.95rem;
  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' fill='none' stroke='%23888' stroke-width='1.6' stroke-linecap='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
  max-width: 100%;
}

.set-num {
  width: 4.2rem;
  background-color: var(--card-bg, #1c1c1e);
  color: inherit;
  border: 1px solid var(--border, #38383a);
  border-radius: 10px;
  padding: 8px 6px;
  font-size: 0.95rem;
}

input[type="date"].set-select,
input[type="datetime-local"].set-select {
  background-image: none;
  padding-right: 10px;
}

input[type="range"]#set-quality {
  width: 10rem;
  accent-color: var(--accent, #d4a24e);
}

input[type="range"]#set-quality:disabled {
  opacity: 0.35;
}

#set-qualityVal {
  font-variant-numeric: tabular-nums;
}

/* EXIF inspector table in the lightbox Info panel */
.exif-sec {
  margin-top: 14px;
}

.exif-sec h3 {
  font-size: 0.95rem;
  margin: 0 0 4px;
}

.exif-table {
  display: grid;
  grid-template-columns: minmax(7rem, auto) 1fr;
  gap: 6px 12px;
  margin: 8px 0 0;
  font-size: 0.9rem;
}

.exif-table dt {
  color: var(--muted, #9a9aa0);
}

.exif-table dd {
  margin: 0;
  overflow-wrap: anywhere;
}

.exif-table dd.na {
  color: var(--muted, #9a9aa0);
  font-style: italic;
}


/* Date tools card: long selects and date inputs must not squeeze the labels
   on narrow screens. */
#view-settings .set-row > .set-select,
#view-settings .set-row > input.set-num {
  min-width: 0;
  flex-shrink: 1;
}
#view-settings .set-row > .set-select { max-width: 58%; }

/* Date tools: rows with several controls wrap below their label instead of
   squeezing on narrow screens. */
#dt-rangeRow, #dt-shiftRow { flex-wrap: wrap; }
#dt-rangeRow > .grow, #dt-shiftRow > .grow { flex: 1 1 100%; }
#dt-rangeRow > .set-select, #dt-shiftRow > .set-select { max-width: none; }

/* ---------- Splash (every-launch brand moment; see js/splash.js) ---------- */
body.sp-open { overflow: hidden; }

.sp-overlay {
  position: fixed;
  inset: 0;
  z-index: 400; /* above the onboarding sheet (300) and the grain overlay (200) */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 20px calc(20px + env(safe-area-inset-bottom, 0px));
  /* Warm gallery glow over the dark ground, in the editor's gold family. */
  background:
    radial-gradient(130% 70% at 50% -10%, rgba(232, 185, 60, 0.14), rgba(0, 0, 0, 0) 55%),
    radial-gradient(120% 90% at 50% 0%, rgba(64, 52, 24, 0.28), rgba(0, 0, 0, 0) 60%),
    var(--bg-soft);
  opacity: 0;
  transition: opacity 0.24s var(--spring);
}
.sp-overlay.sp-in { opacity: 1; }
.sp-overlay.sp-leave { opacity: 0; }
.sp-overlay.sp-leave .sp-inner { transform: translateY(6px) scale(0.97); }

.sp-inner {
  width: 100%;
  max-width: 356px;
  max-height: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  transform: translateY(10px) scale(0.97);
  transition: transform 0.34s var(--spring);
}
.sp-overlay.sp-in .sp-inner { transform: none; }

.sp-logo {
  width: 84px;
  height: 84px;
  border-radius: 20px;
  border: 0.5px solid rgba(232, 185, 60, 0.35);
  box-shadow: 0 14px 38px rgba(0, 0, 0, 0.55), 0 0 44px rgba(232, 185, 60, 0.14);
  margin-bottom: 14px;
}

.sp-name {
  font-family: var(--font-display);
  font-size: 40px;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.1;
  margin: 0 0 6px;
  background: linear-gradient(180deg, #fdfcf8 20%, #d9d2bd 130%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--text);
}

.sp-tagline {
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: #c9a959;
  margin: 0 0 18px;
}

/* The three promises as a glass panel: translucent surface, backdrop
   blur, hairline gold-tinted edge, soft depth shadow. */
.sp-unique {
  list-style: none;
  margin: 0 0 16px;
  padding: 14px 16px;
  width: 100%;
  box-sizing: border-box;
  background: rgba(255, 255, 255, 0.045);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  backdrop-filter: blur(14px) saturate(150%);
  border: 0.5px solid rgba(232, 185, 60, 0.22);
  border-radius: 16px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.06);
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.sp-unique li {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-2);
}
.sp-unique li::before { content: "✦ "; color: #e8b93c; }

.sp-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: center;
  margin-bottom: 20px;
  max-width: 340px;
}
.sp-chip {
  font-size: 11px;
  font-weight: 500;
  color: var(--text-2);
  background: rgba(255, 255, 255, 0.05);
  border: 0.5px solid var(--hairline-soft);
  border-radius: 999px;
  padding: 5px 11px;
  white-space: nowrap;
}

/* Enter: the app's primary treatment, in the editor's gold. */
.sp-enter.primary {
  appearance: none;
  border: 1px solid #e8b93c;
  background: linear-gradient(180deg, #f0c452, #e0aa2e);
  color: #171204;
  font-size: 16px;
  font-weight: 600;
  padding: 13px 0;
  width: 100%;
  border-radius: 999px;
  margin-bottom: 10px;
  box-shadow: 0 10px 26px rgba(232, 185, 60, 0.22);
  cursor: pointer;
  transition: transform 0.18s var(--spring), filter 0.18s var(--spring), box-shadow 0.18s var(--spring);
}
.sp-enter.primary:hover { filter: brightness(1.06); box-shadow: 0 12px 30px rgba(232, 185, 60, 0.3); }
.sp-enter.primary:active { transform: scale(0.97); }

.sp-hint {
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--text-3);
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .sp-overlay, .sp-inner { transition: none; }
  .sp-inner { transform: none; }
}

/* === EDITOR LAYOUT === */
/* Standing rule: edit controls must NEVER overlap the photo.
   - Desktop (>=900px): the settings panel is DOCKED on the right and owns
     its space: the stage is inset by the panel width (--pp-ed-panel-w)
     and the photo is fitted into the stage area only, never under the
     panel. The panel is always visible and is made of three zones: a
     76px tool rail on its left edge (all 12 tools, icon over label,
     grouped by job, History and Versions demoted to the rail foot), the
     controls column, and a pinned action footer (Reset, Copy edits,
     Compare, Export, Save). Only the slim top bar (Back + file name)
     floats over the photo; the user explicitly allows top titles over
     the photo.
   - Mobile (<900px): the editor root is a flex column. The stage
     flexes on top and the panel docks BELOW it in normal flow, never
     over the photo. The panel opens as a minimal strip (grab handle,
     zoom pill row, one horizontal scrolling rail row, action footer);
     tapping a tool expands the controls region and the stage shrinks
     to make room, the photo pushing up fully visible. The grab handle
     drags between snap states: rail-only, controls (~42vh), tall
     (panel at most ~70vh). Only the top bar floats over the photo.
   All layout rules live in this block so the layout can be tuned without
   touching the rest of the stylesheet. Spring easing: cubic-bezier(0.32,0.72,0,1). */

.pp-editor {
  --ed-spring: cubic-bezier(0.32, 0.72, 0, 1);
  /* The one source of truth for the docked panel's width: the stage
     inset, the panel width, and the zoom bar offset all derive from it. */
  --pp-ed-panel-w: 352px;
}

/* Grab handle for the mobile panel (hidden on desktop). */
.pp-ed-grab {
  display: flex; align-items: center; justify-content: center;
  padding: 10px 0 6px; cursor: grab; touch-action: none; flex-shrink: 0;
  -webkit-tap-highlight-color: transparent;
}
.pp-ed-grab::before {
  content: ""; width: 42px; height: 5px; border-radius: 3px;
  background: rgba(255, 255, 255, 0.30);
}

/* Springy enter for tab content on both layouts. */
@keyframes ppEdIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}
.pp-ed-enter > * { animation: ppEdIn 0.38s var(--ed-spring); }
@media (prefers-reduced-motion: reduce) {
  .pp-ed-enter > * { animation: none; }
}

/* ---------------- Desktop: docked panel, photo never overlapped --------
   The stage yields the panel's width on the right, so the photo is
   fitted beside the panel and no control ever sits on top of it. The
   panel is three zones: a 76px tool rail on its left edge, the controls
   column (scrolling body from the very top), and a pinned action footer
   (Reset, Copy edits, Compare, Export, Save). The video editor's plain
   panel (body only) is untouched by the rail and footer rules, which
   scope to children it never creates. Only the slim top bar (Back +
   file name) floats over the photo, confined to the stage's width,
   scrim behind it. */
@media (min-width: 900px) {
  .pp-ed-grab { display: none; }
  .pp-ed-stage { right: var(--pp-ed-panel-w); }
  /* Top bar and scrim stop at the panel edge: the panel owns the top
     right corner now. */
  .pp-ed-top { right: var(--pp-ed-panel-w); }
  .pp-ed-scrim { right: var(--pp-ed-panel-w); }
  .pp-ed-panel {
    top: 0; right: 0; bottom: 0;
    width: var(--pp-ed-panel-w);
    background: #0d0d11;
    border-left: 0.5px solid var(--hairline-soft);
  }
  /* Columns row: the rail and the controls column fill the panel
     height; the action footer is pinned beneath them (base .pp-ed-foot
     rules), so no header block eats control space anymore. */
  .pp-ed-cols {
    display: flex; flex-direction: row;
    flex: 1; min-height: 0;
  }
  /* Tool rail: a 76px column of icon-over-label entries grouped by job,
     all 12 tools visible at once with their names, one tap each. The
     list scrolls inside the rail only on very short viewports. */
  .pp-ed-rail {
    display: flex; flex: none;
    width: 76px;
    background: #0a0a0e;
    border-right: 0.5px solid var(--hairline-soft);
  }
  .pp-ed-tabs {
    position: static;
    display: flex; flex-direction: column; gap: 2px;
    flex: 1; min-height: 0;
    padding: 10px 7px; overflow-y: auto; overflow-x: hidden;
    scrollbar-width: none;
  }
  .pp-ed-tabs::-webkit-scrollbar { display: none; }
  .pp-ed-tabs::after { display: none; }
  .pp-tabcap {
    font-size: 8.5px; font-weight: 700; letter-spacing: 0.14em;
    text-transform: uppercase; color: #6f6a5e;
    text-align: center; margin: 9px 0 3px;
  }
  .pp-tabcap:first-child { margin-top: 0; }
  /* The Meta group (History, Versions) is pinned to the rail foot under
     a stronger divider, demoted from the creative tools. */
  .pp-tabcap-meta {
    margin-top: auto; padding-top: 10px;
    border-top: 1px solid #2c2c33;
  }
  .pp-ed-tabs .pp-tab {
    display: flex; flex-direction: column; align-items: center; gap: 3px;
    border: 1px solid transparent; border-radius: 10px;
    background: transparent; color: #cfcabb;
    padding: 7px 2px 6px; font-size: 10px; line-height: 1.1;
    text-align: center; white-space: normal; text-shadow: none;
    cursor: pointer;
  }
  .pp-ed-tabs .pp-tab svg { width: 22px; height: 22px; }
  .pp-ed-tabs .pp-tab:hover { background: #17171c; color: #f2efe6; }
  .pp-ed-tabs .pp-tab:focus-visible { outline: 2px solid #e8b93c; outline-offset: -2px; }
  .pp-ed-tabs .pp-tab.active {
    background: #e8b93c; border-color: #e8b93c;
    color: #171106; font-weight: 600;
  }
  /* Controls column: the body scrolls from the very top of the panel. */
  .pp-ed-main {
    display: flex; flex-direction: column;
    flex: 1; min-width: 0; min-height: 0;
  }
  .pp-ed-body {
    flex: 1; min-height: 0;
    overflow-y: auto; overflow-x: hidden;
    padding: 12px 14px 24px;
    overscroll-behavior: contain;
  }
  /* Compact slider rows sized for the narrower controls column. */
  .pp-ed-main .pp-lab { min-width: 74px; font-size: 13px; }
  .pp-ed-main .pp-slider { min-width: 56px; }
  .pp-ed-main .pp-val { min-width: 38px; }
  /* Bottom-right of the stage: clear of the docked panel. */
  .pp-ed-zoombar { right: calc(var(--pp-ed-panel-w) + 16px); bottom: 16px; }
  /* Worst case for the floating top bar is a bright sky at the top of the
     photo: a soft shadow keeps the file name readable. */
  .pp-ed-title { text-shadow: 0 1px 8px rgba(0, 0, 0, 0.75), 0 0 2px rgba(0, 0, 0, 0.6); }
}

/* ---- FLIP open/close chrome ----
   .pp-ed-flip: chrome starts hidden, slides/fades in when removed
   (after the canvas FLIPs from the lightbox rect). .pp-ed-closing: chrome
   fades out while the canvas FLIPs back to the lightbox. The photo never
   jumps; only the floating chrome animates. */
.pp-ed-flip .pp-ed-top,
.pp-ed-flip .pp-ed-tabs,
.pp-ed-flip .pp-ed-panel,
.pp-ed-flip .pp-ed-scrim {
  opacity: 0;
  transform: translateY(-16px);
}
.pp-ed-top, .pp-ed-tabs, .pp-ed-panel, .pp-ed-scrim {
  transition: opacity 0.42s var(--ed-spring), transform 0.42s var(--ed-spring);
}
.pp-ed-closing .pp-ed-top,
.pp-ed-closing .pp-ed-tabs,
.pp-ed-closing .pp-ed-panel,
.pp-ed-closing .pp-ed-scrim {
  opacity: 0;
  transform: translateY(-12px);
  transition: opacity 0.28s ease, transform 0.28s ease;
}
@media (prefers-reduced-motion: reduce) {
  .pp-ed-flip .pp-ed-top,
  .pp-ed-flip .pp-ed-tabs,
  .pp-ed-flip .pp-ed-panel,
  .pp-ed-flip .pp-ed-scrim,
  .pp-ed-closing .pp-ed-top,
  .pp-ed-closing .pp-ed-tabs,
  .pp-ed-closing .pp-ed-panel,
  .pp-ed-closing .pp-ed-scrim {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ---- Filters: sticky strength header ----
   Selected filter name + Strength slider pinned at the top of the panel;
   the card grid scrolls beneath it. */
.pp-filterhead {
  position: sticky;
  top: 0;
  z-index: 6;
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  /* Full-bleed: counter the body's padding so the bar sits flush at the
     top edge and sticks without a gap. */
  margin: -12px -14px 8px;
  padding: 12px 14px 10px;
  background: rgba(13, 13, 17, 0.97);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border-bottom: 0.5px solid var(--hairline-soft);
}
@media (max-width: 899px) {
  .pp-filterhead { margin: -8px -16px 8px; padding: 10px 20px; }
}
.pp-filterhead-name {
  font-weight: 700;
  font-size: 14px;
  color: var(--text);
  min-width: 140px;
}
.pp-filterhead:not(.has-filter) .pp-filterhead-name { color: var(--text-3); font-weight: 600; }
.pp-filterhead-strength {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 220px;
  max-width: 520px;
  opacity: 0.45;
  transition: opacity 0.25s ease;
}
.pp-filterhead.has-filter .pp-filterhead-strength { opacity: 1; }

/* ---- History tab ----
   Tidy step list: one clean row per step, label left, time right.
   "Original" is the base row, styled as a subtle reset affordance. */
.pp-history { display: flex; flex-direction: column; gap: 4px; }
.pp-history-title {
  font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--text-2); font-weight: 700; margin: 4px 0 8px;
}
.pp-history-list { display: flex; flex-direction: column; gap: 6px; max-width: 980px; }
.pp-history-empty {
  padding: 20px 16px; border: 1px dashed rgba(255, 255, 255, 0.16);
  border-radius: var(--radius); color: var(--text-2); font-size: 14px;
  text-align: center; background: rgba(255, 255, 255, 0.02);
}
.pp-history-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; text-align: left;
  padding: 11px 14px; border-radius: 12px;
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid rgba(255, 255, 255, 0.08);
  color: var(--text); cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.pp-history-row:hover { background: rgba(255, 255, 255, 0.08); border-color: rgba(255, 255, 255, 0.14); }
.pp-history-row:disabled { opacity: 0.5; cursor: default; }
.pp-history-row.is-current { border-color: rgba(232, 185, 60, 0.55); }
.pp-history-label {
  font-size: 14px; font-weight: 600; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pp-history-meta { font-size: 12px; color: var(--text-2); flex-shrink: 0; font-variant-numeric: tabular-nums; }
.pp-history-row.is-current .pp-history-meta { color: #e8b93c; }
.pp-history-original {
  margin-top: 6px;
  border-style: dashed;
  border-color: rgba(255, 69, 58, 0.35);
  background: rgba(255, 69, 58, 0.06);
}
.pp-history-original:hover { background: rgba(255, 69, 58, 0.12); border-color: rgba(255, 69, 58, 0.5); }
.pp-history-original .pp-history-label { color: #ff8a80; }

/* ---- Versions tab ----
   Name input + save button on one tidy row; version entries as rows with
   thumbnail, name/date, and actions; the hold-to-preview tip as a note. */
.pp-versions { display: flex; flex-direction: column; gap: 10px; }
.pp-versions-save {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap; max-width: 980px;
}
.pp-versions-name {
  flex: 1; min-width: 200px;
  appearance: none; border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(255, 255, 255, 0.05); color: var(--text);
  font-size: 14px; padding: 10px 14px; border-radius: 12px; outline: none;
}
.pp-versions-name:focus { border-color: rgba(232, 185, 60, 0.6); }
.pp-versions-name::placeholder { color: var(--text-3); }
.pp-versions-savebtn {
  appearance: none; border: 1px solid rgba(232, 185, 60, 0.5);
  background: rgba(232, 185, 60, 0.12); color: #e8b93c;
  font-size: 14px; font-weight: 700; padding: 10px 18px; border-radius: 12px;
  cursor: pointer; white-space: nowrap;
  transition: background 0.2s ease;
}
.pp-versions-savebtn:hover { background: rgba(232, 185, 60, 0.22); }
.pp-versions-savebtn:disabled { opacity: 0.5; cursor: default; }
.pp-versions-hint { font-size: 12px; color: var(--text-3); max-width: 980px; line-height: 1.5; }
.pp-versions-grid { display: flex; flex-direction: column; gap: 8px; max-width: 980px; }
.pp-versions-empty {
  padding: 20px 16px; border: 1px dashed rgba(255, 255, 255, 0.16);
  border-radius: var(--radius); color: var(--text-2); font-size: 14px;
  text-align: center; background: rgba(255, 255, 255, 0.02);
}
.pp-version-card {
  display: flex; align-items: center; gap: 14px;
  padding: 10px 12px; border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid rgba(255, 255, 255, 0.08);
  transition: border-color 0.2s ease;
}
.pp-version-card.comparing { border-color: rgba(232, 185, 60, 0.6); }
.pp-version-thumb {
  appearance: none; border: 1px solid rgba(255, 255, 255, 0.12);
  background: #000; border-radius: 8px; padding: 0; cursor: pointer;
  width: 72px; height: 72px; flex-shrink: 0; overflow: hidden;
}
.pp-version-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pp-version-nothumb { font-size: 11px; color: var(--text-3); }
.pp-version-namerow {
  display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1;
}
.pp-version-name {
  font-size: 14px; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pp-version-time { font-size: 12px; color: var(--text-2); font-variant-numeric: tabular-nums; }
.pp-version-rename {
  appearance: none; border: 1px solid rgba(232, 185, 60, 0.6);
  background: rgba(255, 255, 255, 0.06); color: var(--text);
  font-size: 14px; padding: 6px 10px; border-radius: 8px; outline: none; width: 100%;
}
.pp-version-actions { display: flex; gap: 8px; flex-shrink: 0; }
.pp-version-act {
  appearance: none; border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(255, 255, 255, 0.05); color: var(--text);
  font-size: 13px; font-weight: 600; padding: 8px 14px; border-radius: 10px;
  cursor: pointer; white-space: nowrap;
}
.pp-version-act:hover { background: rgba(255, 255, 255, 0.1); }
.pp-version-del { color: #ff8a80; border-color: rgba(255, 69, 58, 0.35); }
.pp-version-del:hover { background: rgba(255, 69, 58, 0.12); }

/* ---------------- Mobile: docked panel, photo pushes up ----------------
   The editor root is a vertical flex column: the stage (flex 1) on top,
   the panel docked below it in normal flow. The panel is NOT positioned
   over the stage at any snap state; when it grows, the stage shrinks
   and the photo refits into the smaller stage (the canvas wrap's
   ResizeObserver re-runs the fit continuously). Snap states (classes
   set by edit.js): snap-rail shows only the grab handle, the zoom row,
   the rail row and the footer; snap-mid adds the controls region capped
   at 42vh; snap-tall caps the whole panel at 70vh. */
@media (max-width: 899px) {
  .pp-editor { display: flex; flex-direction: column; }
  .pp-ed-top {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding: 10px 10px;
  }
  .pp-ed-top::-webkit-scrollbar { display: none; }
  .pp-ed-top > * { flex-shrink: 0; }
  .pp-ed-title { flex-shrink: 1; }
  .pp-ed-stage {
    position: relative; inset: auto;
    flex: 1 1 auto; min-height: 0;
  }
  .pp-ed-panel {
    position: relative; left: auto; right: auto; bottom: auto; top: auto;
    flex: none; width: 100%;
    border-radius: 18px 18px 0 0;
    background: #101014;
    box-shadow: 0 -14px 44px rgba(0, 0, 0, 0.6);
    max-height: 70vh;
    min-height: 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  /* Snap states: the controls region is what grows and shrinks. */
  .pp-ed-panel.snap-rail .pp-ed-body { display: none; }
  .pp-ed-panel.snap-mid .pp-ed-body { max-height: 42vh; }
  .pp-ed-panel.snap-tall .pp-ed-body { max-height: 56vh; }
  /* While the grab handle is being dragged, the controls region tracks
     the finger (inline max-height from edit.js) with no transition. */
  .pp-ed-panel.pp-ed-dragging .pp-ed-body { display: block; transition: none; }
  /* The rail is ONE horizontal scrolling row of the same grouped
     entries, pinned at the top of the panel: icon over label, compact;
     all 15 tools are reachable by horizontal scroll. */
  .pp-ed-tabs {
    flex-shrink: 0;
    display: flex; flex-direction: row; flex-wrap: nowrap;
    gap: 4px; padding: 4px 10px 6px;
    overflow-x: auto; overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .pp-ed-tabs::-webkit-scrollbar { display: none; }
  .pp-ed-tabs::after { display: none; }
  .pp-tabcap { display: none; }
  .pp-ed-tabs .pp-tab {
    flex: none; width: 58px;
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    border: 1px solid #2c2c33; border-radius: 9px;
    background: #17171c; color: #cfcabb;
    padding: 6px 1px 5px; font-size: 9px; line-height: 1.1;
    text-align: center; white-space: normal; text-shadow: none;
    cursor: pointer;
  }
  .pp-ed-tabs .pp-tab svg { width: 19px; height: 19px; }
  .pp-ed-tabs .pp-tab.active {
    background: #e8b93c; border-color: #e8b93c; color: #171106; font-weight: 600;
  }
  /* The action footer is pinned to the panel bottom, above the
     safe-area inset (the panel itself carries the inset padding). */
  .pp-ed-foot { padding: 8px 10px 4px; }
  /* The zoom pill lives in the panel header as its own slim row,
     right-aligned under the grab handle: nothing floats over the photo
     except the top bar (Back + file name). */
  .pp-ed-zoombar {
    position: static; flex: none; align-self: flex-end;
    margin: 0 10px 2px;
  }
  .pp-ed-body {
    flex: 1; min-height: 0; overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding: 8px 16px 24px;
    transition: max-height 0.45s var(--ed-spring);
  }
  .pp-ed-body .pp-filtergrid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); }
  .pp-ed-body .pp-tiles { grid-template-columns: repeat(3, 1fr); }
}
/* === END EDITOR REDESIGN === */

/* === ANIMATION SYSTEM (v11) ===
   Hand-rolled WAAPI/CSS motion. No third-party libraries. Everything here
   uses the house spring easing (var(--spring) = cubic-bezier(0.32,0.72,0,1))
   and is transform/opacity-only. prefers-reduced-motion is handled by the
   existing global block earlier in this file plus JS checks in js/anim.js. */

/* ---- bottom sheets: spring slide-up on open, slide-down on dismiss ----
   Markup contract: the overlay carries .anim-in/.anim-shown (toggled by
   springShow/springHide in js/anim.js); the sliding card is .sheet.
   Applies to the album picker sheet, the share-style sheets, and any
   future bottom sheet that follows the .sheet-overlay > .sheet pattern. */
.anim-in { opacity: 0; transition: opacity 0.3s ease; }
.anim-in .sheet {
  transform: translateY(100%);
  transition: transform 0.35s var(--spring);
}
.anim-in.anim-shown { opacity: 1; }
.anim-in.anim-shown .sheet { transform: none; }

/* ---- info sheet (#lb-sheet): normalize to the 350ms spring ----
   It already slides on .open; this keeps it consistent with the rest. */
#lb-sheet { transition: transform 0.35s var(--spring); }

/* ---- editor overlay: slides up as a whole on open, down on dismiss ----
   edit.js calls springShow/springHide on the .pp-editor root. */
.pp-editor.anim-in {
  opacity: 1;
  transform: translateY(48px);
  transition: transform 0.35s var(--spring);
}
.pp-editor.anim-in.anim-shown { transform: none; }

/* ---- tab bar: the newly active tab icon pops with the spring ----
   The view switch itself already animates (viewIn/viewFwd/viewBack); this
   extends spring motion to the tab the user just tapped. */
#tabbar .navbtn.active svg {
  animation: animTabPop 0.32s var(--spring);
}
@keyframes animTabPop {
  from { transform: scale(0.78); }
  to { transform: scale(1); }
}

/* ---- section pullouts/expanders ----
   Markup contract:
     <div class="anim-expander"><div class="anim-expander-body">…</div></div>
   Height animates via the grid-template-rows 0fr->1fr trick plus opacity;
   toggled by expandSection/collapseSection/toggleSection in js/anim.js.
   No layout thrash, no max-height guessing. */
.anim-expander {
  display: grid;
  grid-template-rows: 1fr;
  opacity: 1;
  transition: grid-template-rows 0.35s var(--spring), opacity 0.3s ease;
}
.anim-expander > .anim-expander-body {
  overflow: hidden;
  min-height: 0;
}
.anim-expander.anim-collapsed {
  grid-template-rows: 0fr;
  opacity: 0;
}
/* === END ANIMATION SYSTEM === */

/* === FLUIDITY (v11) === */
/* Blur-up thumbnails: a tiny placeholder paints first under blur, then the
   sharp image swaps in and the blur eases away. `scale` (independent of
   `transform`) is used so the grid's hover zoom keeps working while blurred. */
img.blur-up {
  filter: blur(14px) saturate(1.12);
  scale: 1.06;
}
.photo-cell img.blur-up {
  transition: filter 0.32s ease-out, scale 0.32s ease-out,
    opacity 0.28s ease var(--cd, 0ms), transform 0.45s var(--spring);
}
.lb-slide img.blur-up {
  transition: filter 0.32s ease-out, scale 0.32s ease-out;
}

/* Skeleton shimmer for the grid while the library loads or an import runs.
   Cells match real cell sizing so the scrollbar does not jump on swap. */
.photo-cell.skel {
  background: linear-gradient(100deg, #191a20 30%, #24252d 46%, #191a20 62%);
  background-size: 200% 100%;
  animation: skel-shimmer 1.5s linear infinite;
  cursor: default;
}
@keyframes skel-shimmer {
  to { background-position: -200% 0; }
}

/* Grid rows cost nothing until scrolled near: the browser skips their
   layout/paint while off-screen. contain-intrinsic-size keeps the scrollbar
   steady for any row skipped before its first layout. (Rows are absolutely
   positioned inside the spacer, so this is belt and braces on top of the
   existing virtualization.) */
.grid-row {
  content-visibility: auto;
  contain-intrinsic-size: auto 180px;
}

@media (prefers-reduced-motion: reduce) {
  /* Static placeholder instead of shimmer. */
  .photo-cell.skel { animation: none; background: #1e1f24; }
  .photo-cell img.blur-up, .lb-slide img.blur-up { transition: none; }
}
/* === END FLUIDITY (v11) === */

/* === COLLAGE UPGRADES (v11) === */
/* Interactive collage stage: draggable frames, pinch/corner resize, rotate
   handle with snap, layer toolbar, gap/padding sliders, background swatches
   and blur fill, template + freeform modes. Pointer Events throughout so
   touch and mouse share one code path. */

.col-moderow {
  display: flex;
  align-items: center;
  gap: 12px;
}
.col-seg {
  display: inline-flex;
  background: var(--elev);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 3px;
  gap: 2px;
}
.col-seg button {
  border: 0;
  background: transparent;
  color: var(--text-2);
  font-size: 14px;
  font-weight: 600;
  padding: 8px 18px;
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.25s var(--spring), color 0.25s var(--spring), transform 0.25s var(--spring);
}
.col-seg button:active { transform: scale(0.96); }
.col-seg button.active { background: var(--accent); color: #fff; }

.col-stage {
  position: relative;
  width: 100%;
  overflow: hidden;
  border-radius: 10px;
  background: #000;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}
.col-stage.transparent {
  background:
    repeating-conic-gradient(var(--elev-2) 0% 25%, var(--elev) 0% 50%) 0 0 / 24px 24px;
}
.col-stage-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: blur(18px) brightness(0.55) saturate(1.25);
  transform: scale(1.06);
  pointer-events: none;
}
.col-frame {
  position: absolute;
  overflow: hidden;
  border-radius: 6px;
  background: #0c0c10;
  touch-action: none;
  cursor: grab;
  box-shadow: 0 0 0 0 transparent;
  transition: box-shadow 0.25s var(--spring);
}
.col-frame:active { cursor: grabbing; }
.col-frame.active {
  box-shadow: 0 0 0 2px var(--accent);
  z-index: 50;
}
.col-frame img {
  position: absolute;
  display: block;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
  transform-origin: center;
}
.col-handle {
  position: absolute;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: rgba(12, 12, 18, 0.9);
  border: 1px solid rgba(255, 255, 255, 0.35);
  color: #fff;
  font-size: 16px;
  line-height: 1;
  display: none;
  align-items: center;
  justify-content: center;
  touch-action: none;
  z-index: 6;
  cursor: pointer;
  padding: 0;
}
.col-frame.active .col-handle { display: flex; }
.col-handle.ch-rot {
  top: 6px;
  left: 50%;
  margin-left: -16px;
}
.col-handle.ch-size {
  right: 6px;
  bottom: 6px;
  cursor: nwse-resize;
}
.col-handle .ch-tri {
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 0 0 12px 12px;
  border-color: transparent transparent #fff transparent;
}
.col-readout {
  position: absolute;
  z-index: 60;
  display: none;
  background: rgba(0, 0, 0, 0.8);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: 12px;
  pointer-events: none;
  transform: translate(-50%, -100%);
  white-space: nowrap;
}
.col-toolbar {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.col-toolbar .ghost {
  font-size: 13px;
  padding: 8px 14px;
}
.col-bgrow { display: flex; }
.col-swatches {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.col-swatch {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 2px solid var(--hairline);
  padding: 0;
  cursor: pointer;
  transition: transform 0.2s var(--spring), border-color 0.2s var(--spring);
}
.col-swatch:hover { transform: scale(1.12); }
.col-swatch.active { border-color: var(--accent); }
.col-swatch.sw-trans {
  background:
    repeating-conic-gradient(var(--elev-2) 0% 25%, var(--elev) 0% 50%) 0 0 / 12px 12px;
}
.col-colorinput {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 2px dashed var(--hairline);
  padding: 0;
  background: none;
  cursor: pointer;
}
.col-colorinput::-webkit-color-swatch-wrapper { padding: 3px; }
.col-colorinput::-webkit-color-swatch { border-radius: 50%; border: none; }
.col-colorinput::-moz-color-swatch { border-radius: 50%; border: none; }
.col-swatches .ghost.active {
  border-color: var(--accent);
  color: var(--text);
}
.col-hidden { display: none !important; }
/* Keep the picker from jumping when the selection count text wraps. */
#collage-sheet .collage-head + .meta { min-height: 2.6em; }
/* The sheet is a scrolling flex column: children must never flex-shrink,
   or the picker collapses and later rows overlap its overflowing cells. */
#collage-sheet .collage-sheet > * { flex-shrink: 0; }
/* === END COLLAGE UPGRADES === */

/* === INFO PANEL REDESIGN (v11) ===
   Lightbox info panel (#lb-sheet): sectioned Info tab (Details + Organize),
   labeled rating/organize controls, and a right-side dock on wide screens.
   The show/hide mechanism (.open class toggle) is unchanged; only what
   .open means changes per breakpoint. prefers-reduced-motion is honored by
   the existing global block (all transitions collapse to ~0ms). */

/* ---- section headers: small caps, consistent across the panel ---- */
#lb-meta .lb-sec { margin: 0; }
#lb-meta .lb-sec + .lb-sec { margin-top: 26px; }
#lb-meta .lb-sec-h {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-2);
  margin: 0 0 4px;
}

/* ---- Details list: clean label/value rows (replaces the old grid dl) ---- */
#lb-meta dl.lb-details { display: block; margin: 0; }
#lb-meta .lb-details .lb-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 14px;
  padding: 9px 0;
  border-bottom: 0.5px solid var(--hairline-soft);
  font-size: 14px;
}
#lb-meta .lb-details .lb-row:last-child { border-bottom: none; }
#lb-meta .lb-details dt {
  color: var(--text-2);
  font-weight: 500;
  flex-shrink: 0;
}
#lb-meta .lb-details dd {
  margin: 0;
  color: var(--text);
  text-align: right;
  min-width: 0;
  overflow-wrap: anywhere;
}
#lb-meta .lb-exif-note { margin: 8px 0 0; }

/* ---- tab strip: tidy iOS segmented control ---- */
#lb-sheet .segmented {
  background: rgba(255, 255, 255, 0.07);
  border-radius: 12px;
  padding: 3px;
  margin-bottom: 18px;
}
#lb-sheet .segmented button {
  font-size: 13px;
  font-weight: 600;
  padding: 8px 4px;
  color: var(--text-2);
}
#lb-sheet .segmented button.active {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}
/* Undo the sheet-wide .ghost rule inside the tab strip. */
#lb-sheet .segmented button.ghost { width: auto; margin-top: 0; }

/* ---- Organize: labeled rows with breathing room ---- */
.pp-rate-row {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 11px 0;
  border-bottom: 0.5px solid var(--hairline-soft);
}
.pp-rate-row:last-child { border-bottom: none; }
.pp-rate-row-label {
  flex: 0 0 88px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  padding-top: 8px;
}
.pp-rate-row-body { flex: 1; min-width: 0; }

/* Stars */
.pp-rate-stars { display: flex; gap: 2px; }
.pp-rate-star {
  appearance: none;
  background: none;
  border: none;
  font-size: 27px;
  line-height: 1;
  padding: 3px;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.28);
  transition: color 0.2s var(--spring), transform 0.2s var(--spring);
}
.pp-rate-star.on { color: #ffd60a; }
.pp-rate-star:active { transform: scale(1.18); }

/* Pick / Reject flags */
.pp-rate-flags { display: flex; gap: 8px; }
.pp-rate-flag {
  appearance: none;
  border: 0.5px solid var(--hairline);
  background: rgba(255, 255, 255, 0.07);
  color: var(--text-2);
  font-size: 13px;
  font-weight: 600;
  padding: 8px 16px;
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.25s var(--spring), color 0.25s var(--spring),
    border-color 0.25s var(--spring), transform 0.2s var(--spring);
}
.pp-rate-flag:active { transform: scale(0.96); }
.pp-rate-flag.on { background: #fff; border-color: #fff; color: #111; }

/* Color label swatches: dot plus name, selected state unmistakable */
.pp-rate-labels { display: flex; flex-wrap: wrap; gap: 4px; }
.pp-rate-swatch {
  appearance: none;
  background: none;
  border: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  min-width: 54px;
  padding: 7px 5px;
  border-radius: 12px;
  cursor: pointer;
  transition: background 0.25s var(--spring), transform 0.2s var(--spring);
}
.pp-rate-swatch:active { transform: scale(0.94); }
.pp-swatch-dot {
  width: 27px;
  height: 27px;
  border-radius: 50%;
  border: 2px solid transparent;
  background-clip: padding-box;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
  transition: border-color 0.2s var(--spring), box-shadow 0.2s var(--spring);
}
.pp-swatch-dot-none {
  background: transparent;
  border: 1.5px dashed rgba(255, 255, 255, 0.4);
  box-shadow: none;
}
.pp-swatch-name { font-size: 11px; color: var(--text-2); }
.pp-rate-swatch.on { background: rgba(255, 255, 255, 0.09); }
.pp-rate-swatch.on .pp-swatch-dot {
  border-color: #fff;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.35);
}
.pp-rate-swatch.on .pp-swatch-dot-none { border-style: solid; border-color: #fff; }
.pp-rate-swatch.on .pp-swatch-name { color: #fff; font-weight: 600; }

/* Keywords and caption inputs */
.pp-rate-keywords,
.pp-rate-caption {
  width: 100%;
  box-sizing: border-box;
  background: rgba(255, 255, 255, 0.07);
  border: 0.5px solid var(--hairline);
  border-radius: 10px;
  color: var(--text);
  font: inherit;
  font-size: 14px;
  padding: 9px 11px;
}
.pp-rate-keywords::placeholder,
.pp-rate-caption::placeholder { color: var(--text-3); }
.pp-rate-caption { resize: vertical; min-height: 54px; }
.pp-rate-keywords:focus,
.pp-rate-caption:focus {
  outline: none;
  border-color: var(--accent, #0a84ff);
}

/* ---- wide screens: dock the panel on the right, photo stays visible ----
   Below 900px the bottom-sheet behavior is untouched. */
@media (min-width: 900px) {
  #lb-sheet {
    top: 0;
    bottom: 0;
    left: auto;
    right: 0;
    width: 340px;
    max-width: 340px;
    max-height: none;
    border-top: none;
    border-left: 0.5px solid var(--hairline-soft);
    border-radius: 0;
    transform: translateX(105%);
    /* Clear the floating top bar (close/info/edit buttons). */
    padding: 76px 20px calc(20px + env(safe-area-inset-bottom, 0px));
  }
  #lb-sheet.open { transform: translateX(0); }
  #lb-sheet .lb-grab { display: none; } /* no grab handle on a side panel */
  /* The photo stage NEVER moves or resizes when the panel opens: the
     sheet overlays the photo (frosted glass), so the image stays exactly
     where it was. Only the panel itself animates. */
}
/* === END INFO PANEL REDESIGN === */

/* ---- Mobile: the info sheet docks BELOW the photo ----
   Below 900px the lightbox is a flex column: the stage flexes and the
   info sheet, when open, is a normal-flow sibling under it. Opening
   Info shrinks the photo area and the photo pushes up, fully visible;
   no info content ever paints over the photo. Closing restores the
   full area. (Desktop keeps the overlay side panel above.) */
@media (max-width: 899px) {
  #lightbox { display: flex; flex-direction: column; }
  #lightbox .lb-stage {
    position: relative; inset: auto;
    flex: 1 1 auto; min-height: 0;
  }
  #lb-sheet {
    position: relative; flex: none;
    left: auto; right: auto; bottom: auto;
    width: auto; max-width: none; max-height: 46vh;
    transform: none;
    display: none;
  }
  #lb-sheet.open { display: block; animation: sheetUp 0.35s var(--spring); }
}

/* === BLUR RELOCATE (v11) === */
/* Privacy blur relocated from the lightbox info panel into the editor's
   Retouch tab. Real-time blurred brush preview + Done/Cancel bar. */
.pp-brushov {
  position: absolute; inset: 0; width: 100%; height: 100%; z-index: 5; touch-action: none; cursor: crosshair;
  border-radius: 6px;
}
.pp-brushhint {
  position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
  z-index: 6; pointer-events: none; white-space: nowrap;
  background: rgba(10, 10, 16, 0.78); border: 1px solid rgba(255, 255, 255, 0.16);
  color: #fff; font-size: 12px; font-weight: 600; letter-spacing: 0.2px;
  padding: 7px 12px; border-radius: 999px;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  animation: pp-blurhint-in 0.4s cubic-bezier(0.32, 0.72, 0, 1);
}
@keyframes pp-blurhint-in {
  from { opacity: 0; transform: translateX(-50%) translateY(-8px); }
  to { opacity: 1; transform: translateX(-50%) translateY(0); }
}
.pp-blurbar {
  position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%);
  z-index: 6; display: flex; gap: 10px; align-items: center;
  background: rgba(10, 10, 16, 0.92); border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 14px; padding: 10px 14px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);
  animation: pp-blurbar-in 0.45s cubic-bezier(0.32, 0.72, 0, 1);
}
@keyframes pp-blurbar-in {
  from { opacity: 0; transform: translateX(-50%) translateY(14px) scale(0.96); }
  to { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
}
.pp-blurbar .primary { min-width: 88px; }
.pp-blursec { margin-top: 18px; }
.pp-blursec h3 { margin: 0 0 8px; }
/* === END BLUR RELOCATE === */

/* ---- hover preview (desktop): floating enlarged uncropped preview ---- */
#hover-preview {
  position: fixed;
  z-index: 90;
  pointer-events: none;
  opacity: 0;
  transform: scale(0.96);
  transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.32, 0.72, 0, 1);
  border-radius: 12px;
  overflow: hidden;
  background: #101014;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.65), 0 0 0 0.5px rgba(255, 255, 255, 0.14);
}
#hover-preview.on { opacity: 1; transform: scale(1); }
#hover-preview img {
  display: block;
  width: 100%;
  height: auto;
  max-height: inherit;
  object-fit: contain;
  background: #101014;
}
@media (prefers-reduced-motion: reduce) {
  #hover-preview { transition: opacity 0.12s ease; transform: none; }
}

/* === BUILD 39 PART 5: mobile two-level dock, keyboard help ===
   The dock bar lives in .pp-ed-main above .pp-ed-body. It is inert on
   desktop (display: none) and only takes part in the mobile layout.
   Dock states ride on the panel element: dock-section (one section
   open; a section is always open, the remembered one or the first),
   dock-none (tab has no sections, height cap only). The chips row is
   a persistent sub-tab bar above the open section; the open
   section's chip carries .sel. Children hidden by the dock carry
   .pp-dock-off; that class is only styled inside the mobile media
   query, so the desktop panel is never affected by dock state. */
.pp-ed-dockbar { display: none; }
.pp-kbd-help { flex: none; width: 38px; padding: 8px 0; font-weight: 700; }
.pp-kbd-list {
  display: grid; grid-template-columns: auto 1fr;
  gap: 9px 16px; align-items: center; margin: 6px 0 14px;
  font-size: 13.5px; color: #cfcabb;
}
.pp-kbd-list kbd {
  background: #232329; border: 1px solid #3a3a42; border-bottom-width: 2px;
  border-radius: 6px; padding: 2px 8px; white-space: nowrap;
  font-family: var(--font-ui); font-size: 12px; font-weight: 600; color: #f2efe6;
}

@media (max-width: 899px) {
  /* Zoom is a gesture on phones (pinch on the photo); the pill row is
     desktop-only chrome and just eats panel height here. */
  .pp-ed-zoombar { display: none !important; }
  .pp-kbd-help { display: none; }

  .pp-ed-dockbar {
    display: flex; align-items: center; flex: none;
    background: #101014; border-top: 1px solid #1d1d22;
    min-height: 42px;
  }
  .pp-ed-panel.snap-rail .pp-ed-dockbar { display: none; }
  /* A tab with no sections has nothing for the bar to show. */
  .pp-ed-panel.dock-none .pp-ed-dockbar { display: none; }
  .pp-dock-chips {
    display: flex; gap: 6px; flex: 1; min-width: 0;
    overflow-x: auto; overflow-y: hidden;
    padding: 7px 12px;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
  }
  .pp-dock-chips::-webkit-scrollbar { display: none; }
  .pp-dock-chip {
    flex: none; border: 1px solid #2c2c33; border-radius: 999px;
    background: #17171c; color: #cfcabb;
    padding: 7px 13px; font-size: 12.5px; font-weight: 600;
    white-space: nowrap; cursor: pointer;
  }
  .pp-dock-chip:active { transform: scale(0.96); }
  /* The open section's chip reads selected in the same language as
     the active main tab (.pp-tab.active): full gold fill, dark text. */
  .pp-dock-chip.sel {
    background: #e8b93c; border-color: #e8b93c; color: #171106;
  }

  /* Section visibility + height caps per dock state. The controls
     region stays small; a genuinely tall section scrolls inside its
     own capped area, and the photo keeps the rest of the screen. */
  .pp-dock-off { display: none !important; }
  .pp-ed-panel.snap-mid.dock-section .pp-ed-body,
  .pp-ed-panel.snap-tall.dock-section .pp-ed-body { max-height: 18vh; }
  .pp-ed-panel.snap-mid.dock-none .pp-ed-body,
  .pp-ed-panel.snap-tall.dock-none .pp-ed-body { max-height: 24vh; }
}

/* ---------- Build 40: keyword manager, label chips, batch dialogs ---------- */
.labelchips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.labelchip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-2);
  border: 0.5px solid var(--hairline);
  border-radius: 999px;
  padding: 6px 12px;
  background: rgba(255, 255, 255, 0.03);
  cursor: pointer;
}
.labelchip:hover { border-color: var(--accent); color: var(--text); }
.labelchip.on { border-color: var(--accent); color: var(--accent); background: rgba(232, 184, 75, 0.10); }
.labeldot {
  display: inline-block;
  width: 9px; height: 9px;
  border-radius: 50%;
  flex: none;
}
.pp-kw-dialog { max-width: 560px; width: calc(100vw - 32px); max-height: 84vh; overflow-y: auto; }
.pp-kw-h { margin: 18px 0 8px; font-size: 13px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-2); }
.pp-kw-tree { display: flex; flex-direction: column; gap: 4px; }
.pp-kw-row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 6px 8px;
  border: 0.5px solid var(--hairline);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.02);
}
.pp-kw-name { font-weight: 600; margin-right: 2px; }
.pp-kw-count {
  font-size: 11.5px; color: var(--text-2);
  border: 0.5px solid var(--hairline); border-radius: 999px; padding: 1px 8px;
  margin-right: auto;
}
.pp-kw-act { padding: 4px 10px; font-size: 12.5px; }
.pp-kw-input {
  flex: 1 1 140px;
  min-width: 0;
  padding: 7px 10px;
  border-radius: 8px;
  border: 0.5px solid var(--hairline);
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  font: inherit;
  font-size: 13.5px;
}
.pp-kw-input.wide { width: 100%; flex: none; margin: 4px 0 10px; }
.pp-kw-dialog select.pp-kw-input { flex: 0 1 200px; }
.pp-kw-swatches { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0 12px; }
.pp-kw-swatchbtn { display: inline-flex; align-items: center; gap: 7px; }
.pp-kw-preview { margin: 2px 0 10px; }
.pp-kw-body .meta { line-height: 1.45; }

/* ---- Build 40 part 3: geotag editor + swipe review ---- */
.geo-line { margin: 2px 0 4px; font-size: 14px; }
.geo-note { margin: 0 0 8px; }
.geo-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.geo-ov {
  position: fixed; inset: 0; z-index: 180; background: rgba(0, 0, 0, 0.72);
  display: flex; align-items: center; justify-content: center; padding: 14px;
}
.geo-box {
  background: #141419; border: 1px solid #2c2c33; border-radius: 14px;
  width: min(680px, 100%); max-height: 92vh; overflow: auto; padding: 16px;
}
.geo-head { display: flex; align-items: center; gap: 10px; }
.geo-head h2 { margin: 0; font-size: 18px; }
.geo-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.geo-close { margin-left: auto; }
.geo-map {
  position: relative; height: 44vh; min-height: 240px; border-radius: 10px;
  overflow: hidden; margin: 10px 0; background: #0b0d11;
}
.geo-fields { display: flex; gap: 10px; flex-wrap: wrap; }
.geo-lab { display: flex; flex-direction: column; gap: 4px; font-size: 13px; flex: 1 1 160px; }
.geo-lab input {
  background: #0e0e12; color: #eee; border: 1px solid #34343e; border-radius: 8px;
  padding: 8px 10px; font-size: 15px;
}
.geo-status { min-height: 18px; }

.rv-ov {
  position: fixed; inset: 0; z-index: 250; background: rgba(4, 4, 7, 0.96);
  display: flex; align-items: center; justify-content: center;
}
.rv-box {
  width: min(860px, 100%); height: 100%; max-height: 100vh;
  display: flex; flex-direction: column; padding: 12px 14px;
  box-sizing: border-box;
}
.rv-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.rv-count { font-size: 14px; color: #c9c9d2; }
.rv-stage {
  position: relative; flex: 1 1 auto; min-height: 0; margin: 10px 0 6px;
  display: flex; align-items: center; justify-content: center;
  touch-action: none; overflow: hidden; border-radius: 12px; background: #0b0d11;
}
.rv-img { max-width: 100%; max-height: 100%; object-fit: contain; user-select: none; -webkit-user-drag: none; }
.rv-flag {
  position: absolute; top: 10px; left: 10px; padding: 4px 10px; border-radius: 999px;
  font-size: 13px; font-weight: 600; background: rgba(10, 10, 14, 0.8); color: #eee;
  border: 1px solid #3a3a44;
}
.rv-flag.pick { border-color: #46a758; color: #8fe3a8; }
.rv-flag.reject { border-color: #e5484d; color: #ff9d9d; }
.rv-verdict {
  position: absolute; top: 14px; right: 14px; padding: 6px 14px; border-radius: 10px;
  font-size: 17px; font-weight: 700; pointer-events: none; background: rgba(10, 10, 14, 0.85);
}
.rv-verdict.pick { color: #8fe3a8; border: 2px solid #46a758; }
.rv-verdict.reject { color: #ff9d9d; border: 2px solid #e5484d; }
.rv-verdict.skip { color: #d8d8e0; border: 2px solid #6b6b76; }
.rv-name { text-align: center; font-size: 13px; color: #a9a9b5; min-height: 16px; }
.rv-bar { display: flex; gap: 10px; justify-content: center; margin-top: 8px; }
.rv-btn {
  flex: 0 1 180px; padding: 12px 10px; font-size: 15px; font-weight: 600;
  border-radius: 12px; border: 1px solid #34343e; background: #1b1b21; color: #eee;
  cursor: pointer;
}
.rv-btn.rv-pick { border-color: #46a758; }
.rv-btn.rv-reject { border-color: #e5484d; }
.rv-hint { text-align: center; margin: 8px 0 0; }
.rv-summary { margin: auto; max-width: 480px; text-align: center; padding: 24px 10px; }
.rv-sum-line { font-size: 15px; }
.rv-sum-actions { display: flex; gap: 10px; justify-content: center; margin-top: 14px; flex-wrap: wrap; }

/* ---- Build 41: photobook / contact sheet PDF dialog ---- */
.pp-pdf-label { min-width: 84px; font-size: 13px; color: #8f8a7c; }
.pp-pdf-choices { display: flex; gap: 14px; flex-wrap: wrap; }
.pp-pdf-choice { display: inline-flex; align-items: center; gap: 4px; font-size: 14px; cursor: pointer; }

/* ---------- Build 43: app shell ----------
   PhotoWorks is an app, not a web page. The shell is exactly one
   viewport tall and the document itself never scrolls: the top bar
   and tab bar are fixed chrome, every view owns the space between
   them, and each view scrolls inside its own pane (the Library's
   photo grid scrolls itself). Nothing stacks above the photos any
   more: import lives in the top bar button, storage and diagnostics
   live in Settings, and the Library header is one compact row. */
html { height: 100%; }
body {
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding-bottom: 0;
}
#topbar { position: relative; flex: none; }
#grantbar { flex: none; margin: 12px 16px 0; }
main {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
}
.view {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding-bottom: calc(var(--tabbar-h) + 14px);
}

/* Library: a fixed app screen. Header, segmented control, memories
   and the toolbars are compact flex rows; the grid takes whatever
   space is left and scrolls internally, so photos are on screen
   from the first frame with zero page scroll. */
/* The real tab bar measures 55px tall (24px icon + label + padding);
   the old 52px token left the grid's last row 3px under the bar. */
:root { --tabbar-h: calc(55px + env(safe-area-inset-bottom, 0px)); }
#view-library {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding-bottom: var(--tabbar-h);
}
#view-library .pagetitle {
  flex: none;
  align-items: baseline;
  padding: 10px 16px 2px;
}
#view-library .pagetitle h1 { font-size: 22px; }
#view-library .pagetitle .sub { margin-top: 0; white-space: nowrap; }
#view-library #lib-play {
  align-self: center;
  padding: 7px 13px;
  font-size: 13.5px;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
#view-library #lib-seg { flex: none; margin: 8px 16px 0; }
#view-library .memories {
  flex: none;
  max-height: 178px;
  overflow-y: auto;
  margin: 10px 16px 0;
  padding: 10px 14px;
}
#view-library .memories .meta { display: none; }
#view-library .memories-title { font-size: 16px; }
#view-library .memory-group { margin-top: 8px; }
#view-library .memory-when { font-size: 14px; }
#view-library .memory-thumb { width: 64px; height: 64px; border-radius: 8px; }
#lib-timeline-pane {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}
/* Once the library has photos, the import card leaves the timeline:
   importing lives in the top bar button. The card still introduces
   the empty library on iPhone, where its honest explainer belongs. */
body.has-photos #ios-card { display: none; }
#view-library #lib-tools {
  flex: none;
  padding: 8px 16px 2px;
}
#view-library #lib-actions {
  flex: none;
  display: flex;
  gap: 8px;
  overflow-x: auto;
  margin: 0;
  padding: 6px 16px 8px;
  scrollbar-width: none;
}
#view-library #lib-actions::-webkit-scrollbar { display: none; }
#view-library #lib-actions .ghost {
  flex: none;
  padding: 6px 13px;
  font-size: 13.5px;
  border-radius: 999px;
  white-space: nowrap;
}
#lib-timeline-pane .labelchips { flex: none; margin: 2px 16px 0; }
#view-library #grid {
  flex: 1 1 auto;
  height: auto;
  min-height: 0;
}
#view-library #emptystate { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
#lib-albums-pane {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 16px;
}
#album-grid { height: 54vh; }

/* Settings carries the old page footer's links now. */
.settings-about {
  text-align: center;
  color: var(--text-3);
  font-size: 12px;
  padding: 18px 20px 4px;
  line-height: 1.7;
}
.settings-about a { color: var(--accent); text-decoration: none; }

@media (min-width: 720px) {
  #view-library .pagetitle { padding: 14px 24px 4px; }
  #view-library .pagetitle h1 { font-size: 28px; }
  #view-library #lib-seg { margin-left: 24px; }
  #view-library #lib-tools { padding-left: 24px; padding-right: 24px; }
  #view-library #lib-actions { padding-left: 24px; padding-right: 24px; }
  #view-library .memories { margin-left: 24px; margin-right: 24px; }
}

/* ---------- Build 45: shell fixes ----------
   From his iPhone hands-on pass. Five changes, shell chrome only:

   1. The tab bar is IN FLOW now. Build 43 floated it (position:
      fixed) and made every view reserve its height with
      padding-bottom, but the Library reserved exactly the
      hand-measured --tabbar-h with zero slack, so the timeline's
      last row landed precisely on the bar's top edge and any
      device-side discrepancy (bar line-height rounding, Safari's
      fixed positioning vs the 100dvh body) pushed photos under
      the bar. As a flex child of the body column the bar takes
      real layout space: main ends at its top by construction, on
      every browser, safe-area inset included (the inset is the
      bar's own padding, in flow). Views keep only a small comfort
      padding. --tabbar-h itself stays: the toast still offsets by
      it, and the bar's height is unchanged.
   2. Slimmer top bar and Library header: the Connect button no
      longer wraps to two lines (nowrap + the brand text may
      shrink), the tagline stops eating a second line on phones,
      and the header stack's paddings and gaps are tightened so
      photos start higher. Tap targets stay at least 40px tall.
   3. The Timeline/Albums segmented control stretches to the
      full content width on phones only; the desktop 320px cap
      is untouched.
   4. Glass chrome: the top bar, the tab bar, and the Library's
      pills and chips share one treatment: translucent dark
      backgrounds, backdrop blur with saturate, hairline light
      borders. Labels stay white on dark glass, so a bar over
      photos still reads as a bar. */
#tabbar { position: relative; left: auto; right: auto; bottom: auto; flex: none; }
.view { padding-bottom: 14px; }
#view-library { padding-bottom: 12px; }

/* Glass: bars. */
#topbar {
  background: rgba(16, 16, 22, 0.55);
  -webkit-backdrop-filter: saturate(180%) blur(22px);
  backdrop-filter: saturate(180%) blur(22px);
  border-bottom: 0.5px solid rgba(255, 255, 255, 0.12);
}
#tabbar {
  background: rgba(16, 16, 22, 0.58);
  border-top: 0.5px solid rgba(255, 255, 255, 0.12);
}
/* Glass: floating pills and chips in the shell. */
#view-library #lib-play,
#view-library #lib-tools .ghost,
#view-library #lib-actions .ghost {
  background: rgba(255, 255, 255, 0.10);
  border: 0.5px solid rgba(255, 255, 255, 0.16);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  backdrop-filter: saturate(180%) blur(18px);
  color: #fff;
}
/* Glass: the top bar's Import / Connect button stays the primary
   action: translucent accent glass, white bold label. */
#topbar .primary {
  background: rgba(10, 132, 255, 0.55);
  border: 0.5px solid rgba(255, 255, 255, 0.22);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  backdrop-filter: saturate(180%) blur(18px);
  color: #fff;
  font-weight: 600;
}

/* Slimmer top bar. */
#topbar { padding: calc(6px + env(safe-area-inset-top, 0px)) 14px 6px; }
.brand .mark { width: 28px; height: 28px; }
.brand-text { min-width: 0; }
#topbar .primary {
  min-height: 40px;
  padding: 8px 14px;
  white-space: nowrap;
  flex: none;
  display: inline-flex;
  align-items: center;
}

/* Tighter Library header stack; 40px tap-target floor. */
#view-library .pagetitle { padding: 6px 16px 0; }
#view-library .pagetitle h1 { font-size: 20px; }
#view-library #lib-play { min-height: 40px; }
#view-library #lib-seg { margin: 6px 16px 0; }
#lib-seg button { min-height: 40px; }
#view-library #lib-tools { padding: 5px 16px 0; }
#view-library #lib-tools .ghost { min-height: 40px; }
#view-library #lib-actions { padding: 5px 16px 6px; }
#view-library #lib-actions .ghost { min-height: 40px; }
#view-library .memories { margin-top: 8px; }

@media (max-width: 719px) {
  /* The tagline no longer eats a second line of height. */
  .brand-text .tagline { display: none; }
  .brand-text h1 { font-size: 16px; }
  /* Timeline/Albums sits across the screen on phones only. */
  #view-library #lib-seg { max-width: none; }
}

@media (min-width: 720px) {
  #view-library .pagetitle { padding: 10px 24px 2px; }
}

/* ================= Build 46: library home + selection redesign ====
   Research-led (Apple Photos iOS 26, Google Photos, Samsung Gallery;
   report in research_notes/gallery-library-ui-design-20261022-0948):
   1. The tab bar is a detached floating glass pill again. It stays an
      in-flow flex child of the shell (Build 45), so the space it needs
      is reserved by layout itself: pill height + top gap + float gap
      + safe area, and the views keep their 12-14px bottom padding on
      top of that as explicit slack. The last grid row clears the
      chrome by construction, never by a hand-measured constant.
   2. The Library tools row is gone. Select is a compact pill in the
      title row beside Slideshow (header slot #lib-select-slot).
   3. Selection mode: a slim header (Cancel + live count) replaces
      the old wrapping pile of 25 pill buttons, and a floating action
      bar takes the tab pill's exact place (Export, Favorite, More,
      Delete). Everything else lives in the More bottom sheet.
   4. Checkmarks stay top-left, restyled to the research pattern:
      empty circle outline for every cell in select mode, filled
      accent circle with a white tick when selected. */

/* --- floating glass tab pill --- */
#tabbar {
  width: calc(100% - 24px);
  max-width: 600px;
  align-self: center;
  height: 60px;
  margin: 6px auto calc(10px + env(safe-area-inset-bottom, 0px));
  padding: 4px 8px;
  border-radius: 999px;
  border: 0.5px solid rgba(255, 255, 255, 0.16);
  background: rgba(18, 18, 24, 0.84);
  -webkit-backdrop-filter: saturate(180%) blur(22px);
  backdrop-filter: saturate(180%) blur(22px);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45), 0 2px 8px rgba(0, 0, 0, 0.3);
}
#tabbar .navbtn { padding: 6px 0 4px; border-radius: 999px; }
#tabbar .navbtn.active { background: rgba(255, 255, 255, 0.09); }
#tab-indicator { bottom: 4px; }
/* While selecting, the pill steps aside (space kept) for the
   selection action bar, which floats in exactly the same place. */
body.pp-selecting #tabbar { visibility: hidden; }

/* --- Select pill in the Library title row --- */
#view-library .pagetitle .ghost {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  backdrop-filter: saturate(180%) blur(16px);
}
#view-library #lib-play { margin-left: auto; }
#lib-select-slot { display: contents; }
.pp-select-toggle {
  margin-left: 0;
  min-height: 44px;
  padding: 0 16px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
}

/* --- selection header (replaces the old .pp-selectbar pile) --- */
.pp-selectbar {
  flex-wrap: nowrap;
  gap: 8px;
  min-height: 52px;
  padding: calc(6px + env(safe-area-inset-top, 0px)) 12px 6px;
  background: rgba(16, 16, 22, 0.88);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 0.5px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
}
.pp-selcancel {
  appearance: none;
  border: 0;
  background: transparent;
  cursor: pointer;
  color: var(--accent);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  min-height: 44px;
  padding: 0 10px;
  border-radius: 999px;
  flex: none;
}
.pp-selectbar .pp-selcount {
  flex: 1;
  margin: 0;
  text-align: center;
  font-size: 15px;
  font-weight: 650;
}
.pp-selspacer { width: 72px; flex: none; }

/* --- selection action bar: the pill's stand-in --- */
.pp-selactionbar {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: calc(10px + env(safe-area-inset-bottom, 0px));
  height: 60px;
  max-width: 600px;
  margin: 0 auto;
  z-index: 75;
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  padding: 4px 8px;
  border-radius: 999px;
  border: 0.5px solid rgba(255, 255, 255, 0.16);
  background: rgba(18, 18, 24, 0.88);
  -webkit-backdrop-filter: saturate(180%) blur(22px);
  backdrop-filter: saturate(180%) blur(22px);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45), 0 2px 8px rgba(0, 0, 0, 0.3);
}
.pp-selact {
  appearance: none;
  border: 0;
  background: transparent;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 64px;
  min-height: 48px;
  padding: 2px 10px;
  border-radius: 999px;
  color: var(--text);
  font: inherit;
}
.pp-selact:active { background: rgba(255, 255, 255, 0.1); }
.pp-selact-ic { display: flex; }
.pp-selact-ic svg { width: 22px; height: 22px; }
.pp-selact-lb { font-size: 10.5px; font-weight: 600; letter-spacing: 0.01em; }
.pp-selact-del { color: var(--danger); }

/* --- More sheet (bottom sheet, gif-panel family) --- */
.pp-more-overlay {
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  background: rgba(0, 0, 0, 0.55);
}
.pp-more-panel {
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  border-radius: 20px 20px 0 0;
  max-height: 82dvh;
  gap: 2px;
  padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
}
.pp-more-sub { margin: 0; color: var(--muted); font-size: 13px; }
.pp-more-group {
  margin: 12px 2px 4px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.pp-more-item {
  appearance: none;
  border: 0;
  background: transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 48px;
  padding: 6px 8px;
  border-radius: 12px;
  color: var(--text);
  font: inherit;
  font-size: 15px;
  text-align: left;
}
.pp-more-item:active { background: rgba(255, 255, 255, 0.08); }
.pp-more-ic { display: flex; flex: none; color: var(--accent); }
.pp-more-ic svg { width: 22px; height: 22px; }
.pp-more-lb { flex: 1; }
.pp-more-ct { color: var(--muted); font-size: 13px; }

/* --- checkmarks: top-left, accent fill, white tick --- */
.pp-check {
  top: 8px;
  left: 8px;
  z-index: 3;
  border: 2px solid rgba(255, 255, 255, 0.95);
  background: rgba(0, 0, 0, 0.3);
}
.pp-check.on {
  background: var(--accent);
  border-color: rgba(255, 255, 255, 0.95);
}
.pp-check.on::after { border-color: #fff; }
/* In select mode the check circle is the one selection affordance:
   the other corner badges that share its corners step aside (the
   favorite heart top-right, the LIVE pill top-left, and the Best /
   similarity score badges). Bottom badges (stack count, video
   duration, RAW) never collide and stay. */
body.pp-selecting .live-badge,
body.pp-selecting .fav-btn,
body.pp-selecting .best-badge,
body.pp-selecting .score-badge { display: none; }

/* Build 46 addendum (found in verification, 390px wide): the title
   row did not fit a phone. h1 + the nowrap count line + Slideshow +
   Select measure about 480px, so Select sat half off the right edge
   of a 390px screen (measured: its box ran x 372 to 446). On phones
   the count line wraps onto its own line under the title instead of
   squeezing the pills out; both pills stay labelled and full-size.
   Desktop keeps the single row. */
@media (max-width: 719px) {
  #view-library .pagetitle { flex-wrap: wrap; row-gap: 1px; }
  #view-library .pagetitle .sub { order: 3; flex: 1 1 100%; }
}
