/* ============================================================
   Wedding Selects — "The Keepsake Book" redesign
   Editorial keepsake viewer. Photos dominate; gold is a hairline accent;
   dusty rose is reserved for one thing — a favourite.
   Type: Marcellus (display) · Cormorant italic (captions, the &) · Hanken Grotesk (UI)
   Two axes: data-theme (dark|light) × data-palette (restrained|warmer) on <html>.
   ------------------------------------------------------------
   NOTE: this is the redesign's stylesheet (served only by redesign/serve.py on :5001).
   It keeps every class/id the shared app.js generates, and only reskins + restructures.
   ============================================================ */

/* ---------- DARK · RESTRAINED (default) ---------- */
:root,
:root[data-theme="dark"]{
  --canvas:#15101B; --canvas-2:#1C1524; --surface:#211A2A; --surface-2:#2A2233;
  --bar:rgba(21,16,27,.86); --tileph:#0E0A12;
  --ink:#F2EAE0; --muted:#A89AA2; --faint:#968B95;   /* faint raised for WCAG-AA on small labels */
  --line:rgba(242,234,224,.10); --line-2:rgba(242,234,224,.055);
  --gold:#C9A24B; --gold-deep:#9C7B33; --rose:#D98BA1; --rose-deep:#B5697F; --emerald:#5BB89B;
  --on-gold:#241b10;
  /* Gold used as a saturated BACKGROUND FILL (the GR seal, primary buttons, active pills, badges).
     Kept separate from --gold/--gold-deep because in LIGHT mode those are darkened for readable gold
     TEXT on white — reusing them as a fill made the seal/+album button look like muddy dark boxes.
     In dark mode the fill IS the normal gold (follows the warmer palette via var()). */
  --gold-fill-1:var(--gold); --gold-fill-2:var(--gold-deep);
  --shadow:0 1px 2px rgba(0,0,0,.45),0 14px 40px rgba(0,0,0,.4);
  --shadow-lg:0 30px 80px rgba(0,0,0,.62);
  --dur-micro:130ms; --dur-sheet:255ms; --ease-out:cubic-bezier(.2,.7,.2,1); --ease-std:ease;
  --z-topbar:40; --z-subtabs:30; --z-utility:25; --z-status:35; --z-bulk:42;
  --z-scrim:48; --z-drawer:49; --z-fscrim:54; --z-sheet:55; --z-lightbox:60;
  --tile:160px;

  /* ---------- TYPOGRAPHY (skin-driven; remapped by [data-skin=clean] below) ---------- */
  --font-ui:"Hanken Grotesk",system-ui,-apple-system,"Segoe UI",sans-serif;  /* body / UI — both skins */
  --font-display:"Marcellus",Georgia,serif;       /* refined: elegant serif for names & headings */
  --font-script:"Cormorant",Georgia,serif;        /* refined: italic captions, the ampersand */
  --font-hero:"Great Vibes","Marcellus",cursive;  /* refined: the G&R wordmark flourish */
  --serif:var(--font-display);                    /* legacy alias (old .ff-q reference) */
  /* ---------- SHAPE (corner radii; crisper under the Clean skin) ---------- */
  --r-xs:6px; --r-sm:9px; --r-md:12px; --r-lg:16px; --r-xl:20px; --r-pill:999px;
  /* ---------- SPACING (8-pt rhythm) ---------- */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:20px; --s6:24px; --s7:32px; --s8:48px;
  --touch:46px;                                   /* min comfortable tap target on phones */
  --botnav-h:60px;                                /* mobile bottom navigation height (excl. safe-area) */
}
/* ============================================================
   CLEAN skin — modern photo-app look. Same structure, different
   *tokens*: one sans family throughout, crisper corners, calmer
   chrome. Set on <html data-skin="clean"> (default is "refined").
   Only overrides VARIABLES + a few chrome flourishes — never layout.
   ============================================================ */
:root[data-skin="clean"]{
  --font-display:var(--font-ui);   /* drop the serif — headings become clean sans */
  --font-script:var(--font-ui);    /* drop the italic Cormorant — plain sans */
  --font-hero:var(--font-ui);      /* wordmark becomes a tight sans lockup */
  --r-xs:5px; --r-sm:7px; --r-md:10px; --r-lg:12px; --r-xl:14px;
}
/* ---------- DARK · WARMER ---------- */
:root[data-palette="warmer"],
:root[data-theme="dark"][data-palette="warmer"]{
  --canvas:#1B1016; --canvas-2:#241319; --surface:#281A22; --surface-2:#33222B;
  --bar:rgba(27,16,22,.86);
  --gold:#E6AC4E; --gold-deep:#C0883A; --rose:#E76E94; --rose-deep:#C9557C;
}
/* ---------- LIGHT · RESTRAINED ---------- */
:root[data-theme="light"]{
  --canvas:#F6F0E7; --canvas-2:#EFE7DA; --surface:#FFFFFF; --surface-2:#F3EBDD;
  --bar:rgba(246,240,231,.86); --tileph:#E7DECF;
  --ink:#2A2230; --muted:#6E6163; --faint:#675A50;   /* faint+muted darkened for WCAG-AA on small labels */
  --line:rgba(42,34,48,.13); --line-2:rgba(42,34,48,.06);
  --gold:#9A7320; --gold-deep:#7C5C18; --rose:#B65C77; --rose-deep:#9A4862; --emerald:#2E9C73;
  /* Bright gold for FILLS on the light UI — the readable text-gold above (#9A7320) is too dark to
     fill a chip with. Dark --on-gold text stays legible on this. */
  --gold-fill-1:#E0A93C; --gold-fill-2:#C48E2C;
  --shadow:0 1px 2px rgba(120,100,80,.12),0 14px 40px rgba(120,100,80,.14);
  --shadow-lg:0 30px 80px rgba(90,70,45,.22);
}
/* ---------- LIGHT · WARMER ---------- */
:root[data-theme="light"][data-palette="warmer"]{
  --canvas:#FBF3E8; --surface-2:#F6ECDB;
  --gold:#A87A1E; --gold-deep:#8A6116; --rose:#C25C7E; --rose-deep:#A6486A;
}

*{box-sizing:border-box}
/* overflow-x:clip on the root contains the off-screen slide-in drawers (#navRail / #topActions).
   On iOS Safari an off-screen fixed element (translateX past the edge) otherwise adds horizontal
   scroll area — the off-canvas-menu bug. `clip` (not `hidden`) creates no scroll container, so it
   doesn't break sticky or vertical-scroll smoothness (the earlier jank was the context-bar blur). */
/* overscroll-behavior:none kills the iOS rubber-band: when you pulled the page down, the scrollable
   content bounced but the position:fixed sidebar/topbar didn't, so they visibly came apart. Disabling
   the document overscroll (also blocks accidental pull-to-refresh) keeps the fixed chrome locked to
   the page. */
html,body{margin:0;overflow-anchor:none;overflow-x:clip;overscroll-behavior:none}
/* iOS/Android: kill the grey/blue tap flash (every control provides its own :active feedback);
   lock text auto-sizing so landscape rotation on iOS doesn't inflate fonts. */
html{-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:100%;text-size-adjust:100%}
/* NOTE: #appbody intentionally has NO overflow-x:clip. Clipping there made it the sticky containing
   block for #contextBar, and iOS Safari then positioned the context bar a few px below the topbar —
   a transparent gap that showed the scrolling photos through it. The root clip above already contains
   the off-screen drawers and any grid rounding overflow, so the context bar now sticks to the viewport
   exactly like the topbar (flush, no gap). */
#connect{overflow-x:clip}
body{background:var(--canvas);color:var(--ink);
  font-family:var(--font-ui);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased}
button,input,select{font-family:inherit}
/* buttons: don't let a tap/long-press select their label text on iOS/Android; touch-action manipulation
   removes the 300ms click delay and double-tap-zoom on controls (inputs/textareas keep normal behaviour) */
button{-webkit-user-select:none;user-select:none;touch-action:manipulation}
.hidden{display:none!important}
/* The `hidden` ATTRIBUTE must always win too: any element given a `display` (flex/grid/inline-flex)
   would otherwise override the UA `[hidden]{display:none}` and leak through. Mirror the .hidden class
   so toggling `el.hidden`/the attribute reliably hides — covers all current + future cases. */
[hidden]{display:none!important}
.serif{font-family:var(--font-display)}
.script{font-family:var(--font-script);font-style:italic}
::selection{background:rgba(201,162,75,.3)}
:focus-visible{outline:2px solid var(--gold);outline-offset:2px;border-radius:4px}
.grow{flex:1}.spacer{flex:1}

/* boot splash */
#boot{position:fixed;inset:0;z-index:200;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:20px;
  background:radial-gradient(120% 80% at 50% -10%,rgba(201,162,75,.10),transparent 55%),var(--canvas);transition:opacity .4s ease}
#boot.gone{opacity:0;pointer-events:none}
.boot-mark{font-family:var(--font-display);font-size:34px;color:var(--gold);letter-spacing:.01em;line-height:1}
.boot-spin{width:30px;height:30px;border-radius:999px;border:2.5px solid rgba(242,234,224,.18);border-top-color:var(--gold);animation:bootspin .8s linear infinite}
@keyframes bootspin{to{transform:rotate(360deg)}}

/* ================= LANDING / CONNECT ================= */
#connect{min-height:100vh;min-height:100dvh;display:flex;flex-direction:column;   /* dvh: iOS dynamic toolbar */
  background:radial-gradient(120% 80% at 50% -20%,rgba(201,162,75,.10),transparent 55%),
    radial-gradient(80% 60% at 50% 120%,rgba(217,139,161,.07),transparent 60%),var(--canvas)}
.hero{position:relative;text-align:center;padding:64px 20px 26px}
.hero .mono{font-size:11px;letter-spacing:.32em;text-transform:uppercase;color:var(--gold);font-weight:600}
.hero h1{margin:14px 0 2px;font-family:var(--font-display);font-weight:400;font-size:clamp(42px,12vw,62px);line-height:1.02;color:var(--ink);display:flex;flex-direction:column;align-items:center}
.hero h1 .nm{display:block}
.hero h1 .amp{font-family:var(--font-script);font-style:italic;color:var(--gold);font-weight:500;font-size:.46em;line-height:1.25;margin:3px 0}
.hero .tag{color:var(--muted);max-width:460px;margin:8px auto 0;font-family:var(--font-script);font-style:italic;font-size:18px}
.ornament{display:flex;align-items:center;justify-content:center;gap:12px;margin:22px auto 0;color:var(--gold-deep)}
.ornament::before,.ornament::after{content:"";height:1px;width:70px;background:linear-gradient(90deg,transparent,var(--gold-deep))}
.ornament::after{background:linear-gradient(270deg,transparent,var(--gold-deep))}
.ornament span{font-size:13px;color:var(--gold)}
/* align-items:flex-start so the login card hugs its content height instead of stretching to fill
   the flex column (which left a tall empty gap once the multi-gallery form was turned off). */
.connect-wrap{flex:1;display:flex;justify-content:center;align-items:flex-start;padding:24px 18px 56px}
.card{width:100%;max-width:520px;background:var(--surface);border:1px solid var(--line);border-radius:18px;box-shadow:var(--shadow);padding:26px;position:relative;overflow:hidden}
.card::before{content:"";position:absolute;inset:0 0 auto 0;height:2px;background:linear-gradient(90deg,transparent,var(--gold),transparent);opacity:.85}
.owner{background:rgba(201,162,75,.07);border:1px solid rgba(201,162,75,.22);border-radius:14px;padding:18px;margin-bottom:0}
.owner .lbl{font-family:var(--font-display);font-size:20px;margin-bottom:3px;color:var(--ink)}
.owner .sub,.section .sub{color:var(--muted);font-size:12.5px}
.owner-row{display:flex;gap:10px;margin-top:12px}.owner-row input{flex:1}
.pw-wrap{position:relative;flex:1;display:flex}
.pw-wrap input{width:100%;padding-right:42px}
.pw-eye{position:absolute;right:6px;top:50%;transform:translateY(-50%);width:30px;height:30px;display:flex;
  align-items:center;justify-content:center;border:0;background:none;color:var(--faint);cursor:pointer;padding:0}
.pw-eye .ic{width:18px;height:18px}
.pw-eye:hover{color:var(--ink)}
.pw-eye.on{color:var(--gold)}
/* Multi-gallery connect is permanently off in this owner-only build — hide the whole block (and its
   "connecting is off" divider) so the login card is just a clean "Welcome back" + password. */
#genericConnect.disabled{display:none}
input:disabled,select:disabled,.btn:disabled{cursor:not-allowed}
/* ---- auth panes: sign in / sign up / event code ---- */
.authpane.hidden{display:none}
.authform{display:flex;flex-direction:column;gap:13px;margin-top:16px}
.authform .field{margin:0}
.authform .btn.primary{margin-top:4px}
.auth-switch{margin-top:16px;text-align:center;font-size:13px;color:var(--muted)}
.linkbtn{background:none;border:0;padding:0;margin:0;font:inherit;font-weight:600;color:var(--gold);cursor:pointer;text-decoration:underline;text-underline-offset:2px}
.linkbtn:hover{color:var(--gold-deep)}
.divider{display:flex;align-items:center;gap:12px;color:var(--faint);font-size:11px;letter-spacing:.16em;text-transform:uppercase;margin:6px 0 16px}
.divider::before,.divider::after{content:"";height:1px;background:var(--line);flex:1}
.field{margin:12px 0}
.field label{display:block;font-size:12px;color:var(--muted);margin-bottom:5px;font-weight:600}
.field label b{color:var(--gold)}
input,select{font-size:14px;color:var(--ink);background:var(--canvas);border:1px solid var(--line);border-radius:10px;padding:11px 12px;width:100%}
input::placeholder{color:var(--faint)}
input:focus,select:focus{outline:none;border-color:var(--gold);box-shadow:0 0 0 3px rgba(201,162,75,.18)}
.row2{display:flex;gap:12px}.row2>*{flex:1}
.err{color:var(--rose);font-size:13px;min-height:18px;margin-top:10px;text-align:center}
.err:empty{display:none}   /* no reserved gap when there's no error to show (keeps the login card tight) */
.foot-note{text-align:center;color:var(--faint);font-size:12px;padding:0 20px 30px}
.foot-note b{color:var(--muted)}

/* ================= BUTTONS ================= */
.btn{border:1px solid var(--line);background:var(--surface-2);color:var(--ink);cursor:pointer;
  padding:10px 16px;border-radius:var(--r-sm);font-weight:600;font-size:13px;white-space:nowrap;
  /* every button is a centred icon+label row by default — so an <svg class=ic> and text always align
     vertically and sit a consistent 8px apart, without each call-site having to re-declare it. Contexts
     that need a different alignment (e.g. the left-aligned drawer rows) override justify-content/gap. */
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  transition:background var(--dur-micro) var(--ease-out),border-color var(--dur-micro) var(--ease-out),
    color var(--dur-micro) var(--ease-out),box-shadow var(--dur-micro) var(--ease-out),transform 90ms var(--ease-out)}
.btn:hover{border-color:var(--gold);color:var(--gold)}
.btn:active{transform:translateY(1px)}                                  /* tactile press on every button */
/* primary: a soft top-lit gradient + a low accent-tinted glow so it reads as THE action, with press feedback */
.btn.primary{background:linear-gradient(180deg,var(--gold-fill-1),var(--gold-fill-2));   /* fallback for browsers w/o color-mix (iOS <16.4) */
  background:linear-gradient(180deg,color-mix(in srgb,var(--gold-fill-1) 90%,#fff),var(--gold-fill-2));
  color:var(--on-gold);border-color:transparent;font-weight:700;
  box-shadow:0 1px 1px rgba(0,0,0,.16),0 8px 18px -8px rgba(201,162,75,.42);
  box-shadow:0 1px 1px rgba(0,0,0,.16),0 8px 18px -8px color-mix(in srgb,var(--gold) 65%,transparent)}
.btn.primary:hover{filter:brightness(1.04);color:var(--on-gold);
  box-shadow:0 2px 4px rgba(0,0,0,.2),0 12px 24px -8px rgba(201,162,75,.5);
  box-shadow:0 2px 4px rgba(0,0,0,.2),0 12px 24px -8px color-mix(in srgb,var(--gold) 78%,transparent)}
.btn.primary:active{transform:translateY(1px);filter:brightness(.98)}
.btn.lg{width:100%;padding:14px;font-size:14px;margin-top:8px;letter-spacing:.02em;border-radius:var(--r-md);min-height:var(--touch)}
.btn.sm{padding:7px 13px;font-size:12px}
.ic{width:18px;height:18px;flex:none;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}

/* ================= TOP BAR ================= */
.topbar{position:sticky;top:0;z-index:var(--z-topbar);background:var(--bar);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);
  border-bottom:1px solid var(--line);display:flex;align-items:center;gap:14px;flex-wrap:nowrap;
  /* iOS notch/Dynamic-Island: the bar background fills under the status bar and content sits below it */
  padding:calc(10px + env(safe-area-inset-top,0px)) calc(18px + env(safe-area-inset-right,0px)) 10px calc(18px + env(safe-area-inset-left,0px))}
.brand{display:flex;align-items:center;gap:10px;line-height:1;white-space:nowrap;min-width:0;flex:0 1 auto}
.brand .seal{flex:none;width:30px;height:30px;border-radius:8px;display:grid;place-items:center;
  background:linear-gradient(150deg,var(--gold-fill-1),var(--gold-fill-2));color:var(--on-gold);font-family:var(--font-display);font-size:13px}
/* wordmark uses the ORIGINAL page's Great Vibes script (monogram seal stays to its left).
   Great Vibes has tall ascenders/descenders — generous line-height + no vertical clip so the
   swashes on G/R aren't cut off top/bottom; horizontal ellipsis only, capped width for phones. */
.brand .wordmark{font-family:var(--font-hero);font-weight:400;font-size:24px;color:var(--gold);
  line-height:1.7;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:42vw}
.brand-pill{display:none!important}
.seg{display:flex;background:var(--canvas-2);border:1px solid var(--line);border-radius:999px;padding:3px}
.seg button{border:0;background:none;color:var(--muted);padding:8px 16px;border-radius:999px;font-size:13px;font-weight:600;display:flex;align-items:center;gap:7px;transition:var(--dur-micro)}
.seg button .n{font-variant-numeric:tabular-nums;font-weight:700;font-size:11px;background:rgba(242,234,224,.08);padding:1px 7px;border-radius:999px;color:var(--ink)}
.seg button.on{background:linear-gradient(180deg,var(--gold-fill-1),var(--gold-fill-2));color:var(--on-gold)}
.seg button.on .n{background:rgba(36,27,16,.22);color:var(--on-gold)}
.count{font-family:var(--font-script);font-style:italic;font-size:15px;white-space:nowrap;color:var(--muted)}
/* small version pill, far right of the topbar — tells v2 (Keepsake Book) apart from v3 (People) at a glance */
.verbadge{flex:0 0 auto;font-size:11px;font-weight:700;letter-spacing:.06em;color:var(--gold);border:1px solid rgba(201,162,75,.4);border-radius:999px;padding:2px 8px;opacity:.85;white-space:nowrap;-webkit-user-select:none;user-select:none}
/* refresh + version pair: tight group, sitting 14px (topbar gap) from the rest of the bar */
.verwrap{display:inline-flex;align-items:center;gap:6px;flex:0 0 auto}
/* manual code-refresh button before the version badge: a clean round icon button matched to the
   badge (same gold border/tint). Only the inner icon spins while updating — not the whole frame. */
.verbadge.refresh{cursor:pointer;background:none;width:24px;height:24px;padding:0;border-radius:999px;display:inline-grid;place-items:center;color:var(--gold)}
.verbadge.refresh .ic{width:15px;height:15px}
.verbadge.refresh:hover{opacity:1;border-color:var(--gold);background:rgba(201,162,75,.12)}
.verbadge.refresh.spinning{opacity:1}
.verbadge.refresh.spinning .ic{animation:verspin .8s linear infinite;transform-origin:50% 50%}
@keyframes verspin{to{transform:rotate(360deg)}}
.count b{color:var(--ink);font-variant-numeric:tabular-nums;font-style:normal;font-family:var(--font-ui)}
.hamburger{display:inline-flex;align-items:center;justify-content:center;line-height:1;padding:8px;width:40px;height:40px;border-radius:11px;flex:none}
.hamburger .ic{width:20px;height:20px}

/* ================= RIGHT DRAWER MENU (#topActions) ================= */
.menu-scrim{display:block;position:fixed;inset:0;background:rgba(10,7,13,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);z-index:var(--z-scrim);opacity:0;pointer-events:none;transition:opacity .2s}
.menu-scrim.open{opacity:1;pointer-events:auto}
.topactions{position:fixed;top:0;right:0;bottom:0;z-index:var(--z-drawer);width:330px;max-width:90vw;
  display:flex;flex-direction:column;align-items:stretch;gap:1px;overflow-y:auto;
  background:var(--surface);border-left:1px solid var(--line);box-shadow:var(--shadow-lg);
  /* safe-area: clear the notch/Dynamic Island at the top and the home indicator at the bottom on iOS */
  padding:calc(14px + env(safe-area-inset-top,0px)) 12px calc(22px + env(safe-area-inset-bottom,0px));
  transform:translateX(104%);transition:transform var(--dur-sheet) var(--ease-out)}
.topactions.open{transform:none}
.menu-head{display:flex;align-items:center;gap:11px;padding:4px 6px 12px;border-bottom:1px solid var(--line);margin-bottom:6px}
.menu-id{flex:none;width:40px;height:40px;border-radius:11px;display:grid;place-items:center;
  background:linear-gradient(150deg,var(--gold-fill-1),var(--gold-fill-2));color:var(--on-gold);font-family:var(--font-display);font-size:14px}
.menu-id-text{display:flex;flex-direction:column;gap:2px;min-width:0}
.menu-id-text .nm{font-family:var(--font-display);font-size:17px;color:var(--ink);line-height:1}
.menu-id-text .sub{font-size:11px;color:var(--muted)}
.menu-id-text .sub .ownerbadge{color:var(--gold);font-weight:700}
.menu-x{margin-left:auto;display:inline-flex;background:none;border:0;color:var(--muted);font-size:24px;line-height:1;padding:2px 6px;cursor:pointer}
.menu-x:hover{color:var(--ink)}
.menu-sec{display:block;font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--gold-deep);font-weight:700;padding:14px 10px 5px}
.sync-card{background:rgba(201,162,75,.08);border:1px solid rgba(201,162,75,.28);border-radius:14px;padding:13px 14px;margin:6px 4px 4px}
.sync-card .t{display:flex;align-items:center;gap:8px;font-family:var(--font-display);font-size:15px;color:var(--ink)}
.sync-card .t .pip{width:16px;height:16px;border-radius:5px;background:linear-gradient(150deg,var(--gold-fill-1),var(--gold-fill-2));display:grid;place-items:center;color:var(--on-gold);font-size:9px}
.sync-card .meta{font-size:11.5px;color:var(--muted);margin:5px 0 11px;line-height:1.5}
.sync-card .meta b{color:var(--ink);font-variant-numeric:tabular-nums}
.sync-card .btn{width:100%;justify-content:center}
.topactions .btn{width:100%;text-align:left;display:flex;align-items:center;justify-content:flex-start;gap:11px;border:0;background:none;box-shadow:none;
  border-radius:11px;padding:11px 12px;font-size:13.5px;font-weight:600;min-height:46px;color:var(--ink);white-space:nowrap}
.topactions .btn:hover,.topactions .btn:active{background:var(--surface-2);color:var(--ink)}
.topactions .btn .ic{color:var(--gold)}
.topactions .btn.danger{color:var(--rose)} .topactions .btn.danger .ic{color:var(--rose)}
.topactions .btn.danger:hover{background:rgba(217,139,161,.12)}
/* general danger button (e.g. "Delete album" in the selection header) */
.btn.danger{color:var(--rose);border-color:rgba(217,139,161,.45)}
.btn.danger:hover{background:rgba(217,139,161,.12);border-color:var(--rose)}
.menu-toggle{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 12px}
.menu-toggle>span{font-size:13.5px;font-weight:600;color:var(--ink)}
.mini-seg{display:flex;background:var(--canvas-2);border:1px solid var(--line);border-radius:999px;padding:3px;gap:2px}
.mini-seg button{border:0;background:none;color:var(--muted);padding:6px 11px;border-radius:999px;font-size:12px;font-weight:600;display:flex;align-items:center;gap:6px;cursor:pointer}
.mini-seg button.on{background:var(--surface);color:var(--ink);box-shadow:0 1px 2px rgba(0,0,0,.25)}
.mini-seg .ic{width:15px;height:15px}
.sw{display:inline-flex;gap:3px}.sw i{width:9px;height:9px;border-radius:999px;display:inline-block}
.menu-size{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px 6px}
.menu-size span{font-size:13.5px;font-weight:600;color:var(--ink)}
.menu-size .size-range{flex:1;width:auto;max-width:190px}

/* range slider */
.size-range{-webkit-appearance:none;appearance:none;width:132px;height:22px;background:transparent;cursor:pointer;flex:none}
.size-range:focus{outline:none}
.size-range::-webkit-slider-runnable-track{height:4px;border-radius:999px;background:linear-gradient(90deg,var(--gold-deep),var(--gold))}
.size-range::-moz-range-track{height:4px;border-radius:999px;background:var(--line)}
.size-range::-moz-range-progress{height:4px;border-radius:999px;background:linear-gradient(90deg,var(--gold-deep),var(--gold))}
.size-range::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:16px;height:16px;margin-top:-6px;border-radius:999px;background:var(--ink);border:2px solid var(--gold);box-shadow:0 1px 3px rgba(0,0,0,.45)}
.size-range::-moz-range-thumb{width:16px;height:16px;border-radius:999px;background:var(--ink);border:2px solid var(--gold);box-shadow:0 1px 3px rgba(0,0,0,.45)}

/* ================= SUB-EVENT TABS ================= */
.subtabs{position:sticky;top:var(--topbar-h,55px);z-index:var(--z-subtabs);background:var(--bar);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);
  border-bottom:1px solid var(--line);display:flex;gap:7px;padding:9px 18px;overflow-x:auto;flex-wrap:nowrap;scrollbar-width:none}
.subtabs::-webkit-scrollbar{display:none}
.tab{flex:0 0 auto;border:1px solid var(--line);background:var(--canvas-2);border-radius:999px;padding:8px 15px;font-size:13px;font-weight:600;color:var(--ink);display:flex;gap:8px;align-items:center;white-space:nowrap;transition:var(--dur-micro)}
.tab .n{color:var(--faint);font-variant-numeric:tabular-nums;font-size:11px;font-weight:700}
.tab:hover{border-color:var(--gold);color:var(--gold)}
.tab.on{background:var(--gold-fill-1);color:var(--on-gold);border-color:var(--gold)}
.tab.on .n{color:rgba(36,27,16,.7)}
.tab.wine.on{background:var(--rose);border-color:var(--rose);color:#2a1018}
.tab.wine.on .n{color:rgba(42,16,24,.7)}
.tab.newalbum{border-style:dashed;background:none;color:var(--gold)}

/* ================= UTILITY BAR (single row; .filters) ================= */
.filters{position:sticky;top:calc(var(--topbar-h,55px) + var(--subtabs-h,44px));z-index:var(--z-utility);
  background:var(--bar);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);border-bottom:1px solid var(--line-2);
  display:flex;align-items:center;gap:9px;padding:9px 18px;flex-wrap:nowrap;
  max-height:120px;overflow:hidden;transition:max-height .26s ease,opacity .2s ease,padding .26s ease,border-color .26s ease}
body.nav-collapsed .filters{max-height:0;padding-top:0;padding-bottom:0;opacity:0;border-bottom-color:transparent;pointer-events:none}
#filterBtn .ic,#selectModeBtn .ic,#layoutBtn .ic{width:16px;height:16px}
.filters .btn{display:inline-flex;align-items:center;gap:7px}
.icon-btn{width:34px;height:34px;padding:0;display:inline-grid;place-items:center;flex:none}
.fbadge{display:none;background:var(--rose);color:#2a1018;border-radius:999px;font-size:10px;font-weight:700;padding:1px 6px;margin-left:2px}
.fbadge.on{display:inline-block}
#filterBtn.active{border-color:var(--gold);color:var(--gold)}
.sizectl-inline{display:none!important}    /* photo-size lives in the menu (Appearance) */

/* ================= GRID / TILES ================= */
/* Bottom padding only needs to clear the ~32px status bar on desktop/iPad (no bottom nav here).
   96px left ~60px of empty scrollable space below the last photo — i.e. you could scroll "past the
   end". 52px clears the bar with a small gap and no dead space. (Mobile overrides this below to also
   clear the bottom nav.) */
main{padding:16px 18px 52px}
.section-head{display:flex;align-items:baseline;gap:12px;margin:6px 2px 14px;flex-wrap:wrap}
.section-head h2{font-family:var(--font-display);font-weight:400;font-size:25px;margin:0;color:var(--ink)}
.section-head .meta{color:var(--muted);font-family:var(--font-script);font-style:italic;font-size:15px}
.section-head .rule{flex:1;height:1px;background:var(--line);min-width:20px}
.section-head .btn{display:inline-flex;align-items:center;gap:7px;align-self:center}
.section-head .btn .ic{width:15px;height:15px}
#grid.square,.selgrid.square{display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--tile),1fr));gap:8px}
/* "Just their face" crops: own multi-column sizing so they don't inherit the big --tile (which collapses to
   one full-width column on a phone). Keeps faces a tidy, consistent size when you toggle the view. */
#grid.facegrid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
/* same off-screen culling as .jrow, but for the square grid (flat grid items, no row wrapper). Each
   skipped tile keeps a correct height from aspect-ratio:1/1 on its grid-column width, so the scroll
   height stays accurate; contain-intrinsic-size is only a fallback for tiles never yet rendered. */
#grid.square .tile,.selgrid.square .tile{content-visibility:auto;contain-intrinsic-size:auto var(--tile)}
/* square tiles: force a square box without relying on aspect-ratio (older iOS Safari ignored it → tall, stretched faces) */
#grid.square .tile::before,.selgrid.square .tile::before{content:"";display:block;padding-top:100%}
#grid.square .tile>img,.selgrid.square .tile>img{position:absolute;inset:0}
#grid.full,.selgrid.full{display:block}
/* content-visibility lets the browser skip layout/paint for rows scrolled off-screen — a big win
   on iPad/Safari with thousands of tiles. contain-intrinsic-size:auto remembers each row's real
   height after its first render (so the scrollbar/positions stay stable); 200px is just the
   not-yet-seen fallback. */
.jrow{display:flex;gap:6px;margin-bottom:6px;content-visibility:auto;contain-intrinsic-size:auto 200px}
/* While the lightbox is open, turn culling OFF for the whole session (.lb-active, added at open and
   removed once the close settles). The gallery doesn't scroll behind the overlay, so culling buys
   nothing there — and this lets the off-screen rows the background pre-render builds gain real heights
   incrementally instead of in one reflow burst on close (which felt heavy). It also fixes the blank-on-
   close: Safari leaves content-visibility:auto regions unpainted after the programmatic close-scroll. */
body.lb-active .jrow,
body.lb-active #grid.square .tile,
body.lb-active .selgrid.square .tile{content-visibility:visible}
.tile{position:relative;border-radius:9px;overflow:hidden;background:var(--tileph);aspect-ratio:1/1;cursor:pointer;
  touch-action:manipulation;   /* instant tap-to-open (no 300ms delay / double-tap zoom); scroll still works */
  box-shadow:0 1px 3px rgba(0,0,0,.4);transition:transform .18s var(--ease-out),box-shadow .18s ease}
/* keyboard focus ring on a photo tile (raised so it's never hidden behind a neighbour) */
.tile:focus-visible{outline:2px solid var(--gold);outline-offset:2px;z-index:4}
.tile:focus{outline:none}   /* mouse/touch clicks don't show the ring; :focus-visible (keyboard) does */
.tile.full{aspect-ratio:auto;flex:0 0 auto}
.tile::after{content:"";position:absolute;inset:0;pointer-events:none;border-radius:9px;
  box-shadow:inset 0 -40px 50px rgba(0,0,0,.32),inset 0 0 0 1px rgba(242,234,224,.05)}
.tile img{width:100%;height:100%;object-fit:cover;display:block;background:var(--tileph);opacity:0;transition:opacity .55s ease,transform .35s var(--ease-out)}
.tile img.ld{opacity:1}
/* hover: the photo POPS — scales up, lifts above its neighbours, gains a deep shadow.
   No @media(hover:hover) guard so it also works on iPad (trackpad/pointer reports no real hover). */
.tile:hover{transform:scale(1.05) translateY(-3px);z-index:2;box-shadow:0 14px 34px rgba(0,0,0,.55)}
.tile:hover img{transform:scale(1.07)}
/* touch: brief press "pop" on the image only (tile geometry unchanged → the FLIP open morph stays accurate) */
.tile:active img{transform:scale(1.06)}
.tile::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:linear-gradient(100deg,transparent 25%,rgba(242,234,224,.07) 50%,transparent 75%);background-size:220% 100%;animation:shimmer 1.5s linear infinite}
.tile.imgld::before{display:none}
/* favourite heart (the .pick button) */
.pick{position:absolute;top:7px;right:7px;width:30px;height:30px;border-radius:999px;border:1.5px solid rgba(242,234,224,.7);
  background:rgba(14,10,18,.35);display:grid;place-items:center;color:#fff;z-index:3;font-size:15px;line-height:1;
  transition:transform var(--dur-micro),background var(--dur-micro),opacity var(--dur-micro);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
.pick:hover{transform:scale(1.12)}
.pick.on{background:var(--rose);border-color:#fff;color:#2a1018}
.pick.on:hover{background:var(--rose-deep)}
@media(hover:hover){.tile .pick:not(.on){opacity:0}.tile:hover .pick{opacity:1}}  /* clean tiles on desktop; reveal on hover */
/* album membership = prominent gold name pills, top-left (✦ + album name; all albums shown, stacked) */
.badges{position:absolute;top:8px;left:8px;z-index:3;display:flex;flex-direction:column;align-items:flex-start;gap:4px;max-width:84%}
.badge{display:inline-flex;align-items:center;gap:5px;background:linear-gradient(150deg,var(--gold-fill-1),var(--gold-fill-2));color:var(--on-gold);
  padding:4px 10px 4px 8px;border-radius:999px;font-size:11px;font-weight:700;letter-spacing:.01em;white-space:nowrap;line-height:1.1;
  box-shadow:0 2px 7px rgba(0,0,0,.45);border:1px solid rgba(255,255,255,.2);max-width:100%;overflow:hidden;text-overflow:ellipsis}
.badge i{display:none}
.badge::before{content:"\2726";font-size:10px;line-height:1;flex:none}
/* local-album tag: gold OUTLINE (vs the filled gold seal for studio/Samaro albums) — folder glyph */
.badge.local{background:rgba(14,10,18,.55);color:var(--gold);border:1px solid var(--gold);box-shadow:0 1px 4px rgba(0,0,0,.45);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}
.badge.local::before{content:"\25A4";color:var(--gold)}
/* +Album button on each tile (bottom-right). Revealed on hover (desktop/iPad), always shown on
   touch, gold when the photo is already in an album. The caption stays off for clean tiles. */
.albbtn{position:absolute;bottom:7px;right:7px;z-index:3;background:rgba(14,10,18,.62);color:var(--ink);
  border:1px solid rgba(201,162,75,.45);border-radius:999px;font-size:10px;font-weight:700;padding:4px 9px;
  opacity:0;transition:opacity var(--dur-micro),background var(--dur-micro);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
.tile:hover .albbtn{opacity:1}
.albbtn:hover{background:var(--gold-fill-1);color:var(--on-gold);border-color:var(--gold)}
.albbtn.in{background:var(--gold-fill-1);color:var(--on-gold);opacity:1}
@media(hover:none){.albbtn{opacity:1}}   /* no hover on touch → always visible */
.cap{display:none!important}
/* infinite-scroll tripwire: only its POSITION matters (the observer uses a 3500px bottom rootMargin),
   so 1px is plenty — 30px just added empty scroll space below the last photo ("scroll past the end"). */
#sentinel{height:1px}
.empty{text-align:center;color:var(--muted);padding:70px 20px;font-family:var(--font-display);font-size:22px}
.empty span{display:block;font-family:var(--font-script);font-style:italic;font-size:14px;margin-top:8px;color:var(--faint)}

/* ================= STATUS BAR ================= */
.statusbar{position:fixed;bottom:0;left:0;right:0;z-index:var(--z-status);background:var(--bar);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);
  border-top:1px solid var(--line);display:flex;align-items:center;gap:16px;flex-wrap:wrap;
  padding:9px 18px calc(9px + env(safe-area-inset-bottom,0px))}   /* clear the home indicator */
.statusbar .lc{display:flex;align-items:center;gap:6px;font-size:13px;line-height:1}
.statusbar .lc b{font-weight:700;font-size:13px;font-variant-numeric:tabular-nums;color:var(--ink)}
.statusbar .tag{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.statusbar .heart{color:var(--rose)} .statusbar .seal{color:var(--gold)}
.statusbar .bar{height:5px;width:120px;background:rgba(242,234,224,.1);border-radius:999px;overflow:hidden}
.statusbar .bar i{display:block;height:100%;background:linear-gradient(90deg,var(--gold-deep),var(--gold));width:0;transition:width .3s}
/* scroll progress — a thin gold line riding the bar's top edge: 0% at the top of the gallery,
   100% at the bottom. Sits over the 1px border-top; width is driven by the scroll handler. */
.scrollprog{position:absolute;left:0;top:-1px;height:3px;width:0;
  background:linear-gradient(90deg,var(--gold-deep),var(--gold));border-radius:0 3px 3px 0;
  box-shadow:0 0 8px rgba(201,162,75,.45);transition:width .08s linear;pointer-events:none}

/* On touch devices (iPad/iPhone) drop the frosted-glass blur on the bars that stay on screen while
   you scroll — backdrop-filter blur is recomputed every frame on iOS Safari and is a major source
   of scroll jank (the same reason #contextBar was already made solid). Desktop keeps the blur.
   Also keep text inputs at >=16px so iOS never zooms in on focus — that auto-zoom is what made the
   gallery appear zoomed right after the owner login (and it can stick). */
@media (pointer:coarse){
  .topbar,.subtabs,.statusbar{background:var(--canvas-2);-webkit-backdrop-filter:none;backdrop-filter:none}
  input,select,textarea,input.fld,select.fld,.ppl-search,.ff-pick-search,.ppl-ed-note,#ownerPassword{font-size:16px}
}
/* Suppress the native iOS image callout + drag-lift on UI images (grid tiles, filmstrip, neighbour slides,
   face chips): a native long-press there would only save a tiny thumbnail or "lift" the photo out as a drag
   item, which spoiled the layout. -webkit-user-drag:none stops the drag-lift; touch-callout:none stops the
   menu. The LIGHTBOX photo (#lbImg) is the deliberate exception — see the override below. */
#app img,#lb img,.tile img{-webkit-touch-callout:none;-webkit-user-select:none;user-select:none;
  -webkit-user-drag:none;user-drag:none}
/* The current lightbox photo: RESTORE Apple's native long-press (Save Image / Copy / Share). The lazy
   full-res upgrade (app.js) makes that saved image the full-resolution, time-corrected file. More specific
   than "#lb img" above so it wins. */
#lb #lbImg{-webkit-touch-callout:default;-webkit-user-select:auto;user-select:auto;-webkit-user-drag:auto;user-drag:auto}

/* ================= LIGHTBOX ================= */
/* min-height:100dvh covers the iOS-Safari dynamic-viewport gap: position:fixed/inset:0 pins to the
   LAYOUT viewport, but once the page has scrolled the Safari toolbar collapses and the VISUAL viewport
   grows taller — so inset:0 alone leaves a strip at the bottom where the gallery shows through. dvh
   tracks the real visible height and fills it. (100vh is the fallback for browsers without dvh.) */
.lb{position:fixed;inset:0;min-height:100vh;min-height:100dvh;z-index:var(--z-lightbox);background:rgba(8,6,11,.93);background:color-mix(in srgb,var(--canvas) 30%, #000);
  display:flex;flex-direction:column;visibility:hidden;opacity:0;pointer-events:none;transition:opacity .3s ease,visibility .3s;
  /* a single-image viewer has nothing to scroll vertically — disable browser pan/scroll + rubber-band
     here (the stage runs its own swipe/zoom gestures in JS; the filmstrip re-enables horizontal pan). */
  touch-action:none;overscroll-behavior:none}
.lb.open{visibility:visible;opacity:1;pointer-events:auto}
.lb.open.closing{opacity:0}
/* ONE line: the toolbar never wraps — it scrolls horizontally if the buttons don't all fit
   (keeps the lightbox height consistent). Safe-area keeps Back/Favourite clear of the notch. */
.lb-top{display:flex;align-items:center;gap:12px;color:var(--ink);flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;
  padding:calc(13px + env(safe-area-inset-top,0px)) calc(16px + env(safe-area-inset-right,0px)) 13px calc(16px + env(safe-area-inset-left,0px))}
.lb-top::-webkit-scrollbar{display:none}
.lb-top>*{flex:none}                                       /* buttons/labels keep their width on the scrolling row */
.lb-top .spacer{flex:1 1 auto;min-width:0}                 /* the spacer is the only flexible item (right-aligns actions when they fit, collapses when they don't) */
.lb-top .btn{background:rgba(242,234,224,.06);border-color:rgba(242,234,224,.16)}
/* phones: glyph/icon-only toolbar so the actions stay compact on one line (declutters the top bar).
   Labels return on larger screens. */
@media(max-width:760px){
  .lb-top{gap:7px}
  .lb-top .btn .lbl{display:none}
  .lb-top .btn{padding:9px 11px;font-size:16px;line-height:1}
  .lb-top .btn.sm{padding:9px 10px}
  .lb-top .pos{display:none}            /* position is shown by the filmstrip on phones — saves space */
  /* Fit the bar on one line: Help = KEYBOARD shortcuts (the tour teaches touch), Full-view mostly
     matters for TV mirroring from a desktop — both hide on phones (!important beats inline display). */
  .lb-help,.lb-full{display:none!important}
}
/* Share + Cast now live inside the Save button's menu — never as separate top-bar buttons.
   (!important because their own modules set style.display="" when the capability exists; the menu
   reads that inline style for availability while this rule keeps the buttons visually gone.) */
.lb-top .lb-cast,.lb-top #lbShare{display:none!important}
/* "Add to Album" button state, mirroring the tile badges:
   .in    = in a studio (Samaro) album → filled gold seal (like .badge)
   .local = only in a local album      → gold outline, highlighted but unfilled (like .badge.local) */
#lbAlbum.in{background:var(--gold-fill-1);color:var(--on-gold);border-color:var(--gold)}
#lbAlbum.local{background:transparent;color:var(--gold);border-color:var(--gold)}
.lb-top .pos{font-family:var(--font-script);font-style:italic;font-size:16px;font-variant-numeric:tabular-nums;color:var(--muted)}
.lb-stage{flex:1;display:flex;align-items:center;justify-content:center;position:relative;min-height:0;padding:0;overflow:hidden;touch-action:none}
.lb-track{position:absolute;inset:0;display:flex;will-change:transform}
.lb-slide{flex:0 0 100%;width:100%;height:100%;display:flex;align-items:center;justify-content:center}
.lb-stage img{max-width:94%;max-height:94%;object-fit:contain;border-radius:5px;box-shadow:var(--shadow-lg);transition:filter .3s,transform .25s ease}
/* The open frame is the already-cached 768px thumb (only a slight upscale), so a light blur is
   enough to mask it while the 1100/1600 loads — a heavy blur just made it look "still loading". */
.lb-stage img.lowres{filter:blur(2px)}
.lb-stage img.zoomed{cursor:grab}
.lb-nav{position:absolute;top:50%;transform:translateY(-50%);z-index:3;background:rgba(242,234,224,.08);border:1px solid rgba(242,234,224,.16);color:var(--ink);font-size:24px;width:42px;height:58px;border-radius:12px;display:grid;place-items:center;transition:var(--dur-micro)}
.lb-nav:hover{background:var(--gold-fill-1);color:var(--on-gold);border-color:var(--gold)}
.lb-nav.prev{left:12px}.lb-nav.next{right:12px}
/* metadata on ONE line — scrolls horizontally if it's too long (filename first), never wraps */
.lb-info{padding:10px 18px 8px;color:var(--muted);font-size:12.5px;white-space:nowrap;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;text-align:left}
.lb-info::-webkit-scrollbar{display:none}
.lb-info::after{content:"";display:inline-block;width:18px}   /* trailing breathing room so the last item isn't flush to the edge when scrolled */
.lb-info b{color:var(--ink);font-family:var(--font-display);font-size:15px;font-weight:400}
.lb-info .pill{display:inline-block;background:rgba(201,162,75,.16);color:var(--gold);padding:2px 9px;border-radius:999px;font-size:10.5px;margin-left:8px;font-weight:700;border:1px solid rgba(201,162,75,.3)}
.lb-heart{font-size:15px}
.lb-full .ic{width:18px;height:18px;stroke-width:2}
/* Present / fullscreen mode (best AirPlay-mirroring result): pure black, photo edge-to-edge, chrome
   stripped to just a slim top bar so the photo fills the screen. Toggled by the Fullscreen API. */
.lb.presenting{background:#000}
.lb.presenting .lb-info,.lb.presenting .lb-film,.lb.presenting .lb-people{display:none}   /* immersive: photo only (hide info, film, people chips) */
.lb.presenting .lb-stage{padding:0}
.lb.presenting .lb-stage img{max-width:100%;max-height:100%;border-radius:0;box-shadow:none}
/* MIRROR-TO-TV / full view: the photo fills the screen and the chrome AUTO-HIDES. Controls reveal when
   you move the mouse to the top (desktop) or tap the top of the photo (touch); app.js adds .controls-show. */
.lb.presenting .lb-top{background:linear-gradient(to bottom,rgba(0,0,0,.55),transparent);opacity:0;pointer-events:none;
  padding:calc(10px + env(safe-area-inset-top,0px)) calc(16px + env(safe-area-inset-right,0px)) 14px calc(16px + env(safe-area-inset-left,0px));
  transition:opacity .28s var(--ease-out)}
.lb.presenting .lb-nav{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.12);opacity:0;pointer-events:none;transition:opacity .28s var(--ease-out)}
.lb.presenting.controls-show .lb-top,.lb.presenting.controls-show .lb-nav{opacity:1;pointer-events:auto}
.lb.presenting:not(.controls-show){cursor:none}                      /* hide the cursor for a clean mirrored screen */
/* small hint shown on entering full view (toolbar hidden): how to bring the options back */
.lb-present-hint{position:absolute;left:50%;top:calc(16px + env(safe-area-inset-top,0px));transform:translateX(-50%) translateY(-8px);z-index:5;
  pointer-events:none;background:rgba(0,0,0,.6);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  color:#fff;border:1px solid rgba(255,255,255,.2);border-radius:999px;padding:8px 16px;font-size:13px;font-weight:600;white-space:nowrap;
  opacity:0;transition:opacity .3s var(--ease-out),transform .3s var(--ease-out)}
.lb-present-hint.show{opacity:1;transform:translateX(-50%) translateY(0)}
.lb:not(.presenting) .lb-present-hint{display:none}
/* casting state: the Cast button glows gold while a session is connected */
#lbCast.casting{background:var(--gold-fill-1);color:var(--on-gold);border-color:var(--gold)}
#lbCast.casting .ic{stroke:var(--on-gold)}

/* ================= BOTTOM-SHEET FILTER (#filterPanel) ================= */
/* compact centred panel (like the old app's popover) — not a full-width bottom sheet */
.filter-panel{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:var(--z-sheet);
  background:var(--surface);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow-lg);
  padding:14px;width:340px;max-width:92vw;max-height:82vh;overflow-y:auto}
.fp-head{font-family:var(--font-display);font-size:17px;color:var(--ink);margin:-2px 0 10px;padding-bottom:8px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;gap:10px}
.fp-x{background:none;border:0;color:var(--muted);font-size:24px;line-height:1;padding:0 4px;cursor:pointer;font-family:inherit}
.fp-x:hover{color:var(--ink)}
.fp-row{display:flex;align-items:flex-start;gap:10px;margin:9px 0}
.fp-lbl{font-size:10px;color:var(--faint);text-transform:uppercase;letter-spacing:.1em;width:70px;flex:0 0 70px;padding-top:7px;font-weight:700}
.fp-lbl small{display:block;text-transform:none;letter-spacing:0;font-size:9.5px;color:var(--faint);font-weight:600;margin-top:2px}
.fp-lbl small b{color:var(--gold)}
.fp-show{display:flex;flex-wrap:wrap;align-items:center;gap:8px;flex:1;min-width:0}
.fp-show .chips{display:flex;flex-wrap:wrap;gap:6px;flex:0 1 auto}
.chips{display:flex;gap:6px;flex-wrap:wrap}
.chip{border:1px solid var(--line);background:var(--canvas-2);border-radius:999px;padding:7px 13px;font-size:12.5px;font-weight:600;color:var(--muted);cursor:pointer}
.chip.on{background:var(--ink);color:var(--canvas);border-color:var(--ink)}
#albChips:not(:empty){border-left:1px solid var(--line);padding-left:8px;max-height:120px;overflow-y:auto}
.tickchip{display:inline-flex;align-items:center;gap:4px}
.tickchip .tk{display:inline-grid;place-items:center;width:0;height:13px;overflow:hidden;color:var(--gold);font-size:11px;font-weight:800;transition:width .12s}
.tickchip.on .tk{width:13px}
.tickchip.wine.on{background:var(--rose);border-color:var(--rose);color:#2a1018}
.tickchip.wine.on .tk{color:#2a1018}
.tickchip .cn{opacity:.55;font-size:10px;font-variant-numeric:tabular-nums}
/* People filter row (owner-only face filter). Selected people = round-thumb chips; an inline
   searchable picker drops down to add more; a Together/Any toggle appears once 2+ are chosen. */
.fp-people[hidden]{display:none}
.fp-people-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:8px}
.fp-people .pchip{display:inline-flex;align-items:center;gap:6px;padding:3px 8px 3px 3px}
.fp-people .pchip img{width:22px;height:22px;border-radius:999px;object-fit:cover;background:var(--tileph);flex:none}
.fp-people .pchip.unnamed span{font-style:italic;color:var(--muted)}
.fp-people .pchip .x{display:inline-grid;place-items:center;width:16px;height:16px;border-radius:999px;
  font-size:13px;line-height:1;color:var(--faint);margin-left:1px}
.fp-people .pchip:hover .x{color:var(--rose)}
.fp-people .chip.add{border-style:dashed;color:var(--gold-deep)}
.fp-people .chip.add:hover{border-color:var(--gold);color:var(--gold)}
.fp-people-mode{margin-top:1px}
.fp-people-mode .chip{padding:5px 11px}
.fp-people-pick.hidden{display:none}
.fp-people-pick{border:1px solid var(--line);border-radius:12px;background:var(--canvas-2);padding:8px}
.fp-people-pick .ff-pick-search{margin-bottom:8px}
.fp-people-pick .ff-pick-list{max-height:42vh}
.fp-people-pick .ff-pick-item.chosen{opacity:.4;pointer-events:none}
.fp-people-pick .ff-pick-item.chosen::after{content:"✓ added";color:var(--gold);font-size:11px;margin-left:auto}
.fp-grid{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin:11px 0 2px}
.filter-panel .chip{padding:6px 11px;font-size:12px}
.fp-field{display:flex;flex-direction:column;gap:4px;min-width:0}
.fp-field>span{font-size:10px;color:var(--faint);text-transform:uppercase;letter-spacing:.08em}
.fp-field.wide{grid-column:1 / -1}
.fp-field .fld{width:100%}
select.fld,input.fld{border:1px solid var(--line);background:var(--canvas-2);border-radius:10px;padding:10px 11px;font-size:13px;width:auto;color:var(--ink)}
input.fld::-webkit-calendar-picker-indicator{filter:invert(.6) sepia(1) saturate(3) hue-rotate(5deg)}
.fp-actions{display:flex;gap:8px;margin-top:12px;border-top:1px solid var(--line);padding-top:11px}
.fp-actions .btn{flex:1;justify-content:center}

/* ================= ALBUM MENU + TOAST ================= */
.album-scrim{position:fixed;inset:0;z-index:79;background:rgba(10,7,13,.35)}   /* tap to dismiss the album menu */
.album-menu{position:fixed;z-index:80;background:var(--surface);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow-lg);padding:8px;min-width:248px;max-height:72vh;overflow-y:auto}
.album-menu .am-title{display:flex;align-items:center;justify-content:space-between;font-size:11px;color:var(--faint);text-transform:uppercase;letter-spacing:.1em;padding:5px 9px 7px}
.album-menu .am-x{width:auto;min-width:0;padding:0 4px;font-size:20px;line-height:1;color:var(--muted);font-weight:400}
.album-menu .am-x:hover{background:none;color:var(--ink)}
.album-menu .am-done{justify-content:center;margin-top:4px;border-top:1px solid var(--line);border-radius:0 0 9px 9px;color:var(--gold)}
.album-menu button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:10px;background:none;border:0;text-align:left;padding:10px 9px;border-radius:9px;font-size:13px;color:var(--ink);font-weight:600;cursor:pointer}
.album-menu button:not(:disabled):hover{background:var(--surface-2)}
.album-menu button:disabled{opacity:.4;cursor:default}
.album-menu .st{font-size:11px;color:var(--gold);font-weight:700;white-space:nowrap}
.album-menu .soon{font-size:10px;color:var(--faint)}
.toast{position:fixed;left:50%;bottom:72px;transform:translateX(-50%) translateY(8px);z-index:90;background:var(--surface);color:var(--ink);padding:11px 18px;border-radius:999px;font-size:13px;font-weight:600;box-shadow:var(--shadow-lg);border:1px solid var(--line);opacity:0;pointer-events:none;transition:opacity .25s,transform .25s}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
.toast.err{border-color:var(--rose);color:var(--rose)}
.toast.ok{border-color:var(--emerald);color:var(--emerald)}
.theme-fab{position:fixed;top:calc(14px + env(safe-area-inset-top,0px));right:calc(14px + env(safe-area-inset-right,0px));z-index:50;width:40px;height:40px;border-radius:11px;display:grid;place-items:center;padding:0}
/* scroll-to-top FAB — bottom-right, above the status bar; fades in once scrolled */
.to-top{position:fixed;right:16px;bottom:calc(62px + env(safe-area-inset-bottom,0px));z-index:37;width:44px;height:44px;border-radius:999px;
  display:grid;place-items:center;background:var(--surface);border:1px solid var(--line);box-shadow:var(--shadow);
  opacity:0;transform:translateY(10px) scale(.9);pointer-events:none;transition:opacity .2s,transform .2s,background var(--dur-micro)}
.to-top.show{opacity:1;transform:none;pointer-events:auto}
.to-top:hover{background:var(--surface-2);border-color:var(--gold)}
.to-top .ic{width:20px;height:20px;stroke:var(--gold);stroke-width:2.2}
body.selectmode .to-top{display:none}     /* the bulk bar takes the bottom in select mode */
@media(max-width:760px){.to-top{right:14px;bottom:60px}}

/* ================= MULTI-SELECT + BULK BAR ================= */
.btn:disabled{opacity:.4;pointer-events:none}
#selectModeBtn.on{background:var(--gold-fill-1);color:var(--on-gold);border-color:var(--gold)}
.bulkbar{position:fixed;bottom:0;left:0;right:0;z-index:var(--z-bulk);background:var(--surface);border-top:2px solid var(--gold);box-shadow:0 -8px 24px rgba(0,0,0,.3);padding:11px 18px calc(11px + env(safe-area-inset-bottom,0px));display:none;align-items:center;gap:10px}
.bulkbar.show{display:flex}
body.selectmode .statusbar{display:none}
.bulkbar b{color:var(--gold);font-family:var(--font-display);font-size:17px;font-variant-numeric:tabular-nums;flex:none}
/* actions scroll horizontally between the count (left) and Done (right) so nothing is cut off on a phone */
.bulk-actions{display:flex;align-items:center;gap:8px;flex:1;min-width:0;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.bulk-actions::-webkit-scrollbar{display:none}
.bulk-actions .btn{flex:none}
.bulkbar #bulkClear{flex:none}
.bulk-act.danger{border-color:rgba(192,57,43,.5);color:#e0796e}
.bulk-act.danger:hover{background:#c0392b;border-color:#c0392b;color:#fff}
body.selectmode .pick,body.selectmode .badges{display:none}
body.selectmode .tile{cursor:copy}
.tile.msel{outline:3px solid var(--gold);outline-offset:-3px}
.tile.msel::before{content:"\2713";position:absolute;top:7px;left:7px;z-index:4;width:24px;height:24px;border-radius:999px;background:var(--gold-fill-1);color:var(--on-gold);display:grid;place-items:center;font-weight:800;font-size:13px;animation:none;background-image:none}
/* liked/favourited tile: the same pink hue as the original — a rose ring + soft rose glow.
   The crisp ring is an INSET box-shadow, not an outline: outlines flicker/vanish under the
   hover transform:scale (WebKit/Chrome re-rasterise them), which made the pink hue drop out on
   hover and snap back on leave. An inset box-shadow rides the same layer and stays rock-steady. */
.tile.sel{box-shadow:inset 0 0 0 3px var(--rose),0 1px 3px rgba(0,0,0,.4),0 0 0 4px rgba(217,139,161,.18)}
.tile.sel::after{box-shadow:inset 0 0 40px rgba(217,139,161,.22),inset 0 -40px 50px rgba(0,0,0,.32),inset 0 0 0 1px rgba(242,234,224,.05)}
/* on hover a favourite keeps — and amplifies — its pink glow: the inset rose ring stays, plus a
   soft rose bloom, with the dark lift dialled back a touch so the pink reads clearly instead of
   being swamped by the drop shadow. */
@media(hover:hover){.tile.sel:hover{box-shadow:inset 0 0 0 3px var(--rose),0 14px 34px rgba(0,0,0,.45),0 0 0 4px rgba(217,139,161,.6),0 6px 30px rgba(217,139,161,.5)}}

/* selections per-album sub-event filter */
.selfilter{display:flex;gap:6px;flex-wrap:wrap;margin:0 2px 14px}
.selfilter .chip{cursor:pointer}

/* ================= FILMSTRIP + SHORTCUTS ================= */
.lb-film{display:flex;gap:6px;padding:8px 14px calc(14px + env(safe-area-inset-bottom,0px));overflow-x:auto;scrollbar-width:none;touch-action:pan-x}
.lb-film::-webkit-scrollbar{display:none}
.film-th{flex:0 0 auto;width:54px;height:54px;border:0;padding:0;border-radius:8px;overflow:hidden;background:var(--tileph);opacity:.42;box-shadow:0 0 0 2px transparent;transition:opacity .15s,box-shadow .15s;cursor:pointer}
.film-th img{width:100%;height:100%;object-fit:cover;display:block}
.film-th:hover{opacity:.8}
.film-th.on{opacity:1;box-shadow:0 0 0 2px var(--gold)}
.lb-help{padding:7px 12px}
.shorts{position:fixed;inset:0;z-index:95;background:rgba(10,7,13,.6);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);display:flex;align-items:center;justify-content:center;padding:20px}
.shorts[hidden]{display:none}
.shorts-card{position:relative;background:var(--surface);border:1px solid var(--line);border-radius:18px;box-shadow:var(--shadow-lg);padding:22px 24px;max-width:360px;width:100%;max-height:84vh;overflow-y:auto}
.shorts-sec{font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--gold-deep);font-weight:700;margin:14px 0 6px}
.shorts-card ul+.shorts-sec{border-top:1px solid var(--line);padding-top:12px}
.shorts-card::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;background:linear-gradient(90deg,transparent,var(--gold),transparent)}
.shorts-card h3{margin:0 0 14px;font-family:var(--font-display);font-weight:400;font-size:23px;color:var(--ink)}
.shorts-card ul{list-style:none;margin:0 0 16px;padding:0;display:flex;flex-direction:column;gap:11px}
.shorts-card li{display:flex;align-items:center;gap:8px;color:var(--muted);font-size:13px}
.shorts-card li.g{color:var(--faint);font-size:12px;border-top:1px solid var(--line);padding-top:13px;line-height:1.55}
kbd{font-family:inherit;background:var(--canvas-2);border:1px solid var(--line);border-bottom-width:2px;border-radius:6px;padding:2px 7px;font-size:11px;font-weight:700;color:var(--ink);min-width:22px;text-align:center;display:inline-block}

/* ================= RESPONSIVE ================= */
@media(max-width:760px){
  .topbar{gap:8px;padding:calc(9px + env(safe-area-inset-top,0px)) calc(12px + env(safe-area-inset-right,0px)) 9px calc(12px + env(safe-area-inset-left,0px))}
  .brand .seal{width:26px;height:26px;font-size:11px}
  .brand .wordmark{font-size:22px}      /* Great Vibes script stays, just smaller; ellipsises if tight */
  .topbar .count{display:none}
  .seg button{padding:6px 10px;font-size:12px;gap:4px}
  .seg button .n{display:none}
  .hamburger{margin-left:auto}
  .subtabs{gap:6px;padding:8px 12px}
  .subtabs .tab{padding:7px 12px;font-size:12.5px}
  .filters{padding:8px 12px}
  main{padding:14px 12px 92px}
  .lb-film{padding:6px 10px 10px}.film-th{width:46px;height:46px}.lb-help{display:none}
}
@media(max-width:640px){:root{--tile:112px}.hero{padding:46px 18px 22px}}

/* ===== LEFT NAVIGATION RAIL (collapsible) — Gallery + sub-events, then Selections + albums ===== */
#navRail{position:fixed;top:var(--topbar-h,55px);left:0;bottom:0;width:264px;z-index:38;
  background:var(--canvas-2);border-right:1px solid var(--line);overflow-y:auto;overflow-x:hidden;
  padding:14px 12px calc(24px + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;gap:2px;
  transition:transform var(--dur-sheet) var(--ease-out)}
.nav-scrim{display:none}
/* Left drawer header — mirrors the right Options drawer's .menu-head. Hidden on desktop (the rail
   sits under the topbar there); shown only when the rail is an off-canvas drawer (≤760px, set above). */
.nav-head{display:none;align-items:center;gap:11px;position:sticky;top:0;z-index:1;
  padding:calc(11px + env(safe-area-inset-top,0px)) 4px 11px;margin:0 8px 4px;
  border-bottom:1px solid var(--line);background:var(--canvas-2)}
.nav-head .nav-id{flex:none;width:38px;height:38px;border-radius:11px;display:grid;place-items:center;
  background:linear-gradient(150deg,var(--gold-fill-1),var(--gold-fill-2));color:var(--on-gold);font-family:var(--font-display);font-size:14px}
.nav-head .nav-id-text{display:flex;flex-direction:column;gap:2px;min-width:0}
.nav-head .nav-id-text .nm{font-family:var(--font-display);font-size:16px;color:var(--ink);line-height:1.15;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.nav-head .nav-id-text .sub{font-size:11px;color:var(--muted)}
.nav-head .nav-x{margin-left:auto;flex:none;background:none;border:0;color:var(--muted);font-size:26px;line-height:1;padding:2px 8px;cursor:pointer}
.nav-head .nav-x:hover{color:var(--ink)}
.rail-sec{font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--gold-deep);font-weight:700;padding:14px 10px 6px}
.rail-sec:first-child{padding-top:2px}
.rail-item{display:flex;align-items:center;gap:10px;width:100%;text-align:left;border:0;background:none;
  border-radius:10px;padding:10px 12px;font-size:14px;font-weight:600;color:var(--ink);cursor:pointer;
  border-left:3px solid transparent;transition:background var(--dur-micro)}
.rail-item .ri-t{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rail-item .ri-ic{flex:none;width:20px;display:grid;place-items:center;color:var(--gold)}
.rail-item .ri-svg{width:17px;height:17px;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;display:block}
.rail-item .ri-ic.heart{color:var(--rose)}
.rail-item .ri-ic.heart .ri-svg{fill:var(--rose);stroke:var(--rose)}   /* filled rose heart for Favourites */
.ri-seal{display:grid;place-items:center;width:19px;height:19px;border-radius:5px;background:linear-gradient(150deg,var(--gold-fill-1),var(--gold-fill-2));color:var(--on-gold);font-size:10px;box-shadow:0 1px 2px rgba(0,0,0,.35)}
.rail-item .n{font-variant-numeric:tabular-nums;font-size:11.5px;font-weight:700;color:var(--faint)}
.rail-item.sub{padding-left:24px;font-weight:500;font-size:13.5px}
.rail-item.sub .ri-t{color:var(--muted)}
.rail-item:hover{background:var(--surface-2)}
.rail-item.on{background:var(--surface);color:var(--ink);border-left:3px solid var(--gold)}
.rail-item.on .ri-t,.rail-item.sub.on .ri-t{color:var(--ink)}
.rail-item.on .n{color:var(--muted)}
.rail-item.new{color:var(--gold);border:1px dashed var(--line);margin-top:10px;justify-content:flex-start}
.rail-item.new .ri-ic{color:var(--gold)}
/* sticky context bar (inside #appbody): current section name + count, and the Options tab */
/* solid background (no backdrop-filter): a second blurred sticky bar repainted on every scroll frame
   and hurt scroll smoothness; the topbar keeps the blur. */
#contextBar{position:sticky;top:var(--topbar-h,55px);z-index:29;display:flex;align-items:center;gap:12px;
  padding:10px 18px;background:var(--canvas-2);border-bottom:1px solid var(--line)}
#contextLabel{display:flex;align-items:baseline;gap:9px;min-width:0}
#contextLabel .ctx-name{font-family:var(--font-display);font-size:19px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#contextLabel .ctx-n{font-variant-numeric:tabular-nums;font-size:12.5px;font-weight:700;color:var(--gold);flex:none}
#contextLabel .ctx-total{font-variant-numeric:tabular-nums;font-size:12.5px;font-weight:600;color:var(--faint);flex:none;margin-left:-4px}
#contextBar #menuBtn{display:inline-flex;align-items:center;gap:7px;flex:none}
#contextBar #menuBtn .ic{width:16px;height:16px}
@media(max-width:760px){#contextBar{padding:9px 12px}#contextLabel .ctx-name{font-size:17px}}
/* "Filtered" chip on the top floating bar — visible whenever a filter is active */
.ctx-filter{display:inline-flex;align-items:center;gap:6px;flex:none;cursor:pointer;line-height:1;
  border:1px solid var(--gold);background:transparent;color:var(--gold);
  border-radius:999px;font-size:12.5px;font-weight:600;padding:3px 10px}
.ctx-filter.hidden{display:none}
.ctx-filter .ic{width:13px;height:13px}
.ctx-filter:hover{background:var(--gold-fill-1);color:#241400}
.ctx-filter .fbadge{margin-left:0;background:var(--gold-fill-1);color:#241400}
.ctx-filter:hover .fbadge{background:#241400;color:var(--gold)}
@media(max-width:760px){.ctx-filter .lbl{display:none}}   /* phones: funnel + count only */
/* "Just their face" toggle in the person header (People view) */
.ctx-facetog{display:inline-flex;align-items:center;gap:6px;flex:none;cursor:pointer;line-height:1;
  border:1px solid var(--line);background:var(--surface);color:var(--muted);
  border-radius:999px;font-size:12.5px;font-weight:600;padding:3px 10px}
.ctx-facetog.hidden{display:none}
.ctx-facetog .ic{width:14px;height:14px}
.ctx-facetog:hover{color:var(--ink);border-color:var(--gold)}
.ctx-facetog.on{background:var(--gold-fill-1);border-color:var(--gold);color:#241400}
@media(max-width:760px){.ctx-facetog .lbl{display:none}}   /* phones: icon only */
/* profile "Manage ▾" menu in the context bar (Edit / Just their face / Find more) */
.ctx-manage{position:relative;flex:none}
.ctx-manage.hidden{display:none}
.ctx-manage .caret{font-size:10px;opacity:.8}
.ctx-manage-menu{position:absolute;right:0;top:calc(100% + 6px);z-index:31;display:flex;flex-direction:column;gap:3px;
  background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:7px;box-shadow:var(--shadow-lg);min-width:188px}
.ctx-mi{display:flex;align-items:center;gap:10px;width:100%;padding:9px 10px;border:0;background:none;color:var(--ink);
  border-radius:9px;cursor:pointer;font-size:13.5px;text-align:left;line-height:1}
.ctx-mi:hover{background:var(--tileph)}
.ctx-mi .ic{width:16px;height:16px;flex:none;color:var(--gold)}
.ctx-mi.on,.ctx-mi.on .ic{color:var(--gold)}   /* "Just their face" active */
.tile.facecrop img{object-fit:cover}                        /* face crops fill the square tile */
/* "who's in this photo" chips in the lightbox (v3, owner-only) */
/* FIXED-height single-line strip (reserved the moment a photo opens) so the photo never resizes/bounces
   when "In this photo" loads async, and stays stable as you swipe between photos. Scrolls horizontally
   when there are many people, instead of wrapping to a taller, layout-shifting block. */
.lb-people{display:flex;align-items:center;gap:8px;flex:none;flex-wrap:nowrap;justify-content:flex-start;
  height:48px;padding:0 16px;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.lb-people::-webkit-scrollbar{display:none}
.lb-people>*{flex:none}                 /* chips & label keep their size on the scrolling line */
.lb-people.hidden{display:none}
.lbp-lbl{font-size:10.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--faint)}
.lbp-chip{display:inline-flex;align-items:center;gap:7px;cursor:pointer;border:1px solid var(--line);
  background:var(--surface);color:var(--ink);border-radius:999px;padding:3px 12px 3px 3px;font-size:13px;font-weight:600}
.lbp-chip img{width:26px;height:26px;border-radius:999px;object-fit:cover;background:var(--tileph);flex:none}
.lbp-chip:hover{border-color:var(--gold);color:var(--gold)}
.lbp-chip.unnamed{color:var(--muted);font-weight:500;font-style:italic}
.lbp-more{font-size:12px;color:var(--gold);font-weight:700;align-self:center;cursor:pointer;background:none;
  border:1px solid var(--line);border-radius:999px;padding:5px 11px}
.lbp-more:hover{border-color:var(--gold);background:rgba(201,162,75,.12)}
/* desktop: persistent column; content sits to its right; the toggle collapses it.
   Collapse is INSTANT (no margin/transform transition) so the justified grid re-packs in a single
   clean step — animating the width left the grid mismatched mid-transition (looked bad). */
@media(min-width:761px){
  #navRail{transition:none}
  #appbody{margin-left:264px}
  body.rail-hidden #navRail{transform:translateX(-100%)}
  body.rail-hidden #appbody{margin-left:0}
  /* the fixed bottom bar spans the viewport, so its left edge (and the scroll-progress line that
     starts there) would hide behind the docked rail — start it after the rail, like #appbody. */
  .statusbar{left:264px}
  body.rail-hidden .statusbar{left:0}
}
/* mobile: off-canvas drawer with a scrim; hidden until opened */
@media(max-width:760px){
  /* off-canvas drawer: cover the whole viewport height (top:0, over the topbar) like the right-hand
     Options drawer, and carry its OWN header (.nav-head) — otherwise the open drawer had no top bar
     and the real topbar sat dimmed behind the scrim. padding-top:0 so .nav-head owns the top spacing. */
  #navRail{top:0;padding-top:0;width:284px;max-width:84vw;transform:translateX(-100%);z-index:49;box-shadow:var(--shadow-lg)}
  body.rail-open #navRail{transform:none}
  .nav-head{display:flex}
  .nav-scrim{display:block;position:fixed;inset:0;z-index:48;background:rgba(10,7,13,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;pointer-events:none;transition:opacity .2s}
  body.rail-open .nav-scrim{opacity:1;pointer-events:auto}
}

/* ===== motion ===== */
@keyframes shimmer{from{background-position:120% 0}to{background-position:-120% 0}}
@keyframes rise{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
.reveal #grid .jrow,.reveal #grid.square .tile,.reveal .selgrid .tile,.reveal #selMain .jrow{animation:rise .5s var(--ease-out) both}
.na #grid .jrow,.na #grid.square .tile,.na .selgrid .tile,.na #selMain .jrow{animation:none!important}
body.lb-on{overflow:hidden}
@keyframes heartpop{0%{transform:scale(1)}30%{transform:scale(1.4)}55%{transform:scale(.9)}100%{transform:scale(1)}}
@keyframes ring{from{opacity:.85;transform:scale(.55)}to{opacity:0;transform:scale(1.8)}}
.pick.pop,.lb-heart.pop{animation:heartpop .45s ease}
.pick.pop::before{content:"";position:absolute;inset:-4px;border-radius:999px;border:2px solid var(--rose);animation:ring .55s ease forwards;pointer-events:none}
@media(prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}.tile img{opacity:1;transform:none}}

/* ================= v3 PEOPLE (face recognition) ================= */
#peopleView main{padding:14px 18px 52px}   /* same: just clear the status bar, no scroll-past-end dead space */
.ppl-bar{display:flex;align-items:center;gap:10px;margin:2px 2px 10px}
.ppl-bar .ppl-head{font-size:21px;color:var(--ink)} .ppl-sub{font-size:12px;color:var(--faint)}
/* owner tools collapsed into one menu (no more overflowing button row) */
.ppl-manage{position:relative;flex:none}
.ppl-manage .caret{font-size:10px;opacity:.8}
.ppl-manage-menu{position:absolute;right:0;top:calc(100% + 6px);z-index:30;display:flex;flex-direction:column;gap:6px;
  background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:8px;box-shadow:var(--shadow-lg);min-width:172px}
.ppl-manage-menu .btn{width:100%;justify-content:flex-start}
/* search (grows) + Manage on one compact row */
.ppl-tools{display:flex;align-items:center;gap:8px;margin:0 0 12px}
.ppl-tools .ppl-search{flex:1;width:auto;margin:0}
@media(max-width:640px){
  .ppl-bar{display:none}               /* title lives in the sticky context bar — this bar is empty on mobile */
  .ppl-tools{margin-bottom:10px} .ppl-status{margin-bottom:8px}
}
@media(max-width:760px){#contextBar #menuBtn .lbl{display:none}}   /* phones: Options = gear icon only (declutters a person's profile bar) */
.ppl-status{min-height:18px;margin:0 2px 12px;font-size:12.5px;color:var(--muted)} .ppl-status .muted{color:var(--faint)}
.ppl-search{width:100%;box-sizing:border-box;margin:0 0 12px;padding:10px 14px;border:1px solid var(--line);border-radius:11px;
  background:var(--canvas-2);color:var(--ink);font-size:14px}
.ppl-search::placeholder{color:var(--faint)}
.ppl-search:focus{outline:none;border-color:var(--gold);box-shadow:0 0 0 3px rgba(201,162,75,.18)}
/* small comment marker on a person card that has a note */
.ppl-note-ic{width:12px;height:12px;stroke:var(--gold);fill:none;stroke-width:2;vertical-align:-1px;margin-left:5px;opacity:.85}
.ppl-empty{padding:48px 18px;text-align:center;color:var(--muted);font-size:14px;line-height:1.7}
.ppl-empty .muted{color:var(--faint);font-size:12.5px}
#peopleGrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(118px,1fr));gap:16px 12px}
.ppl-card{position:relative;display:flex;flex-direction:column;align-items:center;gap:7px}
.ppl-thumb-wrap{position:relative;width:104px;height:104px;flex:none}   /* anchors the edit badge to the PICTURE, not the (wider) card */
.ppl-thumb{border:0;padding:0;background:none;cursor:pointer;width:104px;height:104px;border-radius:999px;overflow:hidden;
  box-shadow:0 1px 4px rgba(0,0,0,.45),0 0 0 2px var(--line);transition:box-shadow .15s,transform .15s}
.ppl-thumb img{width:100%;height:100%;object-fit:cover;display:block;background:var(--tileph)}
.ppl-card:hover .ppl-thumb{box-shadow:0 6px 18px rgba(0,0,0,.5),0 0 0 2px var(--gold);transform:translateY(-2px)}
.ppl-card.merging .ppl-thumb{box-shadow:0 0 0 3px var(--gold),0 6px 18px rgba(0,0,0,.5)}
.ppl-meta{border:0;background:none;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:1px;max-width:118px}
.ppl-name{font-size:13px;color:var(--ink);max-width:116px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ppl-name.add{color:var(--faint);font-style:italic}
.ppl-cnt{font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}
.ctx-back{border:1px solid var(--line);background:var(--surface);color:var(--muted);border-radius:999px;
  font-size:12px;padding:2px 9px;cursor:pointer;flex:none}
.ctx-back:hover{color:var(--ink);border-color:var(--gold)}

/* one clear Edit (pencil) button per person — a badge on the lower-right of the cover picture */
.ppl-edit{position:absolute;right:2px;bottom:2px;width:26px;height:26px;border-radius:999px;border:1px solid var(--line);
  background:var(--surface);color:var(--muted);font-size:13px;line-height:1;display:grid;place-items:center;cursor:pointer;
  box-shadow:0 1px 4px rgba(0,0,0,.45);opacity:0;transition:opacity .15s,color .15s,border-color .15s}
.ppl-card:hover .ppl-edit,.ppl-card.merging .ppl-edit{opacity:1}
.ppl-edit:hover{color:var(--gold);border-color:var(--gold)}
@media(hover:none){.ppl-edit{opacity:1}}

/* person editor modal */
.ppl-ed[hidden]{display:none}
.ppl-ed{position:fixed;inset:0;z-index:70;display:flex;align-items:center;justify-content:center;padding:18px}
.ppl-ed-scrim{position:absolute;inset:0;background:rgba(10,7,13,.6);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
.ppl-ed-card{position:relative;background:var(--surface);border:1px solid var(--line);border-radius:18px;box-shadow:var(--shadow-lg);
  width:100%;max-width:420px;max-height:86vh;overflow-y:auto;padding:20px 20px 16px}
.ppl-ed-x{position:absolute;top:10px;right:12px;width:30px;height:30px;border:0;background:none;color:var(--muted);font-size:22px;line-height:1;cursor:pointer}
.ppl-ed-x:hover{color:var(--ink)}
.ppl-ed-top{display:flex;align-items:center;gap:14px;margin:2px 0 16px;padding-right:30px}   /* clear the absolute close (×) at top-right so the name field doesn't run under it */
.ppl-ed-cover{width:72px;height:72px;border-radius:999px;object-fit:cover;background:var(--tileph);box-shadow:0 0 0 2px var(--gold);flex:none}
.ppl-ed-namewrap{display:flex;flex-direction:column;gap:4px;min-width:0;flex:1}
#pplEdName{background:var(--canvas-2);border:1px solid var(--line);border-radius:10px;color:var(--ink);font-size:16px;padding:9px 12px;width:100%}
#pplEdName:focus{outline:none;border-color:var(--gold);box-shadow:0 0 0 3px rgba(201,162,75,.18)}
.ppl-ed-count{font-size:12px;color:var(--faint)}
.ppl-ed-note{width:100%;box-sizing:border-box;background:var(--canvas-2);border:1px solid var(--line);border-radius:10px;
  color:var(--ink);font-size:14px;font-family:inherit;line-height:1.45;padding:10px 12px;resize:vertical;min-height:64px}
.ppl-ed-note::placeholder{color:var(--faint)}
.ppl-ed-note:focus{outline:none;border-color:var(--gold);box-shadow:0 0 0 3px rgba(201,162,75,.18)}
/* custom name typeahead — floats under the input; only shown while there are matches to the typed text */
.ppl-ed-nameinput{position:relative}
.ppl-ed-sugg[hidden]{display:none}   /* the display:flex below would otherwise override the [hidden] attribute, leaving an empty box over "N photos" */
.ppl-ed-sugg{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:6;max-height:240px;overflow-y:auto;scrollbar-width:thin;
  background:var(--surface);border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow-lg);display:flex;flex-direction:column;gap:2px;padding:4px}
.ppl-ed-sugg-item{display:flex;align-items:center;gap:10px;width:100%;padding:6px 8px;border:0;border-radius:9px;background:none;color:var(--ink);cursor:pointer;text-align:left}
.ppl-ed-sugg-item:hover,.ppl-ed-sugg-item.on{background:var(--tileph)}
.ppl-ed-sugg-item img{width:30px;height:30px;border-radius:999px;object-fit:cover;background:var(--tileph);flex:none}
.ppl-ed-sugg-nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ppl-ed-sugg-cnt{color:var(--faint);font-size:12px;flex:none}
.ppl-ed-sec{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--gold-deep);font-weight:700;margin:8px 0 8px}
.ppl-ed-sec .muted{text-transform:none;letter-spacing:0;font-weight:400;color:var(--faint)}
.ppl-ed-faces{display:flex;gap:8px;overflow-x:auto;padding:2px 0 8px;scrollbar-width:thin}
.ppl-ed-face{flex:0 0 auto;width:60px;height:60px;border-radius:10px;overflow:hidden;border:2px solid transparent;padding:0;background:var(--tileph);cursor:pointer}
.ppl-ed-face img{width:100%;height:100%;object-fit:cover;display:block}
.ppl-ed-face.on{border-color:var(--gold);box-shadow:0 0 0 1px var(--gold)}
.ppl-ed-actions{display:flex;flex-direction:column;gap:8px;margin-top:14px;border-top:1px solid var(--line);padding-top:14px}
.ppl-ed-done{width:100%;justify-content:center;margin-top:14px}   /* primary close/confirm, kept clear of the danger Remove button */
#pplEdGuest.on{border-color:var(--gold);color:var(--gold)}        /* active "Not a guest" state */

/* blur tuner — a scale of the blurriest face crops + a threshold slider */
.bt-card{max-width:580px}
.bt-title{margin:2px 0 2px;font-size:21px;color:var(--ink)}
.bt-hint{font-size:12.5px;color:var(--muted);margin:8px 0 12px;line-height:1.45}
/* Vertical-scrolling gallery: each crop at its NATURAL aspect (fixed height, auto width) so it's never
   cropped OR letterboxed — every face is fully visible. Rows wrap; scroll vertically for all of them. */
.bt-scale{display:flex;flex-wrap:wrap;align-content:flex-start;gap:6px;max-height:58vh;overflow-y:auto;overflow-x:hidden;
  padding:6px;background:var(--canvas-2);border:1px solid var(--line);border-radius:12px;scrollbar-width:thin}
.bt-face{position:relative;height:90px;flex:0 0 auto;border-radius:8px;overflow:hidden;background:#000}
.bt-face img{height:100%;width:auto;display:block}
.bt-face.cut{opacity:.3}
.bt-face.cut::after{content:"\2715";position:absolute;inset:0;display:grid;place-items:center;color:#ffd9d9;
  font-weight:800;font-size:17px;background:rgba(120,20,20,.42)}
/* reset the generic input box (padding/border/bg) that otherwise insets the range track so the thumb
   can't reach the ends */
.bt-slider{width:100%;margin:14px 0 4px;accent-color:var(--gold);cursor:pointer;padding:0;border:0;background:transparent;border-radius:0}
.bt-legend{display:flex;justify-content:space-between;font-size:11px;color:var(--faint)}
.bt-count{margin-top:10px;font-size:13.5px;color:var(--ink);font-weight:600;text-align:center}
.ppl-ed-actions .btn{width:100%;justify-content:center}

/* Export chooser — scope chips + a grid of format cards */
.ex-card{max-width:460px}
.ex-scope{margin-bottom:4px}
.ex-scope .chip{display:inline-flex;align-items:center;gap:6px}
.ex-scope .chip .cn{opacity:.55;font-size:10px;font-variant-numeric:tabular-nums}
.ex-scope .chip[disabled]{opacity:.35;cursor:not-allowed}
.ex-formats{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:6px}
.ex-fmt{display:flex;align-items:flex-start;gap:10px;text-align:left;padding:12px;border:1px solid var(--line);
  border-radius:12px;background:var(--canvas-2);color:var(--ink);cursor:pointer;transition:var(--dur-micro)}
.ex-fmt:hover{border-color:var(--gold);background:var(--surface-2)}
.ex-fmt .ic{width:20px;height:20px;color:var(--gold);flex:none;margin-top:1px}
.ex-t{display:flex;flex-direction:column;gap:2px;min-width:0}
.ex-t b{font-size:13.5px;font-weight:700}
.ex-t small{font-size:11px;color:var(--faint);line-height:1.3}
.ex-actions{display:flex;flex-direction:column;gap:8px;margin-top:6px}
.ex-actions .btn{width:100%;justify-content:center;display:inline-flex;align-items:center;gap:8px}
.ex-actions .btn .ic{width:16px;height:16px;color:var(--gold)}
.ex-actions .btn:hover .ic{color:var(--gold)}
@media(max-width:520px){.ex-formats{grid-template-columns:1fr}}

/* People-tab divider: named people above, unnamed below (full-width across the grid) */
.ppl-divider{grid-column:1/-1;display:flex;align-items:center;gap:12px;margin:14px 2px 4px;
  font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--gold-deep);font-weight:700}
.ppl-divider::before,.ppl-divider::after{content:"";flex:1;height:1px;background:var(--line)}

/* "Not <name>" face-correction modal (reuses .ppl-ed shell) */
.lb-notthem{margin-left:8px}
.ff-card{max-width:380px}
.ff-q{font-family:var(--font-display);font-size:18px;color:var(--ink)}
.ff-q span{color:var(--gold)}
.ff-pick{margin-top:14px;border-top:1px solid var(--line);padding-top:12px}
.ff-pick-search{width:100%;box-sizing:border-box;padding:9px 11px;border:1px solid var(--line);border-radius:10px;
  background:var(--tileph);color:var(--ink);font-size:14px;margin-bottom:10px}
.ff-pick-search:focus{outline:none;border-color:var(--gold)}
.ff-pick-list{display:flex;flex-direction:column;gap:4px;max-height:46vh;overflow-y:auto;scrollbar-width:thin}
.ff-pick-item{display:flex;align-items:center;gap:10px;width:100%;padding:6px 8px;border:0;border-radius:10px;
  background:none;color:var(--ink);cursor:pointer;text-align:left;font-size:14px}
.ff-pick-item:hover{background:var(--tileph)}
.ff-pick-item img{width:34px;height:34px;border-radius:999px;object-fit:cover;background:var(--tileph);flex:none}
.ff-pick-nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ff-pick-item.unnamed .ff-pick-nm{color:var(--faint);font-style:italic}
.ff-pick-cnt{color:var(--faint);font-size:12px;flex:none}
.ff-pick-item.tagged{background:rgba(201,162,75,.16);box-shadow:inset 3px 0 0 var(--gold)}
.ff-pick-item.tagged .ff-pick-cnt{color:var(--gold);font-weight:800;font-size:16px}
.btn.ghost{background:none;border-color:transparent;color:var(--muted);margin-top:8px}
.btn.ghost:hover{color:var(--ink)}

/* Find more photos — review grid of borderline candidates (tap to select, ✓ = will be added) */
.fm-card{max-width:460px}
.fm-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:8px;margin-top:12px;max-height:52vh;overflow-y:auto;scrollbar-width:thin}
.fm-cand{position:relative;padding:0;border:2px solid transparent;border-radius:10px;overflow:hidden;background:var(--tileph);cursor:pointer}
.fm-cand::before{content:"";display:block;padding-top:100%}                 /* square box — works even where aspect-ratio isn't honored (older iOS Safari) */
.fm-cand img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.fm-cand.sel{border-color:var(--gold);box-shadow:0 0 0 1px var(--gold)}
.fm-cand.sel::after{content:"\2713";position:absolute;top:3px;right:4px;width:18px;height:18px;display:flex;align-items:center;justify-content:center;border-radius:999px;background:var(--gold-fill-1);color:#241400;font-size:12px;font-weight:800}
.fm-sim{position:absolute;left:3px;bottom:3px;background:rgba(10,7,13,.7);color:#fff;font-size:10px;padding:1px 5px;border-radius:999px}
/* Face/Full-photo preview toggle (reuses the .seg pill, just smaller) + full-photo grid mode */
.fm-viewrow{display:flex;align-items:center;justify-content:flex-end;gap:10px;margin-top:12px;margin-bottom:-2px}
.fm-incl{display:inline-flex;align-items:center;gap:7px;font-size:12.5px;color:var(--muted);cursor:pointer;margin:9px 0 2px}
.fm-incl input{width:16px;height:16px;flex:none;margin:0;accent-color:var(--gold);cursor:pointer}   /* width:16 overrides the global input{width:100%} that stretched the checkbox */
.fm-seg{padding:2px}
.fm-seg button{padding:5px 13px;font-size:12px}
.fm-grid.full{grid-template-columns:repeat(auto-fill,minmax(120px,1fr))}      /* bigger tiles so the whole frame reads */
.fm-grid.full .fm-cand img{object-fit:contain;background:#0b0810}            /* contain = show the ENTIRE photo, letterboxed */
/* per-candidate magnifier → opens the full photo in the lightbox (top-left, clear of the ✓ and the sim% badge) */
.fm-cand .fm-zoom{position:absolute;top:3px;left:3px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;border-radius:8px;background:rgba(10,7,13,.66);color:#fff;font-size:13px;line-height:1;cursor:zoom-in}
.fm-cand .fm-zoom:hover{background:var(--gold-fill-1);color:var(--on-gold)}
/* "not this person" — bottom-right, red, so it reads as a reject (distinct from the select tap) */
.fm-cand .fm-reject{position:absolute;bottom:3px;right:3px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;border-radius:8px;background:rgba(120,22,22,.74);color:#fff;font-size:12px;line-height:1;cursor:pointer}
.fm-cand .fm-reject:hover{background:#c0392b}
.fm-undo{align-self:flex-start;margin-top:8px;background:none;border:1px solid var(--line);color:var(--muted);border-radius:999px;padding:4px 12px;font-size:12px;cursor:pointer}
.fm-undo:hover{border-color:var(--gold);color:var(--ink)}
.lb.lb-overfind{z-index:80}   /* when launched from Find-more, the lightbox must sit ABOVE the modal (.ppl-ed z70) */
/* glowing highlight around the candidate person in the Find-more lightbox preview (positioned by JS) */
.fm-lb-box{position:fixed;z-index:82;border:3px solid var(--gold);border-radius:10px;pointer-events:none;
  box-shadow:0 0 0 3px rgba(201,162,75,.5), 0 0 28px 8px rgba(201,162,75,.7), inset 0 0 18px rgba(201,162,75,.30);
  animation:fmBoxPulse 1.8s ease-in-out infinite}
@keyframes fmBoxPulse{
  0%,100%{box-shadow:0 0 0 3px rgba(201,162,75,.45), 0 0 22px 6px rgba(201,162,75,.55), inset 0 0 16px rgba(201,162,75,.25)}
  50%    {box-shadow:0 0 0 4px rgba(201,162,75,.75), 0 0 38px 13px rgba(201,162,75,.92), inset 0 0 22px rgba(201,162,75,.4)}}
/* floating highlight toggle, bottom-left of the lightbox stage (Find-more candidate preview only) */
.fm-boxtog[hidden]{display:none}   /* display:inline-flex below overrides [hidden]; without this the Highlight pill shows outside Find-more */
.fm-boxtog{position:absolute;bottom:14px;left:14px;z-index:5;display:inline-flex;align-items:center;gap:7px;touch-action:manipulation;
  padding:7px 13px;border-radius:999px;font-size:12.5px;font-weight:600;cursor:pointer;line-height:1;
  background:rgba(14,10,18,.6);border:1px solid rgba(242,234,224,.2);color:var(--ink);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  transition:background .15s,color .15s,border-color .15s,opacity .15s}
.fm-boxtog .ic{width:16px;height:16px;flex:none}
.fm-boxtog[aria-pressed="true"]{background:var(--gold-fill-1);color:var(--on-gold);border-color:var(--gold)}
.fm-boxtog[aria-pressed="false"]{opacity:.85}
.fm-boxtog[aria-pressed="false"] .ic{opacity:.7}
/* shown in place of the Highlight toggle when the current profile photo was manually tagged (no face box) */
.lb-tag-hint[hidden]{display:none}
.lb-tag-hint{position:absolute;bottom:14px;left:14px;z-index:5;display:inline-flex;align-items:center;
  padding:7px 13px;border-radius:999px;font-size:12px;font-weight:600;line-height:1;pointer-events:none;
  background:rgba(14,10,18,.6);border:1px solid rgba(242,234,224,.2);color:var(--muted,#c9c2b8);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}

/* ============================================================
   CLEAN SKIN — chrome overrides (modern photo-app look).
   Scoped to [data-skin="clean"] so the Refined default is untouched.
   Strategy: solid (not gradient) accents, no decorative gold hairlines
   or ornaments, a tight sans wordmark — let the photos carry the colour.
   ============================================================ */
:root[data-skin="clean"] .btn.primary{background:var(--gold-fill-1)}              /* flat accent, no gradient */
:root[data-skin="clean"] .seg button.on,
:root[data-skin="clean"] .tab.on,
:root[data-skin="clean"] .badge{background:var(--gold-fill-1);box-shadow:none}
:root[data-skin="clean"] .brand .seal,
:root[data-skin="clean"] .menu-id,
:root[data-skin="clean"] .sync-card .t .pip,
:root[data-skin="clean"] .ri-seal{background:var(--gold-fill-1)}                  /* flat seals */
/* a clean sans wordmark instead of the Great Vibes script flourish */
:root[data-skin="clean"] .brand .wordmark{font-size:16px;font-weight:700;letter-spacing:-.01em;line-height:1.25;color:var(--ink)}
@media(max-width:760px){:root[data-skin="clean"] .brand .wordmark{font-size:15px}}
/* drop the decorative gold hairline at the top of cards/sheets + soften ornaments */
:root[data-skin="clean"] .card::before,
:root[data-skin="clean"] .shorts-card::before{display:none}
:root[data-skin="clean"] .ornament{opacity:.45}
:root[data-skin="clean"] .ornament span{font-size:9px}
/* calmer landing: the romantic radial washes step back so it reads as a utility, not a keepsake */
:root[data-skin="clean"] #connect,
:root[data-skin="clean"] #boot{background:var(--canvas)}
/* the boot wordmark is a quiet sans in clean (no serif keepsake flourish) */
:root[data-skin="clean"] .boot-mark{letter-spacing:-.01em;font-weight:700;font-size:26px;color:var(--ink)}

/* ============================================================
   MOBILE BOTTOM NAVIGATION (phones only; the left rail is persistent ≥761px).
   Primary, thumb-reachable top-level switch: Gallery · Selections · People · More.
   ============================================================ */
.botnav{display:none}                                   /* desktop: hidden (rail handles nav) */
@media(max-width:760px){
  /* DETERMINISTIC height = --botnav-h + safe-area, box-sizing border-box: the bar is exactly as tall as
     the value the status bar is offset by, so they sit perfectly flush with no strip of photo showing
     through (the iOS gap bug). Opaque background covers the home-indicator safe area too. */
  .botnav{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:44;align-items:center;
    height:calc(var(--botnav-h) + env(safe-area-inset-bottom,0px));box-sizing:border-box;
    background:var(--canvas-2);border-top:1px solid var(--line);
    box-shadow:0 -6px 22px rgba(0,0,0,.22);gap:2px;
    padding:0 6px env(safe-area-inset-bottom,0px)}
  .botnav button{position:relative;flex:1 1 0;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
    border:0;background:none;color:var(--muted);min-height:var(--touch);padding:5px 2px;border-radius:12px;
    font-family:var(--font-ui);font-size:10.5px;font-weight:600;letter-spacing:.01em;line-height:1;cursor:pointer;
    transition:color var(--dur-micro)}
  .botnav button span,.botnav button .ic{position:relative;z-index:1}   /* sit above the active pill */
  .botnav button span{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .botnav button .ic{width:23px;height:23px;stroke-width:1.7;transition:transform var(--dur-micro)}
  .botnav button:active .ic{transform:scale(.88)}                       /* tactile press (no full-button flash) */
  .botnav button.on{color:var(--gold);font-weight:700}
  .botnav button.on .ic{stroke:var(--gold);fill:none}
  /* refined active indicator: a soft gold pill behind the icon (Material-style; no layout shift) */
  .botnav button.on::before{content:"";position:absolute;top:3px;left:50%;width:46px;height:27px;transform:translateX(-50%);
    border-radius:999px;background:rgba(201,162,75,.15);background:color-mix(in srgb,var(--gold) 15%,transparent)}
  /* status bar rides directly on top of the nav: a 1px overlap (the -1px) tucks under the nav's z-index
     so there's never a hairline seam; opaque + single-line (no wrap) so no photo shows through or jitters. */
  .statusbar{left:0;right:0;background:var(--canvas-2);flex-wrap:nowrap;overflow:hidden;gap:12px;
    bottom:calc(var(--botnav-h) + env(safe-area-inset-bottom,0px) - 1px);padding-top:8px;padding-bottom:9px}
  .statusbar .bar{width:54px}                 /* slimmer progress so the row stays on one line */
  .statusbar .lc{min-width:0;flex:none}
  /* mobile: clear the bottom nav + the status bar that rides on top of it (+small gap), no more —
     the old +64 left a strip of empty scroll below the last photo. */
  main{padding-bottom:calc(var(--botnav-h) + 48px + env(safe-area-inset-bottom,0px))}
  #peopleView main{padding-bottom:calc(var(--botnav-h) + 48px + env(safe-area-inset-bottom,0px))}
  .to-top{bottom:calc(var(--botnav-h) + 58px + env(safe-area-inset-bottom,0px))}
  /* select mode: the bulk bar owns the bottom; lightbox: the overlay covers all */
  body.selectmode .botnav,body.lb-on .botnav{display:none}
  /* Options is reached via the thumb-friendly "More" tab on phones — drop the redundant context-bar gear */
  #contextBar #menuBtn{display:none}
  /* float toasts above BOTH bottom bars (status strip + nav) so they're never half-hidden behind them */
  .toast{bottom:calc(var(--botnav-h) + 50px + env(safe-area-inset-bottom,0px))}
}

/* ============================================================
   DESKTOP TOOLBAR (context bar, ≥761px) — the common gallery tools
   surfaced inline on large screens; on phones they stay in the drawer.
   ============================================================ */
.bar-tools{display:none}
@media(min-width:761px){
  .bar-tools{display:inline-flex;align-items:center;gap:8px;flex:none}
  .bar-tools .btn{display:inline-flex;align-items:center;gap:7px}
  .bar-tools .btn .ic{width:15px;height:15px}
  .bar-tools #barFilter.active{border-color:var(--gold);color:var(--gold)}
  .bar-tools #barSelect.on{background:var(--gold-fill-1);color:var(--on-gold);border-color:var(--gold)}
  .bar-tools #barSelect.on .ic{stroke:var(--on-gold)}
  .ctx-filter{display:none!important}   /* the always-visible Filter button replaces the "Filtered" chip on desktop */
}

/* ============================================================
   SCROLL POSITION INDICATOR (comparative-UX pass) — a floating pill that shows the current
   sub-event + date while scrolling a long photo stream, like Google/Apple Photos. Top-centred,
   below the sticky bars; fades out shortly after scrolling stops. Filled by updateScrollHint().
   ============================================================ */
.scroll-hint{position:fixed;left:50%;top:calc(var(--topbar-h,55px) + 58px);z-index:36;
  display:inline-flex;align-items:center;gap:7px;max-width:84vw;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  pointer-events:none;background:var(--surface);border:1px solid var(--line);box-shadow:var(--shadow-lg);
  border-radius:999px;padding:7px 16px;font-size:12.5px;color:var(--muted);
  opacity:0;transform:translate(-50%,-7px);transition:opacity .2s var(--ease-out),transform .2s var(--ease-out)}
.scroll-hint b{color:var(--ink);font-family:var(--font-display);font-weight:400;font-size:14px}
.scroll-hint.show{opacity:1;transform:translate(-50%,0)}
body.selectmode .scroll-hint,body.lb-on .scroll-hint{display:none}
@media(max-width:760px){ .scroll-hint{top:calc(var(--topbar-h,55px) + 52px)} }

/* ============================================================
   iOS toolbar gap-cover. On iPad/iPhone Chrome & Safari the collapsing toolbar can leave a strip
   below a position:fixed;bottom:0 bar where the scrolling page shows through (companion to the
   lightbox min-height:100dvh fix). Paint a tall solid block DOWNWARD from each bottom-anchored bar
   via box-shadow (not clipped by overflow; entirely off-screen on desktop, so it's harmless there)
   so the gap shows the bar's own colour instead of the gallery.
   ============================================================ */
@media(min-width:761px){
  .statusbar{box-shadow:0 340px 0 var(--canvas-2)}      /* desktop/iPad: status bar is the bottom element */
  #navRail{box-shadow:0 340px 0 var(--canvas-2)}        /* and the left rail reaches the bottom */
}
@media(max-width:760px){
  .botnav{box-shadow:0 -6px 22px rgba(0,0,0,.22), 0 340px 0 var(--canvas-2)}   /* phones: nav is the bottom element */
}
.bulkbar{box-shadow:0 -8px 24px rgba(0,0,0,.3), 0 340px 0 var(--surface)}      /* select mode */

/* ============================ FIRST-RUN WELCOME ============================
   Reuses the .ppl-ed modal shell (centred, scrollable card). Refined wedding look: gold monogram
   seal, script "Welcome", a gold-tinted Add-to-Home-Screen card with numbered steps, and a
   rose-dotted quick-tour list. Falls back gracefully under the Clean skin (sans fonts). */
.wc-card{max-width:460px;text-align:left;padding-top:24px}
.wc-hd{text-align:center;margin-bottom:16px}
.wc-mark{display:inline-grid;place-items:center;width:52px;height:52px;border-radius:14px;
  background:linear-gradient(150deg,var(--gold-fill-1),var(--gold-fill-2));color:var(--on-gold);
  font-family:var(--font-display);font-size:19px;letter-spacing:.02em;box-shadow:0 4px 14px rgba(0,0,0,.35)}
.wc-title{font-size:34px;margin:10px 0 2px;color:var(--ink);line-height:1;font-weight:400}
.wc-sub{margin:0;color:var(--muted);font-family:var(--font-script);font-style:italic;font-size:16px}
/* Add-to-Home-Screen highlight card */
.wc-a2hs{border:1px solid var(--gold);border-radius:14px;padding:14px 16px;margin-bottom:16px;
  background:rgba(201,162,75,.08);background:color-mix(in srgb,var(--gold) 9%,transparent)}
.wc-a2hs-hd{font-family:var(--font-display);font-size:17px;color:var(--ink);margin-bottom:2px}
.wc-a2hs-why{margin:0 0 10px;color:var(--muted);font-size:13px;line-height:1.45}
.wc-steps{margin:0;padding:0;list-style:none;counter-reset:wc}
.wc-steps li{position:relative;padding:5px 0 5px 34px;color:var(--ink);font-size:14px;line-height:1.5;counter-increment:wc}
.wc-steps li::before{content:counter(wc);position:absolute;left:0;top:3px;width:23px;height:23px;border-radius:999px;
  background:var(--gold-fill-1);color:var(--on-gold);font-weight:700;font-size:12px;display:grid;place-items:center}
.wc-steps b{color:var(--ink);font-weight:700}
.wc-share{width:17px;height:17px;vertical-align:-3px;stroke:var(--gold);fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;margin:0 1px}
.wc-note{color:var(--faint);font-size:12px}
/* quick-tour list */
.wc-guide-hd{font-family:var(--font-display);font-size:17px;color:var(--ink);margin-bottom:7px}
.wc-guide{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:9px}
.wc-guide li{position:relative;padding-left:20px;color:var(--muted);font-size:14px;line-height:1.5}
.wc-guide li::before{content:"";position:absolute;left:2px;top:8px;width:7px;height:7px;border-radius:999px;background:var(--rose)}
.wc-guide b{color:var(--ink);font-weight:600}
.wc-go{width:100%;justify-content:center;margin-top:18px}

/* welcome footer: primary "Take the tour" + a quiet "Maybe later" */
.wc-actions{display:flex;flex-direction:column;gap:6px;margin-top:18px}
.wc-actions .wc-go{margin-top:0}
.wc-later{background:none;border:0;color:var(--muted);font-size:13.5px;cursor:pointer;padding:7px;border-radius:10px}
.wc-later:hover{color:var(--ink)}

/* ============================ INTERACTIVE TOUR ============================
   .tour-spot is a box positioned over the highlighted control; its huge spread box-shadow dims the
   rest of the screen (a spotlight). .tour-pop is the tooltip the app fills + positions per step.
   The overlay captures clicks so the dimmed UI underneath isn't triggered mid-tour. */
.tour-overlay{display:none;position:fixed;inset:0;z-index:95}
.tour-overlay.on{display:block}
.tour-spot{position:fixed;left:0;top:0;width:0;height:0;border-radius:12px;outline:2px solid var(--gold);outline-offset:0;
  box-shadow:0 0 0 9999px rgba(8,6,11,.66);pointer-events:none;
  transition:left .25s ease,top .25s ease,width .25s ease,height .25s ease,opacity .2s ease}
.tour-pop{position:fixed;left:50%;top:50%;width:min(330px,calc(100vw - 28px));background:var(--surface);
  border:1px solid var(--line);border-radius:16px;box-shadow:var(--shadow-lg);padding:16px 16px 14px;
  transition:left .22s ease,top .22s ease}
.tour-step{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--gold-deep);font-weight:700;margin-bottom:5px;font-variant-numeric:tabular-nums}
.tour-title{font-family:var(--font-display);font-weight:400;font-size:19px;color:var(--ink);margin:0 0 5px;line-height:1.15}
.tour-text{margin:0;color:var(--muted);font-size:14px;line-height:1.5}
.tour-text b{color:var(--ink);font-weight:600}
.tour-btns{display:flex;align-items:center;gap:8px;margin-top:15px}
.tour-sp{flex:1}
.tour-skip{background:none;border:0;color:var(--faint);font-size:13px;cursor:pointer;padding:6px 2px}
.tour-skip:hover{color:var(--ink)}
.tour-btns .btn.sm{padding:7px 15px}

/* ============================================================
   CLEAN skin — make it unmistakably different from Refined, not just a font swap. Refined keeps its
   warm wedding tones; Clean becomes a cool, neutral, modern-photo-app surface with near-square
   thumbnails. The brand gold accent stays. Appended last so it wins over the base palettes (equal
   specificity → source order). Theme-aware so light mode stays light.
   ============================================================ */
:root[data-skin="clean"][data-theme="dark"]{
  --canvas:#121214; --canvas-2:#1a1a1d; --surface:#1d1d21; --surface-2:#27272c;
  --bar:rgba(18,18,20,.86); --tileph:#26262b;
  --line:rgba(255,255,255,.10); --line-2:rgba(255,255,255,.05);
}
:root[data-skin="clean"][data-theme="light"]{
  --canvas:#fafafa; --canvas-2:#f0f0f2; --surface:#ffffff; --surface-2:#f3f3f5;
  --bar:rgba(250,250,250,.85); --tileph:#e8e8ea;
  --line:rgba(0,0,0,.10); --line-2:rgba(0,0,0,.045);
}
/* near-square thumbnails — the modern photo-app grid (vs Refined's softer 9px rounding) */
:root[data-skin="clean"] .tile,
:root[data-skin="clean"] .tile::after{border-radius:3px}

/* lock the page behind the welcome guide / tour so it can't scroll under you (touch-action:none stops
   iOS from panning the background through the overlay; overflow:hidden stops the document scroll). */
html.guide-lock{overflow:hidden}
.tour-overlay,.ppl-ed-scrim{touch-action:none}
.wc-tourhint{margin:14px 0 0;color:var(--muted);font-size:13.5px;line-height:1.5}
.wc-tourhint b{color:var(--ink);font-weight:600}

/* tighten the welcome so the iOS guide (all A2HS steps) fits without scrolling on phones too */
.wc-card{max-height:90vh}
.wc-hd{margin-bottom:12px}
.wc-mark{width:46px;height:46px;font-size:18px}
.wc-title{font-size:30px}
.wc-a2hs{padding:12px 15px;margin-bottom:13px}
.wc-a2hs-why{margin-bottom:8px}
.wc-steps li{padding-top:4px;padding-bottom:4px}
.wc-actions{margin-top:14px}

/* ============================================================================
   Account identity + role-based access (v4 accounts mode)
   Role tiers: guest < family < studio < owner. All colours come from the theme
   tokens, so these adapt to light/dark and the Refined/Clean skins for free.
   ============================================================================ */
/* Drawer "Account" card: who you're signed in as + your access level. */
.acct-card{display:flex;align-items:center;gap:10px;justify-content:space-between;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-md);
  padding:10px 12px;margin:2px 0 8px}
.acct-id{min-width:0}
.acct-name{font-weight:700;font-size:13.5px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.acct-email{font-size:11.5px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:1px}

/* Role chip — a small tinted pill with a colour dot. --role-c drives the tint per tier. */
.role-chip{flex:none;display:inline-flex;align-items:center;gap:6px;
  font-size:10.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  padding:4px 9px;border-radius:var(--r-pill);color:var(--role-ink,var(--muted));
  background:var(--surface);                                            /* fallback (iOS <16.4) */
  background:color-mix(in srgb,var(--role-c,var(--muted)) 14%,transparent);
  border:1px solid var(--line);
  border-color:color-mix(in srgb,var(--role-c,var(--muted)) 40%,transparent)}
.role-chip::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--role-c,var(--muted))}
/* super_admin gets its own violet accent (distinct from Admin's gold); no token exists so use literals. */
.role-chip[data-role=super_admin]{--role-c:#8B78E6; --role-ink:#8B78E6}
.role-chip[data-role=owner] {--role-c:var(--gold);   --role-ink:var(--gold)}
.role-chip[data-role=studio]{--role-c:var(--emerald);--role-ink:var(--emerald)}
.role-chip[data-role=family]{--role-c:var(--rose);   --role-ink:var(--rose-deep)}
.role-chip[data-role=guest] {--role-c:var(--muted);  --role-ink:var(--muted)}
.role-chip.sm{font-size:9.5px;padding:3px 7px;letter-spacing:.05em}

/* Guests & access sheet (owner only). */
.ac-card{max-width:560px;width:100%;max-height:88vh;overflow:auto;padding:22px}
.ac-code{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-md);padding:12px 14px;margin-bottom:4px}
.ac-code-lbl{display:block;font-weight:700;font-size:13px;color:var(--ink)}
.ac-code-sub{display:block;font-size:11.5px;color:var(--faint);margin-top:1px}
.ac-code-val{display:flex;align-items:center;gap:8px}
.ac-code-val code{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:14px;letter-spacing:.06em;color:var(--gold);
  background:var(--canvas);border:1px solid var(--line);border-radius:var(--r-sm);padding:7px 11px;min-width:92px;text-align:center}

.ac-legend{list-style:none;margin:2px 0 0;padding:0;display:grid;gap:7px}
.ac-legend li{display:flex;align-items:baseline;gap:9px;font-size:12.5px;color:var(--muted);line-height:1.45}
.ac-legend .role-chip{transform:translateY(1px)}
.ac-note{font-size:12px;color:var(--faint);margin:9px 0 2px;line-height:1.5}

.ac-list{display:grid;grid-template-columns:minmax(0,1fr);gap:8px;margin-top:2px}  /* minmax(0,…) so rows shrink instead of overflowing the card on narrow phones */
.ac-empty{font-size:12.5px;color:var(--faint);padding:10px 2px}
.ac-row{display:flex;align-items:center;gap:11px;min-width:0;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-md);padding:9px 11px}
.ac-av{flex:none;width:34px;height:34px;border-radius:50%;display:grid;place-items:center;
  font-weight:700;font-size:14px;color:var(--on-gold);--role-c:var(--faint);background:var(--role-c)}
.ac-av[data-role=super_admin]{--role-c:#8B78E6}
.ac-av[data-role=owner] {--role-c:var(--gold-fill-1)}
.ac-av[data-role=studio]{--role-c:var(--emerald)}
.ac-av[data-role=family]{--role-c:var(--rose)}
.ac-av[data-role=guest] {--role-c:var(--faint)}
.ac-who{flex:1;min-width:0}
.ac-who b{display:block;font-size:13.5px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ac-who small{display:block;font-size:11.5px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:1px}
.ac-pending{color:var(--rose-deep);font-weight:600}
/* Marquee for clipped account labels: when a name / email line is wider than its column,
   gently scroll it end-to-end (back-and-forth, with a pause at each end) instead of hiding
   the tail behind an ellipsis. setupMarquees() in admin.html wraps the text in .mq-i and sets
   --mq-shift / --mq-dur ONLY when the text actually overflows, so short labels stay put; it also
   skips setup entirely under reduced-motion, leaving the plain ellipsis as the fallback. */
.ac-who b.is-marquee,.ac-who small.is-marquee{text-overflow:clip}
.ac-who b.is-marquee>.mq-i,.ac-who small.is-marquee>.mq-i{display:inline-block;white-space:nowrap;will-change:transform;animation:mq-scroll var(--mq-dur,7s) ease-in-out infinite alternate}
@keyframes mq-scroll{0%,12%{transform:translateX(0)}88%,100%{transform:translateX(calc(-1*var(--mq-shift,0px)))}}
@media (prefers-reduced-motion:reduce){.ac-who b.is-marquee,.ac-who small.is-marquee{text-overflow:ellipsis}.ac-who b.is-marquee>.mq-i,.ac-who small.is-marquee>.mq-i{animation:none;display:inline}}
/* Generic marquee for the MAIN app (left rail / menu items, account name & email, context title) —
   same mechanism, driven by svMarquee() in app.js. Applies wherever .is-marquee is set on a clipped line. */
.is-marquee{text-overflow:clip}
.is-marquee>.mq-i{display:inline-block;white-space:nowrap;will-change:transform;animation:mq-scroll var(--mq-dur,7s) ease-in-out infinite alternate}
@media (prefers-reduced-motion:reduce){.is-marquee{text-overflow:ellipsis}.is-marquee>.mq-i{animation:none;display:inline}}
.ac-sel select.ac-role{width:auto;min-width:106px;padding:8px 10px;font-size:12.5px;font-weight:600}

/* --- Super Admin: Overview dashboard tiles --- */
.ov-tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;margin-top:4px}
.ov-tile{background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-md);padding:12px 13px;min-width:0}
.ov-l{display:block;font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--faint)}
.ov-v{display:block;font-family:var(--font-display,inherit);font-size:26px;font-weight:700;color:var(--ink);line-height:1.15;margin:3px 0 2px}
.ov-s{display:block;font-size:11.5px;color:var(--muted);line-height:1.5;overflow-wrap:anywhere}
.ov-s .role-chip{margin:2px 4px 0 0}

/* --- Admin/Super Admin: per-user "Photos" slide-in panel --- */
.up-scrim{position:fixed;inset:0;z-index:60;background:rgba(0,0,0,.5)}
.up-panel{position:fixed;z-index:61;top:0;right:0;bottom:0;width:min(560px,94vw);display:flex;flex-direction:column;
  background:var(--canvas);border-left:1px solid var(--line);box-shadow:-14px 0 40px rgba(0,0,0,.3)}
.up-head{flex:none;display:flex;align-items:center;gap:12px;justify-content:space-between;
  padding:calc(12px + env(safe-area-inset-top,0px)) 16px 12px;border-bottom:1px solid var(--line);background:var(--canvas)}
.up-who{min-width:0}
.up-who b{display:block;font-size:15px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.up-who small{display:block;font-size:12px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.up-body{flex:1;overflow:auto;padding:16px;-webkit-overflow-scrolling:touch}
.up-role{margin-bottom:12px}
.up-sec{margin-bottom:20px}
.up-sec h4{margin:0 0 8px;font-size:13px;font-weight:700;color:var(--ink);display:flex;align-items:center;gap:8px}
.up-n{font-size:11px;font-weight:700;color:var(--on-gold);background:var(--gold-fill-1);border-radius:999px;padding:1px 8px}
.up-meta{font-size:12.5px;color:var(--muted);margin:0 0 8px;line-height:1.5}
.up-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(84px,1fr));gap:5px}
.up-grid img{width:100%;aspect-ratio:1/1;object-fit:cover;border-radius:var(--r-sm);background:var(--surface-2);border:1px solid var(--line)}
.up-more{font-size:11.5px;color:var(--faint);margin-top:6px}
.up-alb{display:flex;align-items:center;gap:8px;margin:10px 0 6px;font-size:13px;color:var(--ink)}
/* Guest "profile" contact card at the top of the per-user Photos panel — email + phone (WhatsApp). */
.up-contact{background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-md);padding:10px 12px;margin-bottom:16px}
.up-contact dl{margin:0;display:grid;gap:7px}
.up-contact dl>div{display:flex;gap:10px;align-items:baseline}
.up-contact dt{flex:none;width:52px;font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--faint)}
.up-contact dd{margin:0;font-size:13px;color:var(--ink);min-width:0;overflow-wrap:anywhere}
.up-contact a{color:var(--gold);text-decoration:none;font-weight:600}
.up-contact a:hover{text-decoration:underline}
.up-muted{color:var(--faint);font-weight:400}

/* ============================================================================
   Owner console (admin.html — "Guests & access"). Reuses the app's tokens +
   the .ac-* / .role-chip components above; below is just the page shell.
   ============================================================================ */
.adm-body{min-height:100dvh;background:var(--canvas);padding-bottom:60px}
.adm-top{position:sticky;top:0;z-index:5;display:flex;align-items:center;gap:12px;
  padding:calc(10px + env(safe-area-inset-top,0px)) 16px 10px;background:var(--canvas);
  border-bottom:1px solid var(--line)}
.adm-back{display:inline-flex;align-items:center;gap:6px;color:var(--muted);text-decoration:none;flex:none;white-space:nowrap;
  font-weight:600;font-size:13px;padding:8px 12px;border:1px solid var(--line);border-radius:var(--r-pill)}
.adm-back:hover{color:var(--gold);border-color:var(--gold)}
/* Theme toggle in the console header (this page has no drawer of its own). Pushed to the far right. */
.adm-theme{margin-left:auto;flex:none;display:inline-grid;place-items:center;width:38px;height:38px;
  border-radius:10px;background:var(--surface-2);border:1px solid var(--line);color:var(--muted);cursor:pointer;transition:var(--dur-micro)}
.adm-theme:hover{color:var(--gold);border-color:var(--gold)}
@media(max-width:560px){ .adm-title .sub{display:none} }   /* narrow phones: keep the title on one line */
.adm-title{display:flex;align-items:baseline;gap:10px;min-width:0}
.adm-title h1{margin:0;font-family:var(--font-display);font-weight:400;font-size:20px;color:var(--ink);white-space:nowrap}
.adm-title .sub{color:var(--faint);font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* console tabs — split "Guests & access" from the "Activity" log onto separate pages */
.adm-tabs{max-width:640px;margin:14px auto 0;padding:0 16px;display:flex;gap:22px;border-bottom:1px solid var(--line)}
.adm-tab{border:0;background:none;color:var(--muted);font-family:inherit;font-weight:600;font-size:14px;
  padding:10px 2px;margin-bottom:-1px;border-bottom:2px solid transparent;cursor:pointer;white-space:nowrap}
.adm-tab:hover{color:var(--ink)}
.adm-tab.on{color:var(--ink);border-bottom-color:var(--gold)}
.adm-main{max-width:640px;margin:0 auto;padding:18px 16px;display:grid;gap:14px}
.adm-page{padding-top:16px}
.adm-sec{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--shadow);padding:18px}
.adm-sec>h2{margin:0 0 2px;font-family:var(--font-display);font-weight:400;font-size:17px;color:var(--ink)}
.adm-sec>.hint{margin:0 0 14px;color:var(--muted);font-size:12.5px;line-height:1.5}
.adm-form{display:grid;gap:12px}
.adm-row2{display:flex;gap:10px}
.adm-row2 .field{flex:1}
.adm-actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
/* one-time credential reveal (generated password) — shown once, never retrievable again */
.adm-cred{background:var(--surface-2);border:1px dashed var(--gold-deep);border-radius:var(--r-md);
  padding:12px 14px;display:grid;gap:6px}
.adm-cred .t{font-size:12px;font-weight:700;color:var(--gold)}
.adm-cred .kv{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--muted);flex-wrap:wrap}
.adm-cred code{font-family:ui-monospace,Menlo,Consolas,monospace;color:var(--ink);
  background:var(--canvas);border:1px solid var(--line);border-radius:var(--r-sm);padding:4px 9px}
.adm-cred .note{font-size:11.5px;color:var(--faint)}
/* per-person action buttons under each row on narrow screens, inline on wide */
.ac-row .adm-acts{display:flex;gap:6px;flex:none}
.adm-acts .btn{padding:7px 10px;font-size:11.5px}
.btn.danger-line:hover{border-color:var(--rose);color:var(--rose)}
@media(max-width:560px){
  .ac-row{flex-wrap:wrap}
  .ac-row .adm-acts{width:100%;justify-content:flex-end;margin-top:2px}
}

/* invite-code rows on the owner console */
.adm-code{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:13.5px;letter-spacing:.05em;color:var(--gold);
  background:var(--canvas);border:1px solid var(--line);border-radius:var(--r-sm);padding:5px 10px;display:inline-block}

/* =============================================================================
 * v5: guest face-scan — "Find your photos" modal (#faceScan), the drawer face
 * card (#faceCard) and the My-Photos empty state. Reuses the .ppl-ed modal shell.
 * =============================================================================*/
.fs-card{max-width:420px;width:calc(100vw - 36px)}
.fs-hd h3{margin:0 0 4px;font-size:22px}
.fs-sub{margin:0 0 14px;color:var(--muted);font-size:13.5px;line-height:1.55}
.fs-step{display:flex;flex-direction:column;gap:10px}
.fs-big{padding:12px 16px;font-size:15px}   /* full-width source buttons; base .btn handles icon+label centring */
.fs-hint{margin:4px 0 0;color:var(--faint);font-size:12.5px;line-height:1.55;text-align:center}
/* camera / preview frame: rounded, square-ish, with a soft face-position oval over the live video */
.fs-frame{position:relative;border-radius:16px;overflow:hidden;background:#000;border:1px solid var(--line);
  aspect-ratio:3/4;max-height:min(52vh,420px)}
.fs-frame video,.fs-frame img{width:100%;height:100%;object-fit:cover;display:block}
.fs-frame video{transform:scaleX(-1)}   /* mirror the LIVE preview only (captured frame stays unmirrored) */
.fs-oval{position:absolute;inset:8% 18%;border:2.5px dashed rgba(255,255,255,.55);border-radius:50%/46%;pointer-events:none}
.fs-row{display:flex;gap:10px;justify-content:center}
.fs-row .btn{flex:1;justify-content:center}
/* progress */
#fsProgress{align-items:center;padding:26px 0 18px}
.fs-spin{width:34px;height:34px;border-radius:999px;border:3px solid var(--line);border-top-color:var(--gold);
  animation:bootspin .8s linear infinite}
.fs-phase{color:var(--muted);font-size:13.5px;text-align:center;line-height:1.5}
/* result: score ring + tips */
.fs-scorewrap{display:flex;gap:14px;align-items:flex-start;padding:4px 0 8px;border-bottom:1px solid var(--line);margin-bottom:10px}
.fs-ring{position:relative;width:76px;height:76px;flex:0 0 auto}
.fs-ring svg{width:100%;height:100%;transform:rotate(-90deg)}
.fs-ring-bg{fill:none;stroke:var(--line);stroke-width:6}
.fs-ring-val{fill:none;stroke:#3fae6a;stroke-width:6;stroke-linecap:round;transition:stroke-dashoffset .7s ease,stroke .4s}
.fs-ring-num{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-weight:700;font-size:21px;color:var(--ink)}
.fs-scoreinfo{flex:1;min-width:0}
.fs-scoreline{font-size:14px;color:var(--ink);margin:4px 0 6px}
.fs-tips{margin:0;padding:0 0 0 16px;color:var(--muted);font-size:12.8px;line-height:1.55}
.fs-tips li{margin:2px 0}
.fs-tips li.good{color:#3fae6a;list-style:"✓ ";padding-left:2px}
/* match confirmation */
.fs-q{font-size:15px;font-weight:600;color:var(--ink);margin:2px 0 8px}
.fs-none{color:var(--muted);font-size:13.5px;line-height:1.6;padding:6px 0}
.fs-none.err{color:#d96455}
.fs-cands{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-bottom:10px}
.fs-cand{display:flex;flex-direction:column;align-items:center;gap:5px;padding:10px;border:1.5px solid var(--line);
  border-radius:14px;background:var(--surface);cursor:pointer;min-width:96px;transition:border-color .15s,transform .12s}
.fs-cand:hover,.fs-cand:focus-visible{border-color:var(--gold);transform:translateY(-1px)}
.fs-cand img{width:76px;height:76px;border-radius:50%;object-fit:cover;border:1px solid var(--line)}
.fs-cand.big img{width:110px;height:110px}
.fs-cand-nm{font-size:13px;font-weight:600;color:var(--ink)}
.fs-cand-sim{font-size:11.5px;color:var(--faint)}
.fs-linked{display:flex;gap:14px;align-items:center;padding:6px 0 10px}
.fs-linked img{width:64px;height:64px;border-radius:50%;object-fit:cover;border:2px solid var(--gold)}
.fs-sub2{color:var(--muted);font-size:13px;margin-top:3px}
.fs-foot{display:flex;justify-content:center;margin-top:14px}
.fs-skip{background:none;border:none;color:var(--faint);font-size:13px;cursor:pointer;padding:6px 10px;
  text-decoration:underline;text-underline-offset:3px}
.fs-skip:hover{color:var(--muted)}
/* drawer face card */
.face-card{display:flex;align-items:center;gap:10px;padding:10px 12px;border:1px solid var(--line);
  border-radius:14px;background:var(--surface);margin:2px 0 6px;flex-wrap:wrap}
.fc-img{width:44px;height:44px;border-radius:50%;object-fit:cover;border:1px solid var(--line);flex:0 0 auto}
.fc-ph{display:inline-flex;align-items:center;justify-content:center;color:var(--faint)}
.fc-ph .ri-svg{width:22px;height:22px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.fc-tx{flex:1;min-width:120px}
.fc-l1{font-size:13.5px;color:var(--ink)}
.fc-l2{font-size:12px;color:var(--faint);margin-top:2px}
.fc-btns{display:flex;gap:6px;width:100%;justify-content:flex-end}
/* the card lives inside the drawer (.topactions), so stop its buttons inheriting the full-width,
   left-aligned drawer-row styling — they're compact, right-aligned pills here. */
.face-card .btn{width:auto;justify-content:center;border:1px solid var(--line);background:var(--surface-2);
  border-radius:var(--r-sm);min-height:0;padding:7px 13px;font-size:12px}
.face-card .btn:hover{border-color:var(--gold);color:var(--gold);background:var(--surface-2)}
/* My-Photos empty state (guest #peopleGrid) */
.fs-empty{padding:44px 18px;text-align:center;color:var(--muted);font-size:14px;line-height:1.7;
  display:flex;flex-direction:column;align-items:center;gap:12px;max-width:420px;margin:0 auto}
.fs-empty-ic{color:var(--faint)}
.fs-empty-ic .ri-svg{width:44px;height:44px;stroke:currentColor;fill:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
.fs-empty .btn{min-width:180px;justify-content:center}

/* activity rows (owner console audit trail) */
.act-row{display:flex;align-items:center;gap:10px;min-width:0;background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-md);padding:8px 11px}
.act-av{flex:none;width:26px;height:26px;border-radius:50%;display:grid;place-items:center;font-weight:700;font-size:11.5px;color:var(--on-gold);--role-c:var(--faint);background:var(--role-c)}
.act-av[data-role=owner]{--role-c:var(--gold-fill-1)}
.act-av[data-role=studio]{--role-c:var(--emerald)}
.act-av[data-role=family]{--role-c:var(--rose)}
.act-tx{flex:1;min-width:0;font-size:12.5px;color:var(--muted);line-height:1.4;overflow:hidden;text-overflow:ellipsis}
.act-tx b{color:var(--ink);font-weight:600}
.act-row.warn .act-tx{color:var(--rose-deep)}
.act-row.warn .act-tx b{color:var(--rose-deep)}
.act-time{flex:none;font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}
.act-line{display:block;overflow:hidden;text-overflow:ellipsis}
.act-mail{display:block;font-size:11px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:1px}
.act-row.warn .act-mail{color:var(--rose-deep)}
