/* Invitation sections — self-contained, inv- prefixed, var(--sec-*) only.
   Loaded by both the public layout and the dashboard editor. */

/* ---- Canvas isolation (replaces the iframe's CSS boundary) ---- */
/* The invitation is a phone, everywhere. 390px and 39/84 are not new
   numbers - they are what scripts/template-posters.mjs photographs and what
   .showcase-card is shaped like, because that is the viewport all 35
   layouts were drawn against.

   Width alone is only half of it. Horizontal sizing inside a section is
   already cqw against this element, so capping the width makes every type
   scale and measure phone-identical for free. The hero heights were svh,
   which keeps tracking the window - 390 wide by 1100 tall on a big monitor
   is not a phone. --inv-screen is that "one screen" unit - see .inv-section
   below, which derives it from the canvas width so the shape is the same
   everywhere. */
/* On :root, not on .inv-canvas: the footer sits beside the canvas rather
   than inside it, so it cannot inherit it from there, and one constant
   beats two spellings of 390. */
:root { --inv-width: 390px; }
.inv-canvas {
  container-type: inline-size;
  background: transparent;
  color: var(--sec-text, inherit);
}
@media (min-width: 480px) {
  .inv-canvas {
    width: var(--inv-width);
    margin-inline: auto;
  }
  /* The card. overflow: clip, not hidden - hidden would make this a scroll
     container and nest a second scrollbar inside the page. clip still
     rounds the corners and still lets a spanning decoration cross a section
     seam while stopping it escaping the card. */
  .invitation .inv-canvas {
    border-radius: 18px;
    overflow: clip;
    background: var(--invitation-bg);
    box-shadow: 0 18px 50px -12px rgb(0 0 0 / 0.32), 0 2px 8px rgb(0 0 0 / 0.12);
  }
}
.inv-editor .inv-canvas {
  container-type: inline-size;
  height: auto;
  min-height: 0;
  overflow: visible;
  border: 1px solid var(--border-subtle, #e5e0d8);
  border-radius: 18px;
  background: var(--bg-base, #fff);
  box-shadow: 0 18px 50px -12px rgb(0 0 0 / 0.18), 0 2px 8px rgb(0 0 0 / 0.08);
}
@media (min-width: 1025px) {
  .inv-editor .inv-canvas {
    container-type: size;
    height: min(90svh, 1100px);
    min-height: 640px;
    overflow: auto;
  }
}
/* :where(), not :is(): the point of this block is to stop dashboard
   globals leaking into a section, not to outrank the section's own
   typography. :is() takes the specificity of its most specific argument,
   so `.inv-canvas :is(h1, ...)` scored (0,1,1) and quietly beat every
   single-class rule here - `font: inherit` was overriding
   `.inv-hero-title`, and the couple's names rendered at body size on the
   public page. :where() is always (0,0,0), so this lands at (0,1,0):
   still above every bare element selector in styles.css, still below
   anything deliberate below. */
.inv-canvas :where(h1, h2, h3, h4, h5, h6, p, a, button, label, input, textarea, select, blockquote, cite, figure, figcaption) {
  margin: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  text-decoration: none;
  box-shadow: none;
  display: revert;
  max-width: none;
}
.inv-canvas :where(h1, h2, h3, h4, h5, h6) {
  font-family: "Playfair Display", serif;
  font-weight: 500;
  line-height: 1.15;
}
.inv-canvas a { color: inherit; }
.inv-canvas .form-group label,
.inv-canvas label.inv-label,
.inv-canvas label.inv-radio-label,
.inv-canvas label.inv-check-label {
  display: flex;
}

/* ---- Section chrome ---- */
.inv-section {
  /* "One screen", derived from the canvas width so a hero is the same
     shape on every device - a 375x667 phone would otherwise give a 0.562
     hero against 0.461 on a 430x932 one, and hero-cover's photograph IS
     the hero box, so that is the photo's crop changing per device.
     Declared HERE and not on .inv-canvas: a container query unit in a
     value on the container itself resolves against the container's own
     ANCESTOR, so 100cqw would measure the wrong box. A section is a
     descendant, so it measures the canvas, which is what we want -
     var(--inv-width) above 480px, the viewport below it. */
  --inv-screen: calc(100cqw * 84 / 39);
  position: relative;
  background: var(--sec-bg);
  color: var(--sec-text);
  font-family: "Cormorant Garamond", serif;
  font-size: 18px;
  line-height: 1.55;
  overflow: hidden;
}
.inv-editor .inv-section { overflow: visible; }
/* Hidden means hidden: the section is not rendered on the public page at
   all, and dimming it in the editor only made the canvas noisy. It stays in
   the DOM - reindex() and parseSections both read the document order of the
   sections and their hidden inputs, so removing the node would renumber
   everything - and the restore panel beside undo/redo brings it back. */
.inv-section--disabled {
  display: none;
}
.inv-section.inv-section--gradient {
  background: linear-gradient(
    120deg,
    var(--sec-bg),
    color-mix(in srgb, var(--sec-accent) 22%, var(--sec-bg)),
    var(--sec-bg),
    color-mix(in srgb, var(--sec-accent) 14%, var(--sec-bg))
  );
  background-size: 300% 300%;
  animation: inv-section-gradient-shift 22s ease-in-out infinite;
}
@keyframes inv-section-gradient-shift {
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .inv-section.inv-section--gradient { animation: none; }
}
.inv-section:has(.inv-palette-popover:not([hidden])) { z-index: 6; }
/* Phone-first: a section is full-bleed, its content is what is
   constrained. Vertical rhythm is rem rather than cqh - .inv-canvas is
   only container-type:size from 1025px, so cqh collapses to 0 on a phone.
   Every .inv-inner is a grid, so each layout orders its own children with
   `order` instead of needing a wrapper per arrangement. */
.inv-inner {
  width: min(760px, 100%);
  margin: 0 auto;
  padding: 2.75rem 1.35rem 3rem;
  display: grid;
  gap: 1.15rem;
  align-content: start;
}
.inv-heading {
  font-family: "Playfair Display", serif;
  color: var(--sec-text);
  font-size: clamp(28px, 5.5cqw, 48px);
  letter-spacing: -0.02em;
  text-align: center;
  line-height: 1.15;
}
.inv-inner > .inv-heading {
  margin-bottom: 0;
}
.inv-inner > .inv-heading:not(.inv-hero-title)::after,
.inv-rsvp-title::after {
  content: "";
  display: block;
  width: 36px;
  height: 1px;
  background: var(--sec-accent);
  margin: 0.8rem auto 0;
}
.inv-heading-sm { font-size: clamp(18px, 2.8cqw, 24px); text-align: left; letter-spacing: 0; }
.inv-text { color: var(--sec-text); }
.inv-muted {
  color: var(--sec-muted);
  font-size: 0.9em;
}
.inv-quote {
  font-family: "Cormorant Garamond", serif;
  font-style: italic;
  color: var(--sec-text);
  text-align: center;
  max-width: 38ch;
  margin-inline: auto;
  font-size: 1.15em;
  line-height: 1.45;
}
.inv-cite {
  display: block;
  color: var(--sec-muted);
  font-style: normal;
  font-size: 0.72em;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-family: "DM Sans", sans-serif;
  margin-top: 0.75em;
}
.inv-photo { display: block; width: 100%; height: 100%; object-fit: cover; }
.inv-photo-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 160px;
  width: 100%;
  height: 100%;
  background: var(--sec-surface);
  color: var(--sec-muted);
  border: 1px dashed var(--sec-border);
  font-family: "DM Sans", sans-serif;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: none;
}
.inv-editor [data-edit-type="image"] { cursor: pointer; }
.inv-editor [data-edit-type="image"].inv-editing {
  outline: 2px solid var(--sec-accent);
  outline-offset: -2px;
}
.inv-link { color: var(--sec-accent); text-decoration: underline; }
.inv-is-hidden { display: none !important; }
/* Sits below the card on the ground, not inside it - so it is transparent
   and matched to the card's width rather than painting --invitation-bg
   across the full page, which reads as a stray block once there is a
   ground to sit on. */
.inv-footer { padding: 24px; text-align: center; width: min(100%, var(--inv-width)); margin-inline: auto; }
/* showBorder is a flag, not a layout: an inset hairline ~0.75em from the
   section edge, available on any hero. */
.inv-hero--bordered::after {
  content: "";
  position: absolute;
  inset: 0.75em;
  border: 1px solid var(--sec-border);
  pointer-events: none;
  z-index: 4;
}
.inv-rich { white-space: pre-wrap; }

/* ---- Shared section pieces ---- */
/* The short accent rule (design-system "Hairline"). Rendered by hero and
   closing for every layout and revealed only where the layout asks for
   it, so a layout switch stays a pure fragment swap. */
.inv-rule { display: none; width: 3em; height: 1px; background: var(--sec-accent); }
.inv-emblem {
  display: flex;
  gap: 0.35em;
  align-items: baseline;
  justify-content: center;
  font-family: "Playfair Display", serif;
  font-size: clamp(28px, 5cqw, 44px);
  color: var(--sec-accent);
  letter-spacing: 0.1em;
}
.inv-emblem-amp { color: var(--sec-accent); font-family: "Great Vibes", cursive; }
/* Four equal cells, falling to 2x2 rather than a five-across squeeze. */
.inv-countdown {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(4.25rem, 1fr));
  gap: 0.6rem;
  width: 100%;
  max-width: 22rem;
  margin-top: 0.4rem;
}
.inv-countdown-item { display: grid; justify-items: center; gap: 0.2rem; }
.inv-countdown-value { font-family: "Playfair Display", serif; font-size: clamp(22px, 4cqw, 34px); color: var(--sec-accent); }
.inv-countdown-label { color: var(--sec-muted); font-size: 0.62em; letter-spacing: 0.14em; text-transform: uppercase; font-family: "DM Sans", sans-serif; }
.inv-decoration, .invitation-decoration { position: absolute; width: 18%; pointer-events: none; z-index: 2; }
/* A spanning decoration is allowed out of its own section so one ornament
   can run across the seam between two. Two things have to be true for that
   to show: the anchoring section must stop clipping, and the decoration
   must paint above the NEXT section's background. Sections are
   position:relative with z-index auto, so they create no stacking context
   of their own - a positive z-index here lifts the decoration above later
   siblings in the shared root context, which is exactly what is wanted. */
.inv-section:has(> .inv-decoration--spans) { overflow: visible; }
.inv-decoration--spans { z-index: 3; }
.inv-editable .inv-decoration, .inv-editor .invitation-decoration { pointer-events: auto; }
.inv-decoration-shape, .invitation-decoration-shape {
  width: 100%;
  height: 100%;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}

/* ---- Hero ----
   Five layouts, four of which put every element in one centred column and
   differ only in what leads and what is left out. They share one trick:
   .inv-hero-content becomes display:contents so the photo (a sibling, not
   a child) can be ordered in among the type. hero-cover is the exception -
   there the photo is the section and the type sits on a --sec-surface
   band, because type never goes on a photograph. */
.inv-hero { display: grid; }
.inv-hero > .inv-rail { width: 100%; justify-self: stretch; }
.inv-hero-media { display: none; overflow: hidden; background: var(--sec-surface); min-width: 0; }
.inv-hero-photo { width: 100%; height: 100%; object-fit: cover; }
.inv-hero-content {
  position: relative;
  z-index: 1;
  width: 100%;
  min-width: 0;
  padding: 3rem 1.35rem;
  text-align: center;
  display: grid;
  gap: 0.9rem;
  justify-items: center;
  align-content: center;
}
.inv-hero-content .inv-text,
.inv-hero-content .inv-quote { text-align: center; }
.inv-hero-title, .inv-hero-subtitle { width: 100%; text-align: center; }
.inv-hero-title { font-size: clamp(34px, 9.5cqw, 68px); }
.inv-hero-subtitle { color: var(--sec-muted); max-width: 34ch; margin-inline: auto; }
/* Date and location come from the event, not from a slot. The separator
   is a ::before on the location so an event with none does not render a
   dangling middot. */
.inv-hero-meta {
  color: var(--sec-muted);
  font-size: 0.76em;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-family: "DM Sans", sans-serif;
}
.inv-hero-meta-loc:not(:empty)::before { content: " · "; }

/* hero-cover — the photograph is the viewport; type lives on a surface
   band pinned to the bottom. */
.inv-hero--cover.inv-hero { min-height: var(--inv-screen); grid-template-rows: auto 1fr auto; }
.inv-hero--cover > .inv-rail { grid-row: 1; align-self: start; }
.inv-hero--cover .inv-hero-media { display: block; position: absolute; inset: 0; }
.inv-hero--cover .inv-hero-content {
  grid-row: 3;
  background: var(--sec-surface);
  padding: 1.9rem 1.35rem 2.4rem;
  gap: 0.65rem;
}
.inv-hero--cover .inv-emblem { order: 1; font-size: 1rem; letter-spacing: 0.3em; }
.inv-hero--cover .inv-hero-title { order: 2; font-size: clamp(32px, 8.5cqw, 56px); }
.inv-hero--cover .inv-hero-subtitle { order: 3; }
.inv-hero--cover .inv-hero-meta { order: 4; }
.inv-hero--cover .inv-countdown { order: 5; }
.inv-hero--cover .inv-quote { order: 6; font-size: 0.85em; }

/* hero-window — the photo is an object (3:4), not a background. */
.inv-hero--window.inv-hero {
  min-height: calc(0.9 * var(--inv-screen));
  justify-items: center;
  align-content: center;
  gap: 0.85rem;
  padding: 3rem 1.35rem 3.5rem;
  text-align: center;
}
.inv-hero--window .inv-hero-content { display: contents; }
.inv-hero--window .inv-emblem { order: 1; font-size: 1rem; letter-spacing: 0.3em; }
.inv-hero--window .inv-hero-title { order: 2; }
.inv-hero--window .inv-hero-subtitle { order: 3; }
.inv-hero--window .inv-hero-meta { order: 4; }
.inv-hero--window .inv-hero-media {
  display: block;
  order: 5;
  width: min(100%, 22rem);
  aspect-ratio: 3 / 4;
  max-height: calc(0.7 * var(--inv-screen));
}
.inv-hero--window .inv-quote { order: 6; }
.inv-hero--window .inv-countdown { order: 7; }

/* hero-letter — everything, in stationery order, photo last as a figure. */
.inv-hero--letter.inv-hero {
  min-height: calc(0.9 * var(--inv-screen));
  justify-items: center;
  align-content: center;
  gap: 0.8rem;
  padding: 3.25rem 1.35rem;
  text-align: center;
}
.inv-hero--letter .inv-hero-content { display: contents; }
.inv-hero--letter .inv-emblem { order: 1; font-size: 1.05rem; letter-spacing: 0.3em; }
.inv-hero--letter .inv-hero-title { order: 2; }
.inv-hero--letter .inv-rule { display: block; order: 3; }
.inv-hero--letter .inv-hero-subtitle { order: 4; }
.inv-hero--letter .inv-hero-meta { order: 5; }
.inv-hero--letter .inv-quote { order: 6; }
.inv-hero--letter .inv-countdown { order: 7; }
.inv-hero--letter .inv-hero-media {
  display: block;
  order: 8;
  width: min(100%, 30rem);
  aspect-ratio: 16 / 9;
  margin-top: 0.5rem;
}

/* hero-seal — the monogram is the object. Ignores subtitle and quote. */
.inv-hero--seal.inv-hero {
  min-height: calc(0.88 * var(--inv-screen));
  justify-items: center;
  align-content: center;
  gap: 0.8rem;
  padding: 3.25rem 1.35rem;
  text-align: center;
}
.inv-hero--seal .inv-hero-content { display: contents; }
.inv-hero--seal .inv-emblem { order: 1; font-size: clamp(56px, 16cqw, 92px); }
.inv-hero--seal .inv-rule { display: block; order: 2; }
.inv-hero--seal .inv-hero-title { order: 3; font-size: clamp(22px, 5.5cqw, 34px); letter-spacing: 0.06em; }
.inv-hero--seal .inv-hero-meta { order: 4; }
.inv-hero--seal .inv-countdown { order: 5; }
.inv-hero--seal .inv-hero-media {
  display: block;
  order: 6;
  width: min(12rem, 45cqw);
  aspect-ratio: 1;
  border-radius: 50%;
}
.inv-hero--seal .inv-hero-subtitle,
.inv-hero--seal .inv-quote { display: none; }
.inv-editor .inv-hero--seal .inv-hero-subtitle { display: block; order: 7; opacity: 0.5; }
.inv-editor .inv-hero--seal .inv-quote { display: block; order: 8; opacity: 0.5; }

/* hero-quiet — a name and a date, vertically centred. Nothing else. */
.inv-hero--quiet.inv-hero {
  min-height: var(--inv-screen);
  justify-items: center;
  align-content: center;
  gap: 1rem;
  padding: 3rem 1.5rem;
  text-align: center;
}
.inv-hero--quiet .inv-hero-content { display: contents; }
.inv-hero--quiet .inv-hero-title { order: 1; font-size: clamp(28px, 8cqw, 48px); font-weight: 400; letter-spacing: 0.04em; }
.inv-hero--quiet .inv-rule { display: block; order: 2; }
.inv-hero--quiet .inv-hero-meta { order: 3; }
.inv-hero--quiet .inv-hero-media,
.inv-hero--quiet .inv-hero-subtitle,
.inv-hero--quiet .inv-quote,
.inv-hero--quiet .inv-emblem,
.inv-hero--quiet .inv-countdown { display: none; }
/* A layout that ignores a slot still has to let the couple edit it - they
   may be one tap from switching to a layout that uses it. Ignored slots
   are shown faintly in the editor and never on the public page. */
.inv-editor .inv-hero--quiet .inv-hero-subtitle { display: block; order: 4; opacity: 0.5; }
.inv-editor .inv-hero--quiet .inv-quote { display: block; order: 5; opacity: 0.5; }
.inv-editor .inv-hero--quiet .inv-emblem { display: flex; order: 6; opacity: 0.5; font-size: 1.4rem; }
.inv-editor .inv-hero--quiet .inv-countdown { display: grid; order: 7; opacity: 0.5; }
.inv-editor .inv-hero--quiet .inv-hero-media { display: block; order: 8; width: 6rem; aspect-ratio: 1; opacity: 0.5; }

/* ---- Story ----
   .inv-story-body is display:contents so the section photo and the body
   are siblings of the quote and the item list, and each layout orders the
   four without its own wrapper. Section photo is
   .inv-story-body > .inv-story-photo; item photos are
   .inv-story-item > .inv-story-photo - they share a class. */
.inv-story-body { display: contents; }
.inv-story-photo { overflow: hidden; background: var(--sec-surface); width: 100%; }
.inv-story-items { display: grid; gap: 1.6rem; }
.inv-story-item { display: grid; gap: 0.4rem; text-align: left; }
.inv-story-item .inv-text { text-align: left; }
.inv-story-item .inv-heading-sm { text-align: left; }

/* story-chapter — start-aligned prose with the milestones as an
   appendix. The default when there is no sequence to show. */
.inv-story--chapter .inv-inner { justify-items: start; }
.inv-story--chapter .inv-heading { text-align: left; }
.inv-story--chapter .inv-inner > .inv-heading::after { margin-left: 0; }
.inv-story--chapter .inv-story-body > .inv-story-photo { order: 2; aspect-ratio: 4 / 5; }
.inv-story--chapter .inv-quote {
  order: 3;
  text-align: left;
  border-left: 2px solid var(--sec-accent);
  padding-left: 1rem;
  margin: 0;
}
.inv-story--chapter .inv-rich { order: 4; text-align: left; max-width: 38ch; }
.inv-story--chapter .inv-story-items { order: 5; width: 100%; }
.inv-story--chapter .inv-story-item { border-top: 1px solid var(--sec-border); padding-top: 1.1rem; }
.inv-story--chapter .inv-story-item > .inv-story-photo { order: 4; aspect-ratio: 16 / 10; margin-top: 0.35rem; }

/* story-timeline — one spine, never a zigzag: an alternating timeline is
   two columns pretending to be one on a phone. */
.inv-story--timeline .inv-story-body > .inv-story-photo {
  order: 2;
  width: min(100%, 14rem);
  aspect-ratio: 1;
  border-radius: 50%;
  justify-self: center;
}
.inv-story--timeline .inv-quote { order: 3; }
.inv-story--timeline .inv-rich { order: 4; text-align: center; max-width: 40ch; margin-inline: auto; }
.inv-story--timeline .inv-story-items {
  order: 5;
  position: relative;
  padding-left: 1.9rem;
  gap: 1.75rem;
}
.inv-story--timeline .inv-story-items::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 0.6rem;
  bottom: 0.6rem;
  width: 1px;
  background: var(--sec-border);
}
.inv-story--timeline .inv-story-item { position: relative; }
.inv-story--timeline .inv-story-item::before {
  content: "";
  position: absolute;
  left: -1.9rem;
  top: 0.5rem;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--sec-accent);
}
.inv-story--timeline .inv-story-item > .inv-story-photo { order: 4; aspect-ratio: 1; width: min(100%, 15rem); }

/* story-journal — one surface sheet per milestone. Ignores the section
   photo: the sheets already carry the pictures. */
.inv-story--journal .inv-story-body > .inv-story-photo { display: none; }
.inv-editor .inv-story--journal .inv-story-body > .inv-story-photo { display: block; order: 2; width: 6rem; aspect-ratio: 1; opacity: 0.5; }
.inv-story--journal .inv-rich { order: 3; text-align: center; max-width: 38ch; margin-inline: auto; }
.inv-story--journal .inv-quote { order: 4; }
.inv-story--journal .inv-story-items { order: 5; gap: 1.25rem; }
.inv-story--journal .inv-story-item {
  background: var(--sec-surface);
  border: 1px solid var(--sec-border);
  padding: 1.2rem 1.1rem 1.35rem;
}
.inv-story--journal .inv-story-item > .inv-story-photo { order: 3; aspect-ratio: 3 / 2; }
.inv-story--journal .inv-story-item-text { order: 4; }

/* story-vignette — the quote is the moment; milestones shrink to a date
   and a title so they do not compete with it. */
.inv-story--vignette .inv-inner { justify-items: center; text-align: center; }
.inv-story--vignette .inv-quote {
  order: 1;
  font-size: clamp(24px, 6cqw, 38px);
  max-width: 20ch;
  text-align: center;
}
.inv-story--vignette .inv-story-body > .inv-story-photo {
  order: 2;
  width: min(14rem, 60cqw);
  aspect-ratio: 1;
  border-radius: 50%;
}
.inv-story--vignette .inv-rich { order: 3; text-align: center; max-width: 28ch; color: var(--sec-muted); }
.inv-story--vignette .inv-story-items { order: 4; gap: 0.9rem; width: 100%; }
.inv-story--vignette .inv-story-item { justify-items: center; text-align: center; gap: 0.15rem; }
.inv-story--vignette .inv-story-item .inv-heading-sm { text-align: center; }
.inv-story--vignette .inv-story-item > .inv-story-photo,
.inv-story--vignette .inv-story-item-text { display: none; }
.inv-editor .inv-story--vignette .inv-story-item-text { display: block; opacity: 0.5; }
.inv-editor .inv-story--vignette .inv-story-item > .inv-story-photo { display: block; width: 4.5rem; aspect-ratio: 1; opacity: 0.5; }

/* story-window — chapter's appendix, but the photo stays an object and
   moves beside the type on a wide screen. */
.inv-story--window .inv-story-body > .inv-story-photo { order: 2; aspect-ratio: 4 / 5; width: min(100%, 24rem); justify-self: center; }
.inv-story--window .inv-quote { order: 3; }
.inv-story--window .inv-rich { order: 4; max-width: 38ch; margin-inline: auto; }
.inv-story--window .inv-story-items { order: 5; width: 100%; }
.inv-story--window .inv-story-item { border-top: 1px solid var(--sec-border); padding-top: 1.1rem; }
.inv-story--window .inv-story-item > .inv-story-photo { order: 4; aspect-ratio: 16 / 10; margin-top: 0.35rem; }

/* ---- Gallery ---- */
.inv-gallery-items { display: grid; gap: 0.45rem; }
.inv-gallery-photo { aspect-ratio: 1; overflow: hidden; background: var(--sec-surface); }
.inv-gallery-item figcaption {
  color: var(--sec-muted);
  font-size: 0.74em;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-family: "DM Sans", sans-serif;
  margin-top: 0.4rem;
  text-align: center;
}
.inv-gallery-index {
  display: none;
  color: var(--sec-muted);
  font-size: 0.74em;
  letter-spacing: 0.12em;
  font-family: "DM Sans", sans-serif;
  text-align: center;
}

/* gallery-grid — two up on a phone, three from 1025px. Past six photos
   the captions stop being labels and become a wall of small type, so the
   picture is left to be the content. */
.inv-gallery--grid .inv-gallery-items { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.inv-gallery--grid .inv-gallery-items:has(> .inv-gallery-item:nth-child(7)) figcaption { display: none; }
.inv-gallery--grid .inv-gallery-items:not(:has(> .inv-gallery-item:nth-child(2))) {
  grid-template-columns: minmax(0, 50%);
  justify-content: center;
}

/* gallery-mosaic — one photo leads, the rest fall in behind it. */
.inv-gallery--mosaic .inv-gallery-items { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.inv-gallery--mosaic .inv-gallery-item:first-child { grid-column: 1 / -1; }
.inv-gallery--mosaic .inv-gallery-item:first-child .inv-gallery-photo { aspect-ratio: 4 / 5; }
.inv-gallery--mosaic .inv-gallery-item:not(:first-child) figcaption { display: none; }

/* gallery-film — one photo at a time with a peek of the next, and a
   position readout rather than twenty-four dots. The section height does
   not grow with the photo count, which is the point of it. */
.inv-gallery--film .inv-gallery-items {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  gap: 0.6rem;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.inv-gallery--film .inv-gallery-items::-webkit-scrollbar { display: none; }
.inv-gallery--film .inv-gallery-item { flex: 0 0 calc(100% - 1.6rem); scroll-snap-align: center; min-width: 0; }
.inv-gallery--film .inv-gallery-photo { aspect-ratio: 4 / 5; }
/* Reserve the caption line so slides do not jump past a photo with none. */
.inv-gallery--film .inv-gallery-item figcaption { min-height: 1.6em; }
.inv-gallery--film .inv-gallery-index { display: block; }

/* gallery-paper — each photo on its own sheet. Deliberately no rotation:
   scattered polaroids read as a template, not as the couple's taste. */
.inv-gallery--paper .inv-gallery-items { grid-template-columns: minmax(0, 1fr); gap: 1.1rem; }
.inv-gallery--paper .inv-gallery-item {
  background: var(--sec-surface);
  border: 1px solid var(--sec-border);
  padding: 0.7rem 0.7rem 1rem;
}
.inv-gallery--paper .inv-gallery-item figcaption { text-transform: none; letter-spacing: 0; font-size: 0.9em; }

/* gallery-caption — one photo, one caption, a pause, repeat. */
.inv-gallery--caption .inv-gallery-items { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
.inv-gallery--caption .inv-gallery-photo { aspect-ratio: 4 / 5; }
.inv-gallery--caption .inv-gallery-item figcaption {
  font-size: 1em;
  text-transform: none;
  letter-spacing: 0;
  margin-top: 0.6rem;
}

/* ---- Details ---- */
.inv-details-meta { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.75rem; }
.inv-detail-card {
  background: var(--sec-surface);
  border: 1px solid var(--sec-border);
  padding: 1.2rem 1.1rem;
  display: grid;
  gap: 0.3rem;
  text-align: center;
  justify-items: center;
}
.inv-detail-card > .inv-muted:first-child {
  font-family: "DM Sans", sans-serif;
  font-size: 0.7em;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.inv-detail-card .inv-text { font-family: "Playfair Display", serif; font-size: 1.15em; }
.inv-detail-note { font-style: italic; }
.inv-details-grid { display: grid; gap: 1.75rem; }
.inv-details-grid > .inv-timeline { order: 1; }
.inv-details-grid > .inv-map { order: 2; }
.inv-timeline { display: grid; gap: 1.35rem; }
.inv-timeline-event { display: grid; grid-template-columns: 44px minmax(0, 1fr); gap: 1rem; align-items: start; }
.inv-timeline-body { text-align: left; display: grid; gap: 0.2rem; min-width: 0; }
.inv-timeline-body .inv-text { text-align: left; }
.inv-timeline-time { letter-spacing: 0.14em; text-transform: uppercase; font-family: "DM Sans", sans-serif; font-size: 0.74em; }
.inv-event-location {
  display: block;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
  padding: 0.1em 0;
  border-bottom: 1px dashed color-mix(in srgb, var(--sec-border) 80%, transparent);
  color: var(--sec-text);
  text-align: inherit;
}
.inv-event-location::placeholder { color: var(--sec-muted); opacity: 0.75; }
.inv-event-location:focus { outline: none; border-bottom-color: var(--sec-accent); }
.inv-timeline-marker {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--sec-surface);
  border: 1px solid var(--sec-border);
  display: grid;
  place-items: center;
  flex: none;
  position: relative;
  z-index: 1;
}
/* Icons take the section accent. iconColor is deliberately ignored: a
   colour chosen under one palette goes unreadable under the next, and an
   icon is a small mark, not a place to spend a second colour. */
.inv-icon-shape {
  width: 22px;
  height: 22px;
  background-color: var(--sec-accent);
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}
/* The map box has a real height in every layout - a thin strip is not a
   map. em rather than cqh: .inv-canvas is only container-type:size from
   1025px, so cqh is 0 on a phone and the canvas collapses. */
.inv-map { min-height: 18em; border: 1px solid var(--sec-border); overflow: hidden; }
.inv-map-canvas, .inv-map .map-canvas { width: 100%; height: 18em; min-height: 18em; }
.inv-map-placeholder { min-height: 18em; display: grid; place-items: center; color: var(--sec-muted); background: var(--sec-surface); }
.inv-section .gm-style .gm-style-iw-c { background: var(--sec-bg); }
.inv-section .gm-style .gm-style-iw-t::after { background: var(--sec-bg); }
.inv-section .gm-style .gm-style-iw-d { color: var(--sec-text); }
.inv-section .gm-style .gm-style-iw-d a { color: var(--sec-accent); }

/* details-notice — the date and place as a single notice, then the day. */
.inv-details--notice .inv-details-meta { gap: 0; }
.inv-details--notice .inv-detail-card + .inv-detail-card { border-top: 0; }

/* details-timeline — one spine. Map below on a phone, beside from 1025px. */
.inv-details--timeline .inv-timeline { position: relative; }
.inv-details--timeline .inv-timeline::before {
  content: "";
  position: absolute;
  left: 21px;
  top: 22px;
  bottom: 22px;
  width: 1px;
  background: var(--sec-border);
}

/* details-map — the map is the answer to "where", so it comes first and
   the written address confirms it. display:contents on the grid lets the
   list fall below the date line rather than being trapped beside the map. */
.inv-details--map .inv-details-grid { display: contents; }
.inv-details--map .inv-map { order: 1; }
.inv-details--map .inv-details-meta { order: 2; }
.inv-details--map .inv-timeline { order: 3; }

/* details-ledger — the twenty-row layout: a time column and a name, with
   the place indented under it. .inv-timeline-body goes display:contents so
   the time can hold its own column. */
.inv-details--ledger .inv-timeline { gap: 0; }
.inv-details--ledger .inv-timeline-event {
  grid-template-columns: 4.5em minmax(0, 1fr);
  gap: 0.2rem 0.9rem;
  padding: 0.85rem 0;
  border-top: 1px solid var(--sec-border);
  align-items: baseline;
}
.inv-details--ledger .inv-timeline-marker { display: none; }
.inv-details--ledger .inv-timeline-body { display: contents; }
.inv-details--ledger .inv-timeline-time { grid-column: 1; grid-row: 1; }
.inv-details--ledger .inv-timeline-body .inv-heading-sm { grid-column: 2; grid-row: 1; }
.inv-details--ledger .inv-timeline-location,
.inv-details--ledger .inv-event-location { grid-column: 2; grid-row: 2; }

/* details-cards — one card per moment of the day. */
.inv-details--cards .inv-timeline { gap: 0.85rem; }
.inv-details--cards .inv-timeline-event {
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  gap: 0.5rem;
  background: var(--sec-surface);
  border: 1px solid var(--sec-border);
  padding: 1.15rem 1rem;
  text-align: center;
}
.inv-details--cards .inv-timeline-body { justify-items: center; text-align: center; }
.inv-details--cards .inv-timeline-body .inv-text,
.inv-details--cards .inv-event-location,
.inv-details--cards .inv-timeline-body .inv-heading-sm { text-align: center; }

/* ---- RSVP ----
   The one section with a job. One column, thumb-sized attending targets,
   and the privacy notice welded directly above the button. */
.inv-rsvp-intro { display: grid; gap: 0.5rem; justify-items: center; text-align: center; }
.inv-rsvp-title { width: 100%; text-align: center; }
.inv-rsvp-subtitle { text-align: center; max-width: 40ch; margin-inline: auto; color: var(--sec-muted); text-transform: none; letter-spacing: 0; }
.inv-rsvp-form {
  display: grid;
  gap: 1.1rem;
  text-align: left;
  width: 100%;
  max-width: 30rem;
  margin-inline: auto;
}
/* Attending sits directly under the name on every layout but gate, which
   puts it first: it is the one answer the couple actually needs, and
   burying it under an optional email is how a guest misses it. */
.inv-rsvp-form > * { order: 3; }
.inv-rsvp-form > .inv-form-group:first-child { order: 1; }
.inv-rsvp-form > .inv-rsvp-attending { order: 2; }
.inv-form-group { display: grid; gap: 0.4rem; }
.inv-label { color: var(--sec-muted); font-size: 0.74em; letter-spacing: 0.1em; text-transform: uppercase; font-family: "DM Sans", sans-serif; }
.inv-input {
  width: 100%;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--sec-border);
  background: var(--sec-surface);
  color: var(--sec-text);
  font: inherit;
  border-radius: 6px;
}
.inv-input:focus { outline: 2px solid var(--sec-accent); outline-offset: 1px; }
.inv-radio-group { display: grid; gap: 0.5rem; }
.inv-radio-label, .inv-check-label { position: relative; display: flex; align-items: center; gap: 0.6rem; cursor: pointer; text-transform: none; letter-spacing: 0; }
/* The native control is replaced visually by .inv-radio-custom, so it has
   to be hidden - without this BOTH render, the browser's own circle next
   to ours. The dashboard hides its equivalent with display:none, but that
   rule is .radio-label-scoped and sections are deliberately isolated from
   dashboard CSS, so sections need their own.
   Hidden by opacity rather than display:none on purpose: real guests fill
   this form, and display:none drops the input out of the accessibility
   tree and out of tab order, leaving a keyboard or screen-reader user
   unable to answer at all. This keeps it focusable, with the focus ring
   painted on the visible stand-in below. */
.inv-radio-label > input, .inv-check-label > input {
  position: absolute;
  width: 18px; height: 18px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}
.inv-radio-label:has(> input:focus-visible) .inv-radio-custom,
.inv-check-label:has(> input:focus-visible) .inv-check-custom {
  outline: 2px solid var(--sec-accent);
  outline-offset: 2px;
}
.inv-radio-custom, .inv-check-custom {
  width: 18px; height: 18px; border: 1px solid var(--sec-text); border-radius: 50%; display: inline-block; flex: none;
}
.inv-check-custom { border-radius: 4px; }
.inv-radio-label:has(input:checked) .inv-radio-custom,
.inv-check-label:has(input:checked) .inv-check-custom {
  border-color: var(--sec-accent);
  background: var(--sec-accent);
}
/* Attending is two targets, never a dropdown, and the selected one is the
   section's single filled object. The dot is dropped here - the whole
   target is the control. */
.inv-attending-group { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.6rem; }
.inv-attending-option {
  justify-content: center;
  text-align: center;
  min-height: 3.1em;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--sec-border);
  background: var(--sec-surface);
  border-radius: 6px;
}
.inv-attending-option .inv-radio-custom { display: none; }
.inv-attending-option:has(> input:focus-visible) { outline: 2px solid var(--sec-accent); outline-offset: 2px; }
.inv-attending-option:has(input:checked) {
  background: var(--sec-accent);
  border-color: var(--sec-accent);
  color: var(--sec-accent-text);
}
.inv-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
  padding: 0.85rem 1.4rem; border-radius: 6px; cursor: pointer; font: inherit;
}
.inv-btn-accent { background: var(--sec-accent); color: var(--sec-accent-text); width: 100%; }
.inv-btn-ghost { background: transparent; color: var(--sec-accent); }
.inv-plusones-header { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; }
/* Privacy notice and submit are one footer unit. */
.inv-rsvp-form > .inv-rsvp-notice { margin-top: 0.9rem; }
/* A submitted form is replaced by its own success message rather than
   left sitting there filled in - see app.js's htmx:afterSwap handler. */
.inv-rsvp-form--done > *:not(#rsvp-response) { display: none; }
/* Editor-only warning under the "add question" button. Uses the editor's
   own accent rather than a --sec-* token: it is chrome addressed to the
   couple, not part of the invitation the guests see, and it must stay
   legible on every palette including the dark ones. */
.inv-editor .inv-question-warning {
    font-size: 0.75rem;
    line-height: 1.5;
    max-width: 52ch;
    margin: 0.5rem 0 0;
    padding: 0.5rem 0.65rem;
    border-radius: 6px;
    color: #7a2438;
    background: rgba(228, 68, 121, 0.1);
    border: 1px solid rgba(228, 68, 121, 0.28);
    text-transform: none;
    letter-spacing: 0;
}

/* Art. 13 guest notice, directly above the submit button. Small print,
   but it must stay legible: --sec-muted is already contrast-checked
   against --sec-bg by palettes_test.go, so it degrades safely on every
   palette. The link inherits colour and relies on the underline, since
   accent is reserved here for what is actually actionable (the submit
   button and a checked radio). */
.inv-rsvp-notice {
    font-size: 0.78em;
    line-height: 1.55;
    color: var(--sec-muted);
    text-align: left;
    text-transform: none;
    letter-spacing: 0;
    max-width: 60ch;
    margin: 0.25rem 0 0;
}

.inv-rsvp-notice a { color: inherit; text-decoration: underline; }

.inv-rsvp-inactive { text-align: center; padding: 2rem 0; }

/* rsvp-letter / gate / aside wear underlines; panel and compact are
   boxed. Both are legible; the difference is whether the form reads as
   stationery or as a form. */
.inv-rsvp--letter .inv-input,
.inv-rsvp--gate .inv-input,
.inv-rsvp--aside .inv-input {
  border: 0;
  border-bottom: 1px solid var(--sec-border);
  border-radius: 0;
  background: transparent;
  padding: 0.55rem 0;
}

/* rsvp-panel — the form on a raised sheet, privacy and submit inside it. */
.inv-rsvp--panel .inv-rsvp-form {
  background: var(--sec-surface);
  border: 1px solid var(--sec-border);
  border-radius: 8px;
  padding: 1.5rem 1.25rem 1.75rem;
}

/* rsvp-gate — the question first, as two full-width targets, and nothing
   else until it is answered. Scoped to form.inv-rsvp-form so the editor's
   preview (a <div>, with the inputs disabled and never checked) keeps
   showing every field for editing. */
.inv-rsvp--gate .inv-rsvp-form > .inv-rsvp-attending { order: 0; }
.inv-rsvp--gate .inv-attending-group { grid-template-columns: minmax(0, 1fr); }
.inv-rsvp--gate .inv-attending-option { min-height: 3.25em; }
.inv-rsvp--gate form.inv-rsvp-form:not(:has(.inv-rsvp-attending input:checked)) > *:not(.inv-rsvp-attending) { display: none; }
/* A guest who is not coming has no plus-ones to name. */
.inv-rsvp--gate form.inv-rsvp-form:has(.inv-attending-option[data-attending="no"] input:checked) .inv-plusones { display: none; }

/* rsvp-compact — tighter, but the footer unit is never compacted. */
.inv-rsvp--compact .inv-rsvp-form { gap: 0.7rem; }
.inv-rsvp--compact .inv-input { padding: 0.5rem 0.7rem; }
.inv-rsvp--compact .inv-rsvp-form > .inv-rsvp-notice { margin-top: 1.4rem; }

/* ---- Info ---- */
.inv-info-items { display: grid; gap: 1rem; }
.inv-info-card { display: grid; gap: 0.4rem; text-align: left; }
.inv-info-card .inv-text { text-align: left; }
.inv-info-card .inv-heading-sm { text-align: left; }
.inv-info .inv-rich { text-align: center; max-width: 38ch; margin-inline: auto; }

/* info-letter — running prose with the items as subsequent headings.
   With no items this is the old "simple" layout, which is why simple is
   gone: it was this one, emptier. */
.inv-info--letter .inv-inner { justify-items: start; }
.inv-info--letter .inv-heading { text-align: left; }
.inv-info--letter .inv-inner > .inv-heading::after { margin-left: 0; }
.inv-info--letter .inv-rich { text-align: left; margin-inline: 0; max-width: 38ch; }
.inv-info--letter .inv-info-items { gap: 1.35rem; max-width: 38ch; }
.inv-info--letter .inv-info-card .inv-heading-sm { font-size: 1.05em; }

/* info-cards */
.inv-info--cards .inv-info-card {
  background: var(--sec-surface);
  border: 1px solid var(--sec-border);
  padding: 1.2rem 1.1rem;
}

/* info-index — a real <details> accordion on the public page (native,
   keyboard-operable, collapsed with no JS). The editor renders <article>
   instead, so these rules simply paint hairline rows there. */
.inv-info--index .inv-info-items { gap: 0; }
.inv-info--index .inv-info-card { border-top: 1px solid var(--sec-border); padding: 0; }
.inv-info--index .inv-info-card:last-of-type { border-bottom: 1px solid var(--sec-border); }
.inv-info--index .inv-info-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 2.75em;
  padding: 0.65rem 0;
  cursor: pointer;
  list-style: none;
}
.inv-info--index .inv-info-head::-webkit-details-marker { display: none; }
.inv-info--index .inv-info-marker { position: relative; width: 12px; height: 12px; flex: none; }
.inv-info--index .inv-info-marker::before,
.inv-info--index .inv-info-marker::after { content: ""; position: absolute; background: var(--sec-muted); }
.inv-info--index .inv-info-marker::before { left: 0; right: 0; top: 5px; height: 1px; }
.inv-info--index .inv-info-marker::after { top: 0; bottom: 0; left: 5px; width: 1px; }
.inv-info--index .inv-info-card[open] .inv-info-marker::after { display: none; }
.inv-info--index .inv-info-card > .inv-text { padding: 0 0 0.9rem; }

/* info-list — everything visible, item titles as quiet labels. One
   column at every width: this is a list, not a grid. */
.inv-info--list .inv-info-items { gap: 1.35rem; }
.inv-info--list .inv-info-card { border-top: 1px solid var(--sec-border); padding-top: 1.05rem; }
.inv-info--list .inv-info-card .inv-heading-sm {
  font-family: "DM Sans", sans-serif;
  font-size: 0.74em;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sec-muted);
}

/* info-aside — a list on a phone; body and items side by side when there
   is room for both. */
.inv-info--aside .inv-info-card { border-top: 1px solid var(--sec-border); padding-top: 1.05rem; }

/* ---- Closing ---- */
.inv-closing .inv-inner { padding-block: 3.5rem 4rem; }
.inv-closing-inner { text-align: center; display: grid; gap: 0.85rem; justify-items: center; }
.inv-closing-inner .inv-text,
.inv-closing-inner .inv-quote { text-align: center; }
.inv-signature { font-family: "Great Vibes", cursive; font-size: clamp(28px, 7cqw, 44px); color: var(--sec-accent); }
.inv-closing-photo { overflow: hidden; width: min(240px, 60cqw); aspect-ratio: 1; border-radius: 50%; }

/* closing-signature — the sign-off, ending on the names. Ignores photo. */
.inv-closing--signature .inv-closing-photo { display: none; }
.inv-editor .inv-closing--signature .inv-closing-photo { display: block; order: 6; width: 5rem; opacity: 0.5; }
.inv-closing--signature .inv-heading { order: 1; }
.inv-closing--signature .inv-closing-subtitle { order: 2; }
.inv-closing--signature .inv-quote { order: 3; }
.inv-closing--signature .inv-emblem { order: 4; font-size: 1.35rem; letter-spacing: 0.3em; }
.inv-closing--signature .inv-signature { order: 5; font-size: clamp(34px, 9cqw, 56px); }

/* closing-vignette — a portrait to close on. Ignores quote. */
.inv-closing--vignette .inv-quote,
.inv-closing--vignette .inv-emblem { display: none; }
.inv-editor .inv-closing--vignette .inv-quote { display: block; order: 5; opacity: 0.5; }
.inv-editor .inv-closing--vignette .inv-emblem { display: flex; order: 6; font-size: 1.35rem; opacity: 0.5; }
.inv-closing--vignette .inv-closing-photo { order: 1; width: min(13em, 62cqw); }
.inv-closing--vignette .inv-heading { order: 2; }
.inv-closing--vignette .inv-closing-subtitle { order: 3; }
.inv-closing--vignette .inv-signature { order: 4; font-size: clamp(20px, 4cqw, 28px); }

/* closing-quote — the quote is the object; the names attribute it. */
.inv-closing--quote .inv-closing-photo,
.inv-closing--quote .inv-closing-subtitle,
.inv-closing--quote .inv-emblem { display: none; }
.inv-editor .inv-closing--quote .inv-closing-photo { display: block; order: 5; width: 5rem; opacity: 0.5; }
.inv-editor .inv-closing--quote .inv-closing-subtitle { display: block; order: 6; opacity: 0.5; }
.inv-closing--quote .inv-quote { order: 1; font-size: clamp(24px, 6cqw, 38px); max-width: 20ch; }
.inv-closing--quote .inv-rule { display: block; order: 2; }
.inv-closing--quote .inv-signature { order: 3; }
.inv-closing--quote .inv-heading { order: 4; font-size: 1.05em; color: var(--sec-muted); letter-spacing: 0.1em; }

/* closing-seal — the monogram again, as a wax seal would be. */
.inv-closing--seal .inv-closing-photo,
.inv-closing--seal .inv-quote,
.inv-closing--seal .inv-closing-subtitle { display: none; }
.inv-editor .inv-closing--seal .inv-closing-photo { display: block; order: 5; width: 5rem; opacity: 0.5; }
.inv-editor .inv-closing--seal .inv-quote { display: block; order: 6; opacity: 0.5; }
.inv-editor .inv-closing--seal .inv-closing-subtitle { display: block; order: 7; opacity: 0.5; }
.inv-closing--seal .inv-emblem { order: 1; font-size: clamp(56px, 15cqw, 92px); }
.inv-closing--seal .inv-rule { display: block; order: 2; }
.inv-closing--seal .inv-heading { order: 3; font-size: 1.05em; letter-spacing: 0.1em; }
.inv-closing--seal .inv-signature { order: 4; font-size: clamp(20px, 4cqw, 28px); }

/* closing-quiet — a name, and room around it. Everything empty leaves a
   padded band; it does not invent "The End". */
.inv-closing--quiet .inv-closing-photo,
.inv-closing--quiet .inv-quote,
.inv-closing--quiet .inv-emblem { display: none; }
.inv-editor .inv-closing--quiet .inv-closing-photo { display: block; order: 5; width: 5rem; opacity: 0.5; }
.inv-editor .inv-closing--quiet .inv-quote { display: block; order: 6; opacity: 0.5; }
.inv-editor .inv-closing--quiet .inv-emblem { display: flex; order: 7; font-size: 1.35rem; opacity: 0.5; }
.inv-closing--quiet .inv-signature { order: 1; }
.inv-closing--quiet .inv-heading { order: 2; font-size: 1.05em; color: var(--sec-muted); letter-spacing: 0.1em; }
.inv-closing--quiet .inv-closing-subtitle { order: 3; }

/* ---- Phone / tablet (<=1024px) ---- */
@media (max-width: 1024px) {
  /* Full-screen editing. The dashboard's tab bar and the editor's own
     actions would otherwise stack into two bars at the bottom of a phone,
     which is why the save bar used to be left un-pinned at the end of the
     form. The editor now REPLACES the tab bar instead of adding to it, so
     the actions can be pinned without costing any extra screen. */
  body:has(.inv-editor) .mobile-nav { display: none; }
  body:has(.inv-editor) .main-content { padding-bottom: 0; }
  body:has(.inv-editor) .event-form-page { padding-bottom: 88px; }
}

/* ---- Wide canvas (>=1025px) — currently unreachable ----
   The default is deliberately "the same stack, wider, measure capped".
   Only window, aside and map genuinely become two columns; inventing a
   second design for every layout is how the phone view ends up an
   afterthought again.

   @container, NOT @media, and that is load-bearing rather than tidy: these
   are two-column CONTENT rules, and the canvas is now capped at
   --inv-width. Keyed to the viewport they would still fire on a desktop
   browser and drop a 2-column grid into a 390px column. As a container
   query the block asks the question it always meant to ask - "is the
   canvas wide?" - and answers no everywhere.

   So nothing below currently applies, on any screen, in the editor or on
   the public page. Kept rather than deleted: it is real design work and it
   is now inert by construction. Deleting it is a separate decision.

   The other media queries in this file (the rail, the pinned save bar, the
   bottom sheet) are editor chrome keyed to the real viewport and must stay
   @media. */
@container (min-width: 1025px) {
  .inv-inner { padding: 4.5rem 2rem 5rem; gap: 1.35rem; }

  .inv-hero-content { padding: 4rem 2rem; }
  .inv-hero--cover .inv-hero-content { padding: 2.5rem 2rem 3rem; }
  .inv-hero--cover .inv-hero-content > * { max-width: 46rem; }

  /* hero-window: photo left, type right, both vertically centred. */
  .inv-hero--window.inv-hero {
    grid-template-columns: minmax(0, 0.42fr) minmax(0, 0.58fr);
    align-items: center;
    column-gap: 3rem;
    padding: 4rem 3rem;
    text-align: left;
  }
  .inv-hero--window > .inv-rail { grid-column: 1 / -1; }
  .inv-hero--window .inv-hero-media { grid-column: 1; grid-row: 2 / span 7; width: 100%; max-height: 78svh; align-self: center; }
  .inv-hero--window .inv-emblem,
  .inv-hero--window .inv-hero-title,
  .inv-hero--window .inv-hero-subtitle,
  .inv-hero--window .inv-hero-meta,
  .inv-hero--window .inv-quote,
  .inv-hero--window .inv-countdown { grid-column: 2; justify-self: start; }
  .inv-hero--window .inv-hero-title,
  .inv-hero--window .inv-hero-subtitle,
  .inv-hero--window .inv-hero-content .inv-quote { text-align: left; }
  .inv-hero--window .inv-hero-subtitle { margin-inline: 0; }
  .inv-hero--window .inv-emblem { justify-content: flex-start; }

  /* story-window: photo beside the prose, items full width below it. The
     photo keeps its ratio rather than stretching to the text height. */
  .inv-story--window .inv-inner { grid-template-columns: minmax(0, 0.4fr) minmax(0, 0.6fr); column-gap: 2.5rem; }
  .inv-story--window .inv-inner > .inv-heading { grid-column: 2; }
  .inv-story--window .inv-story-body > .inv-story-photo { grid-column: 1; grid-row: 1 / span 3; align-self: start; width: 100%; }
  .inv-story--window .inv-quote,
  .inv-story--window .inv-rich { grid-column: 2; }
  .inv-story--window .inv-story-items { grid-column: 1 / -1; }
  .inv-story--chapter .inv-story-body > .inv-story-photo { max-width: 28rem; }

  .inv-gallery--grid .inv-gallery-items { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .inv-gallery--mosaic .inv-gallery-items { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .inv-gallery--mosaic .inv-gallery-item:first-child { grid-column: 1 / -1; }
  .inv-gallery--film .inv-gallery-item { flex-basis: 22rem; }
  .inv-gallery--paper .inv-gallery-items { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .inv-gallery--paper .inv-gallery-items:not(:has(> .inv-gallery-item:nth-child(4))) { grid-template-columns: minmax(0, 1fr); }
  .inv-gallery--caption .inv-gallery-item { width: min(32rem, 100%); margin-inline: auto; }

  .inv-details-meta { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
  .inv-details--notice .inv-details-meta { grid-template-columns: minmax(0, 1fr); width: min(36em, 100%); margin-inline: auto; }
  .inv-details--notice .inv-map { width: min(36em, 100%); margin-inline: auto; }
  /* details-timeline: map beside the day, top-aligned, not stretched. */
  .inv-details--timeline .inv-details-grid { grid-template-columns: minmax(0, 0.6fr) minmax(0, 0.4fr); align-items: start; column-gap: 2rem; }
  .inv-details--timeline .inv-details-grid > .inv-map { min-height: 22em; }
  .inv-details--timeline .inv-map-canvas, .inv-details--timeline .inv-map .map-canvas { height: 22em; min-height: 22em; }
  /* details-map: map left, the day beside it. */
  .inv-details--map .inv-inner { grid-template-columns: minmax(0, 0.48fr) minmax(0, 0.52fr); column-gap: 2.5rem; }
  .inv-details--map .inv-inner > .inv-heading { grid-column: 1 / -1; }
  .inv-details--map .inv-map { grid-column: 1; grid-row: 2 / span 2; min-height: 28em; align-self: start; }
  .inv-details--map .inv-map-canvas, .inv-details--map .inv-map .map-canvas { height: 28em; min-height: 28em; }
  .inv-details--map .inv-details-meta { grid-column: 2; }
  .inv-details--map .inv-timeline { grid-column: 2; }
  .inv-details--cards .inv-timeline { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* One line per row once there is measure for it. */
  .inv-details--ledger .inv-timeline-event { grid-template-columns: 5em minmax(0, 1fr) minmax(0, 1fr); }
  .inv-details--ledger .inv-timeline-location,
  .inv-details--ledger .inv-event-location { grid-column: 3; grid-row: 1; }

  .inv-rsvp-form { max-width: 32rem; }
  .inv-rsvp--panel .inv-rsvp-form { max-width: 32em; }
  .inv-rsvp--gate .inv-attending-group { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* rsvp-aside: the invitation to reply on the left, the form on the right. */
  .inv-rsvp--aside .inv-inner { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); column-gap: 3rem; align-items: start; }
  .inv-rsvp--aside .inv-rsvp-intro { justify-items: start; text-align: left; position: sticky; top: 2rem; }
  .inv-rsvp--aside .inv-rsvp-title,
  .inv-rsvp--aside .inv-rsvp-subtitle { text-align: left; }
  .inv-rsvp--aside .inv-rsvp-title::after { margin-left: 0; margin-right: auto; }
  .inv-rsvp--aside .inv-rsvp-form { margin-inline: 0; }

  .inv-info--cards .inv-info-items { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .inv-info--letter .inv-rich,
  .inv-info--letter .inv-info-items { max-width: 40ch; }
  /* info-aside: body left, items right, no equal-height stretch. */
  .inv-info--aside .inv-inner { grid-template-columns: minmax(0, 0.55fr) minmax(0, 0.45fr); column-gap: 3rem; align-items: start; }
  .inv-info--aside .inv-inner > .inv-heading { grid-column: 1; text-align: left; }
  .inv-info--aside .inv-inner > .inv-heading::after { margin-left: 0; }
  .inv-info--aside .inv-inner > .inv-rich { grid-column: 1; text-align: left; margin-inline: 0; max-width: none; }
  .inv-info--aside .inv-info-items { grid-column: 2; grid-row: 1 / span 2; }

  .inv-closing .inv-inner { padding-block: 5rem 5.5rem; }
}


/* ---- Editor rail + toolbar (mobile-first; denser from 1025px) ---- */
.inv-rail {
  position: relative;
  z-index: 5;
  display: none;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 8px;
  background: color-mix(in srgb, var(--sec-surface) 22%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--sec-border) 28%, transparent);
  padding: 6px 8px;
  max-width: none;
}
.inv-editor .inv-rail,
.inv-section:hover > .inv-rail,
.inv-section:focus-within > .inv-rail { display: flex; }
.inv-rail-main {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 6px;
  width: 100%;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.inv-rail-main::-webkit-scrollbar { display: none; }
.inv-canvas .inv-rail-btn,
.inv-canvas .inv-rail-select,
.inv-canvas .inv-rail-more-btn {
  font-family: "DM Sans", sans-serif;
  font-size: 16px;
  padding: 0 12px;
  background: color-mix(in srgb, var(--sec-bg) 28%, transparent);
  color: var(--sec-text);
  border: 1px solid color-mix(in srgb, var(--sec-border) 45%, transparent);
  border-radius: 6px;
  cursor: pointer;
  touch-action: manipulation;
}
.inv-canvas .inv-rail-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-width: 44px;
  min-height: 44px;
}
.inv-rail-label {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-width: 0;
  font-family: "DM Sans", sans-serif;
  font-size: 14px;
  color: var(--sec-muted);
}
.inv-rail-select { flex: 1; min-width: 0; min-height: 44px; }
.inv-rail-palette-wrap { position: relative; flex: none; }
.inv-canvas .inv-rail-palette-btn { width: 44px; height: 44px; padding: 0; position: relative; }
.inv-rail-palette-icon { display: block; width: 20px; height: 20px; }
.inv-rail-palette-chip {
  position: absolute;
  right: 5px;
  bottom: 5px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--sec-accent);
  border: 1px solid var(--sec-border);
}
.inv-palette-popover {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 20;
  width: min(260px, calc(100cqw - 24px));
  padding: 10px;
  background: var(--sec-surface);
  color: var(--sec-text);
  border: 1px solid var(--sec-border);
  border-radius: 8px;
  font-family: "DM Sans", sans-serif;
}
.inv-palette-sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}
.inv-palette-close {
  display: none;
  width: 44px;
  height: 44px;
  flex: none;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: inherit;
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  touch-action: manipulation;
}
.inv-palette-popover-label {
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--sec-muted);
  margin: 0;
}
.inv-palette-host {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
.inv-palette-host[hidden] { display: none !important; }
.inv-palette-scrim {
  position: absolute;
  inset: 0;
  border: 0;
  padding: 0;
  margin: 0;
  background: rgb(20 18 16 / 0.45);
  cursor: pointer;
}
.inv-palette-host .inv-palette-popover {
  position: relative;
  top: auto;
  left: auto;
  width: 100%;
  max-height: min(78dvh, 580px);
  overflow: auto;
  padding: 12px 16px calc(20px + env(safe-area-inset-bottom, 0px));
  border: 0;
  border-radius: 16px 16px 0 0;
  background: var(--bg-base, #fff);
  color: var(--text-base, #1a1a1a);
  z-index: 1;
}
.inv-palette-host .inv-palette-close { display: inline-flex; align-items: center; justify-content: center; color: var(--text-base, #1a1a1a); }
.inv-palette-host .inv-palette-popover-label { color: var(--text-subdued, #666); font-size: 13px; }
.inv-palette-host .inv-rail-palettes {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}
.inv-palette-host .inv-palette-option {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  width: 100%;
  min-height: 0;
  padding: 0;
  overflow: hidden;
  text-align: left;
  background: var(--bg-surface, #f7f4ee);
  color: var(--text-base, #1a1a1a);
  border: 2px solid transparent;
  border-radius: 10px;
  cursor: pointer;
  font-family: "DM Sans", sans-serif;
  font-size: 14px;
  touch-action: manipulation;
}
.inv-palette-host .inv-palette-option.is-active { border-color: var(--accent-primary, #E44479); }
.inv-palette-host .inv-palette-option-swatches {
  display: grid;
  grid-template-columns: 1.6fr 0.8fr 0.8fr;
  width: 100%;
  height: 64px;
  gap: 0;
}
.inv-palette-host .inv-palette-option-swatches i {
  width: auto;
  height: 100%;
  border: 0;
  border-radius: 0;
}
.inv-palette-host .inv-palette-option-name {
  padding: 10px 12px;
  white-space: nowrap;
}
.inv-palette-host .inv-rail-advanced-btn {
  display: block;
  width: 100%;
  margin-top: 12px;
  min-height: 44px;
  padding: 8px 12px;
  background: var(--bg-surface, #f7f4ee);
  color: var(--text-base, #1a1a1a);
  border: 1px solid var(--border-subtle, #e5e0d8);
  border-radius: 8px;
  font-family: "DM Sans", sans-serif;
  font-size: 16px;
  cursor: pointer;
}
.inv-palette-host .inv-rail-token {
  color: var(--text-subdued, #666);
}
.inv-palette-host .inv-rail-token input[type="color"] {
  width: 44px;
  height: 36px;
}

/* Desktop: same host, but a floating panel. The in-rail popover is
   clipped — .inv-canvas is container-type:size (layout containment)
   plus overflow:auto from 1025px, and .inv-rail-main is overflow-x:auto
   which forces overflow-y clipping too. */
.inv-palette-host.inv-palette-host--popover {
  inset: 0;
  display: block;
  background: none;
  justify-content: unset;
  pointer-events: none;
}
.inv-palette-host.inv-palette-host--popover .inv-palette-scrim { display: none; }
.inv-palette-host.inv-palette-host--popover .inv-palette-popover {
  pointer-events: auto;
  position: fixed;
  top: 0;
  left: 0;
  width: 260px;
  max-height: min(70vh, 480px);
  overflow: auto;
  padding: 10px;
  border: 1px solid var(--border-subtle, #e5e0d8);
  border-radius: 8px;
  background: var(--bg-base, #fff);
  color: var(--text-base, #1a1a1a);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.inv-palette-host.inv-palette-host--popover .inv-palette-close { display: none; }
.inv-palette-host.inv-palette-host--popover .inv-palette-popover-label {
  color: var(--text-subdued, #666);
  font-size: 11px;
}
.inv-palette-host.inv-palette-host--popover .inv-rail-palettes {
  display: grid;
  grid-template-columns: 1fr;
  gap: 4px;
  max-height: 240px;
  overflow-y: auto;
}
.inv-palette-host.inv-palette-host--popover .inv-palette-option {
  flex-direction: row;
  align-items: center;
  gap: 10px;
  min-height: 0;
  padding: 6px 8px;
  overflow: hidden;
  background: var(--bg-surface, #f7f4ee);
  border: 1px solid transparent;
  border-radius: 6px;
  font-size: 12px;
}
.inv-palette-host.inv-palette-host--popover .inv-palette-option.is-active {
  border-color: var(--accent-primary, #E44479);
}
.inv-palette-host.inv-palette-host--popover .inv-palette-option-swatches {
  display: flex;
  grid-template-columns: none;
  width: auto;
  height: auto;
  gap: 2px;
  flex-shrink: 0;
}
.inv-palette-host.inv-palette-host--popover .inv-palette-option-swatches i {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1px solid var(--border-subtle, #e5e0d8);
}
.inv-palette-host.inv-palette-host--popover .inv-palette-option-name {
  padding: 0;
}
.inv-palette-host.inv-palette-host--popover .inv-rail-advanced-btn {
  margin-top: 8px;
  min-height: 0;
  padding: 6px 8px;
  font-size: 12px;
}
.inv-rail-palettes { display: grid; gap: 6px; }
.inv-canvas .inv-palette-option {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 8px 10px;
  text-align: left;
  background: var(--sec-bg);
  color: var(--sec-text);
  border: 1px solid transparent;
  border-radius: 6px;
  cursor: pointer;
  font-family: "DM Sans", sans-serif;
  font-size: 16px;
  touch-action: manipulation;
}
.inv-canvas .inv-palette-option.is-active { border-color: var(--sec-accent); }
.inv-palette-option-swatches { display: flex; gap: 2px; flex-shrink: 0; }
.inv-palette-option-swatches i {
  display: block;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1px solid var(--sec-border);
}
.inv-palette-option-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inv-canvas .inv-rail-advanced-btn {
  display: block;
  width: 100%;
  margin-top: 8px;
  min-height: 44px;
  padding: 8px 12px;
  background: var(--sec-bg);
  color: var(--sec-text);
  border: 1px solid var(--sec-border);
  border-radius: 6px;
  cursor: pointer;
  font-family: "DM Sans", sans-serif;
  font-size: 16px;
  touch-action: manipulation;
}
.inv-rail-tokens { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--sec-border); }
label.inv-rail-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 12px;
  border: 1px solid color-mix(in srgb, var(--sec-border) 45%, transparent);
  border-radius: 6px;
  background: color-mix(in srgb, var(--sec-bg) 28%, transparent);
  font-family: "DM Sans", sans-serif;
  font-size: 14px;
  color: var(--sec-muted);
  touch-action: manipulation;
  flex: none;
  white-space: nowrap;
}
.inv-rail-toggle:has(input:checked) { border-color: var(--sec-accent); color: var(--sec-text); }
.inv-rail-toggle input { width: 20px; height: 20px; flex: none; }
.inv-rail-more-toggle {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.inv-canvas .inv-rail-more-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  margin-left: auto;
}
.inv-rail:has(.inv-rail-more-toggle:checked) .inv-rail-more-btn { border-color: var(--sec-accent); }
.inv-rail-flags {
  display: none;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  width: 100%;
}
.inv-rail:has(.inv-rail-more-toggle:checked) .inv-rail-flags { display: flex; }
.inv-rail-token { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 44px; font-size: 14px; color: var(--sec-muted); }
.inv-rail-token input[type="color"] { width: 44px; height: 36px; padding: 0; border: 1px solid var(--sec-border); background: transparent; cursor: pointer; }

.inv-toolbar {
  position: fixed;
  z-index: 40;
  display: flex;
  flex-wrap: nowrap;
  gap: 0;
  align-items: stretch;
  padding: 4px 6px;
  background: #fff;
  color: var(--text-primary, #1a1a1a);
  border: 1px solid var(--border-subtle, #e5e0d8);
  border-radius: 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  font-family: "DM Sans", sans-serif;
  font-size: 14px;
  font-weight: 300;
}
.inv-toolbar[hidden] { display: none !important; }
.inv-toolbar--docked {
  justify-content: center;
  overflow-x: auto;
}
.inv-toolbar:not(.inv-toolbar--docked)::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -6px;
  width: 10px;
  height: 10px;
  background: #fff;
  border-right: 1px solid var(--border-subtle, #e5e0d8);
  border-bottom: 1px solid var(--border-subtle, #e5e0d8);
  transform: translateX(-50%) rotate(45deg);
}
.inv-toolbar--below:not(.inv-toolbar--docked)::after {
  top: -6px;
  bottom: auto;
  transform: translateX(-50%) rotate(-135deg);
}
.inv-toolbar-group {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 0 8px;
}
.inv-toolbar-group + .inv-toolbar-group {
  border-left: 1px solid var(--border-subtle, #e5e0d8);
}
.inv-toolbar select,
.inv-toolbar button {
  font: inherit;
  min-height: 40px;
  border: 0;
  border-radius: 6px;
  padding: 0 6px;
  background: transparent;
  color: inherit;
  touch-action: manipulation;
}
.inv-toolbar select { min-width: 0; max-width: 9.5em; }
.inv-toolbar button[data-size-step] {
  min-width: 36px;
  font-family: "Playfair Display", serif;
  font-weight: 500;
}
.inv-toolbar button[data-size-step="-2"] { font-size: 13px; }
.inv-toolbar button[data-size-step="2"] { font-size: 18px; }

.inv-editor [data-edit][contenteditable] {
  display: inline-block;
  width: fit-content;
  max-width: 100%;
  min-width: 3ch;
  box-sizing: border-box;
  border: 1.5px solid transparent;
  border-radius: 12px;
  padding: 0.35em 0.7em;
  outline: none;
  vertical-align: top;
}
.inv-editor [data-edit][contenteditable].inv-editing,
.inv-editor [data-edit][contenteditable]:focus {
  border-color: var(--sec-accent);
}

.inv-editor { display: grid; gap: 1rem; }
.inv-editor-settings { background: var(--bg-surface, #fff); border: 1px solid var(--border-subtle, #e5e0d8); border-radius: 8px; padding: 1rem 1.25rem; }
.inv-editor-settings > summary {
  min-height: 44px;
  display: flex;
  align-items: center;
  cursor: pointer;
}
.inv-section-fields { display: none; }
/* Opens the decoration picker with this section as its target. Sits in
   the rail's Options panel next to the other per-section controls. */
.inv-rail-add-deco {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: 8px 12px;
  background: var(--bg-surface, #f7f4ee);
  color: var(--text-base, #1a1a1a);
  border: 1px solid var(--border-subtle, #e5e0d8);
  border-radius: 8px;
  font-family: "DM Sans", sans-serif;
  font-size: 13px;
  cursor: pointer;
}
.inv-rail-add-deco:hover { border-color: var(--accent-primary, #E44479); }
/* The reorder/remove buttons are the first child of every editable item,
   and several of those items are grids with meaningful columns - a details
   event is `44px 1fr`. In flow and without this, the chrome takes the 44px
   marker cell, the marker is pushed into column 2 and the body wraps to
   column 1, so the time, name and location input are crushed into a 44px
   sliver. The public page has no chrome and looks correct, which is what
   makes it read as "the editor is broken" rather than "this rule is
   missing". Spanning every column is a no-op on a non-grid parent, so one
   rule covers story items, gallery figures, info cards and form groups
   too. From 1025px the chrome is absolutely positioned and out of flow,
   which is why this only ever showed up on a phone. */
.inv-item-chrome {
  position: static;
  z-index: 4;
  display: flex;
  justify-content: flex-end;
  gap: 6px;
  margin-bottom: 8px;
  grid-column: 1 / -1;
}
.inv-story-item, .inv-gallery-item, .inv-timeline-event, .inv-info-card, .inv-meal-option, .inv-form-group {
  position: relative;
}
.inv-canvas .inv-item-btn {
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--sec-border);
  background: color-mix(in srgb, var(--sec-surface) 30%, transparent);
  color: var(--sec-text);
  border: 1px solid color-mix(in srgb, var(--sec-border) 45%, transparent);
  border-radius: 6px;
  backdrop-filter: blur(6px);
  cursor: pointer;
  font-size: 18px;
  font-family: "DM Sans", sans-serif;
  touch-action: manipulation;
}
.inv-canvas .inv-add-item {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 44px;
  margin-top: 0.75rem;
  padding: 0.7rem 0.9rem;
  border: 1px dashed var(--sec-border);
  background: transparent;
  color: var(--sec-muted);
  border-radius: 6px;
  cursor: pointer;
  font-family: "DM Sans", sans-serif;
  font-size: 16px;
  touch-action: manipulation;
}
.inv-editor .inv-rsvp-form :disabled {
  opacity: 1;
  color: inherit;
}
.inv-meal-option { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.inv-editor-actions {
  position: static;
  padding: 12px 0 8px;
  border-top: 1px solid var(--border-subtle, #e5e0d8);
}

/* Must come after the base rule above, which is unmediated and would
   otherwise win on source order at equal specificity. flex-direction is
   restated because .form-actions stacks its buttons under 480px, which
   would double the height of a bar that is now pinned. */
@media (max-width: 1024px) {
  .inv-editor-actions {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 45;
    display: flex;
    flex-direction: row;
    gap: 8px;
    margin: 0;
    padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
    background: var(--bg-base, #fff);
    border-top: 1px solid var(--border-subtle, #e5e0d8);
  }
  .inv-editor-actions .btn { flex: 1; width: auto; min-height: 44px; }
}

@media (min-width: 1025px) {
  .inv-rail { gap: 6px; padding: 6px 8px; align-items: center; }
  .inv-rail-main { width: 100%; flex-wrap: nowrap; gap: 6px; overflow: visible; }
  .inv-canvas .inv-rail-btn,
  .inv-canvas .inv-rail-select,
  .inv-canvas .inv-rail-more-btn { font-size: 12px; padding: 4px 8px; border-radius: 4px; }
  .inv-canvas .inv-rail-btn { min-width: 0; min-height: 32px; }
  .inv-rail-label { font-size: 12px; gap: 4px; }
  .inv-rail-select { min-height: 32px; }
  .inv-canvas .inv-rail-palette-btn { width: 32px; height: 32px; }
  .inv-rail-palette-icon { width: 16px; height: 16px; }
  .inv-palette-popover {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: auto;
    bottom: auto;
    z-index: 20;
    width: min(260px, calc(100cqw - 24px));
    max-height: none;
    overflow: visible;
    padding: 10px;
  }
  .inv-rail-palettes { max-height: 240px; overflow-y: auto; gap: 4px; }
  .inv-canvas .inv-palette-option { min-height: 0; padding: 6px 8px; font-size: 12px; }
  .inv-canvas .inv-rail-advanced-btn { min-height: 0; padding: 6px 8px; font-size: 12px; }
  label.inv-rail-toggle { min-height: 32px; padding: 2px 8px; font-size: 12px; gap: 4px; }
  .inv-rail-toggle input { width: auto; height: auto; }
  .inv-canvas .inv-rail-more-btn { min-height: 32px; }
  .inv-rail-token { min-height: 0; font-size: 12px; }
  .inv-rail-token input[type="color"] { width: 28px; height: 20px; }
  .inv-toolbar { font-size: 13px; padding: 3px 4px; }
  .inv-toolbar select,
  .inv-toolbar button { min-height: 32px; padding: 0 4px; }
  .inv-item-chrome {
    position: absolute;
    top: 6px;
    right: 6px;
    margin: 0;
    gap: 2px;
  }
  .inv-canvas .inv-item-btn { width: 22px; height: 22px; font-size: 11px; border-radius: 4px; }
  .inv-canvas .inv-add-item { display: inline-flex; width: auto; min-height: 0; font-size: 13px; padding: 0.55rem 0.9rem; }
  .inv-meal-option { padding-right: 4.5rem; }
  .inv-editor-actions {
    position: sticky;
    bottom: 0;
    z-index: 25;
    background: var(--bg-base, #fff);
    padding: 12px 0;
  }
}

/* The layout set is phone-first, so the only breakpoint is 1025px.
   The old 768px/480px collapses existed because the layouts were
   authored desktop-first and had to be undone for phones; there is
   nothing left here to undo. */

/* Shown inside .map-canvas when the visitor has not consented to Google
   Maps. The canvas has an explicit height, so without this it is just an
   unexplained blank box on the invitation. Clicking reopens the consent
   preferences. inv- prefix is not used here because .map-canvas is shared
   with the legacy renderer. */
.map-consent-notice {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 1rem;
    border: 1px dashed var(--sec-border, rgba(0, 0, 0, 0.2));
    border-radius: var(--radius-lg, 8px);
    background: var(--sec-surface, rgba(0, 0, 0, 0.03));
    color: var(--sec-muted-text, var(--sec-text, inherit));
    font: inherit;
    font-size: 0.9rem;
    cursor: pointer;
}

.map-canvas {
    position: relative;
}


/* ==========================================
   Undo / redo
   ========================================== */
.inv-history {
  display: flex;
  justify-content: flex-end;
  gap: 6px;
  margin-bottom: 8px;
}
.inv-history-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 8px;
  border: 1px solid var(--border-subtle, #e5e0d8);
  background: var(--bg-surface, #fff);
  color: var(--text-base, #1a1a1a);
  cursor: pointer;
}
.inv-history-btn:disabled { opacity: 0.4; cursor: default; }
.inv-history-btn:not(:disabled):hover { border-color: var(--accent-primary, #E44479); }

@media (max-width: 1024px) {
  /* Stays reachable while the canvas scrolls - the whole page is the
     editor here, so there is no other stable place to put it. */
  .inv-history {
    position: sticky;
    top: 8px;
    z-index: 30;
    margin-bottom: 0;
  }
  .inv-history-btn { width: 44px; height: 44px; }
}

.inv-autosave {
  margin-right: auto;
  align-self: center;
  font-family: "DM Sans", sans-serif;
  font-size: 12px;
  color: var(--text-subdued, #666);
}

/* ==========================================
   Section options as a bottom sheet (phones)
   The desktop disclosure is untouched; on a phone the same panel is lifted
   into the shared sheet host so it is not a cramped drawer inside a rail.
   ========================================== */
.inv-canvas .inv-rail-more-sheet-btn,
.inv-sheet-head { display: none; }

@media (max-width: 1024px) {
  /* Once hosted, the panel is a sheet rather than part of the rail. */
  .inv-palette-host .inv-rail-flags {
    display: block;
    width: 100%;
    max-height: min(78dvh, 580px);
    overflow: auto;
    padding: 12px 16px calc(20px + env(safe-area-inset-bottom, 0px));
    border-radius: 16px 16px 0 0;
    background: var(--bg-base, #fff);
    color: var(--text-base, #1a1a1a);
    z-index: 1;
  }
  .inv-palette-host .inv-sheet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 8px;
  }
  .inv-palette-host .inv-sheet-title {
    margin: 0;
    font-family: "DM Sans", sans-serif;
    font-size: 13px;
    color: var(--text-subdued, #666);
  }
  .inv-palette-host .inv-rail-flags > label,
  .inv-palette-host .inv-rail-flags > .inv-rail-label { min-height: 44px; }
}

/* ==========================================
   Photo framing editor
   Two copies of the same photo, transformed identically: a dimmed one
   showing everything, and a clipped one on top showing only what will
   survive. The window carries the real shape of the slot being edited
   (--pe-ratio / --pe-radius), so the couple frames against the shape the
   photo will actually appear in rather than a generic square.
   ========================================== */
.photo-editor-stage {
  position: relative;
  /* Honour the slot's real aspect ratio, but never let a tall shape (a
     full-bleed hero is ~0.4) push the stage past the viewport - width is
     whichever is smaller: the available space, or what the height cap
     allows at this ratio.

     The height cap is the dialog's own leftover room, not a round
     fraction of the viewport: .modal is max-height 90svh and this
     dialog's chrome (header, hint, rotation and grayscale rows, footer,
     body padding) measures ~310px, so that subtraction is the space
     actually left for the picture. It was a flat 42svh, which on a tall
     slot left a 175px sliver floating in a 700px card - the right shape,
     too small to frame anything in. The floor keeps a very short
     viewport no worse than it already was; the 520px ceiling stops a
     square slot from becoming a wall on a big monitor. */
  width: min(100%, 460px, calc(clamp(160px, 90svh - 310px, 520px) * var(--pe-ratio, 1)));
  margin: 0 auto;
  aspect-ratio: var(--pe-ratio, 1);
  overflow: hidden;
  border-radius: 8px;
  background: #14120f;
  /* The gesture is the point; never let the browser scroll instead. */
  touch-action: none;
  cursor: grab;
}
.photo-editor-stage:active { cursor: grabbing; }

.photo-editor-img {
  position: absolute;
  /* Anchored at the top and stretched by --pe-fill so the image keeps the
     shape and size of the slot's real image box while the stage shows only
     the part a guest sees. --pe-fill is 1 for every layout but hero-cover,
     where the stage is the photo above the opaque type band. */
  inset: 0 0 auto 0;
  width: 100%;
  height: calc(100% * var(--pe-fill, 1));
  object-fit: cover;
  transform-origin: center;
  will-change: transform;
}
.photo-editor-img--ghost { opacity: 0.3; }

/* The full-opacity cut-out, in the slot's own shape. */
.photo-editor-window {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: var(--pe-radius, 0);
  box-shadow: 0 0 0 1px rgb(255 255 255 / 0.55);
}

.photo-editor-controls {
  display: grid;
  gap: 12px;
  margin-top: 16px;
}
.photo-editor-range {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  align-items: center;
  gap: 10px;
  font-family: "DM Sans", sans-serif;
  font-size: 13px;
}
.photo-editor-range input[type="range"] { width: 100%; }
.photo-editor-actions { display: flex; justify-content: flex-end; gap: 8px; }

.photo-editor-hint {
  margin: 10px 0 0;
  font-family: "DM Sans", sans-serif;
  font-size: 12px;
  color: var(--text-subdued, #666);
  text-align: center;
}

/* ==========================================
   Section rail: icons only, one row that fits
   Arrows, options and palette on the left; hide on the right. With icons
   instead of words the whole rail fits a phone without scrolling, which is
   why nothing here is allowed to wrap.
   ========================================== */
.inv-rail-main {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: nowrap;
}
.inv-rail-remove { margin-left: auto; }
.inv-canvas .inv-rail-remove { color: #d1435b; }
.inv-rail-remove:hover { border-color: #d1435b; }

/* The button shows an X while the section is visible and an eye once it is
   hidden, so one control carries both directions. */
.inv-rail-remove .inv-rail-remove-back { display: none; }
.inv-rail-remove[aria-pressed="true"] .inv-rail-remove-x { display: none; }
.inv-rail-remove[aria-pressed="true"] .inv-rail-remove-back { display: block; }
.inv-rail-remove[aria-pressed="true"] { color: var(--sec-text); }

.inv-rail-options-btn { display: inline-flex; align-items: center; justify-content: center; }

/* The options panel is a popover on desktop, matching the palette. */
.inv-palette-host--popover .inv-rail-flags {
  position: absolute;
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: min(260px, calc(100vw - 24px));
  padding: 10px;
  border: 1px solid var(--border-subtle, #e5e0d8);
  border-radius: 8px;
  background: var(--bg-base, #fff);
  color: var(--text-base, #1a1a1a);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.12);
  z-index: 1;
}

/* ==========================================
   Undo / redo: floating, top right
   ========================================== */
.inv-history {
  position: fixed;
  /* Clears the sticky .main-header, which is --header-height tall and sits
     at z-index 50 - this used to float over it. */
  top: calc(var(--header-height, 64px) + 12px);
  right: 12px;
  z-index: 48;
  display: flex;
  gap: 6px;
  margin: 0;
}

/* Count badge on the hidden-sections button. */
.inv-history-btn { position: relative; }
.inv-history-count {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--accent-primary, #E44479);
  color: #fff;
  font-family: "DM Sans", sans-serif;
  font-size: 10px;
  line-height: 16px;
  text-align: center;
}
.inv-history-count[hidden] { display: none; }

.inv-hidden-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  width: min(260px, calc(100vw - 24px));
  padding: 10px;
  border: 1px solid var(--border-subtle, #e5e0d8);
  border-radius: 8px;
  background: var(--bg-base, #fff);
  color: var(--text-base, #1a1a1a);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.12);
  font-family: "DM Sans", sans-serif;
}
.inv-hidden-panel[hidden] { display: none; }
.inv-hidden-panel-title { margin: 0 0 8px; font-size: 12px; color: var(--text-subdued, #666); }
.inv-hidden-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.inv-hidden-item { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 13px; }

/* Decoration controls inside the floating toolbar. */
.inv-toolbar-swatch { display: inline-flex; align-items: center; padding: 0 4px; cursor: pointer; }
.inv-toolbar-swatch input[type="color"] {
  width: 28px; height: 24px; padding: 0; border: 1px solid var(--border-subtle, #e5e0d8);
  border-radius: 4px; background: transparent; cursor: pointer;
}
.inv-toolbar input[type="range"][data-deco] { width: 92px; margin: 0 6px; }
.inv-toolbar [data-deco="spans"][aria-pressed="true"] { color: var(--accent-primary, #E44479); }
