/* Aisleful — warm illustrated wedding brand (2026 redesign).
   Design system carried from the app mockup:
   - Palette: navy/forest ink, terracotta (meadow) accent, gold (sky), cream paper.
   - Type: Cormorant Garamond (serif display, incl. italic) + Inter (UI/body) —
     the pre-redesign type pairing, kept on the new palette/layout.
   - Signature material: "etched/sunk" surfaces using INSET shadows so cards and
     the nav pill read as pressed into the page, not floating above it.
   - Floating pill nav with the wordmark centred beneath it.
   NOTE: this is a re-skin of a functional app — every class/id below that the JS
   depends on (tiles, dl-btn, play-btn, gallery-grid.grid-N, metric, switch,
   qr-card colour variants, appnav, faq-item, ail- cookie, legal, discount,
   admin, checkout) is preserved. */

:root {
  /* Brand palette — light UK summer countryside: off-white paper, lake blues
     and fell greens. (Variable NAMES are kept from the original brand tokens
     so every rule that already references var(--meadow) etc. repaints without
     being touched individually; only the VALUES moved from navy/terracotta.) */
  --ink: #1F3A4D;        /* deep lake-blue — headlines, dark surfaces */
  --forest: #1F3A4D;
  --nav-blue: #6C93E0;   /* cornflower blue — top nav bar + logged-in page banners only */
  --ink-2: #23394A;      /* body ink */
  --meadow: #3E7CA6;     /* sky/lake blue — primary accent / CTA */
  --meadow-deep: #2C5D80;
  --terracotta: #3E7CA6;
  --gold: #6B9C5E;       /* fell/meadow green — secondary accent, small labels, numerals */
  --sky: #6B9C5E;
  --paper: #F7FAF7;      /* off-white page background, faint green cast */
  --paper-2: #E7F0EC;    /* pale duck-egg green-blue for panels/sections */
  --paper-3: #FBFDFC;    /* near-white etched highlight */
  --body: #23394A;       /* body text */
  --ink-soft: #5B7480;   /* soft blue-grey — secondary text */
  --muted: #5B7480;
  --line: #DCE7E2;
  --white: #ffffff;
  --ok: #3f7d5a;
  --err: #B0524A;
  --danger: #B0524A;
  --blush: #BFE0C9;      /* soft mint — accent dot/kicker on dark surfaces */
  --blush-deep: #2C5D80;

  --radius: 18px;
  --radius-lg: 26px;
  --shadow: 0 14px 40px rgba(31, 58, 77, 0.12);
  --shadow-soft: 0 8px 24px rgba(31, 58, 77, 0.08);

  /* Etched / sunk surfaces — inset shadow signature. */
  --etch: inset 4px 4px 9px rgba(62,124,166,0.09), inset -4px -4px 9px rgba(255,255,255,0.7);
  --etch-strong: inset 4px 4px 10px rgba(62,124,166,0.11), inset -4px -4px 10px rgba(255,255,255,0.75);
  --etch-dark: inset 4px 4px 10px rgba(20,45,58,0.20), inset -4px -4px 10px rgba(255,255,255,0.07);
  --etch-bg: linear-gradient(150deg, #EAF3EE 0%, #FBFDFC 60%);

  --serif: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

* { box-sizing: border-box; }
/* The `hidden` attribute must always win over component display rules. */
[hidden] { display: none !important; }

/* ---------- Smooth load ----------
   Pre-paint hold (html.app-loading / html.auth-checking) + a pure-CSS opacity
   fade so the page never flashes or gets trapped hidden. Data regions use
   skeletons so layout is reserved and content swaps in place. */
html.auth-checking body,
html.app-loading body { visibility: hidden; }
/* Opacity-only fade — a transform on <body> would make it the containing block
   for position:fixed children (cookie banner). Do NOT add a transform here. */
body { animation: page-fade-in .45s ease both; }
@keyframes page-fade-in { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { body { animation: none; } }

/* ---------- Skeleton placeholders ---------- */
.skeleton, .sk {
  position: relative; overflow: hidden; border-radius: 14px;
  background: linear-gradient(100deg, var(--paper-2) 30%, #d9ebe0 50%, var(--paper-2) 70%);
  background-size: 200% 100%; animation: sk-shimmer 1.3s ease-in-out infinite;
  color: transparent !important; pointer-events: none; user-select: none;
}
.skeleton * { visibility: hidden; }
@keyframes sk-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) { .skeleton, .sk { animation: none; } }
.sk-line { height: .9em; border-radius: 6px; margin: 6px 0; }
.sk-tile { aspect-ratio: 1; border-radius: 16px; }

html, body { margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  background: var(--paper);
  color: var(--body);
  font-family: var(--sans);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  /* Sticky footer: full-height column so a short page pushes the footer to the
     bottom. footer.site gets margin-top:auto. flex is safe for fixed children. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}
img, svg { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, .serif { font-family: var(--serif); font-weight: 400; color: var(--ink); line-height: 1.08; letter-spacing: -0.01em; }
/* Every heading's spacing on the marketing pages is deliberately controlled by
   an explicit margin on the heading itself or on whatever follows it (see
   .flow h2, .hero-copy h1, .price-card h3 etc. below) -- so the browser's
   own default h1/h2/h3 margin must not also be in play, or it silently wins
   the collapse against a smaller intended gap (this is why e.g. the FAQ
   heading used to sit ~37px from its list instead of the 16px actually set).
   Reset here; anything that still wants heading margin sets it explicitly. */
h1, h2, h3 { margin: 0; }
button { font-family: inherit; cursor: pointer; }

/* ---------- Layout ---------- */
/* width:100% guards against the same shrink-to-fit-vs-flex-stretch trap fixed
   on .dash above (see its comment) — any non-wrapping, min-width-floored row
   dropped inside .container/.wrap (both are also direct children of the
   column-flex body) would blow these out the same way. */
.container { width: 100%; max-width: 1120px; margin: 0 auto; padding: 0 6vw; }
.wrap { width: 100%; max-width: 1120px; margin: 0 auto; padding: 0 6vw; }
@media (min-width: 1120px) { .container, .wrap { padding: 0 22px; } }
/* 1120px reads well up to a normal laptop/monitor, but on a genuinely large
   screen it leaves huge dead margins either side (720px per side at 2560px
   wide) while the actual content never gets any bigger. Widen the column in
   two more steps rather than one big jump, and rather than go fully edge-to-
   edge (paragraph text at 2500px+ line length gets hard to read). */
@media (min-width: 1440px) { .container, .wrap { max-width: 1320px; } }
@media (min-width: 1900px) { .container, .wrap { max-width: 1560px; } }
@media (min-width: 2400px) { .container, .wrap { max-width: 1760px; } }

/* ---------- Marketing top nav (index): floats over the hero mosaic ---------- */
/* No bar at all behind the nav -- header.site is fully transparent and so is
   .navbar itself. Each link/button is its own floating pill, one flat
   colour at 80% opacity so the mosaic shows through the button itself, not
   just the gaps.
   That 80% CANNOT just be per-pill background alpha once pills need to
   visually join up, though: two overlapping rgba(...,.8) shapes composite
   into something darker than either alone (that's alpha blending doing
   exactly what it's supposed to), so any overlap between a pill and a
   connecting bridge shows up as a visible seam/blotch -- bigger overlap
   just makes a bigger blotch, not a cleaner join.
   So js/site-nav.js instead builds .navpills: one absolutely-positioned
   layer containing a same-shape .navpill-bg duplicate of every real pill
   PLUS a bridge rect in each gap, all painted fully solid/opaque relative
   to each other (so overlaps between them are invisible -- solid colour
   over the same solid colour is just that colour) and given ONE shared
   opacity: .8 on the .navpills container itself. Opacity on a container
   flattens everything inside into a single layer first and blends THAT
   against the mosaic once, instead of blending each piece independently --
   the only way to get one congruent, uniformly-translucent shape out of
   several overlapping pieces. The real <a>/<button> elements go background:
   transparent once that layer exists (JS adds .navbar.js-pills to flip
   them) and just hold the crisp, full-opacity text/icon on top; their CSS
   background-color below is only the no-JS fallback. */
header.site {
  position: relative; z-index: 5;
}
header.site:not(.plain) {
  position: absolute; top: 0; left: 0; right: 0; z-index: 6;
  padding-top: 14px;
}
header.site .navbar {
  position: relative;
  display: flex; align-items: center; gap: 8px; justify-content: flex-end;
  /* Vertical only -- .navbar already carries the .wrap class (same one
     .hero-copy uses), so leaving its own left/right padding alone (instead
     of overriding it here, which used to win on specificity) is what makes
     the nav's left edge land exactly under "Aisleful" in the hero once it's
     left-aligned on desktop below, at every breakpoint, not just one.
     Longhand, deliberately: `padding: 4px 0` would set left/right to 0
     instead of leaving them alone, undoing exactly that. */
  padding-top: 4px; padding-bottom: 4px;
}
@media (min-width: 760px) {
  header.site .navbar { justify-content: flex-start; }
}
.navpills {
  position: absolute; inset: 0; z-index: 0;
  opacity: .8;
  pointer-events: none;
}
.navpill-bg {
  position: absolute;
  background-color: rgb(62, 124, 166); /* solid -- the .navpills group above carries the alpha, once, for the whole layer */
}
.navpill-bg--pill { border-radius: 999px; }
/* The link between two pills: a meniscus shape (tallest at each end, where
   it meets a button; pinched thin in the middle), not a rounded rect --
   surface-tension/magnetic-pull, not a floating dot or a second button.
   clip-path is a per-element bezier path computed in JS (js/site-nav.js
   bridgePath()) since it depends on that bridge's own width/height. */
.navpill-bg--link { border-radius: 0; }
.navbar.js-pills nav a,
.navbar.js-pills .navcta,
.navbar.js-pills .navburger {
  background-color: transparent;
}
header.site nav {
  position: relative; z-index: 1;
  display: flex; gap: 2px; min-width: 0;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
}
header.site nav::-webkit-scrollbar { display: none; }
/* Mobile menu toggle -- hidden on wide screens (see the 760px override
   below), where the bar shows all links inline as normal. */
.navburger {
  position: relative; z-index: 1;
  display: none; width: 40px; height: 40px; margin: 0; flex-shrink: 0;
  border: none; border-radius: 999px; background-color: rgba(62, 124, 166, .8);
  cursor: pointer; align-items: center; justify-content: center; padding: 0;
}
.navburger:hover { background-color: rgba(62, 124, 166, .95); }
.navburger:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.navburger .bars { display: inline-flex; flex-direction: column; gap: 4px; width: 18px; }
.navburger .bars span { display: block; height: 2px; width: 100%; background: var(--ink); border-radius: 2px; transition: transform .2s ease, opacity .2s ease; }
#siteNav.nav-open .navburger .bars span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
#siteNav.nav-open .navburger .bars span:nth-child(2) { opacity: 0; }
#siteNav.nav-open .navburger .bars span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
header.site nav a {
  position: relative; z-index: 1;
  display: block; margin: 0; padding: 9px 14px; border-radius: 999px;
  background-color: rgba(62, 124, 166, .8);
  font-size: .87rem; font-weight: 500; color: #fff; white-space: nowrap; flex-shrink: 0;
}
header.site nav a:hover { background-color: rgba(62, 124, 166, .95); }
.navbar-actions { position: relative; z-index: 1; display: flex; align-items: center; gap: 8px; flex-shrink: 0; margin-left: 6px; }
.navcta {
  flex-shrink: 0; padding: 10px 20px; border-radius: 999px; font-weight: 600; font-size: .84rem;
  background-color: rgba(62, 124, 166, .8); color: #fff; white-space: nowrap;
}
.navcta:hover { background-color: rgba(62, 124, 166, .95); }
/* Once .navpills exists (JS), the real pills are transparent -- hovering
   one of them washes it with white instead of the old darker-blue bump,
   since there's no per-pill alpha left to darken without reintroducing the
   same overlap-seam problem the whole .navpills layer exists to avoid. */
.navbar.js-pills nav a:hover,
.navbar.js-pills .navcta:hover,
.navbar.js-pills .navburger:hover {
  background-color: rgba(255, 255, 255, .15);
}

.brand { font-family: var(--serif); font-weight: 600; color: var(--ink); text-decoration: none; display: inline-flex; align-items: baseline; gap: .08em; }
.brand .dot { color: var(--gold); }
/* Flight, played once by js/hero-scroll.js on load: the h1's .brand (its
   permanent home -- there's no .brand left in the nav at all) is jumped to
   a transform that makes it overlay the nav's old brand-pill spot, then the
   transform is cleared so it animates back to sitting in the h1 normally.
   Real layout size/font-size of .brand itself never changes (that would
   fight the transform-based FLIP math with its own reflow) -- the "was a
   small pill" look during flight is a ::before behind it, faded out via
   opacity, sized/positioned by the same transform since it's inside .brand. */
.hero-copy h1 .brand {
  position: relative; transform-origin: top left;
  transition: transform .8s cubic-bezier(.2,.8,.2,1);
}
.hero-copy h1 .brand::before {
  content: ""; position: absolute; inset: -9px -14px; z-index: -1;
  border-radius: 999px; background-color: rgba(120, 152, 214, .82);
  opacity: 0; transition: opacity .8s ease;
}
.hero-copy h1 .brand.brand-flight::before { opacity: 1; }
.hero-copy h1 .brand.brand-flight .dot { color: var(--meadow); }
.hero-copy h1 .brand .dot { transition: color .8s ease; }
@media (prefers-reduced-motion: reduce) {
  .hero-copy h1 .brand, .hero-copy h1 .brand::before { transition: none; }
}

/* Plain header for utility pages (checkout / login / dashboard / g / guest
   gallery): a simple centred ink wordmark on the cream page — NOT the dark hero
   pill. */
header.site.plain { padding: 22px 26px; text-align: center; }
header.site.plain .brand {
  display: inline-flex; margin: 0; font-style: normal; font-size: 1.7rem;
  font-weight: 600; color: var(--ink); letter-spacing: .3px;
}
header.site.plain .brand .dot { color: var(--gold); font-style: normal; }
header.site.plain .btn { color: #fff; }
header.site.plain .btn.secondary { color: var(--ink); }
/* Guest gallery header: the wordmark stays centred like every other .plain
   header (rule above), but "Add photos" needs a spot too -- pinned to the
   corner instead of sitting inline next to the brand, which would pull the
   whole centred group off-centre. */
#guestHeader { position: relative; }
#guestHeader #addBtn { position: absolute; top: 22px; right: 26px; }

.kicker, .eyebrow {
  font-family: var(--sans); text-transform: none; letter-spacing: .01em;
  font-size: .92rem; font-weight: 600; color: var(--meadow-deep); display: inline-block;
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  background: var(--meadow); color: #fff; border: none; border-radius: 999px;
  padding: 13px 26px; font-family: var(--sans); font-size: .96rem; font-weight: 600; cursor: pointer;
  text-decoration: none; min-height: 44px; transition: transform .08s ease, box-shadow .2s, background .2s, filter .2s;
  box-shadow: 0 12px 24px -12px rgba(31,58,77,.4);
}
.btn:hover { transform: translateY(-1px); filter: brightness(1.03); box-shadow: 0 16px 30px -12px rgba(31,58,77,.45); }
.btn:active { transform: translateY(0); }
.btn.secondary { background: transparent; color: var(--ink); border: 1.5px solid var(--ink); box-shadow: none; }
.btn.secondary:hover { background: var(--ink); color: #fff; filter: none; }
.btn.blush { background: var(--ink); color: #fff; box-shadow: 0 12px 24px -12px rgba(31,58,77,.4); }
.btn.blush:hover { background: var(--meadow-deep); }
.btn.gold { background: var(--gold); color: #fff; }
.btn.full { width: 100%; }
.btn[disabled], .btn:disabled { opacity: .5; cursor: not-allowed; transform: none; box-shadow: none; }
/* Danger zone */
.btn.danger { background: transparent; color: var(--danger); border: 1.5px solid var(--danger); box-shadow: none; }
.btn.danger:hover { background: var(--danger); color: #fff; filter: none; }
.btn.danger:disabled { opacity: .6; cursor: default; }
.danger-card { border-top: 3px solid var(--danger); }
.btn.small { padding: 8px 16px; font-size: .85rem; min-height: 40px; }

/* ---------- HERO (index): full-bleed photo mosaic behind the copy ---------- */
/* A grid of square tiles, not one cover photo. The mosaic is absolutely
   positioned to fill .hero exactly -- inset:0, no gap ever, regardless of
   .hero's own height -- and the row/column count at each breakpoint is
   picked so a tile's width and height (hero-height/rows vs hero-width/cols)
   land within a few percent of each other, i.e. genuinely square, at the
   widths that breakpoint actually covers. .hero's own height is the single
   source of truth for the section's size (clamp: short on mobile, capped on
   ultra-wide, same band the old single-photo hero used). Every .hero-tile is
   a placeholder grey square until real photos are dropped in (each becomes
   a plain <img> inside the tile, object-fit: cover). */
.hero { position: relative; overflow: hidden; background: var(--paper); height: clamp(520px, 40vw, 680px); }
.hero-mosaic {
  position: absolute; inset: 0;
  display: grid; grid-template-columns: repeat(5, 1fr); grid-template-rows: repeat(7, 1fr); gap: 3px;
}
.hero-tile { background: var(--tile-c, #8a8f8c); overflow: hidden; }
.hero-tile img { display: block; width: 100%; height: 100%; object-fit: cover; }
.hero-tile:nth-child(8n+1) { --tile-c: #6b7370; }
.hero-tile:nth-child(8n+2) { --tile-c: #9aa19d; }
.hero-tile:nth-child(8n+3) { --tile-c: #565e5b; }
.hero-tile:nth-child(8n+4) { --tile-c: #b7bcb8; }
.hero-tile:nth-child(8n+5) { --tile-c: #7d8481; }
.hero-tile:nth-child(8n+6) { --tile-c: #454b49; }
.hero-tile:nth-child(8n+7) { --tile-c: #a3a8a4; }
.hero-tile:nth-child(8n+8) { --tile-c: #626966; }
.hero-tile:nth-child(n+36) { display: none; }
@media (min-width: 480px) {
  .hero-mosaic { grid-template-columns: repeat(6, 1fr); grid-template-rows: repeat(5, 1fr); }
  .hero-tile:nth-child(n+1) { display: block; }
  .hero-tile:nth-child(n+31) { display: none; }
}
@media (min-width: 760px) {
  .hero-mosaic { grid-template-columns: repeat(8, 1fr); grid-template-rows: repeat(5, 1fr); }
  .hero-tile:nth-child(n+1) { display: block; }
}
@media (min-width: 1024px) {
  .hero-mosaic { grid-template-columns: repeat(10, 1fr); grid-template-rows: repeat(4, 1fr); }
}
/* A light scrim, not a dark one -- the hero background is white like the
   rest of the site, so the copy reads as normal dark ink text on a paper-ish
   panel faded over the mosaic, not white text needing a dark tint to work.
   Below 760px the copy spans nearly the full width (no room for a "text
   left, mosaic right" fade), so it gets a top-to-bottom fade instead of the
   left-to-right one used once there's a clear text column to fade across. */
.hero-scrim {
  position: absolute; inset: 0; z-index: 2;
  /* Same shape as desktop's left-to-right fade below, just rotated: opaque
     near the floating nav, holding that opacity down past the copy before
     fading out (to a legible floor, not fully transparent -- the copy runs
     the full height here, unlike desktop's untouched side). --v-mid/--v-end
     are nudged down by js/hero-scroll.js as the user scrolls, pulling that
     fade point back up toward the nav so the mosaic's real colour spreads up
     the hero. Static 60%/92% defaults below are the resting (unscrolled,
     no-JS, reduced-motion) shape, so the gradient is there before any
     scrolling; the floor stays at .68 (not near-transparent) throughout so
     the copy over it never gets hard to read. */
  background: linear-gradient(180deg, rgba(247,250,247,.9) 0%, rgba(247,250,247,.78) var(--v-mid, 60%), rgba(247,250,247,.68) var(--v-end, 92%));
  transition: background .2s linear;
}
@media (min-width: 760px) {
  .hero-scrim {
    background: linear-gradient(100deg, rgba(247,250,247,.94) 0%, rgba(247,250,247,.94) 32%, rgba(247,250,247,.55) 52%, rgba(247,250,247,.05) 78%);
  }
}
@media (prefers-reduced-motion: reduce) {
  .hero-scrim { transition: none; }
}
.hero-copy {
  position: absolute; inset: 0; z-index: 3;
  display: flex; flex-direction: column; justify-content: center;
  /* Top padding also has to clear the nav bar now floating over the hero
     (position:absolute, ~74px tall including its own padding), not just
     give breathing room -- hence the higher floor than the bottom side. */
  padding-top: clamp(88px, 10vw, 128px); padding-bottom: clamp(36px, 7vw, 72px);
  box-sizing: border-box;
}
.hero-copy h1 { margin: 0; color: var(--ink); font-size: clamp(2.1rem, 3.6vw, 3.3rem); line-height: 1.06; font-weight: 400; }
.hero-copy h1 em { font-style: italic; color: var(--meadow); }
.hero-copy p { color: var(--ink-soft); margin: 14px 0 0; font-size: clamp(1rem, 1.2vw, 1.12rem); line-height: 1.5; max-width: 42ch; }
.hero-copy .cta-row { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 24px; }
.hero-facts { list-style: none; margin: 26px 0 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
.hero-facts li { color: var(--ink-soft); font-size: .86rem; padding-left: 22px; position: relative; }
.hero-facts li::before { content: "✓"; position: absolute; left: 0; top: 0; color: var(--sky); font-weight: 700; }
@media (max-width: 759px) {
  /* The mobile scrim (above) is a lighter touch than desktop's, so the copy
     gets darker ink here too, on top of it, to keep contrast comfortable
     against the mosaic photos showing through. */
  .hero-copy h1, .hero-copy h1 em { color: #142129; }
  .hero-copy p, .hero-facts li { color: #33474F; }
}

/* ---------- Page banner (inner pages, dark w/ blobs) ---------- */
/* z-index here (not just on the children) matters: position:relative alone
   makes this a stacking context for ITS OWN children, but without an
   explicit z-index on the banner ITSELF, main.dash (a later sibling with no
   z-index of its own) paints on top of it in normal document order wherever
   they overlap, regardless of the #appnav dropdown's own child z-index:50.
   Set above every other in-page z-index (gallery tile controls go up to 3,
   the filter-menu dropdown is 5) so nothing dash-side can tie with it --
   a tie falls back to DOM order, which favours main.dash since it comes
   after the banner, and the tile download/hide/delete buttons end up
   painting over an open nav dropdown despite its own z-index:50. */
.page-banner { position: relative; z-index: 7; background: var(--nav-blue); color: #F5EFE6; padding-bottom: clamp(16px, 2.5vw, 28px); }
/* The decorative .blob circles deliberately bleed outside the banner's box,
   so they need their OWN clipping wrapper (.blob-clip, inset:0 within the
   banner) rather than overflow:hidden on .page-banner itself. A single
   overflow property can't clip one child (the blobs) while letting another
   (the #appnav dropdown, which must grow past the banner's bottom edge when
   open) escape uncut -- and the two overflow-axis workarounds tried before
   this both failed: `overflow: visible` on both axes let the blobs' bleed
   escape body's overflow-x:hidden and made the whole page horizontally
   scrollable while the menu was open; splitting to `overflow-x: hidden;
   overflow-y: visible` re-clipped the dropdown too, because a browser can't
   actually leave one axis visible while the other isn't -- it silently
   computes the "visible" axis as auto instead (still clipped, just
   scrollable), per the CSS overflow spec's cross-axis coupling rule. */
.blob-clip { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.page-banner .blob { position: absolute; border-radius: 50%; filter: blur(70px); opacity: 0.35; }
.page-banner .pb1 { width: 40vw; height: 40vw; background: var(--meadow); top: -16vw; left: -10vw; }
.page-banner .pb2 { width: 32vw; height: 32vw; background: var(--sky); bottom: -16vw; right: -8vw; }
.page-banner .wrap, .page-banner .container { position: relative; z-index: 2; padding-top: clamp(8px, 1.5vw, 16px); }
.page-banner h1 { font-size: clamp(1.5rem, 2.6vw, 2rem); font-weight: 400; color: #F5EFE6; }
.page-banner .sub { opacity: 0.78; margin-top: 6px; font-size: .98rem; max-width: 46ch; }
.stat-row { display: flex; gap: 9vw; margin-top: 14px; flex-wrap: wrap; }
.stat b { font-family: var(--serif); font-weight: 400; font-size: clamp(1.8rem, 3vw, 2.3rem); display: block; }
.stat span { opacity: 0.65; font-size: 0.82rem; }
.page-banner .kicker, .page-banner .eyebrow { color: var(--blush); }
/* Dashboard metrics reused as the page-banner's stat-row: same JS-built
   .metric/.metric-val/.metric-label markup, restyled to read as .stat. */
.stat-row .metric { background: none; border: 0; padding: 0; text-align: left; }
.stat-row .metric-val { font-family: var(--serif); font-weight: 400; font-size: clamp(1.8rem, 3vw, 2.3rem); color: #fff; line-height: 1; }
.stat-row .metric-label { opacity: .65; font-size: .82rem; color: #fff; margin-top: 2px; }
.stat-row .metric-sub { opacity: .55; font-size: .72rem; color: #fff; text-transform: none; letter-spacing: 0; }

/* ---------- Gallery overview: horizontal preview strip + side-by-side panels
   (matches the mockup's Gallery overview page) ---------- */
.preview-row { display: flex; gap: 16px; overflow-x: auto; padding: 6px 2px 18px; margin-top: 8px; scrollbar-width: none; }
.preview-row::-webkit-scrollbar { display: none; }
.preview-row .tile { flex: 0 0 30vw; max-width: 170px; min-width: 130px; }
@media (min-width: 760px) { .preview-row .tile { flex-basis: 150px; } }
.panel-row { display: grid; gap: 5vw; grid-template-columns: 1fr; margin-top: clamp(20px, 4vw, 48px); }
@media (min-width: 800px) { .panel-row { grid-template-columns: 1fr 1fr; } }
.panel-row .dash-card { margin: 0; }
.pill-btn { padding: 13px 24px; border-radius: 999px; font-weight: 600; font-size: .9rem; line-height: 1.3; min-height: 48px; display: inline-flex; align-items: center; white-space: nowrap; }
.pill-btn.solid { background: var(--forest); color: #fff; box-shadow: 0 10px 20px -10px rgba(31,58,77,.35); }
.pill-btn.ghost { border: 1.5px solid var(--forest); color: var(--forest); }
.btn-row { display: flex; gap: 12px; flex-wrap: wrap; }

/* Etched surface utility (matches the mockup .etched). */
.etched {
  background: var(--etch-bg); border-radius: 26px; border: 1px solid rgba(31,58,77,0.05);
  box-shadow: var(--etch);
}

/* ---------- Section rhythm ---------- */
/* clamp()'d, NOT a bare vw: vw tracks viewport WIDTH, so on a wide-but-short
   16:9 laptop screen (e.g. 1366x768) an un-clamped vw padding stays huge even
   though vertical space is scarce, forcing content (e.g. the pricing cards)
   below the fold with room to spare. The clamp still scales down gracefully
   on narrow phones, it just stops growing past a sane cap on wide viewports. */
section.flow { padding: clamp(28px, 6vw, 44px) 0 clamp(24px, 5vw, 40px); }
@media (min-width: 900px) { section.flow { padding: clamp(30px, 3vw, 46px) 0; } }
.flow h2 { font-size: clamp(1.5rem, 2.6vw, 2.15rem); line-height: 1.18; font-weight: 400; max-width: 20ch; }
.eyebrow { margin-bottom: 10px; color: var(--meadow-deep); }

/* ---------- Homepage section bands ---------- */
/* Alternating flat tints (paper / paper-2) instead of one flat page colour
   throughout -- the rhythm between sections is now a colour change, not
   just a gap, which is what lets each section run tighter without the page
   reading as one undifferentiated scroll. */
section.band { padding: clamp(44px, 6vw, 68px) 0; background: var(--paper); }
section.band.tint { background: var(--paper-2); }
.section-heading { max-width: 640px; margin: 0 0 28px; }
.section-heading .eyebrow { margin-bottom: 8px; }
.section-heading h2 { margin: 0; font-size: clamp(1.5rem, 2.4vw, 2.05rem); font-weight: 400; line-height: 1.2; }
.section-head { max-width: 640px; margin: 0 0 32px; }
.section-head.compact { margin-bottom: 24px; }
.section-head h2 { font-size: clamp(1.9rem, 4.5vw, 2.8rem); margin: 10px 0 10px; font-weight: 400; }
.section-head p { color: var(--muted); margin: 0; font-size: 1.05rem; }
.kicker { display: inline-block; margin-bottom: 12px; }

.rule { width: 64px; height: 1px; background: var(--gold); border: 0; margin: 0 auto; opacity: .8; }
.rule.wide { width: 100%; background: var(--line); }

/* ---------- Story (index): asymmetric pull-quote ---------- */
/* An editorial pull-quote treatment (narrower quote column, gold rule,
   eyebrow inline with it) beside the body copy, rather than two equal
   mirrored columns -- reads as a magazine-style aside, not a template
   block repeated with different text. */
.story-layout { margin-top: clamp(16px, 2.4vw, 28px); }
@media (min-width: 900px) {
  .story-layout { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: 56px; align-items: start; }
}
.story-lede .eyebrow { display: block; margin-bottom: 14px; }
.story-quote-wrap { border-left: 3px solid var(--gold); padding-left: 24px; }
.story-quote { margin: 0; font-size: clamp(1.4rem, 2vw, 1.85rem) !important; line-height: 1.34 !important; font-weight: 400; font-style: italic; max-width: 26ch !important; }
.story-body { margin-top: 20px; max-width: 62ch; }
@media (min-width: 900px) { .story-body { margin-top: 6px; } }
.story-body p { color: var(--ink-soft); font-size: 1.02rem; line-height: 1.65; }
.story-body p + p { margin-top: 1em; }

/* ---------- Steps (kept, restyled) ---------- */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; margin-top: 6vw; }
.step { text-align: center; padding: 8px; }
.step .num {
  width: 54px; height: 54px; margin: 0 auto 16px; border-radius: 50%;
  display: grid; place-items: center; font-family: var(--serif); font-size: 1.5rem; font-weight: 500;
  color: var(--meadow-deep); background: var(--etch-bg); box-shadow: var(--etch);
}
.step h3 { font-size: 1.35rem; margin: 0 0 8px; font-weight: 500; }
.step p { color: var(--ink-soft); margin: 0; font-size: .98rem; }

/* ---------- Pricing (index): a four-tier comparison row ---------- */
/* All four real tiers shown as one scannable row of compact cards (a SaaS
   pricing-table convention) instead of two large hero panels plus a text
   link burying the other two tiers -- same real prices/features from
   window.PRICING (js/pricing.js), just no longer hidden behind prose. */
.price-grid { display: grid; grid-template-columns: 1fr; gap: 16px; margin-top: 4px; }
@media (min-width: 640px) { .price-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1080px) { .price-grid { grid-template-columns: repeat(4, 1fr); } }
.price-card {
  background: #fff; border: 1px solid var(--line); border-radius: 16px;
  padding: 24px 22px; display: flex; flex-direction: column;
  box-shadow: var(--shadow-soft);
}
.price-card.featured {
  background: linear-gradient(150deg, var(--meadow) 0%, var(--sky) 100%);
  border-color: transparent; color: #fff; box-shadow: var(--etch-dark);
}
.pc-tag { font-size: .7rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--gold); margin-bottom: 10px; }
.price-card.featured .pc-tag { color: var(--blush); }
.pc-name { font-family: var(--serif); font-size: 1.25rem; font-weight: 400; color: inherit; }
.pc-price { font-family: var(--serif); font-size: 1.65rem; margin: 6px 0 10px; color: inherit; }
.pc-price small { font-family: var(--sans); font-size: .72rem; font-weight: 500; opacity: .75; }
.pc-desc { font-size: .88rem; line-height: 1.5; color: var(--muted); margin: 0 0 20px; flex: 1; }
.price-card.featured .pc-desc { color: rgba(255,255,255,.88); }
.price-card .btn { align-self: flex-start; }
.price-card.featured .btn { background: #fff; color: var(--meadow-deep); }

/* ---------- Pricing card grid (checkout/other pages that still use .pricing) ---------- */
.pricing { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; margin: 8px 0 12px; align-items: stretch; }
.card {
  background: var(--etch-bg); border: 1px solid rgba(31,58,77,0.05); border-radius: var(--radius-lg);
  padding: 30px 26px; box-shadow: var(--etch); display: flex; flex-direction: column;
  position: relative; transition: transform .15s, box-shadow .2s;
}
.card:hover { transform: translateY(-3px); }
.card.featured { box-shadow: var(--etch-strong); }
.card .tag {
  position: absolute; top: -13px; left: 50%; transform: translateX(-50%);
  background: var(--gold); color: #fff; font-family: var(--sans);
  font-size: .68rem; font-weight: 700; letter-spacing: .12em; padding: 5px 16px; border-radius: 999px; text-transform: uppercase;
}
.card h3 { margin: 0 0 4px; font-size: 1.5rem; font-weight: 500; }
.card .blurb { color: var(--ink-soft); font-size: .9rem; min-height: 60px; }
.card .price { font-family: var(--serif); font-size: 2rem; font-weight: 500; color: var(--ink); margin: 12px 0 2px; line-height: 1.1; }
.card .price small { font-family: var(--sans); font-size: .78rem; font-weight: 500; color: var(--muted); display: block; letter-spacing: .04em; margin-top: 4px; }
.card ul { list-style: none; padding: 0; margin: 14px 0; flex: 1; }
.card li { padding: 6px 0 6px 24px; position: relative; font-size: .9rem; color: var(--body); }
.card li::before { content: "✦"; position: absolute; left: 0; color: var(--gold); font-weight: 700; }

/* ---------- Partner-of-the-day / dark blob panel ---------- */
.dark, .partner {
  position: relative; background: var(--forest); color: #F5EFE6; overflow: hidden;
  border-radius: 0; padding: clamp(36px, 8vw, 56px) clamp(22px, 6vw, 48px);
}
.dark .blob, .partner .blob { position: absolute; border-radius: 50%; filter: blur(70px); opacity: 0.35; }
.dark .blob1, .partner .blob1 { width: 46vw; height: 46vw; background: var(--meadow); top: -14vw; left: -10vw; }
.dark .blob2, .partner .blob2 { width: 36vw; height: 36vw; background: var(--sky); bottom: -14vw; right: -8vw; }
.dark-inner, .partner .partner-inner { position: relative; z-index: 2; max-width: 640px; margin: 0 auto; text-align: center; }
.dark h2, .partner h2 { color: #fff; font-size: clamp(1.65rem, 3vw, 2.4rem); line-height: 1.18; font-weight: 400; margin: 0 0 14px; }
.dark p, .partner p { color: rgba(245,239,230,0.80); font-size: 1.02rem; line-height: 1.55; margin: 0 0 22px; }
.dark .cta, .partner .cta { display: inline-flex; padding: 13px 28px; border-radius: 999px; background: var(--meadow); color: #fff; font-weight: 600; font-size: .96rem; box-shadow: 0 14px 26px -10px rgba(8,12,22,0.5); }
.partner .kicker { color: var(--blush); }
/* .split is an additional modifier used only on index.html's closing CTA --
   .dark-inner itself (and its default centred stack) is shared by five
   other pages' end-of-page CTAs and stays exactly as-is for them. */
@media (min-width: 860px) {
  .dark-inner.split {
    display: grid; grid-template-columns: 1fr auto; gap: 40px; align-items: center;
    max-width: 980px; text-align: left;
  }
  .dark-inner.split h2 { margin-bottom: 6px; }
  .dark-inner.split p { margin-bottom: 0; max-width: 50ch; }
  .dark-inner.split .cta { flex-shrink: 0; }
}

/* ---------- Forms / panels ---------- */
.panel {
  background: var(--etch-bg); border: 1px solid rgba(31,58,77,0.05); border-radius: var(--radius-lg);
  padding: 40px; box-shadow: var(--etch); max-width: 580px; margin: 36px auto;
}
.panel h2 { margin-top: 0; font-size: 1.9rem; font-weight: 400; }
.panel .lead { color: var(--ink-soft); margin-top: -4px; }
label { display: block; font-weight: 600; margin: 18px 0 8px; font-size: .86rem; color: var(--meadow-deep); }
input, select, textarea {
  width: 100%; padding: 14px 16px; font-size: 16px; border: 1px solid rgba(31,58,77,0.16);
  border-radius: 14px; background: #fff; color: var(--ink); font-family: var(--sans);
  transition: border-color .15s, box-shadow .15s;
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--sky); outline-offset: 1px; border-color: var(--sky); }
textarea { resize: vertical; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.duration-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-top: 6px; }
.duration-grid button {
  padding: 13px 6px; border: 1.5px solid var(--line); background: #fff; border-radius: 12px;
  cursor: pointer; font-weight: 600; color: var(--ink); min-height: 48px; font-family: var(--sans); transition: all .12s;
}
.duration-grid button:hover { border-color: var(--gold); }
.duration-grid button.active { border-color: var(--ink); background: var(--ink); color: #fff; }
.summary { background: var(--paper-2); border-radius: 14px; padding: 16px 18px; margin-top: 20px; border: 1px solid var(--line); }
.summary .headline { font-family: var(--serif); font-size: 1.7rem; font-weight: 500; color: var(--ink); }
.summary .sub { color: var(--muted); font-size: .9rem; }
.note { font-size: .85rem; color: var(--muted); }
.msg { padding: 13px 16px; border-radius: 12px; margin: 14px 0; font-size: .93rem; }
.msg.err { background: #f3e1de; color: var(--err); }
.msg.ok { background: #e6f0ea; color: var(--ok); }

/* ---------- Consent checkbox (checkout) ---------- */
.consent { display: flex; align-items: flex-start; gap: 12px; margin: 20px 0 4px; font-size: .9rem; color: var(--body); line-height: 1.5; cursor: pointer; }
.consent input[type="checkbox"] { flex: 0 0 auto; width: 20px; height: 20px; margin: 2px 0 0; accent-color: var(--meadow); cursor: pointer; }
.consent a { color: var(--ink); text-decoration: underline; }
.consent.consent-error input[type="checkbox"] { outline: 2px solid var(--err); outline-offset: 2px; }

/* ---------- Gallery grid ---------- */
.gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 14px; margin: 26px 0; }
.gallery-grid.grid-3x3 { grid-template-columns: repeat(3, 1fr); gap: 10px; }
.tile { position: relative; aspect-ratio: 1; border-radius: 16px; overflow: hidden; background: var(--paper-2); box-shadow: var(--shadow-soft); }
.tile img { width: 100%; height: 100%; object-fit: cover; transition: transform .3s; }
.tile:hover img { transform: scale(1.04); }
.tile .badge { position: absolute; bottom: 8px; left: 8px; background: rgba(31,58,77,.72); color: #fff; font-size: .7rem; padding: 3px 9px; border-radius: 999px; }
.tile .who { position: absolute; top: 8px; left: 8px; right: 8px; color: #fff; font-size: .72rem; text-shadow: 0 1px 3px rgba(0,0,0,.6); }

/* ---------- Upload progress ---------- */
.upload-list { margin: 18px 0; }
.upload-row { display: flex; align-items: center; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--line); font-size: .9rem; }
.progress { flex: 1; height: 8px; background: var(--line); border-radius: 999px; overflow: hidden; }
.progress > span { display: block; height: 100%; width: 0; background: var(--gold); transition: width .2s; }

/* ---------- QR / links ---------- */
.qr-wrap { text-align: center; margin: 22px 0; }
.qr-wrap canvas, .qr-wrap img { display: block; margin: 0 auto; border: 8px solid #fff; border-radius: 12px; box-shadow: var(--shadow-soft); }
.linkbox { display: flex; gap: 8px; margin-top: 8px; }
.linkbox input { font-size: 14px; }
.linkbox input.is-private { opacity: .55; text-decoration: line-through; }

/* ---------- Privacy toggles ---------- */
.privacy-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-top: 10px; padding: 12px 0; border-bottom: 1px solid var(--line); }
.privacy-title { font-weight: 600; color: var(--ink); font-size: .95rem; }
.privacy-sub { color: var(--muted); font-size: .82rem; margin-top: 2px; }
.switch { position: relative; display: inline-block; width: 52px; height: 30px; flex: 0 0 auto; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch .slider { position: absolute; inset: 0; cursor: pointer; background: #c7d6d2; border-radius: 999px; transition: background .18s; }
.switch .slider::before { content: ""; position: absolute; height: 24px; width: 24px; left: 3px; top: 3px; background: #fff; border-radius: 50%; transition: transform .18s; box-shadow: 0 1px 3px rgba(0,0,0,.3); }
.switch input:checked + .slider { background: var(--gold); }
.switch input:checked + .slider::before { transform: translateX(22px); }
.switch input:focus-visible + .slider { outline: 2px solid var(--gold); outline-offset: 2px; }
.btn-disabled { opacity: .45; pointer-events: none; }

/* ---------- Account metrics ---------- */
.metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin: 8px 0 22px; }
.metric { background: var(--paper-2); border: 1px solid var(--line); border-radius: 16px; padding: 18px 12px; text-align: center; }
.metric-val { font-family: var(--serif); font-size: 2rem; font-weight: 500; color: var(--ink); line-height: 1; }
.metric-label { font-size: .8rem; color: var(--muted); margin-top: 6px; }
.metric-sub { font-size: .72rem; color: var(--gold); text-transform: uppercase; letter-spacing: .1em; margin-top: 2px; }
@media (max-width: 560px) { .metrics { grid-template-columns: repeat(3, 1fr); gap: 8px; } .metric { padding: 12px 6px; } .metric-val { font-size: 1.5rem; } }

/* ---------- Gallery tile controls ---------- */
.tile .dl-btn {
  position: absolute; bottom: 8px; right: 8px; z-index: 2;
  width: 36px; height: 36px; min-width: 36px; border-radius: 50%;
  border: none; cursor: pointer; background: rgba(31,58,77,.82); color: #fff;
  font-size: 1.1rem; line-height: 1; display: grid; place-items: center;
  box-shadow: 0 2px 8px rgba(0,0,0,.3); transition: background .15s, transform .08s;
}
.tile .dl-btn:hover { background: var(--ink); }
.tile .dl-btn:active { transform: scale(.94); }

.tile .play-btn {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  z-index: 2; width: 56px; height: 56px; border-radius: 50%; border: none;
  cursor: pointer; background: rgba(31,58,77,.78); color: #fff;
  display: grid; place-items: center; padding: 0 0 0 3px;
  box-shadow: 0 4px 16px rgba(0,0,0,.35); transition: background .15s, transform .1s;
}
.tile .play-btn:hover { background: var(--ink); transform: translate(-50%, -50%) scale(1.06); }
.tile .play-btn:active { transform: translate(-50%, -50%) scale(.96); }
.tile .play-btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
.tile .play-btn svg { display: block; }

.tile .hide-btn, .tile .del-btn, .tile .fav-btn {
  position: absolute; top: 8px; z-index: 3;
  width: 34px; height: 34px; border-radius: 50%; border: none; cursor: pointer;
  background: rgba(31,58,77,.82); color: #fff; display: grid; place-items: center;
  box-shadow: 0 2px 8px rgba(0,0,0,.3); transition: background .15s, transform .08s;
}
.tile .hide-btn { right: 8px; }
.tile .del-btn { right: 48px; }
.tile .fav-btn { right: 88px; }
.tile .hide-btn:hover, .tile .fav-btn:hover { background: var(--ink); }
.tile .del-btn:hover { background: var(--err); }
.tile .hide-btn:active, .tile .del-btn:active, .tile .fav-btn:active { transform: scale(.94); }
.tile .hide-btn.hidden-on { background: var(--gold); color: var(--ink); }
.tile .fav-btn.fav-on { background: var(--gold); color: var(--ink); }
.tile .hide-btn svg, .tile .del-btn svg, .tile .fav-btn svg { display: block; }
.tile.is-hidden img { opacity: .45; filter: grayscale(.3); }
.tile.is-hidden::after {
  content: "Hidden from guests"; position: absolute; left: 8px; bottom: 8px; z-index: 2;
  background: rgba(31,58,77,.82); color: #fff; font-size: .68rem; padding: 3px 8px; border-radius: 999px;
}

/* ---------- Gallery filter + layout controls ---------- */
.head-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Filter dropdown: All / Photos / Videos / Favourites / Hidden, all checked by
   default. An item shows if it matches ANY checked box (its kind, or a
   checked status flag it has) -- so checking only "Favourites" isolates to
   favourites, and with everything checked every item matches via its
   Photos-or-Videos kind, which is how "all" naturally means "everything". */
.filter-picker { position: relative; }
.filter-btn {
  display: inline-flex; align-items: center; gap: 8px; height: 40px; min-height: 40px;
  padding: 0 16px; border-radius: 999px; border: none; cursor: pointer;
  background: var(--paper-2); box-shadow: var(--etch); color: var(--ink);
  font-size: .85rem; font-weight: 600; font-family: inherit;
}
.filter-btn svg { transition: transform .15s; flex-shrink: 0; }
.filter-picker.open .filter-btn svg { transform: rotate(180deg); }
.filter-menu {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 5; min-width: 190px;
  background: #fff; border-radius: 14px; padding: 8px; box-shadow: 0 14px 32px rgba(31,58,77,.18);
  border: 1px solid var(--line);
}
.filter-option {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 8px;
  font-size: .9rem; color: var(--ink); cursor: pointer;
}
.filter-option:hover { background: var(--paper-2); }
.filter-option input { width: 16px; height: 16px; accent-color: var(--forest); cursor: pointer; }
.filter-all { font-weight: 600; }
.filter-menu hr { border: none; border-top: 1px solid var(--line); margin: 4px 6px; }

/* ---------- Gallery layout picker (1/2/3/4 across) ---------- */
.layout-picker { display: inline-flex; align-items: center; gap: 6px; padding: 6px; border-radius: 999px; background: var(--paper-2); box-shadow: var(--etch); }
.layout-picker .layout-label { font-size: .8rem; color: var(--muted); margin: 0 6px; }
.layout-btn {
  width: 28px; height: 28px; min-height: 28px; border-radius: 999px; cursor: pointer;
  border: none; background: transparent; color: var(--ink-soft);
  font-size: .82rem; font-weight: 600; display: grid; place-items: center;
  transition: background .12s, color .12s;
}
.layout-btn:hover { color: var(--ink); }
.layout-btn.active { background: var(--forest); color: #fff; }
.gallery-grid.grid-1 { grid-template-columns: 1fr; }
.gallery-grid.grid-2 { grid-template-columns: repeat(2, 1fr); }
.gallery-grid.grid-3 { grid-template-columns: repeat(3, 1fr); }
.gallery-grid.grid-4 { grid-template-columns: repeat(4, 1fr); }

/* ---------- Dashboard head actions + details rows ---------- */
.dash-head-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.detail-row { display: flex; align-items: center; gap: 10px; margin-top: 6px; }
.detail-value { font-weight: 500; color: var(--ink); }
.icon-btn {
  width: 34px; height: 34px; min-height: 34px; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--line); background: #fff; color: var(--ink);
  display: inline-grid; place-items: center; vertical-align: middle;
  transition: border-color .12s, color .12s;
}
.icon-btn:hover { border-color: var(--gold); color: var(--gold); }
.icon-btn svg { display: block; }
.icon-btn.labeled {
  display: inline-flex; align-items: center;
  width: auto; height: auto; min-height: 38px; padding: 8px 14px; border-radius: 999px;
  gap: 8px; font-size: .88rem; font-weight: 600; font-family: inherit;
}

/* ---------- FAQ / Help ---------- */
.faq-search input {
  width: 100%; font-size: 16px; padding: 16px 20px; border: 1px solid rgba(31,58,77,0.08);
  border-radius: 999px; background: #fff; box-shadow: var(--etch);
}
.faq-search input:focus { outline: 2px solid var(--gold); outline-offset: 1px; }
.faq-list { margin-top: 16px; }
.faq-item { border-top: 1px solid rgba(31,58,77,.14); padding: 8px 0; }
.faq-item:last-child { border-bottom: 1px solid rgba(31,58,77,.14); }
.faq-item summary { cursor: pointer; padding: 14px 4px; font-weight: 500; color: var(--ink); list-style: none; display: flex; justify-content: space-between; align-items: center; gap: 16px; font-size: 1.05rem; }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: "+"; color: var(--sky); font-size: 1.5rem; flex-shrink: 0; }
.faq-item[open] summary::after { content: "–"; }
.faq-answer { padding: 0 4px 16px 4px; color: var(--ink-soft); font-size: .98rem; line-height: 1.65; max-width: 64ch; }
.faq-answer a { color: var(--ink); text-decoration: underline; }

/* ---------- Public FAQ (index): list + sidebar, not list-then-links ---------- */
/* A new wrapper around the existing .faq-list (untouched, still shared with
   faq.html) -- main content left, a "more detail" links card right, so the
   guide links use the width beside the accordion instead of a trailing
   sentence stacked under it. */
.faq-layout { margin-top: 6px; }
@media (min-width: 900px) {
  .faq-layout { display: grid; grid-template-columns: 1fr 250px; gap: 48px; align-items: start; }
}
.faq-aside { margin-top: 28px; }
@media (min-width: 900px) { .faq-aside { margin-top: 4px; } }
.faq-aside h3 { font-size: .85rem; text-transform: uppercase; letter-spacing: .07em; color: var(--meadow-deep); font-weight: 600; margin: 0 0 12px; }
.faq-aside ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.faq-aside a { color: var(--ink); font-size: .92rem; text-decoration: underline; }

/* ---------- Dense gallery layouts: declutter tiles ---------- */
.gallery-grid.grid-3 .tile .dl-btn,
.gallery-grid.grid-4 .tile .dl-btn,
.gallery-grid.grid-3 .tile .hide-btn,
.gallery-grid.grid-4 .tile .hide-btn,
.gallery-grid.grid-3 .tile .del-btn,
.gallery-grid.grid-4 .tile .del-btn,
.gallery-grid.grid-3 .tile .fav-btn,
.gallery-grid.grid-4 .tile .fav-btn,
.gallery-grid.grid-3 .tile .who,
.gallery-grid.grid-4 .tile .who,
.gallery-grid.grid-3 .tile .badge,
.gallery-grid.grid-4 .tile .badge { display: none; }
.gallery-grid.grid-3 .tile.is-hidden::after,
.gallery-grid.grid-4 .tile.is-hidden::after { content: none; }
.gallery-grid.grid-3 .tile .play-btn,
.gallery-grid.grid-4 .tile .play-btn { width: 40px; height: 40px; box-shadow: 0 2px 10px rgba(0,0,0,.3); }
.gallery-grid.grid-3 .tile .play-btn svg,
.gallery-grid.grid-4 .tile .play-btn svg { width: 18px; height: 18px; }

/* Prevent background scroll while the player is open. */
body.no-scroll { overflow: hidden; }

/* Fullscreen-style overlay viewer: photos and videos share this box, only
   one of <video>/<img> is visible at a time (js/gallery.js, js/my-gallery.js,
   js/account.js). */
.video-player { position: fixed; inset: 0; z-index: 10000; background: rgba(10,13,22,.92); display: grid; place-items: center; padding: 16px; }
.video-player[hidden] { display: none !important; }
.video-box { position: relative; width: 100%; max-width: 1100px; }
.video-box video, .video-box img { width: 100%; max-height: 86vh; max-height: 86svh; display: block; background: #000; border-radius: 10px; box-shadow: 0 20px 60px rgba(0,0,0,.5); }
.video-box img { object-fit: contain; }
.video-box:fullscreen { max-width: none; width: 100%; height: 100%; display: grid; place-items: center; }
.video-box:fullscreen video { max-height: 100%; height: 100%; border-radius: 0; }
.video-close { position: absolute; top: -14px; right: -6px; z-index: 2; width: 44px; height: 44px; border-radius: 50%; border: none; cursor: pointer; background: rgba(255,255,255,.92); color: var(--ink); font-size: 1.6rem; line-height: 1; display: grid; place-items: center; box-shadow: 0 3px 12px rgba(0,0,0,.4); }
.video-close:hover { background: #fff; }
.video-close:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
@media (max-width: 560px) { .video-close { top: 6px; right: 6px; } }

/* Prev/next: same round-button treatment as .video-close, either side of the
   box, vertically centred. */
.video-nav { position: absolute; top: 50%; transform: translateY(-50%); z-index: 2; width: 44px; height: 44px; border-radius: 50%; border: none; cursor: pointer; background: rgba(255,255,255,.92); color: var(--ink); font-size: 1.6rem; line-height: 1; display: grid; place-items: center; box-shadow: 0 3px 12px rgba(0,0,0,.4); }
.video-nav:hover { background: #fff; }
.video-nav:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.video-nav[hidden] { display: none !important; }
.video-prev { left: -14px; }
.video-next { right: -14px; }
@media (max-width: 560px) { .video-prev { left: 6px; } .video-next { right: 6px; } }

/* "Are you sure?" confirm modal (replaces window.confirm()) */
.confirm-overlay { position: fixed; inset: 0; z-index: 10001; background: rgba(20,30,38,.55); display: grid; place-items: center; padding: 16px; }
.confirm-overlay[hidden] { display: none !important; }
.confirm-box { width: 100%; max-width: 400px; background: var(--paper-3); border-radius: var(--radius); box-shadow: var(--shadow); padding: 26px 26px 22px; }
.confirm-box h3 { margin: 0 0 8px; font-family: var(--serif); font-size: 1.35rem; font-weight: 500; color: var(--ink); }
.confirm-box .note { margin: 0 0 20px; }
.confirm-checkbox {
  display: flex; align-items: flex-start; gap: 10px; margin: -6px 0 20px;
  font-size: .9rem; color: var(--ink); cursor: pointer;
}
.confirm-checkbox input { width: 18px; height: 18px; min-width: 18px; margin-top: 1px; accent-color: var(--danger); cursor: pointer; }
.confirm-type { display: block; margin: -6px 0 20px; }
.confirm-type span { display: block; font-size: .85rem; font-weight: 600; color: var(--ink); margin-bottom: 6px; }
.confirm-type input { width: 100%; }
.confirm-actions { display: flex; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }
.confirm-actions .btn { padding: 10px 20px; min-height: 42px; }

/* ---------- Password field with show/hide icon toggle ---------- */
.pw-wrap { position: relative; }
.pw-wrap input { padding-right: 48px; }
.pw-toggle { position: absolute; top: 50%; right: 6px; transform: translateY(-50%); border: none; background: transparent; cursor: pointer; color: var(--muted); width: 40px; height: 40px; min-height: 40px; padding: 0; display: grid; place-items: center; border-radius: 10px; transition: color .15s, background .15s; }
.pw-toggle:hover { color: var(--ink); background: rgba(31,58,77,.06); }
.pw-toggle:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.pw-toggle svg { display: block; }
.pw-toggle .eye-off { display: none; }
.pw-toggle.revealed .eye { display: none; }
.pw-toggle.revealed .eye-off { display: block; }
#pwMsg:empty { display: none; }

/* ---------- Footer ---------- */
footer.site { text-align: center; color: var(--ink-soft); font-size: .85rem; padding: 8vw 6vw 6vw; background: var(--paper); margin-top: auto; }
footer.site .brand, footer.site .logo { font-family: var(--serif); font-style: italic; color: var(--forest); font-size: 1.3rem; margin-bottom: 8px; justify-content: center; display: inline-flex; }
footer.site .brand .dot, footer.site .logo b, footer.site .logo .dot { color: var(--sky); font-style: normal; }
footer.site .tag-line { font-style: italic; font-family: var(--serif); font-size: 1.05rem; color: var(--ink); margin-bottom: 10px; }

/* Guide-page links in the homepage footer. Deliberately a separate class from
   .footer-legal (not reused): js/legal.js checks for .footer-legal to decide
   whether it's already injected its Terms/Privacy/DPA block, so reusing that
   class here would make it think its own block already exists and skip it. */
.footer-guides { max-width: 720px; margin: 18px auto 0; padding-top: 16px; border-top: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: 6px 18px; justify-content: center; }
.footer-guides a { color: var(--ink); text-decoration: underline; font-size: .85rem; }

/* .footer-grid (index only): brand block + guide links side by side on a
   wide screen instead of one centred stack -- a plain two-column footer,
   not scoped onto the shared bare footer.site that other pages get from
   js/legal.js, so this has no effect on any page but index.html. */
@media (min-width: 760px) {
  .footer-grid { display: flex; justify-content: space-between; align-items: flex-start; gap: 40px; text-align: left; }
  .footer-grid .footer-brand { max-width: 340px; }
  .footer-grid .footer-brand .logo { justify-content: flex-start; }
  .footer-grid .footer-guides {
    margin: 0; padding-top: 0; border-top: 0; flex-direction: column; align-items: flex-start; gap: 10px;
  }
}

.footer-legal { max-width: 720px; margin: 18px auto 0; padding-top: 16px; border-top: 1px solid var(--line); }
.footer-legal .legal-links { display: flex; flex-wrap: wrap; gap: 6px 18px; justify-content: center; margin-bottom: 10px; }
.footer-legal .legal-links a, .footer-legal .legal-links button {
  color: var(--ink); text-decoration: underline; font-size: .85rem; background: none; border: 0; padding: 0; cursor: pointer; font: inherit;
}
.footer-legal .legal-business { font-size: .8rem; color: var(--muted); line-height: 1.5; }

/* ---------- Legal document pages ---------- */
.legal { max-width: 760px; margin: 0 auto; padding: 48px 6vw 72px; }
@media (min-width: 820px) { .legal { padding: 48px 22px 72px; } }
.legal h1 { font-family: var(--serif); font-weight: 500; font-size: 2.2rem; color: var(--ink); margin: 0 0 6px; }
.legal .updated { color: var(--muted); font-size: .9rem; margin: 0 0 8px; }
.legal .legal-toc { margin: 20px 0 8px; font-size: .9rem; }
.legal h2 { font-family: var(--serif); font-weight: 500; font-size: 1.5rem; color: var(--ink); margin: 34px 0 10px; }
.legal h3 { font-size: 1.05rem; color: var(--ink); margin: 22px 0 6px; }
.legal p, .legal li { color: var(--body); line-height: 1.65; }
.legal ul, .legal ol { padding-left: 22px; }
.legal li { margin: 4px 0; }
.legal a { color: var(--ink); text-decoration: underline; }
.legal hr { border: 0; border-top: 1px solid var(--line); margin: 28px 0; }
.legal table { width: 100%; border-collapse: collapse; margin: 14px 0; font-size: .93rem; }
.legal th, .legal td { text-align: left; padding: 9px 12px; border: 1px solid var(--line); vertical-align: top; }
.legal th { background: var(--paper-2); font-weight: 600; }
#acctTable, #aTable { width: 100%; border-collapse: collapse; margin: 0; font-size: .9rem; }
#acctTable th, #acctTable td, #aTable th, #aTable td { text-align: left; padding: 8px 12px; border: 1px solid var(--line); white-space: nowrap; }
#acctTable th, #aTable th { background: var(--paper-2); font-weight: 600; }
#aTable input[type="number"], #aTable select { padding: 4px 6px; min-width: 60px; }
.legal .todo-fill, .footer-legal .todo-fill { background: #ffe9a8; color: #7a5900; padding: 0 4px; border-radius: 3px; font-weight: 600; }

/* ============================================================
   COOKIE CONSENT (ail- prefixed, injected by js/legal.js)
   ============================================================ */
:root{
  --ail-cream:#F7FAF7; --ail-ink:#1F3A4D; --ail-rose:#3E7CA6;
  --ail-rose-dark:#2C5D80; --ail-line:#DCE7E2; --ail-muted:#5B7480;
}
#ail-consent-banner, #ail-consent-modal{ font-family: var(--sans); box-sizing: border-box; }
#ail-consent-banner *, #ail-consent-modal *{ box-sizing: border-box; }
#ail-consent-banner{
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 9999;
  background: var(--ail-cream); border-top: 1px solid var(--ail-line);
  box-shadow: 0 -6px 24px rgba(31,58,77,0.08); padding: 20px; display: none;
}
#ail-consent-banner.ail-visible{ display: block; }
.ail-banner-inner{ max-width: 780px; margin: 0 auto; display: flex; flex-wrap: wrap; align-items: center; gap: 16px; }
.ail-banner-text{ flex: 1 1 360px; color: var(--ail-ink); font-size: 14px; line-height: 1.5; }
.ail-banner-text a{ color: var(--ail-rose-dark); text-decoration: underline; }
.ail-banner-actions{ flex: 0 0 auto; display: flex; gap: 10px; flex-wrap: wrap; }
.ail-btn{ border: 1px solid transparent; padding: 10px 16px; border-radius: 7px; font-size: 13.5px; font-weight: 600; cursor: pointer; white-space: nowrap; }
.ail-btn-primary{ background: var(--ail-rose); color: #fff; }
.ail-btn-primary:hover{ background: var(--ail-rose-dark); }
.ail-btn-secondary{ background: transparent; color: var(--ail-ink); border-color: var(--ail-line); }
.ail-btn-secondary:hover{ border-color: var(--ail-muted); }
.ail-btn-text{ background: transparent; color: var(--ail-muted); text-decoration: underline; padding: 10px 4px; }
#ail-consent-modal{ position: fixed; inset: 0; z-index: 10000; display: none; align-items: center; justify-content: center; background: rgba(31,58,77,0.45); padding: 20px; }
#ail-consent-modal.ail-visible{ display: flex; }
.ail-modal-box{ background: #fff; border-radius: 12px; max-width: 520px; width: 100%; max-height: 86vh; overflow-y: auto; padding: 28px; }
.ail-modal-box h2{ margin: 0 0 6px; font-size: 19px; color: var(--ail-ink); }
.ail-modal-box > p{ margin: 0 0 20px; font-size: 13.5px; color: var(--ail-muted); line-height: 1.5; }
.ail-category{ border-top: 1px solid var(--ail-line); padding: 16px 0; display: flex; justify-content: space-between; gap: 16px; }
.ail-category:last-of-type{ border-bottom: 1px solid var(--ail-line); margin-bottom: 20px; }
.ail-category-info h3{ margin: 0 0 4px; font-size: 14.5px; color: var(--ail-ink); }
.ail-category-info p{ margin: 0; font-size: 13px; color: var(--ail-muted); line-height: 1.45; }
.ail-toggle{ flex: 0 0 auto; position: relative; width: 42px; height: 24px; }
.ail-toggle input{ opacity: 0; width: 0; height: 0; }
.ail-toggle-track{ position: absolute; inset: 0; background: #D7E2DD; border-radius: 999px; transition: background .15s ease; cursor: pointer; }
.ail-toggle-track::before{ content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; background: #fff; border-radius: 50%; transition: transform .15s ease; box-shadow: 0 1px 2px rgba(0,0,0,0.25); }
.ail-toggle input:checked + .ail-toggle-track{ background: var(--ail-rose); }
.ail-toggle input:checked + .ail-toggle-track::before{ transform: translateX(18px); }
.ail-toggle input:disabled + .ail-toggle-track{ opacity: .6; cursor: not-allowed; }
.ail-modal-actions{ display: flex; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }
@media (prefers-reduced-motion: reduce){ .ail-toggle-track, .ail-toggle-track::before{ transition: none; } }
@media (max-width: 560px){
  #ail-consent-banner{ padding: 16px; max-height: 85vh; overflow-y: auto; }
  .ail-banner-inner{ flex-direction: column; align-items: stretch; gap: 14px; }
  .ail-banner-text{ flex: 1 1 auto; font-size: 13.5px; }
  .ail-banner-actions{ flex-direction: column; align-items: stretch; gap: 8px; }
  .ail-banner-actions .ail-btn{ width: 100%; white-space: normal; padding: 12px 16px; }
  #ail-consent-modal{ padding: 12px; }
  .ail-modal-box{ padding: 22px 18px; max-height: 90vh; }
  .ail-modal-actions{ flex-direction: column; align-items: stretch; }
  .ail-modal-actions .ail-btn{ width: 100%; }
}

/* ---------- Short-viewport laptops (e.g. 1366x768) ---------- */
/* A 1366-1920px-wide laptop isn't "mobile" by width, but it's often only
   700-800px tall, and this whole file used to be tuned against a taller
   reference screen with a full-viewport hero, which is why a 0.75 zoom was
   needed here to make everything fit. The hero is now height-capped
   (clamp(460px, 58svh, 640px), not min-height:100svh) and every section's
   padding/type scale is smaller across the board, so there's substantially
   less to compensate for. Kept as a smaller safety-net zoom rather than
   removed outright -- still scoped by height + a width floor so it never
   touches true mobile (own rules already) or a tall desktop monitor (kept
   at 100%). `zoom` (not transform: scale) is what actually reflows layout
   the way browser zoom does, so vw/vh and everything else stays consistent
   instead of leaving a scaled-but-not-reflowed gap. */
@media (max-height: 820px) and (min-width: 761px) {
  html { zoom: 0.9; }
}

/* ---------- Responsive ---------- */
@media (max-width: 1080px) {
  .pricing { grid-template-columns: repeat(2, 1fr); gap: 16px; }
  .card .blurb { min-height: 0; }
}
@media (max-width: 860px) {
  .steps { grid-template-columns: 1fr; gap: 26px; }
  .duration-grid { grid-template-columns: repeat(2, 1fr); }
  .field-row { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
  /* Same reasoning as the #appnav burger above: not everyone realises a
     row can be swiped, so below 760px the links move behind an explicit
     tap target instead of relying on the always-visible horizontal scroll.
     Opening it reveals the same links, stacked, in a floating card under
     the bar -- the same "burger reveals a rounded dark card" pattern used
     sitewide by #appnav. */
  .navburger { display: flex; }
  header.site nav { display: none; }
  #siteNav.nav-open nav {
    display: flex; flex-direction: column; align-items: stretch;
    overflow-x: visible; width: min(80vw, 280px); gap: 2px;
    position: absolute; top: calc(100% + 8px); right: 0;
    padding: 6px; border-radius: 20px; background: rgba(20,26,20,0.94);
    box-shadow: 0 12px 30px rgba(0,0,0,.35);
  }
  /* The dropdown card keeps its original colours (not the flat pill fill
     the desktop bar got) -- plain ink-soft text straight on the dark card. */
  #siteNav.nav-open nav a {
    width: 100%; text-align: center;
    background-color: transparent; color: var(--ink-soft);
  }
  #siteNav.nav-open nav a:hover { color: var(--ink); }
}
@media (max-width: 560px) {
  .pricing { grid-template-columns: 1fr; }
}

/* Mobile centering pass for marketing sections */
@media (max-width: 860px) {
  .section-head, .section-head.compact { margin-left: auto; margin-right: auto; text-align: center; }
  .section-head p { margin-left: auto; margin-right: auto; }
  .steps { justify-items: center; }
  .step { max-width: 420px; margin: 0 auto; }
  .pricing .card { text-align: center; max-width: 440px; margin: 0 auto; width: 100%; }
  .pricing .card ul { display: inline-block; text-align: left; }
  .pricing .card .tag { left: 50%; }
  .container > .note, section .note { margin-left: auto; margin-right: auto; }
}

/* ================================================================
   Account dashboard
   ================================================================ */
/* width:100% (not just max-width/margin:auto) matters here: `body` is a column
   flex container (for the sticky footer), and `.dash` is a direct flex item.
   Auto cross-axis margins (margin:0 auto) disable flexbox's default stretch
   alignment, so a width:auto item instead gets shrink-to-fit-sized from its
   content's preferred width. .preview-row (the dashboard's horizontally
   scrolling photo strip) never wraps and has a min-width floor per tile, so
   its unclamped preferred width (~1300px for 9 tiles) was winning that
   calculation and blowing .dash out to its 1120px max-width on EVERY
   viewport, well past the actual (narrower) screen — real horizontal page
   overflow, not just an internal scroll of the strip. An explicit width:100%
   makes the cross size definite again so it fills (and is capped/centred by
   max-width+margin as intended) instead of shrink-to-fitting. */
.dash { width: 100%; max-width: 1120px; margin: 0 auto; padding: 24px 6vw 60px; }
@media (min-width: 1120px) { .dash { padding: 24px 22px 60px; } }
/* Same large-screen widening as .container/.wrap above -- kept as a separate
   rule here (not merged into that shared selector) so it comes after this
   base .dash rule in source order and actually wins the cascade tie. */
@media (min-width: 1440px) { .dash { max-width: 1320px; } }
@media (min-width: 1900px) { .dash { max-width: 1560px; } }
@media (min-width: 2400px) { .dash { max-width: 1760px; } }

.dash-hero {
  position: relative; overflow: hidden;
  background: var(--forest); color: var(--paper); border-radius: var(--radius-lg);
  padding: 40px 40px; box-shadow: var(--shadow); margin: 14px 0 26px;
  display: flex; align-items: center; justify-content: space-between; gap: 24px; flex-wrap: wrap;
}
.dash-hero .kicker { color: var(--blush); display: block; margin-bottom: 8px; }
.dash-hero h1 { color: #fff; font-size: clamp(1.9rem, 4vw, 2.9rem); font-weight: 400; margin: 0; line-height: 1.05; }
.dash-hero .hero-sub { color: #C7D9DC; margin: 10px 0 0; font-size: 1.02rem; }
.dash-hero .hero-count { text-align: center; flex: 0 0 auto; position: relative; z-index: 2; }
.dash-hero .hero-count .n { font-family: var(--serif); font-size: 3.4rem; font-weight: 500; color: var(--blush); line-height: 1; display: block; }
.dash-hero .hero-count .l { color: #C2D6D6; font-size: .8rem; text-transform: uppercase; letter-spacing: .14em; margin-top: 6px; }
.dash-hero > div { position: relative; z-index: 2; }

.dash-card {
  background: #fff; border: 1px solid rgba(31,58,77,0.05); border-radius: var(--radius);
  padding: 28px 30px; box-shadow: none; margin: 0 0 20px;
}
.dash-card > h3 { margin: 0 0 6px; font-family: var(--serif); font-size: 1.55rem; font-weight: 500; color: var(--ink); }
.dash-card .card-sub { color: var(--muted); font-size: .92rem; margin: 0 0 16px; }
.dash-section-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }

details.dash-card { padding: 0; }
details.dash-card > summary {
  list-style: none; cursor: pointer; padding: 22px 30px; display: flex; align-items: center;
  justify-content: space-between; gap: 12px; font-family: var(--serif); font-size: 1.4rem; color: var(--ink); font-weight: 500;
}
details.dash-card > summary::-webkit-details-marker { display: none; }
details.dash-card > summary::after { content: "＋"; font-family: var(--sans); color: var(--sky); font-size: 1.4rem; transition: transform .2s; }
details.dash-card[open] > summary::after { content: "－"; }
details.dash-card > .details-body { padding: 0 30px 28px; }

/* Dashboard page only: share/privacy are always fully open on desktop with
   no collapse affordance (JS in account.js keeps them open; this hides the
   now-nonfunctional +/- marker and click cursor). Mobile is untouched. */
@media (min-width: 641px) {
  #dashPage #shareCard > summary, #dashPage #privacyCard > summary { cursor: default; }
  #dashPage #shareCard > summary::after, #dashPage #privacyCard > summary::after { content: none; }
}

.album-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 18px; }
.album-tab { border: 1.5px solid var(--line); background: #fff; color: var(--ink); border-radius: 999px; padding: 7px 16px; font-family: var(--sans); font-size: .88rem; font-weight: 600; cursor: pointer; min-height: 38px; transition: all .12s; display: inline-flex; align-items: center; gap: 7px; }
.album-tab:hover { border-color: var(--gold); }
.album-tab.active { background: var(--ink); color: #fff; border-color: var(--ink); }
.album-tab .cnt { font-weight: 500; opacity: .7; font-size: .8rem; }
.album-tab.add { border-style: dashed; color: var(--muted); }

.select-bar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; background: var(--paper-2); border: 1px solid var(--line); border-radius: 12px; padding: 12px 16px; margin: 0 0 16px; }
.select-bar .count { font-weight: 600; color: var(--ink); }
.select-bar .spacer { flex: 1; }
.select-bar select { width: auto; min-width: 160px; padding: 9px 12px; }
.select-bar .btn { padding: 9px 18px; min-height: 40px; font-size: .9rem; }

.tile.selectable { cursor: pointer; }
.tile.selected { outline: 3px solid var(--gold); outline-offset: -3px; }
.tile .sel-check { position: absolute; top: 8px; left: 8px; z-index: 3; width: 26px; height: 26px; border-radius: 50%; background: rgba(255,255,255,.85); border: 1.5px solid var(--ink); display: grid; place-items: center; color: var(--ink); font-size: .9rem; font-weight: 700; }
.tile.selected .sel-check { background: var(--gold); border-color: var(--gold); color: #fff; }
.tile .album-pill { position: absolute; bottom: 8px; left: 8px; background: rgba(31,58,77,.72); color: #fff; font-size: .68rem; padding: 3px 9px; border-radius: 999px; max-width: 70%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.empty-state { text-align: center; color: var(--muted); padding: 30px 10px; }
.empty-state .big { font-size: 2.2rem; margin-bottom: 8px; }

@media (max-width: 640px) {
  .dash { padding: 0 0 40px; }
  .dash > .flow { padding-left: 18px; padding-right: 18px; }
  .dash-hero { flex-direction: column; text-align: center; align-items: center; padding: 32px 22px 28px; margin: 0 0 6px; border-radius: 0; }
  .dash-hero .hero-count .n { font-size: 2.8rem; }
  .dash-card, details.dash-card { background: #fff; border: 0; border-radius: 0; box-shadow: none; margin: 0; padding: 22px 18px; border-bottom: 1px solid var(--line); }
  .dash-card.billing-card { border-top: 3px solid var(--gold); }
  details.dash-card > summary { padding: 18px; }
  details.dash-card > .details-body { padding: 0 18px 22px; }
  .gallery-grid.grid-3x3 { gap: 6px; margin: 16px 0 4px; }
  .gallery-grid.grid-3x3 .tile { border-radius: 10px; box-shadow: none; }
}

/* ============================================================
   Logged-in navigation — compact app-bar: brand left, nav pill right,
   one row, every width. This only ever sits on the dashboard-style
   page-banner (a utility header, not a marketing hero), so it stays
   compact permanently rather than just on short viewports.
   ============================================================ */
.appnav {
  position: relative; z-index: 50;
  display: flex; flex-direction: row; justify-content: space-between; align-items: center;
  padding: 12px 5vw; background: transparent; border: 0; gap: 12px;
}
.appnav-brand {
  font-family: var(--serif); font-style: italic; font-weight: 400;
  font-size: clamp(1.3rem, 2.2vw, 1.6rem); color: #fff; text-decoration: none; margin: 0;
  flex-shrink: 0;
}
.appnav-brand .dot { color: var(--blush); font-style: normal; }
/* When the appnav sits on a cream page (no dark banner behind), darken it. */
.appnav.on-light .appnav-brand { color: var(--forest); }

.appnav-burger {
  order: 1; display: none; width: 44px; height: 44px; margin: 0;
  border: none; border-radius: 999px; background: rgba(255,255,255,0.16);
  cursor: pointer; align-items: center; justify-content: center; padding: 0;
}
.appnav-burger:hover { background: rgba(255,255,255,0.24); }
.appnav-burger:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.appnav-burger .bars { display: inline-flex; flex-direction: column; gap: 4px; width: 20px; }
.appnav-burger .bars span { display: block; height: 2px; width: 100%; background: #fff; border-radius: 2px; transition: transform .2s ease, opacity .2s ease; }
.appnav.nav-open .appnav-burger .bars span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.appnav.nav-open .appnav-burger .bars span:nth-child(2) { opacity: 0; }
.appnav.nav-open .appnav-burger .bars span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* The collapse is the pill: links + logout inside an etched-dark rounded bar. */
.appnav-collapse {
  order: 1; display: flex; align-items: center; gap: 4px;
  padding: 6px 8px; border-radius: 999px;
  background: rgba(0,0,0,0.10);
  box-shadow: inset 0 2px 6px rgba(0,0,0,0.3), inset 0 -1px 0 rgba(255,255,255,0.10);
  max-width: 100%; flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
}
.appnav-collapse::-webkit-scrollbar { display: none; }
.appnav-links { display: flex; gap: 2px; }
.appnav-link {
  color: #fff; opacity: .82; text-decoration: none; font-weight: 500; font-size: .9rem;
  padding: 10px 16px; border-radius: 999px; min-height: 40px; display: inline-flex; align-items: center; white-space: nowrap;
}
.appnav-link:hover { opacity: 1; }
.appnav-link.active { opacity: 1; font-weight: 600; background: rgba(255,255,255,0.16); }
.appnav-logout {
  color: #fff; opacity: .82; text-decoration: none; font-weight: 500; font-size: .9rem;
  padding: 10px 16px; border-radius: 999px; min-height: 40px; display: inline-flex; align-items: center; white-space: nowrap;
}
.appnav-logout:hover { opacity: 1; }

/* On-light variant (pages without a dark banner). */
.appnav.on-light .appnav-collapse { background: rgba(31,58,77,0.06); box-shadow: var(--etch); }
.appnav.on-light .appnav-link, .appnav.on-light .appnav-logout { color: var(--ink-soft); opacity: 1; }
.appnav.on-light .appnav-link.active { background: var(--forest); color: #fff; }
.appnav.on-light .appnav-burger { background: rgba(31,58,77,0.08); }
.appnav.on-light .appnav-burger .bars span { background: var(--forest); }

/* Below 720px: not everyone realises a pill can be swiped sideways, so
   swap the always-visible horizontal-scroll row for an explicit tap target.
   Same floating-card treatment as the marketing header.site nav (#siteNav)
   at its own mobile breakpoint -- one consistent "burger reveals a rounded
   dark card below the pill" pattern site-wide, regardless of on-light vs
   dark page theme (the card itself is always dark, like the marketing one,
   so link text stays forced-white here even on .on-light pages). */
@media (max-width: 720px) {
  .appnav-burger { display: flex; }
  .appnav-collapse { display: none; max-width: 92vw; }
  .appnav.nav-open .appnav-collapse {
    display: flex; flex-direction: column; align-items: stretch;
    overflow-x: visible; width: min(80vw, 280px); gap: 2px;
    position: absolute; top: calc(100% + 8px); right: 5vw;
    padding: 6px; border-radius: 20px; background: rgba(20,26,20,0.94);
    box-shadow: 0 12px 30px rgba(0,0,0,.35);
  }
  .appnav.nav-open .appnav-links { flex-direction: column; width: 100%; gap: 2px; }
  .appnav.nav-open .appnav-link, .appnav.nav-open .appnav-logout { width: 100%; justify-content: center; color: #fff; opacity: .82; }
  .appnav.nav-open .appnav-link:hover, .appnav.nav-open .appnav-logout:hover { opacity: 1; }
  .appnav.nav-open .appnav-link.active { opacity: 1; font-weight: 600; color: #fff; background: rgba(255,255,255,0.16); }
  .appnav-link, .appnav-logout { padding: 10px 14px; font-size: .86rem; }
}

/* ============================================================
   Account / settings page
   ============================================================ */
.plan-line { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.plan-name { font-family: var(--serif); font-size: 1.6rem; color: var(--ink); font-weight: 500; }
.plan-sub { color: var(--muted); font-size: .95rem; margin-top: 2px; }
.plan-badge { background: rgba(190,115,80,0.16); color: var(--meadow-deep); font-size: .8rem; font-weight: 600; padding: 6px 12px; border-radius: 999px; white-space: nowrap; }
.sub-head { font-family: var(--serif); font-size: 1.3rem; color: var(--ink); margin: 22px 0 4px; font-weight: 500; }
.upgrade-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; margin-top: 14px; }
.upgrade-opt { border: 1px solid var(--line); border-radius: 14px; padding: 16px; text-align: center; background: #fff; }
.uo-name { font-family: var(--serif); font-size: 1.25rem; color: var(--ink); font-weight: 500; }
.uo-price { color: var(--gold); font-weight: 600; margin: 6px 0 12px; }

.pay-table { display: flex; flex-direction: column; }
.pay-row { display: grid; grid-template-columns: 1fr auto auto; gap: 12px; align-items: center; padding: 12px 0; border-bottom: 1px solid rgba(31,58,77,.08); }
.pay-row:last-child { border-bottom: none; }
.pay-when { color: var(--muted); font-size: .88rem; }
.pay-what { color: var(--ink); font-weight: 500; }
.pay-what.failed { color: var(--err); }
.pay-amt { color: var(--ink); font-weight: 600; text-align: right; min-width: 64px; }

/* ---------- API keys table (account page) ---------- */
.key-table { display: flex; flex-direction: column; margin-top: 14px; }
.key-row { display: grid; grid-template-columns: 1fr auto; gap: 12px; align-items: center; padding: 12px 0; border-bottom: 1px solid rgba(31,58,77,.08); }
.key-row:last-child { border-bottom: none; }
.key-name { color: var(--ink); font-weight: 500; }
.key-meta { color: var(--muted); font-size: .82rem; margin-top: 2px; }
.key-actions { display: flex; gap: 8px; flex-shrink: 0; }
.key-actions .btn { min-height: 36px; padding: 8px 14px; font-size: .88rem; }
.key-new-row { align-items: center; }
.key-new-row input { min-height: 36px; padding: 8px 14px; font-size: .88rem; }
.key-new-row .btn { min-height: 36px; padding: 8px 14px; font-size: .88rem; flex-shrink: 0; white-space: nowrap; }
@media (max-width: 480px) {
  .key-row { grid-template-columns: 1fr; }
  .key-actions { justify-content: flex-start; }
}

/* Key-reveal modal: reuses .confirm-overlay/.confirm-box, adds the copy row. */
.key-reveal-box { max-width: 460px; }
.key-reveal-row { display: flex; gap: 8px; align-items: center; margin: 14px 0; }
.key-reveal-row input { flex: 1; font-family: ui-monospace, "SF Mono", Consolas, monospace; font-size: .85rem; }
.key-reveal-box .confirm-actions { justify-content: flex-end; }

.kv { display: flex; justify-content: space-between; gap: 16px; padding: 10px 0; border-bottom: 1px solid rgba(31,58,77,.08); }
.kv:last-child { border-bottom: none; }
.kv .k { color: var(--muted); } .kv .v { color: var(--ink); font-weight: 500; text-align: right; }
.btn-link { background: none; border: 0; padding: 0; margin: 0; font: inherit; color: var(--ink); text-decoration: underline; cursor: pointer; }
.btn-link:hover { color: var(--meadow-deep); }

.billing-card { border-top: 3px solid var(--gold); margin-top: 9vw; }
/* Dashboard only: this is the gap right after the share/privacy panel-row,
   not after a hero (unlike settings.html's billing-card) -- 9vw there was
   excessive. */
#dashPage .billing-card { margin-top: clamp(20px, 4vw, 48px); }

/* ============================================================
   Printable QR cards
   ============================================================ */
.style-tabs { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.style-tab { border: 1px solid var(--line); background: #fff; color: var(--ink-soft); padding: 9px 16px; border-radius: 999px; cursor: pointer; font-size: .9rem; min-height: 40px; }
.style-tab.active { background: var(--forest); color: #fff; border-color: var(--forest); }

.print-sheet { display: flex; justify-content: center; margin-top: 8px; }
.qr-card {
  position: relative; width: 340px; max-width: 100%; aspect-ratio: 3 / 4;
  padding: 34px 28px; text-align: center; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 8px;
  background: #fff; color: #111; border-radius: 6px; overflow: hidden;
  box-shadow: 0 8px 24px rgba(31,58,77,.10); break-inside: avoid;
}
.qc-frame { position: absolute; inset: 0; pointer-events: none; border: 1px solid #e5e5e5; margin: 8px; border-radius: 2px; }
.qc-names { font-family: var(--sans); font-weight: 600; font-size: 1.15rem; letter-spacing: .12em; text-transform: uppercase; line-height: 1.2; color: #111; }
.qc-msg { font-family: var(--sans); font-size: .95rem; color: #555; line-height: 1.35; }
.qc-qr { background: #fff; padding: 8px; border-radius: 8px; }
.qc-qr canvas { width: 100%; max-width: 190px; height: auto; display: block; }
.qc-sub { color: #999; font-size: .78rem; margin-top: 8px; letter-spacing: .06em; text-transform: uppercase; }

.qr-card.colour-white { background: #fff; color: #111; }
.qr-card.colour-cream { background: #faf5ef; color: #1f2a44; }
.qr-card.colour-cream .qc-names { color: #1f2a44; }
.qr-card.colour-cream .qc-msg { color: #4a5266; }
.qr-card.colour-cream .qc-sub { color: #8a7a63; }
.qr-card.colour-cream .qc-frame { border-color: rgba(169,133,79,.5); }
.qr-card.colour-sage { background: #f6f4ec; color: #3d4a2f; }
.qr-card.colour-sage .qc-names { color: #3d4a2f; }
.qr-card.colour-sage .qc-msg { color: #5a6547; }
.qr-card.colour-sage .qc-sub { color: #7d8a5c; }
.qr-card.colour-sage .qc-frame { border-color: rgba(125,138,92,.55); }
.qr-card.colour-ink { background: #16213E; color: #f7ede3; }
.qr-card.colour-ink .qc-names { color: #f7ede3; }
.qr-card.colour-ink .qc-msg { color: #E8C9A8; }
.qr-card.colour-ink .qc-sub { color: #9aa7c0; }
.qr-card.colour-ink .qc-frame { border-color: rgba(247,237,227,.35); }
.qr-card.colour-ink .qc-qr { background: #f7ede3; }
@media (max-width: 640px) { .qr-card { width: 300px; } }

@media print {
  .no-print, #appnav, #msg, #hero, #controls { display: none !important; }
  body { background: #fff; }
  .dash { padding: 0 !important; max-width: none !important; }
  .print-sheet { display: flex; justify-content: center; align-items: center; min-height: 90vh; }
  .qr-card { width: 100mm; box-shadow: none; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  @page { size: A4; margin: 16mm; }
}

/* ============================================================
   Admin dashboard
   ============================================================ */
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
@media (max-width: 700px) { .two-col { grid-template-columns: 1fr; } }

.bar-row { display: grid; grid-template-columns: minmax(90px, 30%) 1fr auto; gap: 10px; align-items: center; padding: 7px 0; }
.bar-label { color: var(--ink); font-size: .9rem; font-weight: 500; }
.bar-track { background: rgba(31,58,77,.08); border-radius: 999px; height: 12px; overflow: hidden; }
.bar-fill { background: var(--gold); height: 100%; border-radius: 999px; min-width: 4px; }
.bar-val { color: var(--muted); font-size: .82rem; white-space: nowrap; text-align: right; }

.chart { margin: 8px 0 4px; }
.vis-svg { width: 100%; height: 140px; display: block; }
.chart-legend { color: var(--muted); font-size: .82rem; margin-top: 6px; }
.chart-legend .lg { display: inline-block; width: 12px; height: 12px; border-radius: 3px; vertical-align: middle; }
.chart-legend .lg.gold { background: var(--gold); }
.chart-legend .lg.navy { background: var(--forest); }

#rangeSel { border: 1px solid rgba(31,58,77,.2); border-radius: 10px; padding: 8px 12px; font: inherit; background: #fff; color: var(--ink); min-height: 40px; }

/* ---- Discount codes ------------------------ */
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; }
.field { display: flex; flex-direction: column; gap: 6px; }
.field > span { font-size: 0.86rem; color: var(--meadow-deep); font-weight: 500; }
.field input, .field select { width: 100%; padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px; font: inherit; background: #fff; color: var(--ink); }
.tier-checks { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 8px; }
.tier-check { display: inline-flex; align-items: center; gap: 8px; font-size: 0.92rem; }
.tier-check input { width: 18px; height: 18px; }

.code-card { border: 1px solid var(--line); border-radius: 14px; padding: 16px; margin-bottom: 14px; background: var(--paper-3); }
.code-card.code-inactive { opacity: 0.65; }
.code-top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.code-name { font-family: var(--serif); font-size: 1.5rem; font-weight: 500; color: var(--ink); letter-spacing: 0.04em; }
.code-badge { font-size: 0.78rem; background: var(--forest); color: var(--paper); padding: 3px 10px; border-radius: 999px; }
.code-badge.muted { background: #A9B8B5; }
.code-meta { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0; }
.code-chip { font-size: 0.8rem; color: var(--ink-soft); background: #E9F1ED; padding: 3px 10px; border-radius: 6px; }
.code-chip.link-chip { background: #E3EEF5; color: #2C5D80; }
.code-chip.chip-warn { background: #f7e0da; color: var(--err); }
.code-stats { display: flex; gap: 22px; flex-wrap: wrap; margin: 8px 0 4px; }
.code-stat-val { font-size: 1.15rem; font-weight: 600; color: var(--ink); }
.code-stat-label { font-size: 0.76rem; color: #7C8B8D; }
.code-actions { margin-top: 12px; }

.discount-row { display: flex; gap: 8px; align-items: stretch; }
.discount-row input { flex: 1 1 auto; }
.discount-row .btn { flex: 0 0 auto; }
#discountCode.code-ok { border-color: #4b8a5a; background: #f3faf4; }
.discount-line { margin-top: 6px; color: #4b8a5a; font-weight: 600; }
.note.err-text { color: var(--err); }
.note.ok-text { color: #4b8a5a; }
@media (max-width: 560px) { .discount-row { flex-direction: column; } }

/* ---------- Mosaic order admin ---------- */
.mosaic-admin-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 12px; margin-top: 14px; }
.mosaic-admin-tile { position: relative; border-radius: 12px; overflow: hidden; aspect-ratio: 1; background: var(--paper-3); border: 1px solid var(--line); cursor: grab; }
.mosaic-admin-tile:active { cursor: grabbing; }
.mosaic-admin-tile.dragging { opacity: 0.4; }
.mosaic-admin-tile.drag-over { outline: 3px solid var(--forest); outline-offset: -3px; }
.mosaic-admin-tile img { display: block; width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
.mosaic-admin-tile .num { position: absolute; top: 6px; left: 6px; min-width: 20px; height: 20px; padding: 0 5px; border-radius: 999px; background: rgba(31,58,77,.78); color: #fff; font-size: 0.72rem; font-weight: 700; display: flex; align-items: center; justify-content: center; }
.mosaic-admin-tile .nudge { position: absolute; bottom: 4px; right: 4px; display: flex; gap: 3px; }
.mosaic-admin-tile .nudge button { width: 22px; height: 22px; border-radius: 999px; border: none; background: rgba(255,255,255,.9); color: var(--ink); font-size: 0.8rem; font-weight: 700; cursor: pointer; line-height: 1; }
.mosaic-admin-tile .nudge button:hover { background: #fff; }
