/* ============================================================
   everinviting — visual system

   THEME — everything below the theme block references these tokens by
   role, never by colour name, so re-skinning the whole site means
   editing this one block. Current theme: Ivory & Plum.

   THE GOVERNING RULE — the interface is deliberately quiet. Colour,
   personality and variety come from the invitation artwork, which is
   drawn entirely from PALETTES in js/data.js and never reads a token
   from this file. That separation is what lets one calm brand carry
   templates ranging from a child's birthday to a formal anniversary,
   so keep it: no renderer should ever reference var(--…).

   Roughly 65-70% of any screen should be ivory or white, 8-12% plum,
   and no more than 2-3% champagne. Champagne fails contrast as text
   (2.13:1 on ivory) and is decorative ONLY — hairlines, dividers and
   small ornament. --champagne-ink is the text-safe relative.

   TYPE SCALE — `rem` resolves against the ROOT element, not `body`.
   The root is 17px, so every rem here is 17px-based. UI text never goes
   below --fs-100 (13.6px); smaller than that and the page feels shrunken
   no matter how good the layout is.
   ============================================================ */

/* ------------------------------------------------------------
   The [hidden] guard.

   Every dropdown here is rendered into the DOM with the `hidden`
   attribute and toggled by JavaScript. The user agent hides [hidden]
   with display:none, but that is a UA rule, so ANY author rule setting
   display on the same element beats it — and every menu sets
   display:grid to lay itself out. Without this, a menu ships
   permanently open.

   That has now happened twice: once on the invitation card menus, and
   again on the signed-in account menu, where it meant a real user's
   name, email, "Download my data" and "Sign out" sat exposed in the
   header on every page. Making the guard global rather than
   per-component is what stops it happening a third time.
   ------------------------------------------------------------ */
[hidden] { display: none !important; }


:root {
  /* ---------- THEME: Ivory & Saffron ----------
     Re-measured after the plum was replaced, against --paper #faf7f2:
     ink 14.55, ink-2 8.86, ink-3 5.43, muted 3.52, brand 4.85, accent 4.87,
     ok 4.96, warn 4.88, danger 6.05. Marigold (--accent-lt) is 2.02 and so
     never carries text — it is the decorative slot champagne used to hold.

     NOTE THE HEADROOM CHANGED. Under the old plum, --brand measured 10.5
     and --accent 7.2; both now sit just under 5. They still clear AA for
     normal text (4.5), but there is no longer room to darken the ground or
     lighten the brand without failing. If either is retuned, re-measure
     rather than assuming the margin that used to be there. */
  --paper:      #faf7f2;   /* warm ivory — the dominant ground */
  --paper-2:    #f4efe7;   /* alt sections, wells              */
  --paper-3:    #ebe3d8;   /* deepest tint                     */
  --card:       #ffffff;

  --ink:        #262326;   /* deep charcoal, not pure black */
  --ink-2:      #4a444a;   /* body copy                     */
  --ink-3:      #6b636b;   /* labels                        */
  --muted:      #8a8189;   /* fine print                    */

  /* Bronze, not saffron. This is the 2026 overhaul's rebrand: saffron read
     as "festive app", and the brief asked for something quieter and more
     premium. A deep antique bronze keeps the warmth (it is still firmly in
     the gold family, not a cold neutral) while reading as considered rather
     than bright. Contrast re-verified against --paper (#faf7f2): --brand
     ~7.5:1, --brand-soft ~4.9:1 (the top of a gradient, briefly seen) — both
     clear AA (4.5) for white text. */
  --brand:      #7a4a1e;   /* antique bronze — primary actions, active states */
  --brand-deep: #5c3714;
  --brand-soft: #9c6530;
  --on-brand:   #ffffff;   /* text ON a brand fill; inverts inside .on-plum */

  --accent:      #7a4a1e;  /* rules, small labels, ticks — text-safe */
  --accent-lt:   #d9a24c;  /* marigold — decorative ONLY, fails as text */
  --accent-pale: #f5e8d3;  /* tint fills                             */

  /* the raw brand accents, for the few places that want the hue itself
     rather than a role. Use sparingly; see the ratio note above. */
  --champagne:     #c5a875;
  --champagne-ink: #8a6633;  /* text-safe relative, 4.6 on ivory */
  --rose:          #c99fa6;
  --rose-ink:      #a0616b;
  --sage:          #879b8a;
  --sage-ink:      #5a7160;

  --metal-1:    #e4cfa6;   /* the "premium" gradient pair — champagne */
  --metal-2:    #c5a875;
  --on-metal:   #3b2334;

  /* ---------- semantic status ----------
     Kept separate from the brand accents on purpose: these carry meaning,
     so they must stay legible as green/amber/red rather than being folded
     into plum. Each is pulled toward the palette's warmth and each clears
     AA on ivory — ok 4.96, warn 4.88, warn-deep 6.74, danger 6.05. */
  --ok:         #5a7160;   /* sage, so success sits inside the palette */
  --warn:       #8a6633;   /* champagne-ink: "published but stale"     */
  --warn-deep:  #6f5228;
  --danger:     #a33a44;   /* plum-leaning red, 6.47 against white     */
  --note-bg:    #f3ecf1;   /* notices */
  --note-ink:   #513047;
  --tint-active:#f6f0f4;   /* selected rows and options */

  --mesh-1: rgba(201, 159, 166, .34);   /* hero colour wash — rose      */
  --mesh-2: rgba(197, 168, 117, .24);   /*                  — champagne */
  --mesh-3: rgba(135, 155, 138, .20);   /*                  — sage      */
  --skel-1: #ede6dc;
  --skel-2: #f7f2ea;

  /* ---------- lines: tinted, never flat grey ---------- */
  --line:        rgba(38, 35, 38, .10);
  --line-2:      rgba(38, 35, 38, .16);
  --line-accent: rgba(197, 168, 117, .48);  /* the champagne hairline */
  --overlay-hover: rgba(38, 35, 38, .06);   /* hover wash on menus/lists */

  /* ---------- glass: the floating layer (header, dropdowns, toasts).
     In-flow cards stay opaque with a hairline + shadow; anything that
     floats over content gets this translucent + blurred treatment instead,
     so "glass" reads as a deliberate second material, not a texture
     slapped on everything. ---------- */
  --glass-bg:     rgba(250, 247, 242, .70);
  --glass-bg-2:   rgba(250, 247, 242, .92);
  --glass-border: rgba(38, 35, 38, .08);

  /* ---------- depth: restrained. Premium reads as edges, not shadows. ---------- */
  --sh-1: 0 1px 2px rgba(38,35,38,.04), 0 2px 5px rgba(38,35,38,.04);
  --sh-2: 0 2px 6px rgba(38,35,38,.04), 0 10px 24px rgba(38,35,38,.07);
  --sh-3: 0 5px 14px rgba(38,35,38,.06), 0 26px 56px rgba(38,35,38,.10);
  --sh-brand: 0 5px 16px rgba(122,74,30,.24);
  --sh-metal: 0 5px 16px rgba(197,168,117,.28);
  /* A stronger, softer glow reserved for the hero's primary CTA only. */
  --sh-glow: 0 10px 40px -8px rgba(122,74,30,.35), 0 2px 14px rgba(122,74,30,.18);
  /* The warm aura behind the fanned cards — gold at the core, terracotta at
     the edge, so it reads as light rather than a flat coloured disc. */
  --hero-glow: radial-gradient(closest-side, rgba(214,150,58,.62), rgba(193,108,68,.34) 46%, transparent 72%);
  /* A neutral, wide, soft-focus shadow for glass-hover lifts — distinct from
     --sh-glow, which is warm/brand-tinted and reserved for the primary CTA. */
  --sh-lift: 0 26px 56px -18px rgba(38,35,38,.24), 0 4px 14px rgba(38,35,38,.08);

  /* ---------- shape: flatter for the 2026 overhaul. A geometric, editorial
     feel reads as edges and hairlines rather than rounded-app corners, so
     the whole radius scale comes down a notch from the original. ---------- */
  --r-xs: 4px;
  --r-sm: 6px;
  --r:    8px;
  --r-lg: 12px;

  /* ---------- type scale (17px root) ---------- */
  --fs-100: .80rem;   /* 13.6px — counts, badges, finest print */
  --fs-200: .88rem;   /* 15.0px — secondary text, captions     */
  --fs-300: .95rem;   /* 16.2px — default UI: buttons, inputs  */
  --fs-400: 1rem;     /* 17.0px — body copy                    */
  --fs-500: 1.16rem;  /* 19.7px — lead paragraphs              */

  /* Serif for expressive headings only. Metadata and category labels are
     sans by design — a serif eyebrow is what made the old page read as a
     template rather than a brand. */
  --display: "Fraunces", "Cormorant Garamond", Georgia, serif;

  /* The hero headline only: a true high-contrast editorial serif, airier and
     more delicate than Fraunces reads at display size — Fraunces is a soft,
     rounded serif and stays heavier-looking than intended even at a light
     weight. Cormorant is already loaded at 300; Playfair backs it up. */
  --display-hero: "Cormorant Garamond", "Playfair Display", Georgia, serif;

  /* The wordmark now shares --display, which it did not always. When the display
     face was Instrument Serif it could not: that face is narrow and tightly
     fitted, so "EverInviting" — one word, no space, an r running straight into
     an I — collapsed into a blur at header size, and tracking alone did not fix
     it. The two halves need different WEIGHTS to separate, and Instrument Serif
     ships only one.

     Fraunces has the range, so the wordmark and the headings can finally be the
     same face. Kept as its own token anyway, because the wordmark is the one
     piece of type that may need to diverge later without dragging every heading
     with it. */
  --wordmark: var(--display);
  --sans:    "Manrope", "Inter", "Segoe UI", system-ui, sans-serif;
  --label:   "Manrope", "Inter", "Segoe UI", system-ui, sans-serif;

  /* Structural face — nav, eyebrows, stat numbers, step numerals. Doing the
     "clean geometric" work the serif/sans pair can't: a grotesque with true
     geometric bones, used only where the UI is labelling or counting rather
     than speaking in its own voice. */
  --font-label: "Space Grotesque", var(--sans);

  --header-h: 80px;
  --ease: cubic-bezier(.22, .68, .36, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);

  /* ---------- motion: slow, elegant, intentional ---------- */
  --dur-1: .18s;   /* button and control feedback   */
  --dur-2: .32s;   /* hover states, image zoom      */
  --dur-3: .62s;   /* scroll reveals, entrances     */

  /* ---------- spacing ---------- */
  --sp-1: 6px;
  --sp-2: 12px;
  --sp-3: 20px;
  --sp-4: 32px;
  --sp-5: 52px;
  --sp-6: 84px;
}

/* ------------------------------------------------------------------
   PUBLIC DARK MODE — opt-in via the header seal, not the page default.

   Redefines the same role tokens :root already carries, so every rule
   below that reads a token rather than a literal colour inverts for free.
   Warm near-black + muted gold on purpose: the two AI-dark-mode clichés
   this deliberately avoids are true black-and-neon (cold, tech-generic)
   and a straight invert of the light palette (washes out, loses warmth).
   Scope: the PUBLIC site only (homepage, browse). The creator studio gets
   its own neutral-graphite dark theme in a later phase — kept deliberately
   separate, because a canvas needs a neutral surround to judge colour
   accurately, while the marketing site can afford a tinted one.
   ------------------------------------------------------------------ */
:root[data-theme="dark"] {
  --paper:      #17151a;
  --paper-2:    #1d1a20;
  --paper-3:    #242026;
  --card:       #1d1a20;

  --ink:        #f1ede3;
  --ink-2:      #d9d2c7;
  --ink-3:      #b3aa9e;
  --muted:      #8f877c;

  --brand:      #d9ae6e;   /* bright enough to glow on dark; needs dark text */
  --brand-deep: #b88a46;
  --brand-soft: #e7c48c;
  --on-brand:   #241a0c;

  --accent:      #d9ae6e;
  --accent-lt:   #b88a46;
  --accent-pale: rgba(217, 174, 110, .14);

  --line:        rgba(241, 237, 227, .12);
  --line-2:      rgba(241, 237, 227, .20);
  --line-accent: rgba(217, 174, 110, .50);
  --overlay-hover: rgba(241, 237, 227, .08);

  --sh-1: 0 1px 2px rgba(0,0,0,.35), 0 2px 5px rgba(0,0,0,.25);
  --sh-2: 0 2px 6px rgba(0,0,0,.30), 0 10px 24px rgba(0,0,0,.35);
  --sh-3: 0 5px 14px rgba(0,0,0,.35), 0 26px 56px rgba(0,0,0,.45);
  --sh-brand: 0 6px 20px rgba(217,174,110,.30);
  --sh-glow: 0 10px 44px -8px rgba(217,174,110,.42), 0 2px 16px rgba(217,174,110,.22);
  --hero-glow: radial-gradient(closest-side, rgba(217,174,110,.42), rgba(184,110,74,.24) 48%, transparent 72%);
  --sh-lift: 0 28px 60px -18px rgba(0,0,0,.55), 0 4px 16px rgba(0,0,0,.35);

  --mesh-1: rgba(217, 174, 110, .16);
  --mesh-2: rgba(184, 138, 70, .14);
  --mesh-3: rgba(90, 113, 96, .16);
  --skel-1: #201c22;
  --skel-2: #2a242c;

  --tint-active: rgba(217, 174, 110, .10);
  --note-bg:  rgba(217, 174, 110, .10);
  --note-ink: #e7c48c;

  --glass-bg:     rgba(23, 21, 26, .62);
  --glass-bg-2:   rgba(23, 21, 26, .90);
  --glass-border: rgba(241, 237, 227, .10);

  color-scheme: dark;
}

/* ------------------------------------------------------------------
   INVERTED SURFACE — the one dramatic plum section.

   It redefines the same role tokens rather than restyling components,
   so every button, rule, heading and card inside it inverts on its own
   and nothing needs a second set of selectors. Anything dropped in
   here keeps working because it was already reading these roles.
   ------------------------------------------------------------------ */
.on-plum {
  --paper:      #513047;
  --paper-2:    #5c3852;
  --paper-3:    #6e4763;
  --card:       #5c3852;

  --ink:        #faf7f2;
  --ink-2:      #e7dbe3;
  --ink-3:      #cdb8c6;
  --muted:      #c3b2bd;

  --brand:      #faf7f2;         /* a CTA here is ivory on plum */
  --brand-deep: #ffffff;
  --brand-soft: #e7dbe3;
  --on-brand:   #513047;

  --accent:      #e4cfa6;
  --accent-lt:   #c99fa6;
  --accent-pale: rgba(250,247,242,.10);

  --line:        rgba(250,247,242,.18);
  --line-2:      rgba(250,247,242,.28);
  --line-accent: rgba(197,168,117,.55);

  --note-bg:  rgba(250,247,242,.08);
  --note-ink: #faf7f2;

  background: var(--paper);
  color: var(--ink);
}

* { box-sizing: border-box; }

/* The single lever for overall UI size. `clip` rather than `hidden`:
   hidden on html/body turns it into a scroll container, which breaks
   window.scrollTo and position:sticky. */
html {
  font-size: 17px;
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  overflow-x: clip;
}
html, body { margin: 0; padding: 0; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--fs-400);
  line-height: 1.6;
  font-weight: 400;
  letter-spacing: -.006em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* The theme toggle flips these tokens instantly; this is what makes the
     surfaces most people look at while doing it (page + text) ease rather
     than snap. Everything else inherits the tokens and changes with them. */
  transition: background-color .35s var(--ease), color .35s var(--ease);
}
:root { color-scheme: light; }

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }
::selection { background: var(--accent-pale); color: var(--brand-deep); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

/* --- headings --- */
h1, h2, h3, h4 { margin: 0 0 .46em; line-height: 1.1; font-weight: 500; }
h1 {
  font-family: var(--display); font-weight: 300;
  font-size: clamp(3.1rem, 6.2vw, 5.4rem);
  line-height: .98; letter-spacing: -.022em;
}
h1 em { font-style: italic; font-weight: 400; color: var(--brand); }
h2 {
  font-family: var(--display); font-weight: 400;
  font-size: clamp(2.2rem, 4vw, 3.4rem);
  line-height: 1.06; letter-spacing: -.018em;
}
h2 em { font-style: italic; color: var(--brand); }
h3 { font-size: 1.14rem; font-weight: 700; letter-spacing: -.012em; }
h4 { font-size: var(--fs-300); font-weight: 700; }
p { margin: 0 0 1em; }

.muted { color: var(--muted); }
.small { font-size: var(--fs-200); }
code {
  font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-size: .86em;
  background: var(--paper-2); padding: .16em .48em; border-radius: 5px; color: var(--brand);
}

.wrap { width: min(1360px, 100% - 52px); margin-inline: auto; }

/* the small structural kicker above section titles */
.kicker {
  display: inline-flex; align-items: center; gap: .85em;
  font-family: var(--font-label); font-size: var(--fs-100); letter-spacing: .26em;
  text-transform: uppercase; color: var(--accent); margin-bottom: 1.05em;
}
.kicker::before, .kicker::after { content: ""; width: 32px; height: 1px; background: var(--line-accent); }
.section-head.left .kicker::before { display: none; }

/* flourish under section headings */
.orn-rule { display: block; margin: 22px auto 0; width: 168px; height: 19px; color: var(--accent); }
.orn-rule .l { fill: none; stroke: currentColor; stroke-width: 1; opacity: .42; vector-effect: non-scaling-stroke; }
.orn-rule .d { fill: currentColor; stroke: none; opacity: .85; }

/* ---------------- buttons ---------------- */
.btn {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center; gap: .55em;
  /* Not a pill. A pill button reads as a consumer app; a moderate radius
     reads as stationery. Chips and badges below keep their pill shape,
     which is idiomatic for them and not what this is about. */
  border: 1px solid transparent; border-radius: var(--r-sm);
  padding: .86em 1.85em; font-size: var(--fs-300); font-weight: 600;
  letter-spacing: .006em; white-space: nowrap; line-height: 1.3;
  transition: transform .28s var(--ease), box-shadow .28s var(--ease),
              background .25s var(--ease), color .25s var(--ease), border-color .25s var(--ease);
}
/* light sweeps across on hover — the cheapest way to make a button feel made of something */
.btn::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, transparent 32%, rgba(255,255,255,.42) 48%, transparent 62%);
  transform: translateX(-130%); transition: transform .7s var(--ease-out);
}
.btn:hover::after { transform: translateX(130%); }
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }

.btn-primary { background: linear-gradient(165deg, var(--brand-soft), var(--brand) 46%, var(--brand-deep)); color: var(--on-brand); box-shadow: var(--sh-brand); }
.btn-primary:hover { box-shadow: 0 9px 26px rgba(122,74,30,.32); }
.btn-gold { background: linear-gradient(165deg, var(--metal-1), var(--metal-2) 52%, var(--champagne-ink)); color: var(--on-metal); box-shadow: var(--sh-metal); }
.btn-gold:hover { box-shadow: 0 9px 26px rgba(197,168,117,.40); }
.btn-ghost { background: transparent; border-color: var(--line-2); color: var(--ink); }
.btn-ghost:hover { background: var(--card); border-color: var(--accent); box-shadow: var(--sh-1); }
.btn-soft { background: var(--paper-2); color: var(--ink); }
.btn-soft:hover { background: var(--paper-3); }
.btn-sm { padding: .62em 1.3em; font-size: var(--fs-200); }
.btn-block { width: 100%; }
.btn[disabled] { opacity: .42; pointer-events: none; }

/* ---------------- header ----------------
   Glass, not paper: this is the one persistent chrome element, present over
   every view including the editor, so it is the header's job to feel like a
   pane of something floating above the page rather than a coloured band —
   the --glass-* tokens invert cleanly under [data-theme="dark"]. */
.site-header {
  position: sticky; top: 0; z-index: 60;
  background: var(--glass-bg);
  backdrop-filter: saturate(1.5) blur(18px);
  -webkit-backdrop-filter: saturate(1.5) blur(18px);
  border-bottom: 1px solid transparent;
  transition: background .3s var(--ease), border-color .3s var(--ease), box-shadow .3s var(--ease);
}
.site-header.scrolled {
  background: var(--glass-bg-2);
  border-bottom-color: var(--glass-border);
  box-shadow: 0 10px 30px rgba(0,0,0,.06);
}
.header-inner { display: flex; align-items: center; gap: 32px; height: var(--header-h); }

.brand { display: flex; align-items: center; gap: 15px; font-family: var(--wordmark); font-size: 1.56rem; font-weight: 400; }
.brand-mark {
  width: 40px; height: 40px; border-radius: var(--r); display: grid; place-items: center;
  background: linear-gradient(150deg, var(--brand-soft), var(--brand-deep));
  color: #fff; font-size: 1.2rem; font-weight: 600;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.20), var(--sh-1);
  transition: transform .4s var(--ease-out);
}
.brand:hover .brand-mark { transform: rotate(-6deg) scale(1.05); }

/* The mark is an envelope, drawn rather than set as a letter: an invitation is
   a thing you are sent, and a monogram said nothing about that. Stroked in
   currentColor so it inherits .brand-mark's colour and cannot drift from it.
   Kept to two shapes because the tile is 42px in the header and 32px in a
   browser tab — anything finer turns to mud. */
.brand-mark svg {
  width: 21px; height: 21px; display: block;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
/* Congested-wordmark fix: the two halves need visible air between them as
   well as a weight change, or "EverInviting" still reads as one dense word
   at header size. */
.brand-text { letter-spacing: .012em; }
.brand-text em { font-style: normal; font-weight: 700; color: var(--brand); margin-left: .05em; }

/* Tiny, uppercase, widely tracked — the luxury-portfolio treatment rather
   than ordinary nav-link sizing. */
.main-nav {
  display: flex; gap: 26px; margin-left: auto; font-family: var(--font-label);
  font-size: .74rem; font-weight: 600; letter-spacing: .11em; text-transform: uppercase;
}
.main-nav a { position: relative; color: var(--ink-2); padding: 9px 0; }
.main-nav a::after {
  content: ""; position: absolute; left: 0; right: 100%; bottom: 2px; height: 1px;
  background: var(--accent); transition: right .35s var(--ease-out);
}
.main-nav a:hover { color: var(--ink); }
.main-nav a:hover::after, .main-nav a.active::after { right: 0; }
.main-nav a.active { color: var(--brand); }

.header-actions { display: flex; align-items: center; gap: 14px; }
.pass-badge {
  background: linear-gradient(135deg, var(--metal-1), #7fb8d8); color: var(--on-metal);
  font-size: var(--fs-100); font-weight: 700; padding: .42em .95em; border-radius: 999px;
  letter-spacing: .08em; text-transform: uppercase;
}
.nav-toggle { display: none; background: none; border: 0; font-size: 1.55rem; color: var(--ink); padding: 4px 8px; }

/* ---------------- theme toggle — "the seal" ----------------
   The one place this design spends its boldness: a small wax-seal glyph
   that presses between the two icons on click. Hand-drawn as inline SVG
   (a stroked ring plus a swapped ray/crescent glyph), not a sourced or
   generated asset — matches the account avatar's size so the header's
   right side reads as one row of controls. */
.theme-toggle {
  flex: none; width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center;
  background: var(--paper-2); border: 1px solid var(--line-2); color: var(--accent);
  transition: transform .5s var(--ease-out), background .3s var(--ease), border-color .3s var(--ease), color .3s var(--ease);
}
.theme-toggle:hover { border-color: var(--accent); box-shadow: var(--sh-1); }
.theme-toggle:active { transform: scale(.92); }
.theme-toggle svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.theme-toggle .seal-moon { display: none; fill: currentColor; stroke: none; }
:root[data-theme="dark"] .theme-toggle { background: var(--paper-3); }
:root[data-theme="dark"] .theme-toggle .seal-rays { display: none; }
:root[data-theme="dark"] .theme-toggle .seal-moon { display: block; }
/* the press: a quick flip on the click that actually changes the theme */
.theme-toggle.flip svg { animation: seal-flip .5s var(--ease-out); }
@keyframes seal-flip { from { transform: rotateY(0deg); } 50% { transform: rotateY(90deg); } to { transform: rotateY(0deg); } }

/* ---------------- hero ---------------- */
.hero { position: relative; overflow: hidden; padding: clamp(72px, 9vw, 132px) 0 clamp(64px, 8vw, 112px); }
.hero::before {
  content: ""; position: absolute; inset: -20% -10% auto -10%; height: 150%; z-index: 0;
  background:
    radial-gradient(60% 55% at 8% 4%,  var(--mesh-1) 0%, transparent 62%),
    radial-gradient(52% 48% at 92% 2%, var(--mesh-2) 0%, transparent 60%),
    radial-gradient(70% 60% at 60% 100%, var(--mesh-3) 0%, transparent 65%);
  filter: blur(6px);
}
.hero > * { position: relative; z-index: 1; }
/* Asymmetric, not centred-and-even: the copy takes the narrower 5-ish
   column, the fan takes the wider 7-ish, so the page reads as a deliberate
   grid rather than two boxes split down the middle. */
.hero-grid { display: grid; grid-template-columns: .86fr 1.14fr; gap: clamp(40px, 5.5vw, 80px); align-items: center; }

/* The one headline that gets the editorial serif instead of the brand
   display face — Fraunces is a soft, rounded serif and still reads heavy
   at display size even at weight 300; Cormorant's high-contrast, delicate
   strokes are what "elegant, artistic" actually calls for here. Slightly
   looser tracking too — Fraunces wants tight negative tracking, Cormorant
   wants room to breathe. */
.hero h1 {
  font-family: var(--display-hero); font-weight: 300; letter-spacing: -.008em;
  /* Cormorant sets noticeably wider than Fraunces at the same size — without
     its own, smaller scale "Invitations made" breaks after one word instead
     of wrapping once, on its own line, the way the copy is written for. */
  font-size: clamp(2.5rem, 4.6vw, 4.1rem); line-height: 1.08;
}
.hero h1 em { font-weight: 400; }

.eyebrow {
  display: inline-flex; align-items: center; gap: 11px;
  font-family: var(--font-label); font-size: var(--fs-100); font-weight: 600;
  letter-spacing: .19em; text-transform: uppercase; color: var(--accent);
  background: var(--accent-pale); border: 1px solid var(--line-accent);
  padding: .62em 1.25em; border-radius: 999px; margin-bottom: 32px;
  box-shadow: var(--sh-1); backdrop-filter: blur(6px);
}
.eyebrow .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 3px var(--accent-pale); }

.hero p.lead { font-size: var(--fs-500); line-height: 1.6; color: var(--ink-2); max-width: 30em; }
.hero-cta { display: flex; flex-wrap: wrap; gap: 15px; margin: 36px 0 40px; }
/* Sleek, elongated pills for the hero's own two CTAs — scoped here rather
   than on .btn globally, since the rest of the site keeps the flatter,
   "stationery, not consumer-app" radius the base button styles argue for. */
.hero-cta .btn { border-radius: 999px; padding: 1.05em 2.35em; }
.hero-cta .btn-primary { box-shadow: var(--sh-glow); }
.hero-cta .btn-primary:hover { box-shadow: var(--sh-glow), 0 0 0 1px rgba(255,255,255,.08) inset; }

.hero-stats { display: flex; gap: clamp(24px, 3.4vw, 50px); flex-wrap: wrap; padding-top: 30px; position: relative; }
.hero-stats::before {
  content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 1px;
  background: linear-gradient(90deg, var(--line-accent), transparent 72%);
}
.hero-stats div strong {
  display: block; font-family: var(--display); font-size: 2.3rem; font-weight: 500;
  color: var(--brand); line-height: 1; letter-spacing: -.02em;
}
.hero-stats div span {
  font-family: var(--font-label); font-size: .68rem; color: var(--muted); letter-spacing: .14em;
  text-transform: uppercase; margin-top: 8px; display: block;
}

/* the fanned cards */
.hero-fan { position: relative; height: clamp(380px, 45vw, 570px); perspective: 1400px; }
/* The ambient aura: a soft, blurred glow sitting behind the cards rather
   than a flat background tint, so the centre of the hero has real depth.
   Oversized and heavily blurred so it reads as light bleeding outward, not
   as a coloured circle with a visible edge. Sits below the fan cards, which
   already carry z-index 1-3 from the nth-child rules below. */
.hero-fan::before {
  content: ""; position: absolute; z-index: 0;
  inset: -22% -14%;
  background: var(--hero-glow);
  filter: blur(46px);
  pointer-events: none;
}
.hero-fan .fan-card {
  position: absolute; width: 48%; aspect-ratio: 5 / 7; border-radius: var(--r); overflow: hidden;
  background: #fff; box-shadow: var(--sh-3);
  transition: transform .8s var(--ease-out), box-shadow .8s var(--ease-out);
  will-change: transform;
}
.hero-fan .fan-card::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.55), inset 0 0 44px rgba(38,35,38,.05);
}
.hero-fan .fan-card:nth-child(1) { left: 0;   top: 13%; transform: rotate(-10deg) translateZ(-40px); z-index: 1; }
.hero-fan .fan-card:nth-child(2) { left: 26%; top: 0;   transform: rotate(-1deg) translateZ(20px);  z-index: 3; }
.hero-fan .fan-card:nth-child(3) { left: 52%; top: 15%; transform: rotate(9deg) translateZ(-40px);  z-index: 2; }
.hero-fan:hover .fan-card:nth-child(1) { transform: rotate(-14deg) translate(-10px, -14px) translateZ(-20px); }
.hero-fan:hover .fan-card:nth-child(2) { transform: rotate(-1deg) translateY(-20px) translateZ(50px); }
.hero-fan:hover .fan-card:nth-child(3) { transform: rotate(13deg) translate(10px, -14px) translateZ(-20px); }
.hero-fan .fan-card svg { width: 100%; height: 100%; display: block; }

/* ---- hero entrance ----------------------------------------------------
   One orchestrated sequence on load rather than effects scattered around
   the page: the copy rises line by line, then the fan settles in behind
   it. Everything is CSS, so it costs no JavaScript and cannot fight the
   renderer.

   The three fan cards animate OPACITY ONLY. Each already carries its own
   rotate/translateZ from :nth-child above, and a keyframe that touched
   transform would overwrite that and drop the cards flat — the container
   does the movement instead, so the two never collide.

   `both` matters: without it the elements flash at full opacity for one
   frame before the delay starts. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes ei-rise {
    from { opacity: 0; transform: translateY(15px); }
    to   { opacity: 1; transform: none; }
  }
  @keyframes ei-fan-settle {
    from { opacity: 0; transform: translateY(26px) scale(.965); }
    to   { opacity: 1; transform: none; }
  }
  @keyframes ei-fade { from { opacity: 0; } to { opacity: 1; } }

  /* Slowed to match the same cubic-bezier(.16,1,.3,1) curve (--ease-out)
     and unhurried pacing the invitation pager's own reveal now uses, so
     the two don't read as two different products. Each line still rises
     and holds via `both` — the fill-mode that was already here is what
     stops elements flashing at full opacity before their delay starts;
     that mechanism doesn't change, only the numbers do. */
  .hero .eyebrow,
  .hero h1,
  .hero p.lead,
  .hero-cta,
  .hero-stats { animation: ei-rise 1.2s var(--ease-out) both; }

  .hero .eyebrow  { animation-delay: .08s; }
  .hero h1        { animation-delay: .23s; }
  .hero p.lead    { animation-delay: .38s; }
  .hero-cta       { animation-delay: .53s; }
  .hero-stats     { animation-delay: .68s; }

  .hero-fan { animation: ei-fan-settle 1.4s var(--ease-out) .3s both; }

  /* The cards fade in, then breathe — a slow, small rise and fall that never
     stops. Three different periods (7s / 8.2s / 6.4s) so they drift out of
     phase and the group never pulses in unison, which is what makes this read
     as "alive" rather than "blinking".

     The float animates `translate`, NOT `transform`. Each card carries its own
     rotate and translateZ in `transform` from the :nth-child rules above, and a
     keyframe touching transform would overwrite them and drop the fan flat —
     the mistake that has to be avoided every time this hero is animated.
     `translate` is an independent property, so the two compose and the hover
     spread still works on top of both. */
  @keyframes ei-float {
    0%, 100% { translate: 0 0; }
    50%      { translate: 0 -9px; }
  }

  .hero-fan .fan-card { animation: ei-fade .9s ease-out both, ei-float 7s ease-in-out infinite; }
  .hero-fan .fan-card:nth-child(1) { animation-delay: .69s, 1.6s; animation-duration: .9s, 7s;   }
  .hero-fan .fan-card:nth-child(2) { animation-delay: .51s, 1.4s; animation-duration: .9s, 8.2s; }
  .hero-fan .fan-card:nth-child(3) { animation-delay: .87s, 1.9s; animation-duration: .9s, 6.4s; }

  /* Hover still wins: it moves `transform`, the float moves `translate`. */
  .hero-fan:hover .fan-card { animation-play-state: running; }
}

/* ---------------- sections ----------------
   More air, and a hairline rather than a shadow marking every boundary —
   this single rule is most of what makes the page read as a sequence of
   deliberate geometric sections instead of stacked cards. */
.section { padding: clamp(64px, 7.5vw, 112px) 0; position: relative; border-top: 1px solid var(--line); }
.section:first-of-type { border-top: 0; }
.hero + .section { border-top: 0; }
.section-alt { background: linear-gradient(180deg, var(--paper-2) 0%, var(--paper) 92%); }
.section-tight { padding-top: 0; }
.section-cta { text-align: center; margin-top: 42px; }
.section-head { text-align: center; max-width: 46em; margin: 0 auto clamp(36px, 4.6vw, 60px); }
/* Every homepage section heading gets the same editorial serif as the hero
   — otherwise "Invitations made for your moment" and "Create for any
   occasion" two screens later read as two different sites. Scoped to
   .section-head/.reel-copy rather than the global h2 rule, since browse,
   the editor, the dashboard and legal pages haven't had their own pass yet
   and keep Fraunces until they do. */
.section-head h2 { font-family: var(--display-hero); font-weight: 300; }
.section-head p { color: var(--ink-2); font-size: var(--fs-500); line-height: 1.55; }
.section-head.left { text-align: left; margin-inline: 0; }
.section-head.left .orn-rule { margin-left: 0; }

/* ---------------- occasion tiles ---------------- */
.occasion-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(212px, 1fr)); gap: 16px; }
.occ-tile {
  position: relative; overflow: hidden;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 22px 20px; display: flex; flex-direction: column; gap: 6px;
  transition: transform .38s var(--ease-out), box-shadow .38s var(--ease-out), border-color .3s;
}
.occ-tile::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: linear-gradient(180deg, var(--accent-lt), var(--accent));
  transform: scaleY(0); transform-origin: top; transition: transform .42s var(--ease-out);
}
.occ-tile:hover { transform: translateY(-4px); box-shadow: var(--sh-2); border-color: var(--line-accent); }
.occ-tile:hover::before { transform: scaleY(1); }
.occ-tile .emoji { font-size: 1.6rem; line-height: 1; }
.occ-tile .name { font-weight: 600; font-size: var(--fs-300); letter-spacing: -.01em; }
.occ-tile .count { font-size: var(--fs-100); color: var(--muted); letter-spacing: .03em; }

/* ============================================================
   DISCOVERY — "What are you celebrating?"
   ============================================================ */
.disc { padding-top: clamp(38px, 5vw, 68px); }
.disc-head { text-align: center; max-width: 720px; margin: 0 auto clamp(30px, 4vw, 48px); }
.disc-h1 {
  font-family: var(--display); font-weight: 300; font-size: clamp(2.5rem, 5.4vw, 4rem);
  line-height: 1.02; letter-spacing: -.022em; margin-bottom: .3em;
}
.disc-h1 em { font-style: italic; font-weight: 400; color: var(--brand); }
.disc-head .lead { font-size: var(--fs-500); color: var(--ink-2); margin-bottom: 28px; }

.disc-search { position: relative; max-width: 560px; margin: 0 auto; }
.disc-search input {
  width: 100%; padding: 1.05em 3em 1.05em 3.2em; border: 1px solid var(--line-2);
  border-radius: var(--r-sm); background: var(--card); font: inherit; font-size: var(--fs-400);
  box-shadow: var(--sh-2); transition: border-color .25s, box-shadow .25s;
}
.disc-search input::placeholder { color: var(--muted); }
.disc-search input:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 4px rgba(110,71,99,.15), var(--sh-2);
}
.disc-search .icon { position: absolute; left: 20px; top: 50%; transform: translateY(-50%); opacity: .5; font-size: 1.3rem; }
.disc-clear {
  position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
  border: 0; background: var(--paper-2); color: var(--ink-3); width: 30px; height: 30px;
  border-radius: 50%; font-size: .85rem;
}
.disc-clear:hover { background: var(--paper-3); color: var(--ink); }
.disc-crumbs { display: flex; justify-content: center; gap: 8px; margin-top: 14px; }
.disc-count { text-align: center; margin: 0 0 18px; }
.disc-sub {
  font-family: var(--label); font-size: var(--fs-100); letter-spacing: .18em;
  text-transform: uppercase; color: var(--accent); font-weight: 400;
  margin: 40px 0 16px; text-align: center;
}
.disc-sub:first-child { margin-top: 0; }

/* event cards */
.ev-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(196px, 1fr)); gap: 16px; }
.ev-card {
  position: relative; display: flex; flex-direction: column; gap: 4px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r);
  padding: 20px 18px 18px; overflow: hidden;
  transition: transform .38s var(--ease-out), box-shadow .38s var(--ease-out), border-color .3s;
}
.ev-card:hover { transform: translateY(-5px); box-shadow: var(--sh-2); border-color: var(--line-accent); }
.ev-art {
  position: relative; display: grid; place-items: center; height: 68px; margin-bottom: 8px;
  color: var(--accent);
}
.ev-glyph { width: 56px; height: 56px; display: block; }
.ev-card:hover .ev-glyph { color: var(--brand); }
.ev-culture {
  position: absolute; top: -6px; right: -4px; font-size: .6rem; letter-spacing: .08em;
  text-transform: uppercase; font-weight: 700; color: var(--accent);
  background: var(--accent-pale); border-radius: 999px; padding: .22em .6em;
}
.ev-name { font-weight: 700; font-size: var(--fs-300); letter-spacing: -.01em; }
.ev-tag { font-size: var(--fs-200); color: var(--ink-2); line-height: 1.4; }
.ev-meta { font-size: var(--fs-100); color: var(--muted); margin-top: 5px; }

/* category buttons on the discovery page */
.disc-cats { display: grid; grid-template-columns: repeat(auto-fill, minmax(288px, 1fr)); gap: 12px; }
.disc-cat {
  display: flex; align-items: center; gap: 14px; text-align: left; width: 100%;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 16px 18px; transition: all .3s var(--ease);
}
.disc-cat:hover { border-color: var(--line-accent); transform: translateY(-2px); box-shadow: var(--sh-1); }
.dc-ico { flex: none; width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center; background: var(--paper-2); font-size: 1.25rem; }
.dc-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.dc-name { font-weight: 700; font-size: var(--fs-300); }
.dc-blurb { font-size: var(--fs-200); color: var(--muted); line-height: 1.4; }
.dc-n { margin-left: auto; font-size: var(--fs-100); font-weight: 700; color: var(--accent); }

/* custom-event callout */
.custom-callout {
  display: flex; align-items: center; justify-content: space-between; gap: 22px; flex-wrap: wrap;
  background: linear-gradient(135deg, var(--paper-2), var(--card));
  border: 1px solid var(--line-accent); border-radius: var(--r-lg);
  padding: 26px 30px; margin-top: 42px;
}
.custom-callout.compact { margin-top: 30px; padding: 20px 24px; }
.custom-callout strong { display: block; font-size: 1.14rem; margin-bottom: 4px; }
.custom-callout span { font-size: var(--fs-200); color: var(--ink-2); max-width: 54ch; display: block; }

/* ---------------- event landing ---------------- */
.back-link { display: inline-block; font-size: var(--fs-200); color: var(--muted); margin-bottom: 18px; }
.back-link:hover { color: var(--brand); }
.ev-hero { padding-bottom: clamp(30px, 4vw, 50px); }
.ev-hero-grid { display: grid; grid-template-columns: 1.3fr .7fr; gap: 40px; align-items: center; }
.ev-h1 {
  font-family: var(--display); font-weight: 300; font-size: clamp(2.6rem, 5.4vw, 4.2rem);
  line-height: 1.02; letter-spacing: -.022em;
}
.ev-hero-art { display: grid; place-items: center; color: var(--accent); }
.ev-hero-art .ev-glyph { width: min(220px, 60%); height: auto; opacity: .9; }

.suite-flow {
  display: flex; align-items: center; justify-content: center; gap: 6px; flex-wrap: wrap;
}
.flow-step {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 14px 16px; min-width: 104px; transition: all .3s var(--ease);
}
.flow-step:hover { border-color: var(--line-accent); transform: translateY(-2px); }
.fs-ico { font-size: 1.3rem; line-height: 1; }
.fs-name { font-size: var(--fs-100); font-weight: 600; text-align: center; }
.flow-arrow { color: var(--accent); font-size: 1.1rem; opacity: .55; }
.suite-note { text-align: center; margin: 22px 0 0; }

/* ---------------- category discovery ---------------- */
.cat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 22px 20px; }
.cat-tile {
  position: relative; overflow: hidden; display: flex; align-items: flex-start; gap: 15px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r);
  padding: 22px 24px; transition: transform .4s var(--ease-out), box-shadow .4s var(--ease-out),
    border-color .3s, background .3s var(--ease);
}
/* A gentle stagger — every third tile sits a little lower — so the grid
   reads as arranged rather than a rigid spreadsheet of equal boxes. Purely
   presentational (margin, not content-driven), so it never fights the
   underlying auto-fill column count. */
.cat-tile:nth-child(3n+2) { margin-top: 26px; }
.cat-tile:nth-child(3n+2):hover { transform: translateY(-7px); }
.cat-tile::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: linear-gradient(180deg, var(--accent-lt), var(--accent));
  transform: scaleY(0); transform-origin: top; transition: transform .42s var(--ease-out);
}
/* The hover is where "glass" earns its keep: the tile lifts, its surface
   turns translucent and blurred rather than staying a flat opaque card,
   and the shadow goes soft and wide instead of tight — the same floating
   material the header/menus already use, borrowed for one interactive
   moment. */
.cat-tile:hover {
  transform: translateY(-7px);
  background: color-mix(in srgb, var(--card) 72%, transparent);
  backdrop-filter: blur(14px) saturate(1.3); -webkit-backdrop-filter: blur(14px) saturate(1.3);
  box-shadow: var(--sh-lift); border-color: var(--line-accent);
}
.cat-tile:hover::before { transform: scaleY(1); }
.cat-ico {
  flex: none; width: 46px; height: 46px; border-radius: 13px; display: grid; place-items: center;
  background: var(--paper-2); font-size: 1.35rem; line-height: 1;
}
.cat-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cat-name { font-weight: 700; font-size: var(--fs-300); letter-spacing: -.01em; }
.cat-blurb { font-size: var(--fs-200); color: var(--muted); line-height: 1.45; }
.cat-count {
  margin-left: auto; flex: none; font-size: var(--fs-100); font-weight: 700; color: var(--accent);
  background: var(--accent-pale); border-radius: 999px; padding: .25em .7em; font-variant-numeric: tabular-nums;
}

.occ-chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 26px; justify-content: center; }
.occ-chips-label {
  font-family: var(--label); font-size: var(--fs-100); letter-spacing: .16em;
  text-transform: uppercase; color: var(--muted); margin-right: 4px;
}

/* ---------------- style discovery ---------------- */
.style-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(178px, 1fr)); gap: 18px; }
.style-tile { display: flex; flex-direction: column; gap: 10px; }
.style-thumb {
  display: block; position: relative; aspect-ratio: 5/7; border-radius: 10px; overflow: hidden;
  background: var(--paper-2); box-shadow: var(--sh-1);
  transition: transform .4s var(--ease-out), box-shadow .4s var(--ease-out);
}
.style-thumb::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(38,35,38,.07);
}
.style-thumb svg { width: 100%; height: 100%; display: block; }
.style-tile:hover .style-thumb { transform: translateY(-5px); box-shadow: var(--sh-3); }
.style-name { font-weight: 700; font-size: var(--fs-300); letter-spacing: -.01em; }
.style-blurb { font-size: var(--fs-200); color: var(--muted); line-height: 1.4; margin-top: -6px; }

/* ---------------- template browser ---------------- */
.browse-layout { display: grid; grid-template-columns: 276px 1fr; gap: 44px; align-items: start; }
.filters {
  position: sticky; top: calc(var(--header-h) + 18px);
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r);
  padding: 22px; box-shadow: var(--sh-1);
  max-height: calc(100vh - var(--header-h) - 44px); overflow: auto;
}
.filters h4 {
  font-family: var(--label); font-size: var(--fs-100); letter-spacing: .2em; text-transform: uppercase;
  color: var(--accent); margin: 24px 0 11px; font-weight: 400;
}
.filters h4:first-child { margin-top: 0; }
.filter-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.filter-list button {
  width: 100%; text-align: left; background: none; border: 0; border-radius: var(--r-xs);
  padding: 9px 12px; font-size: var(--fs-200); color: var(--ink-2); transition: background .2s, color .2s;
}
.filter-list button:hover { background: var(--paper-2); color: var(--ink); }
.filter-list button.active {
  background: linear-gradient(165deg, var(--brand-soft), var(--brand)); color: #fff; font-weight: 600;
  box-shadow: var(--sh-1);
}
.filter-list button .n { float: right; opacity: .58; font-size: var(--fs-100); font-variant-numeric: tabular-nums; }

.browse-toolbar { display: flex; gap: 15px; align-items: center; flex-wrap: wrap; margin-bottom: 24px; }
.search-box { position: relative; flex: 1 1 320px; }
.search-box input {
  width: 100%; padding: .95em 1.15em .95em 3em; border: 1px solid var(--line-2);
  border-radius: var(--r-sm); background: var(--card); font: inherit; font-size: var(--fs-300);
  box-shadow: var(--sh-1); transition: border-color .25s, box-shadow .25s;
}
.search-box input::placeholder { color: var(--muted); }
.search-box input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 4px rgba(110,71,99,.14), var(--sh-1); }
.search-box .icon { position: absolute; left: 18px; top: 50%; transform: translateY(-50%); opacity: .5; font-size: 1.15rem; }
.chip-row { display: flex; gap: 9px; flex-wrap: wrap; }
.chip {
  border: 1px solid var(--line-2); background: var(--card); border-radius: 999px;
  padding: .52em 1.15em; font-size: var(--fs-200); color: var(--ink-2); font-weight: 500;
  transition: all .25s var(--ease);
}
.chip:hover { border-color: var(--accent); color: var(--ink); transform: translateY(-1px); }
.chip.active { background: var(--ink); color: var(--paper); border-color: var(--ink); }

.results-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin: 0 0 22px; flex-wrap: wrap; }
.results-head .small { font-size: var(--fs-200); }

.tpl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(252px, 1fr)); gap: 30px 24px; }
.tpl-card { background: transparent; border: 0; padding: 0; text-align: left; display: flex; flex-direction: column; gap: 13px; }
.tpl-thumb {
  position: relative; border-radius: 8px; overflow: hidden; background: var(--paper-2);
  box-shadow: var(--sh-1); aspect-ratio: 5 / 7;
  transition: transform .45s var(--ease-out), box-shadow .45s var(--ease-out);
}
.tpl-thumb::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(38,35,38,.07);
}
.tpl-thumb[data-ratio="square"] { aspect-ratio: 1 / 1; }
.tpl-thumb[data-ratio="story"] { aspect-ratio: 9 / 16; }
.tpl-thumb[data-ratio="landscape"] { aspect-ratio: 7 / 5; }
/* The lift-and-glow half of "glassmorphic hover" — --sh-lift is the same
   wide, soft, neutral shadow the collection tiles use on hover. The thumb
   itself is never blurred or made translucent: it's showing the actual
   invitation artwork, and a real design should never go hazy on hover. */
.tpl-card:hover .tpl-thumb { transform: translateY(-8px) scale(1.012); box-shadow: var(--sh-lift); }
.tpl-thumb svg { width: 100%; height: 100%; display: block; }
.tpl-thumb .skeleton {
  position: absolute; inset: 0;
  background: linear-gradient(100deg, var(--skel-1) 28%, var(--skel-2) 50%, var(--skel-1) 72%);
  background-size: 240% 100%; animation: sweep 1.5s ease-in-out infinite;
}
@keyframes sweep { to { background-position: -190% 0; } }
.tpl-thumb .hover-cta {
  position: absolute; inset: auto 0 0 0; padding: 34px 12px 16px; display: flex; justify-content: center;
  background: linear-gradient(to top, rgba(10,22,32,.8), transparent);
  opacity: 0; transform: translateY(8px);
  transition: opacity .32s var(--ease), transform .32s var(--ease-out);
}
.tpl-card:hover .hover-cta { opacity: 1; transform: translateY(0); }
/* This pill floats over the artwork rather than being part of it, so it's
   the one element on the card that gets the glass treatment: translucent
   and blurred, same material as the header. */
.hover-cta span {
  background: color-mix(in srgb, var(--paper) 78%, transparent);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  color: var(--ink); font-size: var(--fs-200); font-weight: 600;
  padding: .6em 1.3em; border-radius: 999px; box-shadow: var(--sh-2);
  border: 1px solid var(--glass-border, transparent);
}
.tpl-meta .t-name { font-weight: 600; font-size: var(--fs-300); letter-spacing: -.01em; }
.tpl-meta .t-occ { font-size: var(--fs-200); color: var(--muted); margin-top: 2px; }
/* A fixed light chip in both themes, same reasoning as .site-footer: it
   sits on a template thumbnail, which is its own bright artwork regardless
   of site theme, so its text is fixed rather than paired with --brand
   (which turns pale gold under dark mode and would wash out on a white chip). */
.badge {
  position: absolute; top: 12px; left: 12px; z-index: 2;
  background: rgba(255,255,255,.94); backdrop-filter: blur(6px);
  color: #7a4a1e; font-size: var(--fs-100); font-weight: 800;
  letter-spacing: .1em; text-transform: uppercase; padding: .34em .8em; border-radius: 999px;
  box-shadow: var(--sh-1);
}
.badge.new { color: #4a6350; }
.empty-state { text-align: center; padding: 80px 20px; color: var(--muted); font-size: var(--fs-300); }
.empty-state h3 { color: var(--ink); font-family: var(--display); font-size: 1.85rem; font-weight: 400; }
.empty-state p { margin: 0 0 18px; }

/* ---------------- marquee of designs ---------------- */
.marquee {
  overflow: hidden; padding: 6px 0 2px;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.marquee-track { display: flex; gap: 24px; width: max-content; animation: slide 62s linear infinite; }
.marquee:hover .marquee-track { animation-play-state: paused; }
.marquee-track .m-card {
  width: 196px; aspect-ratio: 5/7; border-radius: 8px; overflow: hidden; flex: none;
  background: var(--paper-2); box-shadow: var(--sh-2); transition: transform .45s var(--ease-out);
}
.marquee-track .m-card:hover { transform: translateY(-8px) scale(1.03); }
.marquee-track .m-card svg { width: 100%; height: 100%; display: block; }
@keyframes slide { to { transform: translateX(-50%); } }

/* ---------------- steps / features ---------------- */
.steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 36px; }
.step { text-align: center; }
.step .num {
  width: 64px; height: 64px; border-radius: 50%; margin: 0 auto 20px; display: grid; place-items: center;
  background: var(--card); border: 1px solid var(--line-accent); color: var(--accent);
  font-family: var(--display); font-size: 1.75rem; font-weight: 500;
  box-shadow: var(--sh-1); transition: transform .4s var(--ease-out), box-shadow .4s;
}
.step:hover .num { transform: translateY(-3px); box-shadow: var(--sh-2); }
.step h3 { font-size: 1.14rem; }
.step p { font-size: var(--fs-200); line-height: 1.6; }

.feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(292px, 1fr)); gap: 22px; }
.feature {
  position: relative; overflow: hidden;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r); padding: 28px 26px;
  transition: transform .4s var(--ease-out), box-shadow .4s var(--ease-out), border-color .3s;
}
.feature:hover { transform: translateY(-4px); box-shadow: var(--sh-2); border-color: var(--line-accent); }
.feature .ico {
  font-size: 1.4rem; margin-bottom: 15px; width: 50px; height: 50px; border-radius: 14px;
  display: grid; place-items: center; background: var(--paper-2); color: var(--brand);
}
.feature h3 { font-size: 1.1rem; margin-bottom: .38em; }
.feature p { margin: 0; font-size: var(--fs-200); color: var(--ink-2); line-height: 1.62; }

/* ---------------- pricing ---------------- */
.price-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 26px; align-items: stretch; }
.price-card {
  position: relative; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 38px 32px; display: flex; flex-direction: column;
  transition: transform .4s var(--ease-out), box-shadow .4s var(--ease-out);
}
.price-card:hover { transform: translateY(-5px); box-shadow: var(--sh-2); }
.price-card.featured {
  box-shadow: var(--sh-2);
  background: linear-gradient(var(--card), var(--card)) padding-box,
              linear-gradient(160deg, var(--accent-lt), rgba(110,71,99,.20)) border-box;
  border: 1px solid transparent;
}
.price-card.featured::before {
  content: "Most popular"; position: absolute; top: -14px; left: 50%; transform: translateX(-50%);
  background: linear-gradient(135deg, var(--metal-1), var(--metal-2)); color: var(--on-metal); font-size: var(--fs-100);
  font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  padding: .45em 1.2em; border-radius: 999px; box-shadow: var(--sh-metal); white-space: nowrap;
}
.price-card h3 { font-family: var(--display); font-size: 1.66rem; font-weight: 500; letter-spacing: -.01em; }
.price {
  font-family: var(--display); font-size: 3.4rem; font-weight: 400; color: var(--brand);
  line-height: 1; margin: 12px 0 4px; letter-spacing: -.03em;
}
.price small { font-size: var(--fs-200); color: var(--muted); font-family: var(--sans); letter-spacing: 0; }
.perks { list-style: none; margin: 24px 0 30px; padding: 0; display: flex; flex-direction: column; gap: 12px; flex: 1; }
.perks li { font-size: var(--fs-200); color: var(--ink-2); padding-left: 30px; position: relative; line-height: 1.5; }
.perks li::before {
  content: "✓"; position: absolute; left: 0; top: 1px; color: var(--accent); font-weight: 700;
  width: 20px; height: 20px; display: grid; place-items: center; font-size: .72rem;
  background: var(--accent-pale); border-radius: 50%;
}

/* ---------------- FAQ ---------------- */
.faq-list { max-width: 860px; margin-inline: auto; }
details.faq {
  border: 1px solid var(--line); border-radius: var(--r-sm); margin-bottom: 11px;
  background: var(--card); padding: 0 24px; transition: border-color .3s, box-shadow .3s;
}
details.faq[open] { border-color: var(--line-accent); box-shadow: var(--sh-1); }
details.faq summary {
  cursor: pointer; padding: 21px 0; font-weight: 600; font-size: var(--fs-400); list-style: none;
  display: flex; justify-content: space-between; gap: 18px; align-items: center;
}
details.faq summary::-webkit-details-marker { display: none; }
details.faq summary::after {
  content: "+"; color: var(--accent); font-size: 1.6rem; line-height: 1; font-weight: 300;
  transition: transform .3s var(--ease);
}
details.faq[open] summary::after { transform: rotate(45deg); }
details.faq p { color: var(--ink-2); font-size: var(--fs-300); margin: 0 0 21px; max-width: 66ch; line-height: 1.62; }

/* ---------------- scroll reveal ---------------- */
[data-reveal] { opacity: 0; transform: translateY(22px); transition: opacity .85s var(--ease-out), transform .85s var(--ease-out); }
[data-reveal].in { opacity: 1; transform: none; }
[data-reveal][data-delay="1"] { transition-delay: .09s; }
[data-reveal][data-delay="2"] { transition-delay: .18s; }
[data-reveal][data-delay="3"] { transition-delay: .27s; }

/* ============================================================
   EDITOR
   ============================================================ */
/* In the editor the app fills the window. The header used to be REMOVED here to
   buy the card height, which left no way home but the browser's back button and
   took the signed-in avatar off screen with it. Both were wrong: wherever you
   are in this product, the mark that takes you home and the face that says who
   you are signed in as belong in the same place.

   So the header stays and goes slim instead — 48px rather than 80px, brand and
   account only, the section nav and the "Start designing" call dropped because
   neither means anything while you are already designing. `--header-h` is
   redefined rather than the heights hard-coded, so the editor grid and the
   canvas measurement follow it without knowing anything changed. */
/* ---------------- studio dark theme ----------------
   The creator studio gets its own always-dark theme, independent of the
   public toggle: a NEUTRAL graphite rather than the public site's warm
   dark, on purpose. Every other surface in this product can afford a
   tinted dark ground; a canvas cannot — a warm or saturated backdrop
   would visibly tint how a card's own colours read while editing them,
   the same reason Figma and Photoshop surround their canvas with neutral
   grey rather than a brand colour. Because almost every editor rule below
   already reads --paper/--card/--ink/--line/--accent rather than a literal
   colour, redefining those roles here is enough to re-skin the whole
   studio — the same leverage the public dark theme got from :root. */
body.editing {
  --header-h: 48px;
  --paper:      #161618; --paper-2: #1c1c1f; --paper-3: #212124; --card: #1e1e21;
  --ink:        #ededef; --ink-2:   #c7c7cc; --ink-3:   #9a9aa0; --muted: #7d7d84;
  /* Selection and active states use a desaturated indigo — never the warm
     brand gold — so a chip or focus ring never reads as part of the
     artwork it is sitting next to. */
  --accent:      #9494f2; --accent-lt: #7a7ad6; --accent-pale: rgba(148,148,242,.14);
  --line:        rgba(237,237,239,.10); --line-2: rgba(237,237,239,.18);
  --line-accent: rgba(148,148,242,.45);
  --sh-1: 0 1px 2px rgba(0,0,0,.35), 0 2px 5px rgba(0,0,0,.25);
  --sh-2: 0 2px 6px rgba(0,0,0,.30), 0 10px 24px rgba(0,0,0,.35);
  --sh-3: 0 5px 14px rgba(0,0,0,.35), 0 26px 56px rgba(0,0,0,.45);
  --glass-bg: rgba(22,22,24,.72); --glass-bg-2: rgba(22,22,24,.92); --glass-border: rgba(237,237,239,.10);
  --mesh-1: rgba(148,148,242,.10); --mesh-2: rgba(148,148,242,.06); --mesh-3: rgba(148,148,242,.08);
}
body.editing .site-header {
  background: var(--glass-bg-2); border-bottom-color: var(--glass-border);
}
body.editing .header-inner { gap: 16px; }
body.editing .main-nav { display: none; }
/* the nav carried `margin-left: auto`, so hiding it collapsed the account
   controls against the brand instead of leaving them on the right */
body.editing .header-actions { margin-left: auto; }
body.editing .header-actions .btn-primary { display: none; }
body.editing .brand { font-size: 1.1rem; gap: 9px; }
body.editing .brand-mark { width: 30px; height: 30px; border-radius: 9px; }
body.editing .brand-mark svg { width: 16px; height: 16px; }
body.editing .acct-avatar { width: 28px; height: 28px; font-size: .8rem; }
body.editing .nav-toggle { display: none; }
/* The studio is always dark now — its own neutral theme above, not the
   public toggle's — so there is nothing for the seal to switch here.
   js/theme.js still suspends the public preference while `.editing` is
   set, which is what keeps the two themes from fighting on the header. */
body.editing .theme-toggle { display: none; }
.editor { display: grid; grid-template-columns: 372px 1fr 352px; height: calc(100vh - var(--header-h)); overflow: hidden; }
.ed-panel { background: var(--card); border-right: 1px solid var(--line); overflow-y: auto; scrollbar-width: thin; }
.ed-panel.right { border-right: 0; border-left: 1px solid var(--line); }
.ed-panel::-webkit-scrollbar { width: 11px; }
.ed-panel::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 99px; border: 3px solid var(--card); }

.ed-stage {
  display: flex; flex-direction: column; min-height: 0; overflow: hidden; position: relative;
  background:
    radial-gradient(800px 420px at 50% -6%, var(--mesh-1) 0%, transparent 66%),
    linear-gradient(180deg, var(--paper-2), var(--paper-3));
}
.ed-topbar {
  display: flex; align-items: center; gap: 12px; padding: 14px 22px; flex-wrap: wrap;
  background: var(--glass-bg); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line); z-index: 5;
}
.ed-topbar .spacer { flex: 1; }
.ed-title { font-family: var(--display); font-size: 1.36rem; font-weight: 500; line-height: 1.15; letter-spacing: -.015em; }
.ed-title span {
  display: block; font-family: var(--label); font-size: var(--fs-100); color: var(--accent);
  letter-spacing: .17em; text-transform: uppercase; margin-top: 3px;
}
.ed-canvas-wrap {
  flex: 1; min-height: 0; display: grid; place-items: center;
  padding: 14px 18px 8px; overflow: auto;
}
.card-shell { max-height: 100%; max-width: 100%; }
.card-shell {
  background: #fff; border-radius: 5px; overflow: hidden; position: relative; line-height: 0;
  max-width: 100%; max-height: 100%;
  box-shadow: 0 3px 10px rgba(38,35,38,.10), 0 24px 60px rgba(38,35,38,.20), 0 60px 120px rgba(38,35,38,.10);
}
/* THE CARD TAKES WHAT IS LEFT, MEASURED RATHER THAN GUESSED.

   This was a calc() against 100vh with a hand-tuned reserve for the bars around
   it -- and a hand-tuned reserve is wrong the moment a toolbar wraps, a browser
   is zoomed, or a laptop is short. Twice now the card has been cropped because
   the arithmetic did not match the furniture.

   So the stage is a flex column and the canvas is its flexible row: the card is
   simply told to fit the space that remains, whatever that turns out to be.
   min-height:0 is the part that makes it work -- without it a flex item refuses
   to shrink below its content and the card pushes the strip off the screen. */
/* The ceiling is set in js (fitCardToStage) from the space the stage actually
   has, because a percentage here resolves against a shrink-to-fit parent and a
   calc() against 100vh cannot know how tall the toolbars ended up. The value
   below is only the pre-measurement fallback. */
.card-shell svg {
  display: block; width: auto; height: auto;
  max-height: calc(100vh - 210px); max-width: 100%;
}
.card-shell .el-hit { cursor: pointer; }
/* A drag that starts on the card is always a drag of the card's contents. Left
   to the browser's default, a finger or pen stroke here is a page scroll and the
   element never moves -- the page around the card keeps its own scrolling. */
.card-shell { touch-action: none; }
.card-shell.picking .el-hit:hover { outline: 1.5px dashed var(--brand); outline-offset: 2px; }
.sel-ring { position: absolute; border: 1.5px solid var(--brand); border-radius: 3px; pointer-events: none; box-shadow: 0 0 0 3px rgba(81,48,71,.16); }
.anim-canvas { position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none; }

/* ---------------- left panel tabs ---------------- */
.ed-tabs {
  display: flex; gap: 4px; padding: 12px 14px 0; position: sticky; top: 0; z-index: 3;
  background: var(--card); border-bottom: 1px solid var(--line);
}
.ed-tabs button {
  flex: 1; border: 0; background: none; padding: 11px 8px; border-radius: 10px 10px 0 0;
  font-size: var(--fs-200); font-weight: 600; color: var(--ink-3);
  border-bottom: 2px solid transparent; transition: all .2s;
}
.ed-tabs button:hover { background: var(--paper-2); color: var(--ink); }
.ed-tabs button.active { color: var(--brand); border-bottom-color: var(--brand); }

/* ---------------- elements library ---------------- */
.el-group {
  font-family: var(--label); font-size: var(--fs-100); letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent); font-weight: 400; margin: 18px 0 8px;
}
.el-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(78px, 1fr)); gap: 8px; }
.el-tile {
  border: 1px solid var(--line-2); border-radius: 11px; background: var(--card);
  color: var(--brand); padding: 9px 6px 6px; display: flex; flex-direction: column;
  align-items: center; gap: 4px; transition: all .22s var(--ease);
}
.el-tile:hover { border-color: var(--accent); background: var(--tint-active); transform: translateY(-2px); box-shadow: var(--sh-1); }
.el-tile svg { width: 100%; aspect-ratio: 1; display: block; }
.el-tile span {
  font-size: .66rem; color: var(--ink-3); line-height: 1.2; text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}
.el-layers { display: flex; flex-direction: column; gap: 4px; }
.el-layer {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 9px;
  font-size: var(--fs-200); border: 1px solid transparent; background: none; width: 100%; text-align: left;
}
.el-layer:hover { background: var(--paper-2); }
.el-layer.active { background: var(--tint-active); border-color: var(--brand); font-weight: 600; }
.el-layer .txt { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.el-layer .el-up, .el-layer .el-del {
  opacity: .5; padding: 2px 6px; border-radius: 5px; font-size: .8rem;
}
.el-layer .el-up:hover, .el-layer .el-del:hover { opacity: 1; background: var(--paper-3); }
.el-props { border-top: 1px solid var(--line); margin-top: 12px; padding-top: 14px; }

/* selection grip for placed elements */
.sel-ring.el-ring { border-style: solid; border-color: var(--brand); }
.sel-ring .grip {
  position: absolute; right: -7px; bottom: -7px; width: 14px; height: 14px;
  background: var(--card); border: 2px solid var(--brand); border-radius: 50%;
  cursor: nwse-resize; pointer-events: auto; box-shadow: var(--sh-1);
}

/* ---------------- floating format toolbar ---------------- */
.fmt-bar {
  position: absolute; z-index: 6; display: flex; align-items: center; gap: 2px;
  background: var(--ink); border-radius: 10px; padding: 5px 7px;
  box-shadow: var(--sh-3); pointer-events: auto; line-height: 1;
}
.fmt-bar button {
  background: none; border: 0; color: rgba(255,255,255,.82); width: 28px; height: 28px;
  border-radius: 6px; font-size: .8rem; display: grid; place-items: center; padding: 0;
}
.fmt-bar button:hover { background: rgba(255,255,255,.16); color: #fff; }
.fmt-bar button.on { background: var(--accent-lt); color: var(--ink); }
/* the artwork toolbar: same pill, one destructive action marked as such */
.el-bar button { font-size: 1rem; line-height: 1; min-width: 30px; }
.el-bar .fmt-del { color: #ffb4ad; }
.el-bar .fmt-del:hover { background: rgba(255,120,110,.24); color: #fff; }
.fmt-sep { width: 1px; height: 18px; background: rgba(255,255,255,.22); margin: 0 3px; }
.fmt-col { position: relative; width: 28px; height: 28px; display: grid; place-items: center; cursor: pointer; }
.fmt-col input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.fmt-col span {
  width: 17px; height: 17px; border-radius: 5px; border: 2px solid rgba(255,255,255,.5);
  background: linear-gradient(135deg, #fff 45%, var(--accent-lt) 55%);
}
.al { display: block; width: 13px; height: 9px; position: relative; }
.al::before, .al::after { content: ""; position: absolute; left: 0; right: 0; height: 1.5px; background: currentColor; }
.al::before { top: 0; }
.al::after { bottom: 0; }
.al-left::after { right: 4px; }
.al-right::after { left: 4px; }
.al-center::after { left: 2.5px; right: 2.5px; }

/* ---------------- page strip ---------------- */
.page-strip {
  border-top: 1px solid var(--line); background: var(--glass-bg-2);
  backdrop-filter: blur(14px); padding: 8px 14px 9px; flex: none;
}
.pg-scroll { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; align-items: stretch; }
.pg-scroll::-webkit-scrollbar { height: 8px; }
.pg-scroll::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 99px; }
.pg-cell {
  flex: none; width: 54px; background: none; border: 0; padding: 0; cursor: grab;
  display: flex; flex-direction: column; gap: 5px; text-align: center;
}
.pg-cell .pg-thumb {
  display: block; width: 54px; aspect-ratio: 5/7; border-radius: 6px; overflow: hidden;
  background: var(--card); box-shadow: var(--sh-1); border: 2px solid transparent;
  transition: border-color .2s, transform .25s var(--ease-out), box-shadow .25s;
}
.pg-cell .pg-thumb svg { width: 100%; height: 100%; display: block; }
.pg-cell:hover .pg-thumb { transform: translateY(-2px); box-shadow: var(--sh-2); }
.pg-cell.active .pg-thumb { border-color: var(--brand); box-shadow: var(--sh-2); }
.pg-label {
  font-size: .74rem; color: var(--muted); line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pg-cell.active .pg-label { color: var(--brand); font-weight: 700; }
.pg-label b { color: var(--ink-3); }
.pg-add {
  flex: none; width: 54px; aspect-ratio: 5/7; border: 1.5px dashed var(--line-2);
  border-radius: 6px; background: none; color: var(--ink-3); display: flex;
  flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  font-size: .72rem; transition: all .25s var(--ease); align-self: flex-start;
}
.pg-add:hover { border-color: var(--accent); color: var(--brand); background: var(--card); }
.pg-plus { font-size: 1.5rem; line-height: 1; font-weight: 300; }
.pg-tools { display: flex; align-items: center; gap: 6px; margin-top: 6px; flex-wrap: wrap; }
.pg-tools .btn { padding: .38em .7em; font-size: var(--fs-100); }
.pg-count { font-size: var(--fs-100); color: var(--muted); margin-right: auto; }
.all-pages {
  font-size: .62rem; letter-spacing: .08em; text-transform: uppercase; font-family: var(--sans);
  background: var(--paper-2); color: var(--ink-3); padding: .22em .6em; border-radius: 999px; font-weight: 700;
}

/* ---------------- symbols on a page ---------------- */
.sym-list { display: flex; flex-direction: column; gap: 7px; margin-bottom: 10px; }
.sym-row { display: grid; grid-template-columns: auto 1fr auto; gap: 6px; align-items: center; }
.sym-row .sym-chip {
  font-size: .7rem; background: var(--paper-2); color: var(--ink-3); padding: .3em .55em;
  border-radius: 6px; max-width: 74px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sym-row select { font-size: .74rem !important; padding: .3em .4em !important; }
.sym-row input[type="range"] { grid-column: 1 / -1; accent-color: var(--brand); height: 18px; }
.sym-del { border: 0; background: none; color: var(--muted); font-size: .9rem; padding: 2px 5px; border-radius: 5px; }
.sym-del:hover { background: var(--paper-2); color: var(--ink); }

/* ---------------- pickers ---------------- */
.page-pick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); gap: 10px; }
.page-pick {
  border: 1px solid var(--line-2); border-radius: var(--r-sm); background: var(--card);
  padding: 14px 13px; text-align: left; display: flex; flex-direction: column; gap: 3px;
  transition: all .25s var(--ease);
}
.page-pick:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: var(--sh-1); }
.page-pick .pp-ico { font-size: 1.3rem; line-height: 1; margin-bottom: 3px; }
.page-pick .pp-name { font-weight: 700; font-size: var(--fs-200); }
.page-pick .pp-hint { font-size: .74rem; color: var(--muted); line-height: 1.35; }

.sym-group {
  font-family: var(--label); font-size: var(--fs-100); letter-spacing: .18em; text-transform: uppercase;
  color: var(--accent); font-weight: 400; margin: 20px 0 8px;
}
.sym-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(60px, 1fr)); gap: 8px; }
.sym-tile {
  aspect-ratio: 1; border: 1px solid var(--line-2); border-radius: 10px; background: var(--card);
  color: var(--brand); padding: 9px; transition: all .22s var(--ease);
}
.sym-tile:hover { border-color: var(--accent); background: var(--tint-active); transform: translateY(-2px); }
.sym-tile svg { width: 100%; height: 100%; display: block; }

/* The panels are reference, not reading matter: they hold labels, values and
   controls, and at body size they pushed the interesting part -- the card --
   off the screen. One step down through the whole panel, spacing to match. */
.ed-panel { font-size: var(--fs-200); }
.ed-panel .btn { font-size: var(--fs-200); }
.ed-panel .field input[type="text"], .ed-panel .field textarea,
.ed-panel .field select, .ed-panel input[type="search"] { font-size: var(--fs-200); padding: .58em .75em; }
.ed-panel .field { margin-bottom: 12px; }
.ed-group { border-bottom: 1px solid var(--line); padding: 16px 18px; }
.ed-group h4 {
  font-family: var(--label); font-size: var(--fs-100); letter-spacing: .19em; text-transform: uppercase;
  color: var(--accent); margin: 0 0 11px; font-weight: 400;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.tag-new {
  background: linear-gradient(135deg, var(--metal-1), var(--metal-2)); color: var(--on-metal); font-size: .68rem;
  font-weight: 800; letter-spacing: .09em; padding: .3em .68em; border-radius: 999px; text-transform: uppercase;
}

.field { margin-bottom: 15px; }
.field label { display: block; font-size: var(--fs-100); font-weight: 700; color: var(--ink-3); margin-bottom: 7px; letter-spacing: .015em; }
.field input[type="text"], .field textarea, .field select,
.ed-panel input[type="text"], .ed-panel select, .ed-panel input[type="search"] {
  width: 100%; padding: .7em .85em; border: 1px solid var(--line-2); border-radius: 10px;
  font: inherit; font-size: var(--fs-300); background: var(--paper); color: var(--ink); resize: vertical;
  transition: border-color .2s, box-shadow .2s, background .2s;
}
.field textarea { min-height: 68px; line-height: 1.45; }
.field input:focus, .field textarea:focus, .field select:focus {
  outline: none; background: var(--card); border-color: var(--accent); box-shadow: 0 0 0 3px rgba(110,71,99,.14);
}
.field .hint { font-size: var(--fs-100); color: var(--muted); margin-top: 6px; line-height: 1.45; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }

.swatch-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(58px, 1fr)); gap: 9px; }
.swatch {
  border: 0; border-radius: 10px; padding: 0; overflow: hidden; height: 46px; display: flex;
  box-shadow: inset 0 0 0 1px rgba(38,35,38,.1); transition: transform .25s var(--ease), box-shadow .25s;
}
.swatch span { flex: 1; }
.swatch:hover { transform: translateY(-2px); box-shadow: inset 0 0 0 1px rgba(38,35,38,.1), var(--sh-1); }
.swatch.active { box-shadow: 0 0 0 2px var(--card), 0 0 0 4px var(--brand); }

.opt-list { display: flex; flex-direction: column; gap: 8px; }
.opt {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  border: 1px solid var(--line-2); border-radius: 11px; padding: 10px 14px; background: var(--card);
  font-size: var(--fs-200); text-align: left; width: 100%; transition: all .25s var(--ease);
}
.opt:hover { border-color: var(--accent); transform: translateX(2px); }
.opt.active { border-color: var(--brand); background: var(--tint-active); font-weight: 600; }
.opt .preview-name { font-family: var(--display); font-size: 1.3rem; }

.seg { display: flex; border: 1px solid var(--line-2); border-radius: 10px; overflow: hidden; }
.seg button { flex: 1; border: 0; background: var(--card); padding: .6em; font-size: var(--fs-200); color: var(--ink-2); }
.seg button.active { background: var(--ink); color: var(--paper); font-weight: 600; }

/* Signature ambient-effect controls — three real, working effects (Gold
   dust, Twinkling stars, Soft bokeh) given their own premium chip instead
   of sitting alphabetically inside .mini-btns below. */
.fx-featured-row { display: flex; gap: 9px; flex-wrap: wrap; }
.fx-featured {
  display: flex; align-items: center; gap: 9px; flex: 1 1 auto; min-width: 108px;
  border: 1px solid var(--line-2); background: var(--card); border-radius: var(--r);
  padding: .7em .9em; transition: transform .3s var(--ease-out), border-color .3s, box-shadow .3s, background .3s;
}
.fx-featured:hover { transform: translateY(-2px); border-color: var(--line-accent); box-shadow: var(--sh-1); }
.fx-featured .fx-ico {
  display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; flex: none;
  background: var(--accent-pale); color: var(--accent); font-size: .95rem; transition: transform .4s var(--ease-out);
}
.fx-featured .fx-name { font-size: var(--fs-100); font-weight: 600; color: var(--ink-2); text-align: left; }
.fx-featured:hover .fx-ico { transform: scale(1.12) rotate(-8deg); }
.fx-featured.active {
  border-color: var(--accent); background: var(--accent-pale); box-shadow: 0 0 0 1px var(--accent) inset;
}
.fx-featured.active .fx-ico { background: var(--accent); color: var(--on-brand, #fff); }
.fx-featured.active .fx-name { color: var(--ink); }

/* An honestly-disabled preview of a feature that doesn't exist yet — dashed
   border rather than the solid one real buttons get, so it reads as "not
   here yet" even before the eye reaches the "soon" badge. */
.ai-avatar-btn {
  margin-top: 12px; border: 1px dashed var(--line-accent) !important; gap: 8px;
  justify-content: flex-start !important; position: relative;
}
.ai-avatar-btn .tag-new { margin-left: auto; }
.ai-avatar-ico { font-size: 1rem; }

.mini-btns { display: flex; gap: 7px; flex-wrap: wrap; }
.mini-btns button {
  border: 1px solid var(--line-2); background: var(--card); border-radius: 9px;
  padding: .46em .8em; font-size: var(--fs-100); color: var(--ink-2); transition: all .2s var(--ease);
}
.mini-btns button:hover { border-color: var(--accent); color: var(--ink); }
.mini-btns button.active { background: var(--ink); color: var(--paper); border-color: var(--ink); font-weight: 600; }

.color-row { display: flex; gap: 9px; align-items: center; }
.color-row input[type="color"] { width: 42px; height: 36px; padding: 0; border: 1px solid var(--line-2); border-radius: 8px; background: none; cursor: pointer; }
.range-row { display: flex; align-items: center; gap: 11px; }
.range-row input[type="range"] { flex: 1; accent-color: var(--brand); height: 22px; }
.range-row .val { font-size: var(--fs-100); color: var(--muted); min-width: 46px; text-align: right; font-variant-numeric: tabular-nums; }

.hue-slider {
  -webkit-appearance: none; appearance: none; width: 100%; height: 28px; border-radius: 999px;
  border: 1px solid var(--line-2); cursor: pointer; padding: 0; box-shadow: inset 0 1px 3px rgba(0,0,0,.16);
  background: linear-gradient(to right,
    hsl(0,72%,52%), hsl(30,72%,52%), hsl(60,72%,52%), hsl(90,72%,52%), hsl(120,72%,52%),
    hsl(150,72%,52%), hsl(180,72%,52%), hsl(210,72%,52%), hsl(240,72%,52%), hsl(270,72%,52%),
    hsl(300,72%,52%), hsl(330,72%,52%), hsl(360,72%,52%));
}
.hue-slider::-webkit-slider-thumb {
  -webkit-appearance: none; width: 24px; height: 24px; border-radius: 50%;
  background: #fff; border: 3px solid var(--ink); box-shadow: 0 2px 6px rgba(0,0,0,.4); cursor: grab;
}
.hue-slider::-moz-range-thumb { width: 22px; height: 22px; border-radius: 50%; background: #fff; border: 3px solid var(--ink); }

.role-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.role-row { display: flex; align-items: center; gap: 8px; font-size: var(--fs-100); color: var(--ink-3); }
.role-row input[type="color"] { width: 32px; height: 30px; padding: 0; border: 1px solid var(--line-2); border-radius: 7px; background: none; flex: none; cursor: pointer; }
.role-row span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

details.tune { border: 1px solid var(--line); border-radius: 12px; padding: 0 14px; background: var(--paper); }
details.tune summary {
  cursor: pointer; list-style: none; padding: 13px 0; font-size: var(--fs-200); font-weight: 700;
  color: var(--ink-3); display: flex; justify-content: space-between; align-items: center;
}
details.tune summary::-webkit-details-marker { display: none; }
details.tune summary::after { content: "▾"; color: var(--accent); font-size: .86rem; }
details.tune[open] summary::after { content: "▴"; }
details.tune[open] { padding-bottom: 10px; }
details.tune .field:last-child { margin-bottom: 4px; }

.layer-list { display: flex; flex-direction: column; gap: 4px; }
.layer {
  display: flex; align-items: center; gap: 10px; padding: 9px 11px; border-radius: 9px;
  font-size: var(--fs-200); border: 1px solid transparent; background: none; width: 100%; text-align: left;
  transition: background .2s;
}
.layer:hover { background: var(--paper-2); }
.layer.active { background: var(--tint-active); border-color: var(--brand); font-weight: 600; }
.layer .eye { margin-left: auto; opacity: .5; }
.layer .txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.locked-note { background: var(--note-bg); border: 1px solid var(--line-accent); border-radius: 12px; padding: 14px; font-size: var(--fs-200); color: var(--note-ink); }

/* ---------------- modal ---------------- */
.modal-backdrop {
  position: fixed; inset: 0; background: rgba(9, 20, 30, .6); backdrop-filter: blur(5px);
  display: none; place-items: center; z-index: 200; padding: 24px; overflow: auto;
}
.modal-backdrop.open { display: grid; animation: fadeIn .28s var(--ease); }
@keyframes fadeIn { from { opacity: 0; } }
.modal {
  background: var(--card); border-radius: var(--r-lg); width: min(620px, 100%); padding: 40px;
  box-shadow: var(--sh-3); position: relative; max-height: 92vh; overflow: auto;
  animation: pop .4s var(--ease-out);
}
@keyframes pop { from { opacity: 0; transform: translateY(18px) scale(.985); } }
.modal-close {
  position: absolute; top: 18px; right: 20px; background: none; border: 0; font-size: 1.7rem;
  line-height: 1; color: var(--muted); width: 38px; height: 38px; border-radius: 50%;
  transition: background .2s, color .2s;
}
.modal-close:hover { background: var(--paper-2); color: var(--ink); }
.modal h2 { font-size: 1.95rem; }

.plan-pick { display: flex; flex-direction: column; gap: 12px; margin: 22px 0; }
.plan-opt {
  display: flex; gap: 16px; align-items: flex-start; border: 1px solid var(--line-2);
  border-radius: var(--r-sm); padding: 17px 19px; cursor: pointer; background: var(--card);
  text-align: left; width: 100%; transition: all .25s var(--ease);
}
.plan-opt:hover { border-color: var(--accent); transform: translateY(-1px); }
.plan-opt.active { border-color: var(--brand); background: var(--tint-active); box-shadow: var(--sh-1); }
.plan-opt .p-name { font-weight: 700; font-size: var(--fs-300); }
.plan-opt .p-desc { font-size: var(--fs-200); color: var(--muted); }
.plan-opt .p-price { margin-left: auto; font-family: var(--display); font-size: 1.9rem; color: var(--brand); white-space: nowrap; }

.demo-banner {
  background: var(--note-bg); border: 1px solid var(--line-accent); color: var(--note-ink);
  border-radius: 12px; padding: 16px 18px; font-size: var(--fs-200); line-height: 1.58;
}
.pay-methods { display: grid; grid-template-columns: repeat(auto-fit, minmax(122px, 1fr)); gap: 10px; margin: 16px 0; }
.pay-method {
  border: 1px solid var(--line-2); border-radius: 12px; padding: 15px 8px; text-align: center;
  font-size: var(--fs-200); background: var(--card); transition: all .22s var(--ease);
}
.pay-method:hover { border-color: var(--accent); }
.pay-method.active { border-color: var(--brand); background: var(--tint-active); font-weight: 600; }
.pay-method .m-ico { font-size: 1.5rem; display: block; margin-bottom: 4px; }
.spinner { width: 42px; height: 42px; border: 3px solid var(--paper-3); border-top-color: var(--brand); border-radius: 50%; animation: spin .8s linear infinite; margin: 28px auto; }
@keyframes spin { to { transform: rotate(360deg); } }
.success-tick {
  font-size: 2.8rem; text-align: center; color: var(--ok);
  width: 76px; height: 76px; margin: 4px auto 12px; border-radius: 50%;
  background: #e4f2ee; display: grid; place-items: center; animation: pop .5s var(--ease-out);
}
.dl-list { display: flex; flex-direction: column; gap: 10px; margin-top: 20px; }

/* ---------------- toasts ---------------- */
.toast-stack { position: fixed; bottom: 28px; left: 50%; transform: translateX(-50%); z-index: 300; display: flex; flex-direction: column; gap: 10px; align-items: center; }
/* Fixed dark glass in both themes, same reasoning as .site-footer above. */
.toast {
  background: rgba(38,35,38,.90); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  color: #faf7f2; padding: .82em 1.6em; border-radius: var(--r);
  font-size: var(--fs-300); font-weight: 500; box-shadow: var(--sh-3); animation: rise .38s var(--ease-out);
}
@keyframes rise { from { opacity: 0; transform: translateY(14px); } }

/* ---------------- footer ----------------
   Now theme-native rather than a fixed dark band: --paper-2/--paper-3 and
   --ink already carry the right value in both themes (cream in light,
   velvet-charcoal in dark), so the footer just reads the same role tokens
   as everything above it and a single hairline is the only seam. */
.site-footer {
  background: var(--paper-2); color: var(--ink-2); padding: 76px 0 40px;
  border-top: 1px solid var(--line);
}
:root[data-theme="dark"] .site-footer { background: var(--paper-3); }
.footer-grid { display: grid; grid-template-columns: 1.3fr 1fr 1fr 1fr; gap: 44px; }
.footer-grid h4 {
  font-family: var(--font-label); font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--accent); font-weight: 600; margin-bottom: 1.15em;
}
.footer-copyright { margin-top: 22px; }
.footer-note { margin-top: 16px; }
.brand-footer { margin-bottom: 18px; }
.link-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; font-size: var(--fs-200); }
.link-list a { color: var(--ink-2); transition: color .2s; }
.link-list a:hover { color: var(--accent); }

/* ---------------- responsive ---------------- */
@media (max-width: 1320px) {
  .editor { grid-template-columns: 340px 1fr 328px; }
}
@media (max-width: 1180px) {
  .editor { grid-template-columns: 1fr; height: auto; overflow: visible; }
  .ed-stage {
    order: 1; position: sticky; top: var(--header-h); z-index: 8;
    border-bottom: 1px solid var(--line); box-shadow: 0 8px 22px rgba(38,35,38,.10);
  }
  .ed-panel.left { order: 2; }
  .ed-panel.right { order: 3; border-left: 0; }
  .ed-panel { border-right: 0; border-top: 1px solid var(--line); }
  .ed-canvas-wrap { padding: 18px; }
  .card-shell svg { max-height: 46vh; }
  .ed-topbar { position: sticky; top: 0; }
}
@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; }
  /* The fan's cards are width:48% of their container. That was sized for the
     half-width grid column; when the grid collapses to one column the container
     doubles and the cards inflate to ~553px tall inside a 340px box, spilling
     over the eyebrow badge and the headline beneath.

     Fixing the height alone would not work — the cards would still be huge. The
     container has to keep roughly the width it was designed for, and then take
     its height from that width rather than a fixed pixel value. The ratio: the
     third card sits at top:15% and is 48% x 7/5 = 67% of the width tall, so the
     box needs to be at least 0.79 x its width. 0.82 leaves a little air. */
  .hero-fan {
    order: -1;
    width: min(100%, 460px);
    margin-inline: auto;
    height: auto;
    aspect-ratio: 1 / 0.82;
  }
  .browse-layout { grid-template-columns: 1fr; }
  .filters { position: static; max-height: none; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 760px) {
  :root { --header-h: 68px; }
  html { font-size: 16.5px; }
  .wrap { width: min(1360px, 100% - 34px); }
  .main-nav {
    display: none; position: absolute; top: var(--header-h); left: 0; right: 0;
    background: var(--paper); flex-direction: column; gap: 0; padding: 10px 24px 20px;
    border-bottom: 1px solid var(--line); box-shadow: var(--sh-2);
  }
  .main-nav.open { display: flex; }
  .main-nav a { padding: 15px 0; border-bottom: 1px solid var(--line); }
  .nav-toggle { display: block; }
  .eyebrow { letter-spacing: .15em; padding: .55em 1.05em; }
  /* Deliberately no fixed height here. The ≤900px rule above derives the
     fan's height from its own width via aspect-ratio, and re-pinning a pixel
     height at this breakpoint reintroduced the overlap it was fixing — at
     760px the cards ran 55px past the box and back over the eyebrow. Narrow
     the box instead and let the ratio do the rest. */
  .hero-fan { width: min(100%, 380px); }
  .hero-stats { gap: 22px 28px; }
  .hero-stats div strong { font-size: 1.9rem; }
  .ed-panel { max-height: none; }
  .ed-topbar .ed-title { font-size: 1.12rem; }
  .card-shell svg { max-height: 42vh; }
  .footer-grid { grid-template-columns: 1fr; }
  .tpl-grid { grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); gap: 22px 16px; }
  .modal { padding: 28px 24px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
  .marquee-track { animation: none; }
}

@media print {
  .site-header, .site-footer, .ed-panel, .ed-topbar, .toast-stack { display: none !important; }
  body::after { display: none; }
  .ed-canvas-wrap { padding: 0; }
  .card-shell { box-shadow: none; }
}

/* discovery: mobile-first adjustments */
@media (max-width: 760px) {
  .disc-h1 { font-size: clamp(2.1rem, 9vw, 2.9rem); }
  .disc-search input { font-size: var(--fs-300); padding: .95em 2.8em .95em 3em; }
  .ev-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
  .ev-card { padding: 16px 14px 14px; }
  .ev-art { height: 54px; }
  .ev-glyph { width: 44px; height: 44px; }
  .disc-cats { grid-template-columns: 1fr; }
  .custom-callout { flex-direction: column; align-items: flex-start; padding: 22px; }
  .custom-callout .btn { width: 100%; }
  .ev-hero-grid { grid-template-columns: 1fr; gap: 22px; }
  .ev-hero-art { order: -1; }
  .ev-hero-art .ev-glyph { width: 120px; }
  /* a horizontal rail beats a cramped grid on a phone */
  .suite-flow {
    justify-content: flex-start; flex-wrap: nowrap; overflow-x: auto;
    padding-bottom: 10px; margin-inline: -17px; padding-inline: 17px;
    scroll-snap-type: x mandatory;
  }
  .flow-step { flex: none; scroll-snap-align: start; }
  .occ-chips { justify-content: flex-start; flex-wrap: nowrap; overflow-x: auto; padding-bottom: 8px; }
  .occ-chips .chip { flex: none; }
}

/* ============================================================
   COLLECTIONS — coordinated design sets
   ============================================================ */
.col-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 20px; }
.col-card {
  display: flex; flex-direction: column; gap: 16px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 22px 22px 24px;
  transition: transform .4s var(--ease-out), box-shadow .4s var(--ease-out), border-color .3s;
}
.col-card:hover { transform: translateY(-5px); box-shadow: var(--sh-2); border-color: var(--line-accent); }

/* the three-card fan that says "these belong together" */
.col-stack { position: relative; display: block; height: 172px; }
.col-stack .col-pv {
  position: absolute; top: 0; width: 120px; aspect-ratio: 5/7; border-radius: 7px;
  overflow: hidden; background: var(--paper-2); box-shadow: var(--sh-2);
  transition: transform .45s var(--ease-out);
}
.col-stack .col-pv:nth-child(1) { left: 50%; margin-left: -60px; z-index: 3; }
.col-stack .col-pv:nth-child(2) { left: 50%; margin-left: -118px; transform: rotate(-8deg) translateY(6px); z-index: 1; }
.col-stack .col-pv:nth-child(3) { left: 50%; margin-left: 2px; transform: rotate(8deg) translateY(6px); z-index: 2; }
.col-card:hover .col-stack .col-pv:nth-child(2) { transform: rotate(-12deg) translate(-8px, 2px); }
.col-card:hover .col-stack .col-pv:nth-child(3) { transform: rotate(12deg) translate(8px, 2px); }
.col-pv svg { width: 100%; height: 100%; display: block; }
.col-body { display: flex; flex-direction: column; gap: 5px; }
.col-name { font-family: var(--display); font-size: 1.5rem; font-weight: 500; letter-spacing: -.01em; }
.col-desc { font-size: var(--fs-200); color: var(--ink-2); line-height: 1.5; }
.col-meta { font-size: var(--fs-100); color: var(--accent); font-weight: 700; margin-top: 4px; }

/* collection detail */
.col-hero-grid { display: grid; grid-template-columns: 1.2fr .8fr; gap: 46px; align-items: center; }
.col-hero-art { display: grid; place-items: center; }
.col-pv.big {
  display: block; width: min(300px, 100%); aspect-ratio: 5/7; border-radius: 10px;
  overflow: hidden; background: var(--paper-2); box-shadow: var(--sh-3);
}
.col-facts { display: flex; flex-wrap: wrap; gap: 8px; margin: 20px 0 26px; }
.col-facts span {
  font-size: var(--fs-100); font-weight: 600; color: var(--ink-2);
  background: var(--paper-2); border-radius: 999px; padding: .4em 1em;
}
.col-page-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(184px, 1fr)); gap: 22px 18px; }
.col-page { display: flex; flex-direction: column; gap: 10px; }
.col-page .col-pv {
  display: block; aspect-ratio: 5/7; border-radius: 8px; overflow: hidden;
  background: var(--paper-2); box-shadow: var(--sh-1);
  transition: transform .4s var(--ease-out), box-shadow .4s var(--ease-out);
}
.col-page:hover .col-pv { transform: translateY(-4px); box-shadow: var(--sh-2); }
.cp-name { font-size: var(--fs-200); font-weight: 600; text-align: center; }

@media (max-width: 900px) {
  .col-hero-grid { grid-template-columns: 1fr; gap: 26px; }
  .col-hero-art { order: -1; }
  .col-pv.big { width: min(220px, 60%); }
}
@media (max-width: 760px) {
  .col-grid { grid-template-columns: 1fr; }
  .col-stack { height: 152px; }
  .col-stack .col-pv { width: 104px; }
  .col-stack .col-pv:nth-child(1) { margin-left: -52px; }
  .col-stack .col-pv:nth-child(2) { margin-left: -104px; }
  .col-stack .col-pv:nth-child(3) { margin-left: 0; }
  .col-page-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 16px 12px; }
}

/* shared event details block */
.ed-event { background: linear-gradient(180deg, var(--paper-2), var(--card)); }
.ed-event .field label { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.link-state {
  font-size: .62rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--accent); background: var(--accent-pale); border-radius: 999px; padding: .2em .55em;
}
.link-state.off { color: var(--muted); background: var(--paper-3); }
.relink {
  margin-top: 6px; border: 0; background: none; color: var(--brand); font-size: var(--fs-100);
  font-weight: 600; padding: 2px 0; text-decoration: underline; text-underline-offset: 3px;
}
.relink:hover { color: var(--brand-deep); }

/* suite pricing */
.tier-card {
  display: flex; align-items: center; gap: 16px; border: 1px solid var(--line-accent);
  border-radius: var(--r-sm); padding: 18px 20px; background: var(--tint-active); margin-top: 18px;
}
.tier-card .p-price { margin-left: auto; font-family: var(--display); font-size: 2.1rem; color: var(--brand); }
.tier-nudge {
  background: var(--note-bg); border: 1px solid var(--line-accent); border-radius: 12px;
  padding: 16px 18px; font-size: var(--fs-200); color: var(--note-ink);
}
.tier-nudge strong { display: block; margin-bottom: 3px; }
.tier-ladder { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 18px; }
.tier-ladder span {
  flex: 1 1 auto; text-align: center; font-size: var(--fs-100); color: var(--muted);
  background: var(--paper-2); border-radius: 9px; padding: .5em .4em; line-height: 1.35;
}
.tier-ladder span b { display: block; color: var(--ink-2); font-size: var(--fs-200); }
.tier-ladder span.on { background: var(--accent-pale); color: var(--brand); }
.tier-ladder span.on b { color: var(--brand); }

/* ============================================================
   SUITE PREVIEW — a viewer, not an editor
   ============================================================ */
/* The viewer is a light room, not a cinema. A dark ground made every card look
   like it was lit from behind and misrepresented what comes out of the printer,
   which is ink on paper. Everything in this block is ink-on-light for the same
   reason. */
.sp-overlay {
  position: fixed; inset: 0; z-index: 400; display: flex; flex-direction: column;
  background: radial-gradient(1200px 700px at 50% -10%, #ffffff 0%, #faf6ef 58%, #f2ebe0 100%);
  opacity: 0; transition: opacity .32s var(--ease);
}
.sp-overlay.in { opacity: 1; }

.sp-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px 22px; flex: none; color: var(--brand);
  border-bottom: 1px solid var(--line);
}
.sp-title { font-family: var(--display); font-size: 1.3rem; font-weight: 500; display: flex; align-items: baseline; gap: 14px; }
.sp-title span {
  font-family: var(--sans); font-size: var(--fs-100); font-weight: 700; letter-spacing: .08em;
  color: var(--ink-3); background: var(--accent-pale); border-radius: 999px; padding: .3em .8em;
}
.sp-actions { display: flex; align-items: center; gap: 10px; }
.sp-close {
  width: 38px; height: 38px; border-radius: 50%; border: 1px solid var(--line);
  font-size: 1.1rem; background: var(--card); color: var(--brand);
}
.sp-close:hover { background: var(--tint-active); }

.sp-stage { flex: 1; display: flex; align-items: center; justify-content: center; gap: 20px; padding: 6px 22px 14px; min-height: 0; }
.sp-card {
  max-height: 100%; border-radius: 6px; overflow: hidden; line-height: 0;
  /* a printed card sitting on a table, not a screen glowing in the dark */
  box-shadow: 0 6px 18px rgba(40,30,20,.12), 0 22px 50px rgba(40,30,20,.10);
  background: #fff;
}
.sp-card svg { display: block; height: auto; width: auto; max-height: calc(100vh - 250px); max-width: min(88vw, 620px); }
.sp-card.flip { animation: spFlip .42s var(--ease-out); }
@keyframes spFlip { from { opacity: 0; transform: translateY(12px) scale(.985); } }
.sp-nav {
  flex: none; width: 52px; height: 52px; border-radius: 50%; border: 1px solid var(--line);
  background: var(--card); color: var(--brand); font-size: 1.7rem; line-height: 1;
  display: grid; place-items: center; transition: all .25s var(--ease);
  box-shadow: var(--sh-1);
}
.sp-nav:hover { background: var(--tint-active); transform: scale(1.06); }

.sp-rail {
  flex: none; display: flex; gap: 10px; overflow-x: auto; padding: 12px 22px 18px;
  scrollbar-width: thin;
}
.sp-rail::-webkit-scrollbar { height: 7px; }
.sp-rail::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 99px; }
.sp-thumb {
  flex: none; width: 74px; background: none; border: 0; padding: 0; cursor: pointer;
  display: flex; flex-direction: column; gap: 5px; text-align: center;
}
.sp-thumb-art {
  display: block; width: 74px; aspect-ratio: 5/7; border-radius: 5px; overflow: hidden;
  background: #fff; border: 2px solid var(--line); opacity: .72;
  transition: all .28s var(--ease);
}
.sp-thumb-art svg { width: 100%; height: 100%; display: block; }
.sp-thumb:hover .sp-thumb-art { opacity: .9; }
.sp-thumb.active .sp-thumb-art { border-color: var(--accent); opacity: 1; transform: translateY(-3px); }
.sp-thumb-name {
  font-size: .66rem; color: var(--muted); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.sp-thumb.active .sp-thumb-name { color: var(--brand); font-weight: 600; }

@media (max-width: 760px) {
  .sp-bar { padding: 11px 14px; }
  .sp-title { font-size: 1.02rem; gap: 9px; }
  .sp-actions #spShare { display: none; }
  .sp-stage { padding: 4px 10px 10px; gap: 8px; }
  .sp-nav { display: none; }               /* swipe instead */
  .sp-card svg { max-height: calc(100vh - 210px); max-width: 92vw; }
  .sp-rail { padding: 10px 14px 16px; }
  .sp-thumb, .sp-thumb-art { width: 56px; }
}

/* hidden pages in the strip */
.pg-cell.hidden-page .pg-thumb { opacity: .38; filter: grayscale(.7); }
.pg-cell.hidden-page .pg-label { color: var(--muted); font-style: italic; }
.pg-hidden-tag {
  position: absolute; inset: auto 0 0 0; background: rgba(38,35,38,.78); color: #fff;
  font-size: .58rem; letter-spacing: .06em; text-transform: uppercase; font-weight: 700;
  padding: 2px 0; text-align: center;
}
.pg-cell .pg-thumb { position: relative; }
.pg-eye {
  position: absolute; top: 4px; right: 4px; width: 20px; height: 20px; border-radius: 50%;
  background: rgba(255,255,255,.92); color: var(--brand); font-size: .55rem;
  display: grid; place-items: center; opacity: 0; transition: opacity .2s;
  box-shadow: var(--sh-1); z-index: 2;
}
.pg-cell { position: relative; }
.pg-cell:hover .pg-eye, .pg-cell.hidden-page .pg-eye { opacity: 1; }
.pg-eye:hover { background: #fff; transform: scale(1.1); }
.pg-count em { font-style: normal; color: var(--muted); opacity: .8; }

/* ============================================================
   ELEMENT LIBRARY
   ============================================================ */
.el-chiprow { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-bottom: 10px; }
.el-chiprow.cats { margin-bottom: 12px; }
.el-reflabel {
  font-size: .64rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); font-weight: 700; margin-right: 2px; width: 44px; flex: none;
}
.el-chip {
  border: 1px solid var(--line-2); background: var(--card); border-radius: 999px;
  padding: .34em .8em; font-size: .74rem; color: var(--ink-2); transition: all .2s var(--ease);
}
.el-chip.sm { padding: .28em .68em; font-size: .7rem; text-transform: capitalize; }
.el-chip:hover { border-color: var(--accent); color: var(--ink); }
.el-chip.on { background: var(--ink); color: var(--paper); border-color: var(--ink); font-weight: 600; }
.el-swatch {
  border: 1px solid var(--line-2); background: var(--card); border-radius: 999px;
  padding: .24em .5em; font-size: .68rem; color: var(--ink-3);
  display: inline-flex; align-items: center; gap: 4px;
}
.el-swatch i { width: 13px; height: 13px; border-radius: 50%; display: block; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); }
.el-swatch.on { border-color: var(--brand); box-shadow: 0 0 0 2px rgba(81,48,71,.16); }
.el-refine {
  background: var(--paper); border: 1px solid var(--line); border-radius: 12px;
  padding: 10px 12px 4px; margin-bottom: 12px;
}
.el-count { font-size: var(--fs-100); color: var(--muted); margin: 0 0 10px; }

.el-group {
  display: flex; align-items: baseline; justify-content: space-between;
  font-family: var(--label); font-size: var(--fs-100); letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent); font-weight: 400; margin: 20px 0 8px;
}
.el-group span { font-family: var(--sans); letter-spacing: 0; color: var(--muted); font-size: .68rem; }
.el-seeall {
  border: 0; background: none; color: var(--brand); font-size: var(--fs-100); font-weight: 600;
  padding: 6px 0 0; text-decoration: underline; text-underline-offset: 3px;
}

.el-tile-wrap { position: relative; display: block; }
.el-fav {
  position: absolute; top: 4px; right: 4px; width: 21px; height: 21px; border-radius: 50%;
  border: 0; background: rgba(255,255,255,.9); color: var(--line-2); font-size: .66rem;
  display: grid; place-items: center; opacity: 0; transition: all .2s; z-index: 2;
}
.el-tile-wrap:hover .el-fav, .el-fav.on { opacity: 1; }
.el-fav:hover { background: #fff; transform: scale(1.12); }
.el-fav.on { color: #d4536b; }

.el-empty {
  text-align: center; padding: 26px 16px; border: 1px dashed var(--line-2);
  border-radius: 12px; color: var(--muted);
}
.el-empty strong { display: block; color: var(--ink); font-size: var(--fs-200); margin-bottom: 4px; }
.el-empty span { font-size: var(--fs-100); }

.el-layer.locked { opacity: .72; }
.el-lockmark {
  font-size: .58rem; letter-spacing: .06em; text-transform: uppercase; font-weight: 700;
  color: var(--muted); background: var(--paper-3); border-radius: 999px; padding: .16em .5em;
}

/* mobile: the library becomes a proper sheet with big tap targets */
@media (max-width: 1180px) {
  .el-grid { grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: 10px; }
  .el-tile { padding: 12px 8px 8px; }
  /* 10.9px was too small to read on a phone */
  .el-tile span { font-size: .78rem; }
  .el-fav { opacity: 1; width: 24px; height: 24px; }
  .el-chip { padding: .46em 1em; font-size: .8rem; }
  .el-chiprow.cats { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 6px; margin-inline: -2px; }
  .el-chiprow.cats .el-chip { flex: none; }
  .el-swatch { padding: .34em .6em; }
}

/* ============================================================
   EDITOR HEADER, ZOOM, GUIDES
   ============================================================ */
/* THE COLUMN FLOOR IS NOT OPTIONAL.
   This was `1fr auto 1fr`, and the right-hand group (Preview, Download, the
   overflow menu) is ~470px that will not shrink. On a narrower editor column
   the left track was squeezed to ZERO: the invitation's name and its save
   status spilled out of a 0px box and printed themselves on top of the zoom
   control, one word per line. A named minimum on the title track, and a title
   that truncates instead of wrapping, is what keeps the row a row. */
.ed-topbar {
  display: grid; grid-template-columns: minmax(180px, 1fr) auto minmax(0, auto);
  align-items: center; gap: 14px; padding: 11px 18px;
}
.ed-head-left { display: flex; align-items: center; gap: 12px; min-width: 0; }
.ed-head-mid { display: flex; align-items: center; gap: 10px; justify-content: center; white-space: nowrap; }
.ed-head-right { display: flex; align-items: center; gap: 8px; justify-content: flex-end; flex-wrap: wrap; }
.ed-title {
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.save-status { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.ed-back {
  flex: none; width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
  background: var(--paper-2); color: var(--ink-2); font-size: 1.05rem; text-decoration: none;
  transition: background .2s, color .2s;
}
.ed-back:hover { background: var(--paper-3); color: var(--ink); }
.save-status {
  display: block; font-family: var(--sans); font-size: .68rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ok); margin-top: 2px; font-weight: 700;
}
.save-status.saving { color: var(--muted); }
.save-status::before {
  content: "●"; font-size: .6em; margin-right: .45em; vertical-align: .15em;
}

.crumb-page { font-weight: 700; font-size: var(--fs-200); }
.crumb-count {
  font-size: var(--fs-100); font-weight: 700; color: var(--accent);
  background: var(--accent-pale); border-radius: 999px; padding: .22em .7em;
  font-variant-numeric: tabular-nums;
}

.zoom-group {
  display: flex; align-items: center; border: 1px solid var(--line-2);
  border-radius: 999px; overflow: hidden; background: var(--card);
}
.zoom-group button {
  border: 0; background: none; color: var(--ink-2); padding: .42em .7em;
  font-size: var(--fs-200); line-height: 1;
}
.zoom-group button:hover { background: var(--paper-2); color: var(--ink); }
.zoom-group .zoom-val {
  min-width: 56px; font-variant-numeric: tabular-nums; font-weight: 600;
  border-inline: 1px solid var(--line);
}

.ed-icon {
  width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--line-2);
  background: var(--card); color: var(--ink-2); font-size: 1rem; display: grid; place-items: center;
  transition: all .2s var(--ease);
}
.ed-icon:hover { border-color: var(--accent); color: var(--ink); }
.ed-icon[disabled] { opacity: .35; pointer-events: none; }

.ed-more { position: relative; }
.ed-menu {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 20; min-width: 168px;
  background: var(--card); border: 1px solid var(--line-2); border-radius: 12px;
  box-shadow: var(--sh-3); padding: 6px; display: flex; flex-direction: column;
}
.ed-menu button {
  border: 0; background: none; text-align: left; padding: .6em .8em; border-radius: 8px;
  font-size: var(--fs-200); color: var(--ink-2);
}
.ed-menu button:hover { background: var(--paper-2); color: var(--ink); }

/* zoom transform lives on the shell; guides are children of it */
.card-shell { transform-origin: center center; transition: transform .18s var(--ease); }
.snap-guide { position: absolute; z-index: 5; pointer-events: none; background: var(--accent); opacity: .75; }
.snap-guide.x { top: 0; width: 1px; margin-left: -.5px; }
.snap-guide.y { left: 0; height: 1px; margin-top: -.5px; }
.snap-guide.centre { background: #e0447d; opacity: .85; }

/* What the row sheds as it narrows, in order of how little it is missed: the
   zoom control (a convenience), then undo/redo (Ctrl+Z, and both are in the ...
   menu). Preview and Download stay, because they are what the page is for.
   Letting them wrap instead was what made a 40px bar 109px tall. */
/* The editor bar does not get the window: the two side panels take 724px of it
   before the bar sees a pixel. Shedding at 1400px therefore fired far too late
   — on a 1420px laptop the bar had 681px to fit 434px of buttons plus the
   invitation's name, so it silently wrapped to three rows and squeezed the
   title down to a single letter. The breakpoints below are the window widths at
   which the CENTRE column actually runs out, which is the thing that matters. */
@media (max-width: 1680px) {
  .zoom-group { display: none; }
}
@media (max-width: 1540px) {
  .ed-head-right #btnUndo, .ed-head-right #btnRedo { display: none; }
}
@media (max-width: 1360px) {
  /* the label goes, the gold pill stays: it is still obviously Download */
  .ed-head-right #btnPreviewTop { display: none; }
}
/* whatever is left, the bar is one row and the name is what gives way */
.ed-topbar { row-gap: 0; }
.ed-head-right { flex-wrap: nowrap; }
.ed-head-left { min-width: 0; }
.ed-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 1180px) {
  .ed-topbar { grid-template-columns: minmax(0, 1fr) auto; row-gap: 8px; }
  .ed-head-mid { order: 3; grid-column: 1 / -1; justify-content: flex-start; }
}
@media (max-width: 760px) {
  .ed-head-right .btn { padding: .5em .9em; }
  .ed-title { font-size: 1rem; }
}

/* design switcher */
.cur-collection {
  font-family: var(--display); font-size: 1.2rem; margin: -6px 0 10px; color: var(--brand);
}
.switch-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 12px; margin-top: 18px; }
.switch-card {
  border: 1px solid var(--line-2); border-radius: var(--r-sm); background: var(--card);
  padding: 10px; text-align: left; display: flex; flex-direction: column; gap: 6px;
  transition: all .25s var(--ease);
}
.switch-card:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: var(--sh-1); }
.switch-card.current { border-color: var(--brand); background: var(--tint-active); }
.switch-pv {
  display: block; aspect-ratio: 5/7; border-radius: 6px; overflow: hidden;
  background: var(--paper-2); box-shadow: var(--sh-1);
}
.switch-pv svg { width: 100%; height: 100%; display: block; }
.switch-name { font-weight: 700; font-size: var(--fs-200); }
.switch-name em { font-style: normal; font-size: .62rem; letter-spacing: .07em; text-transform: uppercase;
  color: var(--brand); background: var(--accent-pale); border-radius: 999px; padding: .16em .5em; }
.switch-meta { font-size: var(--fs-100); color: var(--muted); }
.switch-confirm { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin: 18px 0 4px; }
.switch-confirm strong { display: block; font-size: var(--fs-200); margin-bottom: 6px; }
.switch-confirm ul { margin: 0; padding-left: 18px; }
.switch-confirm li { font-size: var(--fs-200); color: var(--ink-2); line-height: 1.6; }
@media (max-width: 620px) { .switch-confirm { grid-template-columns: 1fr; gap: 10px; } }

/* ============================================================
   MOBILE EDITOR — a touch layout, not a squeezed desktop one
   ============================================================ */
.m-bar, .m-pagebar, .m-zoom, .m-sheet-scrim { display: none; }

@media (max-width: 900px) {
  /* the side panels move into bottom sheets */
  .editor { grid-template-columns: 1fr; height: auto; }
  .ed-panel.left, .ed-panel.right { display: none; }
  .page-strip { display: none; }

  .ed-stage {
    position: static; min-height: 100dvh; display: flex; flex-direction: column;
    padding-bottom: calc(126px + env(safe-area-inset-bottom));
  }

  /* --- top bar --- */
  .ed-topbar {
    position: sticky; top: 0; z-index: 12;
    grid-template-columns: auto 1fr auto; gap: 10px;
    padding: calc(8px + env(safe-area-inset-top)) 12px 8px;
  }
  .ed-head-mid { display: none; }
  .ed-title { font-size: 1.05rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .ed-head-right { gap: 6px; }
  .ed-head-right #btnUndo, .ed-head-right #btnRedo, .ed-head-right .ed-more { display: none; }
  .ed-head-right #btnPreviewTop { padding: .5em .9em; }

  /* --- canvas --- */
  .ed-canvas-wrap {
    position: relative; flex: 1; padding: 14px 12px 8px; overflow: hidden;
    touch-action: pan-y;
  }
  .card-shell svg { max-height: 52vh; }

  .m-zoom {
    display: block; position: absolute; right: 14px; top: 14px; z-index: 6;
    background: rgba(38,35,38,.72); color: #fff; border-radius: 999px;
    padding: .3em .8em; font-size: .74rem; font-weight: 700; font-variant-numeric: tabular-nums;
    backdrop-filter: blur(6px);
  }

  /* --- page bar --- */
  .m-pagebar {
    display: flex; align-items: center; gap: 8px; justify-content: center;
    padding: 6px 12px 10px; flex: none;
  }
  .m-pageprev, .m-pagenext {
    width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--line-2);
    background: var(--card); color: var(--ink-2); font-size: 1.25rem; line-height: 1;
    display: grid; place-items: center; flex: none;
  }
  .m-pagepick {
    display: flex; align-items: center; gap: 10px; min-height: 40px; padding: .4em 1.1em;
    border: 1px solid var(--line-2); border-radius: 999px; background: var(--card);
    max-width: 62vw;
  }
  .m-pagepick strong { font-size: var(--fs-200); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .m-pagepick em {
    font-style: normal; font-size: var(--fs-100); font-weight: 700; color: var(--brand);
    background: var(--accent-pale); border-radius: 999px; padding: .16em .55em; flex: none;
  }

  /* --- bottom toolbar --- */
  .m-bar {
    display: block; position: fixed; left: 0; right: 0; bottom: 0; z-index: 14;
    background: rgba(255,255,255,.96); backdrop-filter: blur(16px);
    border-top: 1px solid var(--line);
    padding: 8px 6px calc(8px + env(safe-area-inset-bottom));
  }
  .m-tools { display: grid; grid-template-columns: repeat(5, 1fr); gap: 2px; }
  .m-tools button, .m-ctx button {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
    border: 0; background: none; color: var(--ink-2); font-size: .68rem; font-weight: 600;
    min-height: 54px; border-radius: 12px; padding: 6px 2px;
  }
  .m-tools button span, .m-ctx button span { font-size: 1.15rem; line-height: 1; }
  .m-tools button:active, .m-ctx button:active { background: var(--paper-2); }

  .m-ctx { display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none; }
  .m-ctx::-webkit-scrollbar { display: none; }
  .m-ctx button { flex: none; min-width: 68px; }
  .m-ctx button.primary { color: var(--brand); }
  .m-ctx button.danger { color: var(--danger); }
  .m-ctx button.done { color: var(--ok); }

  /* --- bottom sheet --- */
  .m-sheet-scrim {
    display: block; position: fixed; inset: 0; z-index: 40;
    background: rgba(9,20,30,.42); opacity: 0; transition: opacity .24s var(--ease);
  }
  .m-sheet-scrim.in { opacity: 1; }
  .m-sheet {
    position: absolute; left: 0; right: 0; bottom: 0; max-height: 82dvh;
    background: var(--card); border-radius: 22px 22px 0 0;
    box-shadow: 0 -12px 40px rgba(9,20,30,.28);
    display: flex; flex-direction: column;
    transform: translateY(100%); transition: transform .28s var(--ease-out);
    padding-bottom: env(safe-area-inset-bottom);
  }
  .m-sheet-scrim.in .m-sheet { transform: translateY(0); }
  .m-sheet-head {
    position: relative; display: flex; align-items: center; justify-content: space-between;
    padding: 16px 18px 10px; border-bottom: 1px solid var(--line); flex: none;
  }
  .m-grab {
    position: absolute; top: 7px; left: 50%; transform: translateX(-50%);
    width: 38px; height: 4px; border-radius: 99px; background: var(--line-2);
  }
  .m-sheet-head h3 { margin: 0; font-family: var(--display); font-size: 1.3rem; font-weight: 500; }
  .m-sheet-close {
    width: 40px; height: 40px; border-radius: 50%; border: 0; background: var(--paper-2);
    color: var(--ink-2); font-size: .95rem; flex: none;
  }
  .m-sheet-body { overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
  .m-sheet-body .ed-group { border-bottom: 1px solid var(--line); }
  .m-sheet-body .ed-group:last-child { border-bottom: 0; }
  .m-sheet-body .ed-tabs { position: sticky; top: 0; }

  /* --- sheet content bits --- */
  .m-upload {
    display: flex; align-items: center; gap: 14px; padding: 16px;
    border: 1.5px dashed var(--line-2); border-radius: 14px; background: var(--paper);
  }
  .m-upload-ico {
    width: 46px; height: 46px; border-radius: 13px; background: var(--accent-pale);
    color: var(--brand); display: grid; place-items: center; font-size: 1.5rem; flex: none;
  }
  .m-upload strong { display: block; font-size: var(--fs-300); }
  .m-upload em { font-style: normal; font-size: var(--fs-100); color: var(--muted); }
  .m-file { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
  .m-file-plain { font-size: .82rem; }

  .m-more-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; padding: 16px; }
  .m-more-grid button {
    display: flex; align-items: center; gap: 11px; min-height: 54px; padding: 0 14px;
    border: 1px solid var(--line); border-radius: 13px; background: var(--card);
    font-size: var(--fs-200); font-weight: 600; color: var(--ink-2); text-align: left;
  }
  .m-more-grid button span { font-size: 1.1rem; width: 24px; text-align: center; flex: none; }
  .m-more-grid button.danger { color: var(--danger); }
  .m-more-grid button:active { background: var(--paper-2); }

  .m-pages .page-strip { display: block; border: 0; background: none; padding: 8px 0; }
  .m-pages .pg-tools { padding-inline: 16px; }
  .m-pages .pg-scroll { padding-inline: 16px; }

  /* touch targets */
  .el-tile { min-height: 92px; }
  .mini-btns button { min-height: 38px; padding: .5em .85em; }
  .swatch { height: 52px; }
  .field input[type="text"], .field textarea, .field select { font-size: 16px; }  /* no iOS zoom-on-focus */
}

@media (max-width: 380px) {
  .m-tools button { font-size: .62rem; }
  .m-tools button span { font-size: 1.05rem; }
  .m-pagepick { max-width: 52vw; }
}

/* first-run onboarding (mobile) */
.ob-scrim {
  position: fixed; inset: 0; z-index: 60; display: grid; place-items: end center;
  background: rgba(9,20,30,.5); opacity: 0; transition: opacity .24s var(--ease);
  padding: 0 14px calc(20px + env(safe-area-inset-bottom));
}
.ob-scrim.in { opacity: 1; }
.ob-card {
  background: var(--card); border-radius: 22px; padding: 26px 22px 18px; width: 100%;
  max-width: 420px; text-align: center; box-shadow: var(--sh-3);
  transform: translateY(16px); transition: transform .3s var(--ease-out);
}
.ob-scrim.in .ob-card { transform: none; }
.ob-ico {
  width: 58px; height: 58px; border-radius: 17px; margin: 0 auto 14px;
  display: grid; place-items: center; background: var(--accent-pale); color: var(--brand);
  font-size: 1.5rem; font-family: var(--display);
}
.ob-card h3 { font-family: var(--display); font-size: 1.5rem; font-weight: 500; margin-bottom: .35em; }
.ob-card p { font-size: var(--fs-200); color: var(--ink-2); margin-bottom: 18px; }
.ob-dots { display: flex; gap: 6px; justify-content: center; margin-bottom: 18px; }
.ob-dots i { width: 7px; height: 7px; border-radius: 50%; background: var(--line-2); }
.ob-dots i.on { background: var(--brand); width: 20px; border-radius: 99px; }
.ob-skip { border: 0; background: none; color: var(--muted); font-size: var(--fs-200); padding: 12px; width: 100%; }

/* ============================================================
   PHOTO CROP STAGE
   Full-screen on a phone, a centred panel on a desktop. The image itself is
   the drag handle, so the largest thing on screen is the thing you move.
   ============================================================ */
.crop-scrim{position:fixed;inset:0;z-index:120;display:grid;place-items:center;
  background:rgba(38,35,38,.58);backdrop-filter:blur(3px);
  opacity:0;transition:opacity .2s ease}
.crop-scrim.in{opacity:1}
.crop-panel{background:var(--paper);border-radius:18px;box-shadow:0 24px 70px rgba(38,35,38,.3);
  width:min(520px,calc(100vw - 24px));max-height:calc(100dvh - 24px);overflow:auto;
  padding:16px;transform:translateY(12px);transition:transform .22s ease}
.crop-scrim.in .crop-panel{transform:none}
.crop-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
.crop-head h3{margin:0;font-size:var(--fs-300)}
.crop-x{width:40px;height:40px;border:0;background:transparent;border-radius:10px;
  font-size:1rem;color:var(--ink);cursor:pointer}
.crop-x:hover{background:rgba(38,35,38,.06)}

.crop-stage{position:relative;overflow:hidden;margin:0 auto;border-radius:10px;
  background:#11181e;touch-action:none;cursor:grab;user-select:none}
.crop-stage:active{cursor:grabbing}
.crop-stage img{position:absolute;object-fit:cover;user-select:none;-webkit-user-drag:none;pointer-events:none}
.crop-grid{position:absolute;inset:0;pointer-events:none;
  background:
    linear-gradient(to right,transparent calc(33.33% - 1px),rgba(255,255,255,.28) 33.33%,transparent calc(33.33% + 1px)),
    linear-gradient(to right,transparent calc(66.66% - 1px),rgba(255,255,255,.28) 66.66%,transparent calc(66.66% + 1px)),
    linear-gradient(to bottom,transparent calc(33.33% - 1px),rgba(255,255,255,.28) 33.33%,transparent calc(33.33% + 1px)),
    linear-gradient(to bottom,transparent calc(66.66% - 1px),rgba(255,255,255,.28) 66.66%,transparent calc(66.66% + 1px));
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.35)}

.crop-hint{margin:10px 0 4px;text-align:center;font-size:var(--fs-100);color:var(--muted)}
.crop-zoom{display:flex;align-items:center;gap:10px;margin:6px 0 14px}
.crop-zoom span{width:20px;text-align:center;color:var(--muted);font-size:1rem}
.crop-zoom input[type=range]{flex:1;height:36px}

.crop-tools{display:grid;grid-template-columns:repeat(4,1fr);gap:6px}
.crop-tools button{display:flex;flex-direction:column;align-items:center;gap:3px;
  min-height:54px;padding:8px 2px;border:1px solid var(--line);border-radius:11px;
  background:var(--paper);color:var(--ink);font-size:.76rem;cursor:pointer}
.crop-tools button span{font-size:1.05rem;line-height:1}
.crop-tools button:hover{background:rgba(38,35,38,.04)}
.crop-tools button.danger{color:var(--danger)}

.crop-warn,.lowres-note{margin:12px 0 0;padding:9px 11px;border-radius:10px;
  background:rgba(196,138,32,.11);border:1px solid rgba(196,138,32,.26);
  color:var(--warn-deep);font-size:var(--fs-100);line-height:1.4}
.lowres-note{margin:0 0 10px}

.crop-foot{display:grid;grid-template-columns:1fr 1.4fr;gap:10px;margin-top:14px}
.crop-foot .btn{min-height:48px}

@media (max-width:900px){
  .crop-panel{width:100vw;max-width:100vw;height:100dvh;max-height:100dvh;
    border-radius:0;display:flex;flex-direction:column;padding:14px}
  .crop-stage{flex:0 0 auto}
  .crop-foot{margin-top:auto;padding-top:14px}
}

/* ---------------- background shelf ---------------- */
.bg-types{flex-wrap:wrap;margin-bottom:10px}
.bg-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(66px,1fr));gap:8px;margin-bottom:10px}
.bg-sw{display:flex;flex-direction:column;gap:5px;padding:0;border:0;background:none;cursor:pointer;text-align:center}
.bg-sw-face{display:block;position:relative;height:44px;border-radius:9px;overflow:hidden;
  border:1px solid var(--line);box-shadow:inset 0 0 0 1px rgba(255,255,255,.35)}
.bg-sw.active .bg-sw-face{border-color:var(--brand);box-shadow:0 0 0 2px var(--brand)}
.bg-sw-tex{position:absolute;inset:0;opacity:.5;
  background-image:radial-gradient(currentColor .8px,transparent .9px);
  background-size:7px 7px;color:var(--ink)}
.bg-sw-label{font-size:.72rem;color:var(--muted);line-height:1.2}
.bg-sw.active .bg-sw-label{color:var(--ink);font-weight:600}
@media (max-width:900px){
  .bg-grid{grid-template-columns:repeat(auto-fill,minmax(78px,1fr))}
  .bg-sw-face{height:52px}
  .bg-sw-label{font-size:.78rem}
}

/* ---------------- design suggestions ---------------- */
.sugg-group{background:linear-gradient(180deg,rgba(110,71,99,.07),transparent)}
.sugg{padding:10px 11px;border:1px solid var(--line);border-radius:11px;margin-bottom:8px;
  background:var(--paper)}
.sugg p{margin:0 0 8px;font-size:var(--fs-100);line-height:1.45;color:var(--ink)}
.sugg-acts{display:flex;gap:6px;flex-wrap:wrap}
.sugg-do,.sugg-x{border:1px solid var(--line);background:var(--paper);color:var(--ink);
  border-radius:8px;padding:6px 10px;font-size:.76rem;cursor:pointer;min-height:34px}
.sugg-do{border-color:var(--brand);color:var(--brand);font-weight:600}
.sugg-do:hover{background:rgba(110,71,99,.08)}
.sugg-x{color:var(--muted)}
.sugg-x:hover{background:rgba(38,35,38,.05)}

/* ---------------- arrange proposal ---------------- */
.auto-scrim{position:fixed;inset:0;z-index:120;display:grid;place-items:center;
  background:rgba(38,35,38,.58);backdrop-filter:blur(3px);opacity:0;transition:opacity .2s ease}
.auto-scrim.in{opacity:1}
.auto-panel{background:var(--paper);border-radius:18px;box-shadow:0 24px 70px rgba(38,35,38,.3);
  width:min(680px,calc(100vw - 24px));max-height:calc(100dvh - 24px);overflow:auto;padding:16px;
  transform:translateY(12px);transition:transform .22s ease}
.auto-scrim.in .auto-panel{transform:none}
.auto-head{display:flex;align-items:center;justify-content:space-between}
.auto-head h3{margin:0;font-size:var(--fs-300)}
.auto-note{margin:6px 0 14px;font-size:var(--fs-100);color:var(--muted);line-height:1.45}
.auto-compare{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.auto-compare figure{margin:0}
.auto-compare span{display:block;margin-bottom:6px;font-size:.74rem;letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted)}
.auto-card{border:1px solid var(--line);border-radius:10px;overflow:hidden;background:#fff}
.auto-card svg{display:block;width:100%;height:auto}
@media (max-width:900px){
  .auto-panel{width:100vw;max-width:100vw;height:100dvh;max-height:100dvh;border-radius:0;
    display:flex;flex-direction:column;padding:14px}
  .auto-compare{gap:8px}
  .crop-foot{margin-top:auto}
}

/* body scroll lock while a sheet or full-screen stage is open */
body.no-scroll{overflow:hidden;touch-action:none}

/* ---------------- honest save status ---------------- */
.save-status.failed{color:var(--danger);font-weight:600}
.save-status.offline{color:var(--warn)}

.save-alert{
  position:absolute;top:10px;left:50%;transform:translateX(-50%);z-index:12;
  width:min(440px,calc(100% - 24px));
  background:var(--paper);border:1px solid rgba(163,58,68,.35);border-left:3px solid var(--danger);
  border-radius:11px;padding:12px 14px;box-shadow:0 12px 34px rgba(38,35,38,.18)}
.save-alert strong{display:block;font-size:var(--fs-200);color:#8f2f28;margin-bottom:4px}
.save-alert p{margin:0 0 10px;font-size:var(--fs-100);line-height:1.45;color:var(--ink)}
.save-alert-acts{display:flex;gap:8px}
.save-alert .btn{min-height:36px}

.ed-booting{
  display:grid;place-items:center;min-height:50vh;color:var(--muted);font-size:var(--fs-200)}

@media (max-width:900px){
  .save-alert{top:8px;width:calc(100% - 20px)}
}


/* ============================================================
   MY INVITATIONS
   The card IS the invitation. Name, meta and actions sit quietly beneath it,
   so the page reads as a shelf of work rather than a table of records.
   ============================================================ */
.inv-section{padding-top:clamp(28px,5vw,56px)}
.inv-head{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;
  flex-wrap:wrap;margin-bottom:22px}
.inv-head h1{margin:0 0 4px;font-size:clamp(1.7rem,3.4vw,2.4rem);letter-spacing:-.02em}
.inv-sub{margin:0;color:var(--muted);font-size:var(--fs-200)}

.inv-cloud-note{display:flex;align-items:center;justify-content:space-between;gap:16px;
  flex-wrap:wrap;padding:14px 16px;margin-bottom:20px;border-radius:13px;
  background:linear-gradient(180deg,rgba(110,71,99,.10),rgba(110,71,99,.04));
  border:1px solid var(--line)}
.inv-cloud-note strong{display:block;font-size:var(--fs-200)}
.inv-cloud-note span{display:block;color:var(--muted);font-size:var(--fs-100);margin-top:2px}

.inv-toolbar{display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin-bottom:22px}
.inv-search{position:relative;flex:1 1 260px;max-width:420px}
.inv-search span{position:absolute;left:12px;top:50%;transform:translateY(-50%);
  color:var(--muted);font-size:1rem;pointer-events:none}
.inv-search input{width:100%;padding:11px 14px 11px 34px;border:1px solid var(--line);
  border-radius:11px;background:var(--paper);color:var(--ink);font:inherit;font-size:var(--fs-200)}
.inv-search input:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
.inv-sort{display:flex;align-items:center;gap:8px;font-size:var(--fs-100);color:var(--muted)}
.inv-sort select{padding:10px 12px;border:1px solid var(--line);border-radius:11px;
  background:var(--paper);color:var(--ink);font:inherit;font-size:var(--fs-100)}

.inv-grid{display:grid;gap:clamp(16px,2.2vw,26px);
  grid-template-columns:repeat(auto-fill,minmax(232px,1fr))}

.inv-card{position:relative;background:var(--paper);border:1px solid var(--line);
  border-radius:15px;transition:transform .18s ease,box-shadow .18s ease}
.inv-card:hover{transform:translateY(-2px);box-shadow:0 14px 34px rgba(38,35,38,.11)}
.inv-thumb{display:block;position:relative;aspect-ratio:1/1.4;border-radius:14px 14px 0 0;
  overflow:hidden;background:#eef3f7}
.inv-thumb svg{display:block;width:100%;height:100%}
.inv-thumb-skeleton{position:absolute;inset:0;
  background:linear-gradient(100deg,rgba(255,255,255,0) 30%,rgba(255,255,255,.55) 50%,rgba(255,255,255,0) 70%);
  background-size:220% 100%;animation:invShimmer 1.5s linear infinite}
@keyframes invShimmer{to{background-position:-220% 0}}
@media (prefers-reduced-motion:reduce){.inv-thumb-skeleton{animation:none}}

.inv-body{padding:13px 14px 12px}
.inv-title{margin:0 0 5px;font-size:var(--fs-200);font-weight:640;line-height:1.3;
  cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.inv-title:hover{color:var(--brand)}
.inv-meta{margin:0 0 11px;font-size:var(--fs-100);color:var(--muted);
  display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.inv-meta .dot{opacity:.45}
.inv-foot{display:flex;align-items:center;justify-content:space-between;gap:10px}

.inv-chip{font-size:.7rem;letter-spacing:.05em;text-transform:uppercase;font-weight:650;
  padding:3px 8px;border-radius:6px;background:rgba(38,35,38,.06);color:var(--muted)}
.inv-chip-ready{background:rgba(90,113,96,.12);color:var(--ok)}
.inv-chip-shared{background:rgba(110,71,99,.15);color:var(--brand)}

/* publish state. Published is the calm green; "changes not published" is amber
   because it is not an error — the link works, it is simply behind. */
.inv-chip-published{background:rgba(90,113,96,.14);color:var(--ok)}
.inv-chip-stale{background:rgba(138,102,51,.16);color:var(--warn)}
.inv-chip-unpublished{background:rgba(38,35,38,.08);color:var(--muted)}

/* the public link, shown on the card once there is one to show */
.inv-pub{margin:10px 0 2px;padding:10px;border-radius:10px;
  background:rgba(110,71,99,.07);border:1px solid rgba(110,71,99,.18)}
.inv-pub.is-stale{background:rgba(138,102,51,.07);border-color:rgba(138,102,51,.22)}
.inv-pub-note{margin:0 0 8px;font-size:var(--fs-100);color:var(--warn)}
.inv-pub-row{display:flex;align-items:center;gap:8px;min-width:0}
.inv-pub-url{display:block;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:.78rem;color:var(--ink);background:rgba(255,255,255,.7);
  padding:6px 8px;border-radius:7px;border:1px solid rgba(38,35,38,.08)}
.inv-pub-acts{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}

/* the editor's quiet counterpart, beside the save status */
.pub-status{font-size:var(--fs-100);font-weight:600;padding:2px 8px;border-radius:6px;
  white-space:nowrap}
.pub-status.published{background:rgba(90,113,96,.12);color:var(--ok)}
.pub-status.stale{background:rgba(138,102,51,.16);color:var(--warn);cursor:help}
.pub-status.unpublished{background:rgba(38,35,38,.07);color:var(--muted)}

/* the published invitation itself, as a guest sees it */
.pub-view .pub-title{text-align:center;margin-bottom:18px}
.pub-card{width:min(420px,92vw);margin-inline:auto;box-shadow:0 10px 34px rgba(38,35,38,.14);border-radius:12px;
  overflow:hidden;background:#fff}
.pub-card svg{display:block;width:100%;height:auto}
.pub-foot{text-align:center;margin-top:22px}

/* ---------------- the page-to-page pager ----------------
   One page at a time: only .is-active sits in normal flow (so the pager
   sizes itself to whichever page is showing); every other state is an
   absolutely-positioned overlay on top of it, which is what lets a page
   slide, fade or flip across the one that's settling into place beneath
   it. See mountPager() in js/app.js for the state machine this renders. */
.pub-pager{ position: relative; margin: 0 auto; max-width: 420px; }
.pub-pager[data-transition="bookflip"]{ perspective: 1400px; }
/* Without this, a real mouse drag across the card triggers the browser's
   native image drag-ghost and text-selection highlighting instead of the
   plain gesture mountPager() listens for — the drag looks like it does
   nothing because the browser quietly intercepted it first. */
.pub-pager, .pub-pager *{ -webkit-user-drag: none; user-select: none; -webkit-user-select: none; }
/* ...except the RSVP form's own fields, which a guest still needs to be
   able to select and edit normally. */
.pub-pager input, .pub-pager textarea{ user-select: text; -webkit-user-select: text; }
.pub-page{
  /* The actual bug: every page at rest — not active, not mid-transition —
     used to have no position rule at all, so it defaulted to static and
     sat in normal document flow even while invisible. Four pages stacked
     that way is a pager four screens tall with only the top one visible,
     which reads as "stacked cards" and made a click look like it jumped
     to the next physical card down. Absolute-by-default is what makes
     .is-active (position:relative) the only page that ever occupies real
     space, exactly as the rest of this file already assumed it did. */
  position: absolute; inset: 0;
  opacity: 0; visibility: hidden; pointer-events: none; transform: none;
  /* The editorial curve she asked for by name — cubic-bezier(.25,1,.5,1) —
     is its own token rather than reusing --ease-out, since --ease-out's
     bezier is tuned for the rest of the site's quick UI feedback, not a
     1.2s page dissolve. Both transform and opacity share the exact same
     1.2s so nothing finishes early and looks like it "arrived" before the
     rest of the page — that's what silky rather than staggered means here.
     visibility still snaps only at the very end (0s duration, delayed by
     the full 1.2s), so a resting page never intercepts clicks or shows up
     in a screen reader mid-fade. */
  transition: transform 1.2s cubic-bezier(.25,1,.5,1), opacity 1.2s cubic-bezier(.25,1,.5,1), visibility 0s 1.2s;
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
}
.pub-page.is-active{
  position: relative; opacity: 1; visibility: visible; pointer-events: auto; cursor: pointer;
  transition: transform 1.2s cubic-bezier(.25,1,.5,1), opacity 1.2s cubic-bezier(.25,1,.5,1);
}
.pub-page.is-out, .pub-page.is-in-next, .pub-page.is-in-prev{
  position: absolute; inset: 0; visibility: visible;
}
/* The one-time, automatic "this moves" cue — a quick peek to the side
   right after the page settles, faster than the normal page transition so
   it reads as a nudge rather than an accidental page change. */
.pub-page.is-active.is-hinting{
  transition: transform .4s ease-in-out;
  transform: translateX(-18px);
}

/* fade — the default look every style falls back to for the parts it
   doesn't otherwise touch (a bare .pub-page never needs a transform). */
.pub-pager[data-transition="fade"] .pub-page.is-out{ opacity: 0; }
.pub-pager[data-transition="fade"] .pub-page.is-in-next,
.pub-pager[data-transition="fade"] .pub-page.is-in-prev{ opacity: 0; }

/* elegant slide */
.pub-pager[data-transition="slide"] .pub-page.is-in-next,
.pub-pager[data-transition="slide"] .pub-page.is-in-prev{ opacity: 1; }
.pub-pager[data-transition="slide"][data-dir="next"] .pub-page.is-out{ transform: translateX(-16%); opacity: 0; }
.pub-pager[data-transition="slide"][data-dir="prev"] .pub-page.is-out{ transform: translateX(16%);  opacity: 0; }
.pub-pager[data-transition="slide"] .pub-page.is-in-next{ transform: translateX(100%); }
.pub-pager[data-transition="slide"] .pub-page.is-in-prev{ transform: translateX(-100%); }

/* book-flip — a page turning on its spine rather than sliding sideways.
   An honest approximation (one flat face, not a true two-sided page) —
   real double-sided page geometry is a much larger job than a CSS pass. */
.pub-pager[data-transition="bookflip"] .pub-page.is-in-next,
.pub-pager[data-transition="bookflip"] .pub-page.is-in-prev{ opacity: 1; }
.pub-pager[data-transition="bookflip"][data-dir="next"] .pub-page.is-out{
  transform: rotateY(-100deg); transform-origin: left center; opacity: .0;
}
.pub-pager[data-transition="bookflip"][data-dir="prev"] .pub-page.is-out{
  transform: rotateY(100deg); transform-origin: right center; opacity: .0;
}
.pub-pager[data-transition="bookflip"] .pub-page.is-in-next{
  transform: rotateY(100deg); transform-origin: right center;
}
.pub-pager[data-transition="bookflip"] .pub-page.is-in-prev{
  transform: rotateY(-100deg); transform-origin: left center;
}

/* The only nudge to advance — no persistent Next button, no page dots, and
   now no text either: a plain click/tap on the card is the whole
   interaction, so this is purely a quiet ambient glow rather than an
   instruction. --accent/--accent-pale already carry the right value in
   both themes, same as everywhere else in the public token system. */
.pub-next-hint{
  display: flex; align-items: center; justify-content: center; margin: 14px auto 0;
  width: 34px; height: 34px; background: none; border: 0; cursor: pointer;
}
.pub-next-chevron{
  font-size: 1.3rem; line-height: 1; color: var(--accent); transform: translateX(0);
  text-shadow: 0 0 10px var(--accent-pale);
  animation: pub-hint-nudge 1.8s ease-in-out infinite, pub-hint-glow 2.6s ease-in-out infinite;
}
@keyframes pub-hint-nudge{ 0%, 100% { transform: translateX(0); } 50% { transform: translateX(4px); } }
@keyframes pub-hint-glow{ 0%, 100% { opacity: .5; } 50% { opacity: 1; } }

/* ---------------- cinematic per-page polish ----------------
   Both effects live here, scoped to the published guest view only — never
   inside js/render.js's svgText()/toSVG(), which the editor's live canvas,
   thumbnails and exports all share. Baking either into the renderer itself
   would make every keystroke in the editor glide and fade, or make the
   card the guest is actively editing slowly breathe under their cursor. */
@media (prefers-reduced-motion: no-preference){
  /* Outgoing text exits first, fast and on its own — .pub-page in app.js
     only adds this class for a fixed 400ms window (TEXT_EXIT_MS) before
     the container itself starts moving, so page 1 really is text-then-
     container, not both fading in a pile. Down and out, the mirror image
     of the incoming reveal's up-and-in. */
  .pub-page.is-text-exiting .pub-card svg text{
    transition: opacity .4s ease, transform .4s ease;
    opacity: 0; transform: translateY(10px);
  }

  /* Incoming text: .is-active is added to the NEW page by app.js only
     once the container cross has fully finished (1600ms after the
     click), not at click time — so these 0/250/500/750ms delays are
     relative to the page actually having arrived, and any <text> that
     newly matches this selector plays the reveal fresh with no JS
     re-triggering needed. 1.5s is a deliberately unhurried single-line
     journey; the delays are the "one beat at a time" cascade. */
  .pub-page.is-active .pub-card svg text{
    animation: pub-text-reveal 1.5s cubic-bezier(.16, 1, .3, 1) both;
  }
  .pub-page.is-active .pub-card svg text:nth-of-type(1){ animation-delay: 0ms; }
  .pub-page.is-active .pub-card svg text:nth-of-type(2){ animation-delay: 250ms; }
  .pub-page.is-active .pub-card svg text:nth-of-type(3){ animation-delay: 500ms; }
  .pub-page.is-active .pub-card svg text:nth-of-type(4){ animation-delay: 750ms; }
  .pub-page.is-active .pub-card svg text:nth-of-type(n+5){ animation-delay: 1000ms; }

  /* Camera breathe: on the card's own wrapper, never on .pub-page itself —
     that element is what the page-flip's translateX/rotateY already
     animates, and a second, independent transform on the same element
     would simply overwrite one or the other rather than compose. */
  .pub-page.is-active .pub-card{
    animation: pub-card-breathe 8s ease-in-out infinite;
  }
}
@keyframes pub-text-reveal{
  from { opacity: 0; transform: translateY(15px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes pub-card-breathe{
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.03); }
}

@media (prefers-reduced-motion: reduce){
  .pub-page{ transition-duration: .01s !important; }
  .pub-next-chevron{ animation: none; }
}

.inv-acts{display:flex;align-items:center;gap:4px}
.inv-open{font-size:var(--fs-100);font-weight:600;color:var(--brand);text-decoration:none;
  padding:6px 10px;border-radius:8px}
.inv-open:hover{background:rgba(110,71,99,.10)}
.inv-more{width:32px;height:32px;border:0;background:transparent;border-radius:8px;
  color:var(--muted);cursor:pointer;font-size:1.05rem;line-height:1}
.inv-more:hover{background:rgba(38,35,38,.06);color:var(--ink)}

.inv-menu{position:absolute;right:12px;bottom:52px;z-index:5;min-width:158px;
  background:var(--paper);border:1px solid var(--line);border-radius:11px;
  box-shadow:0 16px 40px rgba(38,35,38,.17);padding:5px;display:grid;gap:1px}
.inv-menu button{text-align:left;border:0;background:transparent;padding:9px 11px;
  border-radius:8px;font:inherit;font-size:var(--fs-100);color:var(--ink);cursor:pointer}
.inv-menu button:hover{background:rgba(38,35,38,.06)}
.inv-menu button.danger{color:var(--danger)}
.inv-menu button.danger:hover{background:rgba(163,58,68,.09)}

.inv-rename{display:grid;gap:8px;margin-bottom:8px}
.inv-rename input{width:100%;padding:8px 10px;border:1px solid var(--brand);border-radius:9px;
  font:inherit;font-size:16px;background:var(--paper);color:var(--ink)}
.inv-rename-acts{display:flex;gap:6px}

.inv-card.is-loading{pointer-events:none}
.sk-line{display:block;height:11px;border-radius:5px;background:rgba(38,35,38,.08);margin-bottom:8px}
.sk-line.short{width:55%}

.inv-empty{text-align:center;padding:clamp(36px,7vw,80px) 20px;color:var(--ink)}
.inv-empty.small{padding:clamp(28px,5vw,54px) 20px}
.inv-empty-art{color:var(--brand);opacity:.5;margin-bottom:20px}
.inv-empty h2{margin:0 0 8px;font-size:clamp(1.2rem,2.4vw,1.5rem);letter-spacing:-.012em}
.inv-empty p{margin:0 auto 20px;color:var(--muted);max-width:44ch;font-size:var(--fs-200)}

/* ---------------- account + sign in ---------------- */
.account-slot{display:flex;align-items:center}
.acct{position:relative}
.acct-btn{border:0;background:transparent;padding:0;cursor:pointer;border-radius:50%}
.acct-avatar{display:grid;place-items:center;width:34px;height:34px;border-radius:50%;
  background:var(--brand);color:var(--on-brand);font-weight:650;font-size:.9rem}
/* Floating over the page, so it gets the glass treatment rather than a flat
   card fill — same material as the header it drops out of. */
.acct-menu{position:absolute;right:0;top:44px;min-width:210px;z-index:40;
  background:var(--glass-bg-2);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);
  border:1px solid var(--glass-border);border-radius:var(--r);
  box-shadow:0 18px 44px rgba(0,0,0,.16);padding:6px;display:grid;gap:1px}
.acct-who{padding:9px 11px 10px;border-bottom:1px solid var(--line);margin-bottom:4px}
.acct-who strong{display:block;font-size:var(--fs-100)}
.acct-who span{display:block;color:var(--muted);font-size:.76rem;overflow:hidden;text-overflow:ellipsis}
.acct-menu a,.acct-menu button{text-align:left;border:0;background:transparent;
  padding:9px 11px;border-radius:var(--r-xs);font:inherit;font-size:var(--fs-100);
  color:var(--ink);cursor:pointer;text-decoration:none;display:block}
.acct-menu a:hover,.acct-menu button:hover{background:var(--overlay-hover)}

.signin-google{display:flex;align-items:center;justify-content:center;gap:10px}
.g-mark{display:grid;place-items:center;width:20px;height:20px;border-radius:50%;
  background:#fff;color:#4285f4;font-weight:800;font-size:.8rem}
.signin-or{display:flex;align-items:center;gap:12px;margin:16px 0;color:var(--muted);
  font-size:var(--fs-100)}
.signin-or::before,.signin-or::after{content:"";flex:1;height:1px;background:var(--line)}
.signin-mail label{display:block;font-size:var(--fs-100);color:var(--muted);margin-bottom:6px}
.signin-mail-row{display:flex;gap:8px}
.signin-mail-row input{flex:1;padding:11px 13px;border:1px solid var(--line);border-radius:11px;
  font:inherit;font-size:16px;background:var(--paper);color:var(--ink)}
.signin-sent{padding:14px;border-radius:11px;background:rgba(90,113,96,.09);
  border:1px solid rgba(90,113,96,.2)}
.signin-sent strong{display:block;margin-bottom:3px}
.signin-sent span{color:var(--muted);font-size:var(--fs-100)}
.signin-pending{padding:12px 13px;border-radius:11px;background:rgba(110,71,99,.10);
  border:1px solid var(--line);margin-top:14px}
.signin-pending strong{display:block;font-size:var(--fs-100)}
.signin-pending span{display:block;color:var(--muted);font-size:.78rem;margin-top:2px}

.migrate-fail{list-style:none;margin:14px 0 0;padding:0;display:grid;gap:8px}
.migrate-fail li{padding:10px 12px;border-radius:10px;background:rgba(163,58,68,.07);
  border:1px solid rgba(163,58,68,.18)}
.migrate-fail strong{display:block;font-size:var(--fs-100)}
.migrate-fail span{display:block;color:var(--muted);font-size:.78rem;margin-top:2px}

.btn-danger{background:var(--danger);color:#fff;border-color:var(--danger)}
.btn-danger:hover{background:#9c352e}

/* ---------------- sync status ---------------- */
.save-status.syncing{color:var(--muted)}
.save-status.conflict{color:var(--warn);font-weight:600}
.sync-conflict{position:absolute;top:10px;left:50%;transform:translateX(-50%);z-index:12;
  width:min(460px,calc(100% - 24px));background:var(--paper);
  border:1px solid rgba(138,102,51,.35);border-left:3px solid var(--warn);
  border-radius:11px;padding:12px 14px;box-shadow:0 12px 34px rgba(38,35,38,.18)}
.sync-conflict strong{display:block;font-size:var(--fs-200);color:var(--warn-deep);margin-bottom:4px}
.sync-conflict p{margin:0 0 10px;font-size:var(--fs-100);line-height:1.45;color:var(--ink)}
.sync-conflict .acts{display:flex;gap:8px;flex-wrap:wrap}

/* ---------------- mobile ---------------- */
@media (max-width:900px){
  .inv-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:13px}
  .inv-body{padding:11px 11px 10px}
  .inv-title{font-size:var(--fs-100)}
  .inv-open{padding:8px 10px}
  .inv-head{align-items:flex-start}
  .inv-menu{right:8px;bottom:48px;min-width:146px}
  .inv-menu button{padding:11px 12px}
  .inv-cloud-note{flex-direction:column;align-items:stretch}
  .sync-conflict{top:8px;width:calc(100% - 20px)}

  /* 16px minimum, or iOS zooms the whole page the moment the field is
     focused -- the same rule the editor sheets already follow */
  .inv-search input,.inv-sort select{font-size:16px}

  /* a comfortable touch target; 32px is a mouse size, not a thumb size */
  .inv-more{width:40px;height:40px}
}

/* Two columns hold down to about 360px, where a card is still ~165px wide and
   the thumbnail is legible. Below that a single column reads better than two
   cramped ones. */
@media (max-width:359px){
  .inv-grid{grid-template-columns:minmax(0,1fr)}
}

/* ============================================================
   LEGAL PAGES
   Long-form reading rather than marketing: a narrower measure,
   generous leading, and headings that are quiet enough to scan
   past. These pages exist to be found and read once, not admired.
   ============================================================ */
.wrap-narrow { max-width: 46rem; }

.legal-updated {
  font-family: var(--label); font-size: var(--fs-100); letter-spacing: .16em;
  text-transform: uppercase; color: var(--muted); margin: 0 0 var(--sp-5);
}

.legal-block { margin-bottom: var(--sp-5); }
.legal-block h3 {
  font-family: var(--display); font-weight: 400; font-size: 1.42rem;
  color: var(--ink); margin: 0 0 var(--sp-2); letter-spacing: .005em;
}
.legal-block p { color: var(--ink-2); line-height: 1.75; margin: 0 0 var(--sp-2); }
.legal-block p:last-child { margin-bottom: 0; }
.legal-block strong { color: var(--ink); font-weight: 600; }
.legal-block a { color: var(--brand); text-decoration: underline; text-underline-offset: 2px; }
.legal-block a:hover { color: var(--brand-deep); }

.legal-list { margin: 0 0 var(--sp-2); padding-left: 1.15em; }
.legal-list li { color: var(--ink-2); line-height: 1.72; margin-bottom: var(--sp-2); }
.legal-list li:last-child { margin-bottom: 0; }

.legal-address {
  font-style: normal; line-height: 1.75; color: var(--ink-2);
  border-left: 2px solid var(--line-accent); padding: var(--sp-2) 0 var(--sp-2) var(--sp-3);
  margin: 0 0 var(--sp-3);
}
.legal-address strong { color: var(--ink); }

@media (max-width: 760px) {
  .legal-block h3 { font-size: 1.28rem; }
}

/* ============================================================
   HERO — BLUE GROUND  (prototype)
   ============================================================
   Taken from the reference invitation the owner supplied: a deep
   teal-navy watercolour wash lifting towards cerulean at the top
   right, a thin inset gold rule, gold stars, gold-and-white
   botanical sprays in opposite corners, gold serif type.

   Scoped to .hero only — deliberately. The rest of the site stays
   on the ivory ground, so this is one bold panel rather than a
   re-theme of the whole product, and the calm-chrome principle
   still holds everywhere the visitor actually works.
   ============================================================ */

.hero-panel {
  /* This panel was `.hero-blue` and actually navy, built from a navy-and-gold
     reference image. It is plum now and renamed to match — the old name was
     kept for exactly one commit and then fixed, because a class that says
     "blue" on a plum panel misleads the next person to read this file.

     Why it left navy. Two reasons, both about the rest of the product:

     1. The whole site runs on plum (--brand: #513047) and champagne. A navy
        hero was a second, unrelated colour language sitting at the top of
        every page, and that incoherence reads as "unfinished" long before
        anyone can name why.
     2. Navy-and-gold is the standard wedding palette. This catalogue now
        covers 63 occasions including birthdays, Lohri and memorials, and the
        hero should not quietly announce that we are a wedding company.

     Everything below is driven by these seven tokens, so the entire panel
     re-colours from this one block. */
  --hb-deep:  #24121f;
  --hb-mid:   #4a2440;
  --hb-lift:  #7d3f5e;   /* the warm rose corner */
  --hb-gold:  #e8b96a;
  --hb-gold-lt: #f4dcae;
  --hb-ink:   #f7eff3;
  --hb-ink-2: rgba(247, 239, 243, .78);


  background:
    radial-gradient(78% 62% at 88% 6%,  var(--hb-lift) 0%, transparent 58%),
    radial-gradient(64% 58% at 12% 92%, #1d0f1a 0%, transparent 60%),
    linear-gradient(158deg, var(--hb-mid) 0%, var(--hb-deep) 62%, #1b0e18 100%);
  color: var(--hb-ink);
  isolation: isolate;
}

/* The existing ivory mesh would wash straight over the blue. */
.hero-panel::before { display: none; }

/* Paper tooth. Generated rather than a bitmap so it costs nothing to
   ship and stays sharp at any size. Kept very low — it should read as
   texture on the ground, never as noise over the type. */
.hero-panel::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  opacity: .30; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
}

.hero-art { position: absolute; inset: 0; z-index: 0; pointer-events: none; }

.hero-stars {
  position: absolute; inset: 0; width: 100%; height: 100%;
  color: var(--hb-gold-lt);
}

/* The thin gold rule, inset from the panel edge like a printed border. */
/* The inset here MUST stay smaller than the content wrap's horizontal padding,
   which is 26px. It used to be clamp(18px, 2.4vw, 34px), and 34 > 26 meant the
   rule crossed the text: at 1280, 1366 and 1440 — every common laptop width —
   the border ran straight through the capital I of "Invitations". It only looked
   right on a very wide window, where the wrap centres and leaves a big margin,
   which is the width I happened to test at.

   Capped at 18px so there is always at least 8px of clearance between the rule
   and the first letter, at every viewport. */
.hero-rule {
  position: absolute; inset: clamp(12px, 1.1vw, 18px);
  border: 1px solid rgba(232, 185, 106, .55);
  border-radius: 2px;
}

/* --- type and controls on the blue --- */
.hero-panel h1 { color: var(--hb-gold); }
.hero-panel h1 em { color: var(--hb-gold-lt); }
.hero-panel p.lead { color: var(--hb-ink-2); }

.hero-panel .eyebrow {
  color: var(--hb-gold-lt);
  background: rgba(255, 255, 255, .07);
  border-color: rgba(232, 185, 106, .42);
  box-shadow: none;
}
.hero-panel .eyebrow .dot { background: var(--hb-gold); box-shadow: 0 0 0 3px rgba(232, 185, 106, .22); }

.hero-panel .btn-primary {
  background: var(--hb-gold); color: #21160a; border-color: transparent;
  box-shadow: 0 6px 18px rgba(9, 44, 69, .45);
}
.hero-panel .btn-primary:hover { background: var(--hb-gold-lt); }
.hero-panel .btn-ghost {
  color: var(--hb-ink); border-color: rgba(234, 241, 246, .38); background: transparent;
}
.hero-panel .btn-ghost:hover { border-color: var(--hb-gold); color: var(--hb-gold-lt); }

.hero-panel .hero-stats { border-top-color: rgba(234, 241, 246, .16); }
.hero-panel .hero-stats strong { color: var(--hb-gold); }
.hero-panel .hero-stats span { color: var(--hb-ink-2); }


@media (max-width: 720px) {
  .hero-rule { inset: 14px; }
}

/* The fan sits on the blue now, so its cards need a deeper, cooler shadow
   than the one tuned for the ivory ground, and a faint warm edge to stop
   pale cards bleeding into the wash. */
.hero-panel .fan-card {
  box-shadow: 0 18px 44px rgba(4, 22, 36, .55), 0 3px 10px rgba(4, 22, 36, .4);
  outline: 1px solid rgba(244, 220, 174, .22);
  outline-offset: -1px;
}

/* ============================================================
   NARROW PHONES
   ============================================================
   Measured, not guessed: at a 375px viewport the document was
   514px wide and the whole 139px came from the header. Brand +
   "Sign in" + "Start designing" + the menu toggle add up to
   303px of actions beside a 211px wordmark, and a page that
   slides sideways reads as broken before anyone looks at it.

   This matters more here than on most sites, because an
   invitation link is nearly always opened on a phone — it is
   the most common way anyone will ever see this product.

   The breakpoint is 820px, not the 760px used elsewhere in this
   file, and the eight pixels matter. iPad portrait is 768px wide
   and media queries evaluate against innerWidth, so a 760px
   breakpoint misses it — on an iPad the header kept the desktop
   layout and ran 917px wide inside 768px. Every other rule in
   this stylesheet has the same eight-pixel blind spot; this
   block is deliberately wider so the most common tablet size is
   actually covered.

   960px, finally, because that is where the header genuinely
   fits. Measured intrinsic width with the full nav and both
   buttons is 917px, so anything narrower overflows no matter
   which breakpoint we choose — 900px was still 17px over. Below
   960 the duplicate CTA goes; above it there is real room.
   ============================================================ */
/* The wordmark moved from Instrument Serif to Playfair to fix its
   congestion, and Playfair is a wider face: the brand went from 169px
   to 207px, which pushed the header's intrinsic width from 917px to
   955px. At the old 960px breakpoint that left 9px of slack, so one
   more nav item would have overflowed. Dropping the duplicate CTA
   20px earlier restores the margin the original measurement had.
   Kept separate from the 960 block below so the touch-target sizing
   there still starts where it always did. */
@media (max-width: 1000px) {
  .header-actions .btn-primary { display: none; }
}

@media (max-width: 960px) {
  /* The hero's "Create an invitation" sits a few hundred pixels
     below this and says the same thing, so the header's copy of
     the CTA is redundant on a phone. Removing the duplicate is
     what actually stops the overflow. */
  .header-actions .btn-primary { display: none; }
  .header-actions { gap: 10px; }
  .brand-text { font-size: 1rem; }
  .brand-mark { width: 30px; height: 30px; }

  /* Apple and Google both put the minimum comfortable touch
     target at 44px. Buttons here were 38px and the occasion
     chips 40px — small enough to mis-tap with a thumb. */
  .btn, .btn-sm { min-height: 44px; }
  .chip { min-height: 44px; display: inline-flex; align-items: center; }
  .nav-toggle { min-width: 44px; min-height: 44px; }
  .main-nav a { padding: 16px 0; }
}

/* ------------------------------------------------------------
   Browse filters: two states.

   Open, the panel is 11 categories, 11 styles and 63 occasions — a column
   about three viewport-heights tall. Capping the long list so it scrolls
   inside itself keeps the whole panel reachable without the page growing to
   62,000px; collapsing it once a filter is active is what stops twelve
   results sitting below the sixty-two occasions the user did not pick.
   ------------------------------------------------------------ */
.filter-list-long{max-height:min(46vh,380px);overflow-y:auto;overscroll-behavior:contain}
.filters.is-collapsed{align-self:start}
.filters-active{display:grid;gap:10px;padding:14px;border:1px solid var(--line);
  border-radius:12px;background:var(--paper)}
.filters-active h4{margin:0}
.active-chips{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:6px}
.active-chip{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--line);
  background:var(--brand);color:#fff;border-radius:999px;padding:5px 10px;
  font:inherit;font-size:var(--fs-100);cursor:pointer}
.active-chip span{opacity:.75;font-size:1.05em;line-height:1}
.active-chip:hover span{opacity:1}
.filters-done{justify-self:start;margin-bottom:4px}
.browse-head{margin-bottom:14px}
.browse-head h2{margin-bottom:2px}

/* ------------------------------------------------------------
   Browse is a results page, so the results have to be visible without
   scrolling. The generic section rhythm (90px of top padding, a full
   section head, a two-row toolbar) pushed the first card to 477px inside
   a 900px viewport — you arrived at a search page and saw no cards.
   These trims are scoped to this page so the rest of the site keeps its
   spacing.
   ------------------------------------------------------------ */
.browse-page{padding-top:32px}
.browse-page .browse-head{margin-bottom:10px}
.browse-page .browse-head h2{margin-bottom:2px}
@media (min-width:900px){
  /* search and the quick chips share a row rather than stacking */
  .browse-page .browse-toolbar{display:flex;align-items:center;gap:14px;
    flex-wrap:nowrap;margin-bottom:16px}
  .browse-page .browse-toolbar .search-box{flex:0 1 340px}
  .browse-page .browse-toolbar .chip-row{flex:1 1 auto;min-width:0;
    display:flex;gap:6px;overflow-x:auto;overscroll-behavior-x:contain;
    scrollbar-width:thin;padding-bottom:2px}
  .browse-page .browse-toolbar .chip-row .chip{flex:0 0 auto}
}

/* ---- artwork tiles in the Elements panel ----
   These carry real paintings and drawings rather than a single-colour motif, so
   they need more room than an ornament tile -- a marigold spray at 78px is a
   smudge -- and a warm paper ground, because most of the library is painted on
   white and white on white loses the edges of the artwork. */
.art-grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
.art-tile { background: #fffdf8; }
.art-tile img { width: 100%; aspect-ratio: 1; object-fit: contain; display: block; }
.art-tile span { white-space: normal; line-height: 1.15; }

/* colour swatches and the provenance note inside the right-click menu */
.ctx-swatches { display: flex; gap: 6px; padding: 6px 10px 8px; }
.ctx-swatch {
  width: 22px; height: 22px; border-radius: 50%; border: 1px solid var(--line-2);
  cursor: pointer; padding: 0;
}
.ctx-swatch:hover { transform: scale(1.12); }
.ctx-swatch.auto { background: var(--card); color: var(--muted); font-size: .8rem; line-height: 1; }
.ctx-note {
  padding: 0 10px 6px; font-size: .68rem; color: var(--muted); max-width: 220px;
}

/* ============================================================
   RSVP
   Three surfaces, one visual idea: a reply is a quiet note pinned
   under the card, never a form that competes with it.
   ============================================================ */

/* ---- the guest form, under a published invitation ---- */
.pub-rsvp {
  max-width: 460px; margin: 34px auto 0; padding: 26px 24px 24px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: 14px; text-align: left;
  display: flex; flex-direction: column; gap: 14px;
}
.pub-rsvp .rsvp-q {
  margin: 0; font-family: var(--font-display, Georgia, serif);
  font-size: 1.32rem; font-weight: 500; line-height: 1.2;
  color: var(--ink); text-align: center; text-wrap: balance;
}
.pub-rsvp .rsvp-by { margin: -8px 0 0; text-align: center; font-size: .82rem; }
.rsvp-f { display: flex; flex-direction: column; gap: 6px; font-size: .82rem; color: var(--ink-3); }
.rsvp-f em { font-style: normal; color: var(--muted); }
.rsvp-f input, .rsvp-f textarea {
  font: inherit; font-size: .95rem; color: var(--ink);
  padding: 10px 12px; border: 1px solid var(--line-2); border-radius: 9px;
  background: var(--paper); width: 100%;
}
.rsvp-f textarea { resize: vertical; min-height: 68px; }
.rsvp-f input:focus-visible, .rsvp-f textarea:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent;
}
.rsvp-choices { display: flex; flex-direction: column; gap: 8px; }
.rsvp-choice {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px;
  font-size: .92rem; color: var(--ink-2); background: var(--paper);
}
.rsvp-choice:has(input:checked) { border-color: var(--accent); background: var(--paper-2); color: var(--ink); }
.rsvp-choice input { accent-color: var(--accent); }
.rsvp-size { max-width: 260px; }
.rsvp-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.rsvp-msg { margin: 0; font-size: .84rem; min-height: 0; }
.rsvp-msg.bad { color: #a8321e; }
.rsvp-send { align-self: flex-start; }
.rsvp-done { text-align: center; }
.rsvp-done .rsvp-q { margin-bottom: 4px; }
.rsvp-done p { margin: 0; }

/* ---- the owner's list, on the dashboard ---- */
.inv-rsvp { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); font-size: .85rem; }
.rsvp-nums { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 10px; }
.rsvp-nums span { display: flex; align-items: baseline; gap: 5px; color: var(--ink-3); font-size: .78rem; }
.rsvp-nums b { font-size: 1.15rem; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.rsvp-list { list-style: none; margin: 0 0 12px; padding: 0; max-height: 260px; overflow-y: auto; }
.rsvp-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: 2px 10px; padding: 8px 0; border-bottom: 1px solid var(--line);
}
.rsvp-row:last-child { border-bottom: 0; }
.rsvp-who { font-weight: 600; color: var(--ink); }
.rsvp-who em { font-style: normal; color: var(--muted); font-weight: 400; }
.rsvp-ans { font-size: .76rem; color: var(--ink-3); text-align: right; }
.rsvp-row.is-yes .rsvp-ans { color: #2f7a4d; }
.rsvp-row.is-no .rsvp-ans { color: var(--muted); }
.rsvp-note { grid-column: 1 / -1; color: var(--ink-2); font-size: .82rem; }
.rsvp-when { grid-column: 1 / -1; font-size: .72rem; }

/* ---- the switch, in the editor sheet ---- */
.m-switch {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  font-size: .9rem; color: var(--ink); margin-bottom: 4px;
}
.m-switch input { accent-color: var(--accent); width: 17px; height: 17px; }
.m-lbl {
  display: flex; flex-direction: column; gap: 5px;
  font-size: .78rem; color: var(--ink-3); margin: 12px 0 0;
}
.m-lbl em { font-style: normal; color: var(--muted); }
.m-lbl input {
  font: inherit; font-size: .92rem; color: var(--ink);
  padding: 9px 11px; border: 1px solid var(--line-2); border-radius: 8px; background: var(--paper);
}


/* ---------------- the motion showcase ----------------
   A real card playing its real entrance, beside the words that explain it.
   The stage is deliberately quiet — paper, a soft shadow, one caption — because
   the moving card is the whole point and anything else competes with it. */
.reel-section { padding-top: 8px; }
.reel-grid {
  display: grid; grid-template-columns: minmax(0, 0.92fr) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 72px); align-items: center;
}
.reel-copy h2 {
  font-family: var(--display-hero); font-weight: 300; font-size: clamp(1.9rem, 3.4vw, 2.9rem);
  line-height: 1.08; margin: 10px 0 14px; text-wrap: balance;
}
.reel-copy h2 em { font-style: normal; color: var(--brand); }
.reel-copy .lead { max-width: 46ch; }
.reel-copy .hero-cta { margin-top: 22px; }

.reel-stage {
  position: relative; display: grid; place-items: center;
  padding: clamp(18px, 3vw, 34px);
  border-radius: 18px; background: var(--paper-2);
  border: 1px solid var(--line);
}
.reel-stage canvas {
  width: min(100%, 330px); height: auto; aspect-ratio: 5 / 7; display: block;
  border-radius: 6px; background: #fff; box-shadow: var(--sh-3);
}
.reel-label {
  margin-top: 14px; font-family: var(--label); font-size: var(--fs-100);
  letter-spacing: .15em; text-transform: uppercase; color: var(--muted);
  min-height: 1.2em; text-align: center;
}
@media (max-width: 900px) {
  .reel-grid { grid-template-columns: 1fr; }
  .reel-stage { order: -1; }
}

/* ---------------- storefront filter nav ----------------
   The homepage portfolio grid (js/storefront.js). Grid itself reuses
   .tpl-grid / .tpl-card / .tpl-thumb; only the two-level filter nav is
   new here. */
#sfBody { margin-top: 4px; }
.sf-nav { display: flex; flex-direction: column; gap: 16px; margin-bottom: 38px; }

.sf-tabs {
  display: inline-flex; gap: 4px; align-self: start;
  padding: 4px; background: var(--paper-2);
  border: 1px solid var(--line); border-radius: 999px;
}
.sf-tab {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  font-family: var(--label, "Manrope", system-ui, sans-serif);
  font-size: var(--fs-100); font-weight: 600; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-3);
  padding: 9px 20px; border-radius: 999px;
  transition: color .2s var(--ease-out), background .2s var(--ease-out), box-shadow .2s;
}
.sf-tab:hover { color: var(--ink); }
.sf-tab.is-active { background: var(--card); color: var(--ink); box-shadow: var(--sh-1); }

.sf-cats { display: flex; flex-wrap: wrap; gap: 10px; }
.sf-cat {
  appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 8px;
  font: inherit; font-size: var(--fs-300); color: var(--ink-2);
  padding: 10px 18px; border-radius: 999px;
  background: var(--card); border: 1px solid var(--line-2);
  transition: color .2s, border-color .2s, transform .2s var(--ease-out), box-shadow .2s;
}
.sf-cat:hover { color: var(--ink); border-color: var(--line-accent); transform: translateY(-2px); box-shadow: var(--sh-1); }
.sf-cat.is-active {
  color: var(--ink); border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-pale);
}
.sf-cat-ico { font-size: 1.1em; line-height: 1; }

.sf-note { margin-top: 16px; font-size: var(--fs-200); color: var(--ink-3); }
.sf-more { margin-top: 34px; text-align: center; }
.sf-empty { padding: 44px 0; text-align: center; color: var(--ink-3); font-size: var(--fs-400); }

@media (max-width: 560px) {
  .sf-tabs { align-self: stretch; justify-content: center; }
}

/* ---------------- greeting-card tiles ----------------
   A distinct product presentation: landscape, artwork-led, metadata kept
   outside the card. Scoped entirely to `.sf-gc-*` / `.sf-grid.is-greeting`
   so the invitation tile system (.tpl-grid / .tpl-card / .tpl-thumb) is
   untouched. Uses the existing EverInviting tokens — no new colours. */
.sf-grid.is-greeting {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
  gap: 46px 36px;
  margin-top: 4px;
}
.sf-gc-card {
  display: flex; flex-direction: column; gap: 18px;
  text-decoration: none;
}
.sf-gc-thumb {
  position: relative;
  aspect-ratio: 7 / 5;                /* landscape — a card cover, not an invite */
  overflow: hidden;
  border-radius: 3px;
  background: var(--paper-2);
  border: 1px solid var(--line-accent);
  box-shadow: 0 1px 2px rgba(38, 35, 38, .05), 0 20px 44px -24px rgba(38, 35, 38, .30);
  transition: transform .45s var(--ease-out), box-shadow .45s var(--ease-out);
}
:root[data-theme="dark"] .sf-gc-thumb {
  box-shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 20px 44px -22px rgba(0, 0, 0, .55);
}
.sf-gc-thumb svg { display: block; width: 100%; height: 100%; }
.sf-gc-card:hover .sf-gc-thumb {
  transform: translateY(-6px);
  box-shadow: 0 2px 6px rgba(38, 35, 38, .06), 0 34px 66px -26px rgba(38, 35, 38, .36);
}
.sf-gc-skeleton {
  position: absolute; inset: 0;
  background: linear-gradient(100deg, var(--paper-2) 38%, var(--paper-3) 50%, var(--paper-2) 62%);
  background-size: 220% 100%;
  animation: sfGcShimmer 1.5s ease-in-out infinite;
}
@keyframes sfGcShimmer { to { background-position: -120% 0; } }
@media (prefers-reduced-motion: reduce) {
  .sf-gc-skeleton { animation: none; }
  .sf-gc-card:hover .sf-gc-thumb { transform: none; }
}
.sf-gc-meta {
  display: flex; flex-direction: column; gap: 4px;
  text-align: center; padding: 0 8px;
}
.sf-gc-name {
  font-family: var(--display);
  font-size: var(--fs-500);
  font-weight: 500;
  letter-spacing: .005em;
  color: var(--ink);
}
.sf-gc-sub {
  font-family: var(--font-label);
  font-size: var(--fs-100);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-3);
}
@media (max-width: 520px) {
  .sf-grid.is-greeting { grid-template-columns: 1fr; gap: 34px; }
}
