/* Display face: Barlow Condensed ExtraBold (SIL OFL 1.1, BarlowCondensed-OFL.txt),
   subset to Latin and Latin-1 and preloaded by the layout. The two fallback faces
   scale Arial Bold and Roboto Bold to the webfont's width on the headline copy,
   with matching vertical metrics, so the swap after loading does not reflow. */
@font-face {
  font-display: swap;
  font-family: "Barlow Condensed";
  font-style: normal;
  font-weight: 800;
  src: url("/assets/barlow-condensed-latin-800-7668f288.woff2") format("woff2");
}
@font-face {
  ascent-override: 145.98%;
  descent-override: 29.2%;
  font-family: "Barlow Condensed Fallback Arial";
  font-weight: 800;
  line-gap-override: 0%;
  size-adjust: 68.5%;
  src: local("Arial Bold"), local("Arial-BoldMT");
}
@font-face {
  ascent-override: 133.8%;
  descent-override: 26.76%;
  font-family: "Barlow Condensed Fallback Roboto";
  font-weight: 800;
  line-gap-override: 0%;
  size-adjust: 74.74%;
  src: local("Roboto Bold"), local("Roboto-Bold");
}

/* Body face: Manrope (SIL OFL 1.1, Manrope-OFL.txt), a Latin and Latin-1 variable
   font covering weights 200-800, preloaded by the layout. Without the weight
   range Chrome would pin it to 400, and font-synthesis is off, so labels at
   700-800 would render regular. The fallback faces scale Arial and Roboto (regular
   up to 549, bold from 550) to Manrope's width on the app's es and en copy, with
   matching vertical metrics, so the swap after loading does not reflow. */
@font-face {
  font-display: swap;
  font-family: "Manrope";
  font-style: normal;
  font-weight: 200 800;
  src: url("/assets/manrope-latin-variable-757d91ba.woff2") format("woff2");
}
@font-face {
  ascent-override: 104.55%;
  descent-override: 29.42%;
  font-family: "Manrope Fallback Arial";
  font-weight: 200 549;
  line-gap-override: 0%;
  size-adjust: 101.96%;
  src: local("Arial"), local("ArialMT");
}
@font-face {
  ascent-override: 106.47%;
  descent-override: 29.96%;
  font-family: "Manrope Fallback Arial";
  font-weight: 550 800;
  line-gap-override: 0%;
  size-adjust: 100.12%;
  src: local("Arial Bold"), local("Arial-BoldMT");
}
@font-face {
  ascent-override: 103.54%;
  descent-override: 29.14%;
  font-family: "Manrope Fallback Roboto";
  font-weight: 200 549;
  line-gap-override: 0%;
  size-adjust: 102.96%;
  src: local("Roboto"), local("Roboto-Regular");
}
@font-face {
  ascent-override: 99.88%;
  descent-override: 28.11%;
  font-family: "Manrope Fallback Roboto";
  font-weight: 550 800;
  line-gap-override: 0%;
  size-adjust: 106.73%;
  src: local("Roboto Bold"), local("Roboto-Bold");
}

:root {
  color-scheme: light;
  --ink: #2b2e29;
  --muted: #62655d;
  --canvas: #f1ebdf;
  --surface: #f8f2e7;
  --surface-soft: #ddd5c7;
  --line: #30332e;
  --accent: #aa543f;
  --accent-dark: #79382c;
  --accent-soft: #e6c5b9;
  /* The one celebratory accent: cempasúchil, the marigold of a Mexican
     fiesta, warm beside the coral papel picado in the hero. Used sparingly and
     only on ink: as display text (the Plus price, 6.6:1) and as a fill behind
     ink text (the Plus flag). It is too light for text on the creams. */
  --fiesta: #f2a33a;
  --action: #2b2e29;
  --action-hover: #aa543f;
  --on-action: #f8f2e7;
  --control-border: #30332e;
  --focus: #aa543f;
  --danger: #9b3d35;
  --shadow: 0 24px 60px rgba(55, 45, 34, 0.18);
  --radius: 0;
  --radius-small: 0;
  /* Display headings size their max-width in em, not ch: 1ch of Barlow
     Condensed ExtraBold is 0.456em, but during the font swap ch would follow
     the fallback's zero and rewrap the heading. */
  --font-display: "Barlow Condensed", "Barlow Condensed Fallback Arial", "Barlow Condensed Fallback Roboto", sans-serif;
  --font-body: "Manrope", "Manrope Fallback Arial", "Manrope Fallback Roboto", sans-serif;
  /* Typewriter face, only for the strings that are really postal: the
     proof's 'DE: … A: …' route and the gallery's postmark. */
  --font-postal: ui-monospace, "SFMono-Regular", "Cascadia Mono", "Liberation Mono", monospace;
  /* About 65 characters of body copy; Manrope averages 0.46em a character. In
     em, not ch, for the same reason as the display headings. */
  --measure: 30em;
  font-family: var(--font-body);
  font-synthesis: none;
}

* { box-sizing: border-box; }
html { background: var(--canvas); color: var(--ink); scroll-behavior: smooth; }
body { margin: 0; min-width: 320px; background: var(--canvas); color: var(--ink); line-height: 1.5; }
/* Display-face text keeps the face's own line height unless a rule sets one. */
:where(h1, h2, h3, .wordmark, .postal-seal span, .proof-toolbar strong, .step-number, .event-code strong) { line-height: 1.2; }
body.modal-open { overflow: hidden; }
button, input, select { font: inherit; }
button, a { -webkit-tap-highlight-color: transparent; }
a { color: inherit; }
img, video { display: block; max-width: 100%; }

.wordmark {
  color: var(--ink);
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  text-decoration: none;
}

.eyebrow {
  margin: 0 0 0.8rem;
  color: var(--accent-dark);
  font-size: 0.75rem;
  font-weight: 800;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

.button {
  align-items: center;
  border: 1px solid transparent;
  border-radius: var(--radius-small);
  cursor: pointer;
  display: inline-flex;
  font-weight: 750;
  justify-content: center;
  line-height: 1.1;
  min-height: 46px;
  padding: 0.8rem 1.05rem;
  text-decoration: none;
  transition: transform 180ms ease, background-color 180ms ease, border-color 180ms ease;
  white-space: nowrap;
}
.button:hover { transform: translateY(-2px); }
.button:active { transform: translateY(1px) scale(0.99); }
.button:focus-visible, button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}
.button-primary { background: var(--action); color: var(--on-action); }
.button-primary:hover { background: var(--action-hover); }
.button-secondary { background: var(--surface); border-color: var(--control-border); color: var(--ink); }
.button-light { background: var(--surface); color: var(--ink); }
.button-compact { background: var(--ink); color: var(--canvas); min-height: 40px; padding: 0.65rem 0.9rem; }
.text-button { background: none; border: 0; color: var(--ink); cursor: pointer; font-weight: 700; padding: 0.35rem; text-decoration: underline; text-underline-offset: 0.2em; transition: color 180ms ease, text-underline-offset 180ms ease; }
.text-button:hover { color: var(--accent-dark); text-underline-offset: 0.32em; }

.skip-link {
  background: #f7f0e4;
  color: #292c27;
  left: 1rem;
  padding: 0.8rem 1rem;
  position: fixed;
  top: 1rem;
  transform: translateY(-180%);
  transition: transform 180ms ease;
  z-index: 50;
}
.skip-link:focus { transform: translateY(0); }
/* Read by screen readers, not shown: the selection's announced count. */
.visually-hidden { border: 0; clip-path: inset(50%); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; white-space: nowrap; width: 1px; }
/* On dark surfaces (ink, the hero photo) the terracotta ring is too faint, so
   it turns cream and an ink band fills the outline offset: the two tones keep
   3:1 against each other on any part of the photo and against a cream button.
   The outline stays the main tone because forced-colors mode drops box-shadow.
   The skip link can surface over the hero photo, so it gets the pair too, and
   so does the lightbox, whose cream buttons sit on its dark backdrop, and the
   organizer's usage line when it turns ink near the limit. */
.skip-link, .landing .site-nav, .plan-plus, .lightbox, .usage-alert { --focus: var(--surface); --focus-gap: var(--ink); }
.skip-link:focus-visible, .landing .site-nav :focus-visible, .plan-plus :focus-visible, .lightbox :focus-visible, .usage-alert :focus-visible { box-shadow: 0 0 0 3px var(--focus-gap); }

.landing {
  --ink: #2b2e29;
  --muted: #62655d;
  --canvas: #f1ebdf;
  --surface: #f8f2e7;
  --surface-soft: #ddd5c7;
  --line: #30332e;
  --accent: #aa543f;
  --accent-dark: #79382c;
  --accent-soft: #e6c5b9;
  --action: #2b2e29;
  --action-hover: #aa543f;
  --on-action: #f8f2e7;
  --control-border: #30332e;
  --focus: #aa543f;
  background: var(--canvas);
  color: var(--ink);
  color-scheme: light;
  min-height: 100dvh;
  overflow: hidden;
  position: relative;
}
/* --muted reaches 4.5:1 only on the light creams; on the darker pricing band
   and the guest's terracotta-tint chat bubble it steps down a shade. */
.pricing-section, .proof-bubble-guest { --muted: #4f524b; }
.landing h1,
.landing h2,
.landing h3,
.landing .wordmark {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
}
.landing h1, .landing h2, .landing h3 { text-wrap: balance; }
.landing p { text-wrap: pretty; }
.landing .button {
  border-radius: 0;
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.035em;
  min-height: 47px;
  padding: 0.85rem 1rem;
  text-transform: uppercase;
}
.landing .button-secondary { background: transparent; border-color: currentColor; }
.landing .button-light { background: var(--surface); color: var(--ink); }
.landing .button.is-disabled { cursor: not-allowed; opacity: 0.55; }
/* Touch targets: the landing's small links keep their look, and a transparent
   ::after gives each a hit area at least 44px (2.75rem) tall, centred on the
   text and 0.5rem wider on each side (half the smallest gap between them).
   The focus ring still follows the element's own box. */
.landing .site-nav .wordmark,
.site-nav-links > a:not(.button),
.landing .button-compact,
.hero-actions .text-link,
.landing .site-footer nav a { position: relative; }
.landing .site-nav .wordmark::after,
.site-nav-links > a:not(.button)::after,
.landing .button-compact::after,
.hero-actions .text-link::after,
.landing .site-footer nav a::after {
  content: "";
  height: 100%;
  inset-inline: -0.5rem;
  min-height: 2.75rem;
  position: absolute;
  top: 50%;
  translate: 0 -50%;
}
.landing .button-compact::after { inset-inline: 0; }
.landing .configuration-warning {
  background: var(--accent-soft);
  border-left: 3px solid var(--accent);
  color: var(--ink);
  font-size: 1rem;
  line-height: 1.5;
  margin: 0;
  max-width: var(--measure);
  padding: 1rem;
}

.landing-hero { background: #161915; color: var(--surface); position: relative; }
.landing .site-nav {
  align-items: center;
  display: flex;
  height: 76px;
  inset: 0 0 auto;
  justify-content: space-between;
  margin: 0 auto;
  max-width: 1440px;
  padding: 0 clamp(1rem, 3vw, 2.5rem);
  position: absolute;
  z-index: 5;
}
.landing .site-nav .wordmark { color: var(--surface); font-size: 1.55rem; letter-spacing: -0.005em; }
.site-nav-links { align-items: center; display: flex; gap: clamp(1rem, 2.5vw, 2.4rem); }
.site-nav-links > a:not(.button) {
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-decoration: none;
  text-transform: uppercase;
}
.site-nav-links > a:not(.button):hover { text-decoration: underline; text-underline-offset: 0.35em; }
.landing .button-compact { background: var(--surface); color: var(--ink); }

.landing .hero {
  display: grid;
  isolation: isolate;
  margin: 0;
  max-width: none;
  min-height: min(900px, 100dvh);
  overflow: hidden;
  padding: 7rem clamp(1.5rem, 4vw, 5rem) 4.5rem;
  place-items: center start;
  position: relative;
}
/* The photo shows in its own colour. A deeper band at the top keeps the nav
   legible over the bright sky; the middle stays clear and the floor darkens
   under the caption. */
.landing .hero::after {
  background: linear-gradient(180deg, rgba(24, 20, 14, 0.7), rgba(24, 20, 14, 0.36) 11%, rgba(24, 20, 14, 0.04) 30%, rgba(24, 20, 14, 0.04) 50%, rgba(24, 20, 14, 0.74));
  content: "";
  inset: 0;
  position: absolute;
  z-index: -1;
}
.landing .hero-image-wrap { inset: 0; margin: 0; min-height: 0; overflow: hidden; position: absolute; z-index: -2; }
.landing .hero-image {
  height: 100%;
  object-fit: cover;
  object-position: left 47%;
  transform: scale(1.015);
  width: 100%;
}
.hero-stamp-shell {
  background: var(--surface);
  color: var(--ink);
  filter: drop-shadow(0 22px 30px rgba(24, 19, 12, 0.28));
  max-width: calc(100vw - 3rem);
  position: relative;
  transform: rotate(-0.45deg);
  width: min(540px, 40vw);
  z-index: 1;
}
.hero-stamp-shell::before,
.hero-stamp-shell::after {
  background: radial-gradient(circle, var(--surface) 0 7px, transparent 7.5px) 0 0 / 24px 16px repeat-x;
  content: "";
  height: 16px;
  left: 0;
  position: absolute;
  right: 0;
}
.hero-stamp-shell::before { top: -8px; }
.hero-stamp-shell::after { bottom: -8px; }
.hero-stamp { align-items: center; display: flex; flex-direction: column; padding: clamp(2.5rem, 5vw, 4.75rem) clamp(1.5rem, 3vw, 3rem) clamp(2.3rem, 4vw, 3.8rem); position: relative; text-align: center; }
.hero-stamp::before,
.hero-stamp::after {
  background: radial-gradient(circle, var(--surface) 0 7px, transparent 7.5px) 0 0 / 16px 24px repeat-y;
  bottom: 0;
  content: "";
  position: absolute;
  top: 0;
  width: 16px;
}
.hero-stamp::before { left: -8px; }
.hero-stamp::after { right: -8px; }
.landing .hero h1 {
  align-items: center;
  display: flex;
  flex-direction: column;
  font-size: clamp(3rem, 5.3vw, 5.2rem);
  letter-spacing: -0.015em;
  line-height: 0.82;
  margin: 0.35rem auto 0;
  max-width: 5.02em;
}
.hero-headline-prefix,
.hero-headline-word-line,
.hero-headline-suffix { display: block; }
.hero-headline-prefix { font-size: 0.56em; letter-spacing: 0; line-height: 0.9; margin-bottom: 0.28em; }
.hero-headline-word-line { align-items: baseline; display: flex; justify-content: center; white-space: nowrap; }
.hero-cycle-word { display: inline-grid; isolation: isolate; place-items: center; position: relative; }
.hero-cycle-sizer,
.hero-cycle-current { grid-area: 1 / 1; }
.hero-cycle-sizer { visibility: hidden; }
/* contain: inline-size: only the sizers set the cell's width, never the
   word (or a glitch frame) in it. */
.hero-cycle-current {
  contain: inline-size;
  display: block;
  justify-self: stretch;
  position: relative;
  text-align: center;
  transform: translateZ(0);
  will-change: clip-path, opacity, transform;
}
.hero-cycle-current::before,
.hero-cycle-current::after {
  content: attr(data-glitch-text);
  inset: 0;
  opacity: 0;
  pointer-events: none;
  position: absolute;
}
.hero-cycle-current::before { color: var(--accent); }
.hero-cycle-current::after { color: var(--ink); text-shadow: 0.035em 0 var(--surface); }
.hero-headline-suffix {
  font-size: 0.56em;
  letter-spacing: 0;
  line-height: 0.9;
  margin-top: 0.14em;
  max-width: 4.5em;
}
/* Each new word glitches in (word_cycle_controller.js): while the cell has
   .is-glitching, the word jitters and two copies of it (data-glitch-text,
   drawn by ::before / ::after) split into an accent upper half and an ink
   lower half, each pushed a different way. */
.hero-cycle-word.is-glitching .hero-cycle-current { animation: hero-glitch-main 420ms steps(2, end); }
.hero-cycle-word.is-glitching .hero-cycle-current::before {
  animation: hero-glitch-upper 420ms steps(2, end);
  clip-path: inset(0 0 53% 0);
  opacity: 0.78;
}
.hero-cycle-word.is-glitching .hero-cycle-current::after {
  animation: hero-glitch-lower 420ms steps(2, end);
  clip-path: inset(48% 0 0 0);
  opacity: 0.72;
}
@keyframes hero-glitch-main {
  0%, 100% { opacity: 1; transform: translate(0) skewX(0); }
  18% { opacity: 0.75; transform: translate(-0.035em, 0.012em) skewX(-5deg); }
  37% { opacity: 1; transform: translate(0.045em, -0.015em) skewX(3deg); }
  58% { opacity: 0.62; transform: translate(-0.02em, 0) skewX(6deg); }
  78% { opacity: 1; transform: translate(0.025em, 0.01em) skewX(-2deg); }
}
@keyframes hero-glitch-upper {
  0%, 100% { transform: translate(0); }
  22% { transform: translate(0.075em, -0.025em); }
  46% { transform: translate(-0.055em, 0.02em); }
  72% { transform: translate(0.035em, 0); }
}
@keyframes hero-glitch-lower {
  0%, 100% { transform: translate(0); }
  16% { transform: translate(-0.065em, 0.02em); }
  42% { transform: translate(0.045em, -0.015em); }
  68% { transform: translate(-0.025em, 0); }
}
/* The pause control for the word cycle (WCAG 2.2.2): a small inked square in
   the stamp's top corner, out of the flow so showing or hiding it never moves
   the headline. 28px clears WCAG 2.5.8's 24px minimum. z-index lifts it,
   its focus ring and hit area above the stamp's perforation strips (inside
   the stamp shell's own stacking context, so never above the nav). It is
   rendered hidden and the controller shows it only while the cycle can run. */
.hero-cycle-toggle {
  align-items: center;
  background: var(--surface);
  border: 1.5px solid var(--ink);
  border-radius: 0;
  color: var(--ink);
  cursor: pointer;
  display: inline-flex;
  height: 28px;
  justify-content: center;
  padding: 0;
  position: absolute;
  right: 1rem;
  top: 1rem;
  transition: background-color 180ms ease, border-color 180ms ease, color 180ms ease;
  width: 28px;
  z-index: 1;
}
.hero-cycle-toggle:hover { border-color: var(--accent); color: var(--accent); }
/* Like the landing's small links, a transparent ::after makes the touch
   target 44px square while the inked square stays small. Its inset counts
   from inside the 1.5px border, so the border is added back. */
.hero-cycle-toggle::after { content: ""; inset: calc(-0.5rem - 1.5px); min-height: 2.75rem; position: absolute; }
.hero-cycle-toggle[hidden],
.hero-cycle-toggle .hero-cycle-icon-play,
.hero-cycle-toggle.is-paused .hero-cycle-icon-pause { display: none; }
.hero-cycle-toggle.is-paused .hero-cycle-icon-play { display: block; }
.landing .hero-lede { color: var(--muted); font-size: clamp(1rem, 0.9rem + 0.3vw, 1.125rem); line-height: 1.5; margin: 1.2rem auto 1.5rem; max-width: 24em; }
.hero-actions { align-items: center; display: flex; gap: 1.25rem; justify-content: center; }
.hero-actions .text-link { font-size: 0.8125rem; font-weight: 700; text-transform: uppercase; text-underline-offset: 0.3em; }
.postmark-lines { bottom: 1.25rem; display: grid; gap: 4px; opacity: 0.55; position: absolute; right: 1.5rem; transform: rotate(7deg); width: 84px; }
.postmark-lines span { border-top: 1px solid currentColor; }
.hero-caption { bottom: 1.25rem; font-size: 0.75rem; left: clamp(1rem, 3vw, 2.5rem); letter-spacing: 0.08em; margin: 0; position: absolute; text-transform: uppercase; z-index: 1; }
/* Phones: the nav's call to action steps aside while the hero's is fully on
   screen, so the same button never shows twice in one viewport.
   nav_cta_controller.js sets the class; without it the nav button stays. */
@media (max-width: 560px) {
  .landing .site-nav .button-compact { transition: opacity 160ms ease, visibility 160ms; }
  .landing-hero.hero-cta-in-view .site-nav .button-compact { opacity: 0; visibility: hidden; }
}

.postal-intro {
  border-bottom: 1px solid var(--line);
  display: grid;
  gap: clamp(3rem, 8vw, 8rem);
  grid-template-columns: minmax(0, 0.9fr) minmax(420px, 1.1fr);
  margin: 0 auto;
  max-width: 1400px;
  padding: clamp(6rem, 10vw, 10rem) clamp(1rem, 3vw, 2.5rem) clamp(7rem, 12vw, 12rem);
}
.postal-copy { align-self: center; max-width: 590px; }
.postal-copy h2 { font-size: clamp(3rem, 6vw, 6.3rem); letter-spacing: -0.01em; line-height: 0.88; margin: 0; }
.postal-copy > p { color: var(--muted); font-size: clamp(1rem, 0.9rem + 0.3vw, 1.125rem); line-height: 1.55; margin: 1.5rem 0 1.8rem; max-width: var(--measure); }
.postcard { border-left: 1px solid var(--line); min-height: 390px; padding: 1rem 0 0 clamp(2rem, 6vw, 6rem); position: relative; }
.postcard > p { font-size: 0.875rem; letter-spacing: 0.08em; margin: 10rem 0 2.5rem; max-width: 14em; text-transform: uppercase; }
.postal-seal { align-items: center; border: 1px solid var(--ink); border-radius: 50%; display: flex; height: 92px; justify-content: center; opacity: 0.7; position: absolute; right: 1rem; top: 0; transform: rotate(10deg); width: 92px; }
.postal-seal::before { border: 1px dashed currentColor; border-radius: inherit; content: ""; inset: 6px; position: absolute; }
.postal-seal::after { border-top: 1px solid currentColor; box-shadow: 0 7px currentColor, 0 14px currentColor; content: ""; left: -54px; position: absolute; top: 49%; width: 60px; }
.postal-seal span { font-family: var(--font-display); font-size: 2rem; font-weight: 800; }
.address-lines { display: grid; gap: 1.3rem; }
.address-lines span { border-top: 2px solid var(--ink); }
.postcard strong { color: var(--accent); display: block; font-family: var(--font-display); font-size: clamp(1.5rem, 2.6vw, 2.2rem); letter-spacing: 0.01em; line-height: 1; margin: 1.2rem 0 0 auto; text-align: right; text-transform: uppercase; transform: rotate(-2deg); width: max-content; }

.landing .how-it-works { margin: 0 auto; max-width: 1400px; padding: clamp(7rem, 12vw, 12rem) clamp(1rem, 3vw, 2.5rem); }
.landing .section-heading { align-items: end; display: grid; gap: clamp(2rem, 7vw, 7rem); grid-template-columns: minmax(0, 1fr) minmax(280px, 0.55fr); max-width: none; }
.landing .section-heading h2 { font-size: clamp(3.2rem, 7vw, 7.5rem); letter-spacing: -0.015em; line-height: 0.86; margin: 0; max-width: 4.1em; }
.landing .section-heading > p { color: var(--muted); font-size: clamp(1rem, 0.9rem + 0.3vw, 1.125rem); line-height: 1.55; margin: 0 0 0.25rem; max-width: var(--measure); }
/* Product proof: the chat beside the gallery it fills. The phone is a neutral
   chat (no WhatsApp colors or wallpaper); the gallery reuses the real
   .media-grid/.media-tile styles, scoped down to a card. The markup is the
   finished state; product_demo_controller.js adds .is-armed (steps hidden) and
   then .is-playing (steps play once) only when motion is allowed. */
.landing .product-story { align-items: start; display: grid; gap: clamp(3rem, 6vw, 6rem); grid-template-columns: minmax(0, 1.3fr) minmax(320px, 0.7fr); margin-top: clamp(4rem, 7vw, 7rem); }
.landing .product-proof { background: var(--surface); border: 1px solid var(--line); border-radius: 0; margin: 0; }
.landing .proof-toolbar { align-items: flex-end; display: flex; gap: 2rem; justify-content: space-between; padding: 1rem; }
.landing .proof-route { color: var(--muted); font-size: 0.75rem; letter-spacing: 0.05em; text-transform: uppercase; }
.landing .proof-toolbar strong { font-family: var(--font-display); font-size: clamp(1.25rem, 2.4vw, 2rem); text-transform: uppercase; }
.proof-route { font-family: var(--font-postal); max-width: 22ch; text-align: right; }
.proof-demo {
  align-items: start;
  background: var(--surface-soft);
  border-block: 1px solid var(--line);
  display: grid;
  grid-template-columns: minmax(0, 15.5rem) minmax(0, 1fr);
  padding: clamp(1.5rem, 3vw, 2.5rem) clamp(1rem, 3vw, 2.5rem) clamp(2rem, 3.5vw, 3rem);
}
.proof-phone {
  background: var(--ink);
  border-radius: 1.6rem;
  box-shadow: 0 22px 40px rgba(43, 46, 41, 0.28);
  display: flex;
  flex-direction: column;
  min-height: 30rem;
  padding: 0.45rem;
  position: relative;
  transform: rotate(-1.4deg);
  z-index: 1;
}
.proof-chat-header { align-items: center; background: var(--surface); border-bottom: 1px solid color-mix(in srgb, var(--line), transparent 80%); border-radius: 1.2rem 1.2rem 0 0; display: flex; gap: 0.6rem; padding: 0.95rem 0.85rem 0.7rem; }
.proof-avatar { align-items: center; border: 1px dashed var(--accent-dark); border-radius: 50%; color: var(--accent-dark); display: flex; flex: none; font-family: var(--font-display); font-size: 1.1rem; font-weight: 800; height: 2.1rem; justify-content: center; width: 2.1rem; }
.proof-chat-header strong { font-size: 0.9375rem; font-weight: 800; }
.proof-chat { background: var(--canvas); display: flex; flex: 1; flex-direction: column; gap: 0.65rem; justify-content: flex-end; padding: 1rem 0.65rem; }
.proof-bubble { border: 1px solid color-mix(in srgb, var(--line), transparent 82%); font-size: 0.8125rem; line-height: 1.4; margin: 0; max-width: 90%; padding: 0.55rem 0.65rem 0.4rem; }
.proof-bubble-bot { align-self: flex-start; background: color-mix(in srgb, var(--surface), #fff 60%); border-radius: 0 0.9rem 0.9rem; }
.proof-bubble-guest { align-self: flex-end; background: var(--accent-soft); border-radius: 0.9rem 0 0.9rem 0.9rem; padding: 0.3rem 0.3rem 0.35rem; }
.proof-photos { display: grid; gap: 0.25rem; grid-template-columns: 1fr 1fr; width: 9.5rem; }
.proof-photos img { aspect-ratio: 1; border-radius: 0.6rem; height: auto; object-fit: cover; width: 100%; }
.proof-step-reply { display: grid; }
.proof-step-reply strong { font-weight: 800; }
.proof-link { color: var(--accent-dark); overflow-wrap: anywhere; text-decoration: underline; text-underline-offset: 0.15em; }
.proof-time { color: var(--muted); display: block; font-size: 0.75rem; line-height: 1.2; margin-top: 0.2rem; text-align: right; }
.proof-bubble-guest .proof-time { padding: 0 0.3rem; }
.proof-composer { background: var(--surface); border-radius: 0 0 1.2rem 1.2rem; border-top: 1px solid color-mix(in srgb, var(--line), transparent 80%); color: var(--muted); font-size: 0.8125rem; padding: 0.75rem 0.95rem 0.95rem; }
.proof-gallery { background: var(--canvas); border: 1px solid var(--line); box-shadow: 0 16px 34px rgba(55, 45, 34, 0.14); margin: 2.25rem 0 0 -1.25rem; min-width: 0; padding-bottom: 0.45rem; transform: rotate(0.5deg); }
.proof-address { border-bottom: 1px solid var(--line); color: var(--muted); font-size: 0.75rem; overflow-wrap: anywhere; padding: 0.45rem 0.85rem 0.45rem 2rem; }
.proof-gallery-head { display: grid; padding: 0.85rem 0.85rem 0.8rem 2rem; }
.landing .proof-gallery-head .wordmark { font-size: 1rem; margin-bottom: 0.9rem; }
.proof-gallery-label { color: var(--accent-dark); font-size: 0.75rem; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; }
.proof-event { font-family: var(--font-display); font-size: clamp(1.6rem, 2.6vw, 2.3rem); font-weight: 800; line-height: 0.9; margin-top: 0.2rem; text-transform: uppercase; }
.proof-count { color: var(--muted); display: grid; font-size: 0.75rem; letter-spacing: 0.04em; margin-top: 0.45rem; text-transform: uppercase; }
.proof-count > span { grid-area: 1 / 1; }
.proof-count-before { visibility: hidden; }
.proof-gallery .media-grid { column-gap: 0.45rem; columns: 3; padding: 0 0.85rem 0 2rem; }
/* The proof's prints are scattered a fraction of a degree, as before; the
   gallery's tiles stand straight. */
.proof-gallery .media-tile { margin-bottom: 0.45rem; transform: rotate(-0.12deg); }
.proof-gallery .media-tile:nth-child(3n + 2) { transform: rotate(0.18deg); }
.proof-gallery .media-tile:nth-child(4n) { transform: rotate(-0.28deg); }
.proof-gallery .media-open, .proof-gallery .media-open img { cursor: default; min-height: 0; }
.proof-gallery .media-open img { height: auto; transition: none; width: 100%; }
.proof-gallery .media-open:hover img { transform: none; }
/* The proof keeps its own uppercase attribution strip; the gallery tiles have
   the sender line (.sender-line). */
.proof-gallery .tile-attribution { border-top: 1px solid var(--line); display: block; font-size: 0.75rem; font-weight: 700; letter-spacing: 0.02em; overflow-wrap: anywhere; padding: 0.35rem 0.45rem; text-transform: uppercase; }
.landing .proof-description { color: var(--muted); font-size: 0.9375rem; line-height: 1.5; margin: 0; max-width: var(--measure); padding: 1rem; }

.product-proof.is-armed:not(.is-playing) :is(.proof-step-sent, .proof-step-reply, .proof-step-new) { opacity: 0; }
.product-proof.is-armed:not(.is-playing) .proof-count-before { visibility: visible; }
.product-proof.is-armed:not(.is-playing) .proof-count-after { visibility: hidden; }
.product-proof.is-playing .proof-step-sent { animation: proof-rise 420ms ease-out 250ms both; }
.product-proof.is-playing .proof-step-new { animation: proof-drop 480ms cubic-bezier(0.2, 0.8, 0.2, 1) 1100ms both; }
.product-proof.is-playing .proof-step-new + .proof-step-new { animation-delay: 1300ms; }
.product-proof.is-playing .proof-count-before { animation: proof-hold-visible 1100ms step-end; }
.product-proof.is-playing .proof-count-after { animation: proof-hold-hidden 1100ms step-end; }
.product-proof.is-playing .proof-step-reply { animation: proof-rise 420ms ease-out 2000ms both; }
@keyframes proof-rise { from { opacity: 0; transform: translateY(0.6rem); } }
@keyframes proof-drop { from { opacity: 0; transform: translateY(-0.9rem) rotate(-2deg) scale(0.96); } }
@keyframes proof-hold-visible { from, to { visibility: visible; } }
@keyframes proof-hold-hidden { from, to { visibility: hidden; } }

@media (max-width: 1100px) {
  .landing .product-story { grid-template-columns: 1fr; }
  .landing .product-proof { max-width: 52rem; }
}

@media (max-width: 560px) {
  .landing .proof-toolbar { align-items: flex-start; flex-direction: column; gap: 0.75rem; }
  .proof-demo { grid-template-columns: minmax(0, 1fr); padding: 1.5rem 1rem 1.75rem; }
  .proof-phone { margin-right: 1.5rem; min-height: 0; }
  .proof-gallery { margin: -1rem 0 0 1.5rem; }
  .proof-address, .proof-gallery-head { padding-left: 0.85rem; }
  .proof-address { padding-top: 1.4rem; }
  .proof-gallery .media-grid { padding-left: 0.85rem; }
  /* A shorter picture on phones: no repeated route or wordmark, and the
     gallery as one row of square tiles (newest first, left to right) with the
     next row fading out under it, like a gallery that goes on. */
  .proof-route, .landing .proof-gallery-head .wordmark { display: none; }
  .proof-gallery .media-grid {
    columns: auto;
    display: grid;
    gap: 0.45rem;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    max-height: 11rem;
    -webkit-mask-image: linear-gradient(#000 70%, transparent);
    mask-image: linear-gradient(#000 70%, transparent);
    overflow: hidden;
  }
  .proof-gallery .media-tile { margin: 0; }
  .proof-gallery .media-open img { aspect-ratio: 1; height: auto; object-fit: cover; }
}

@media (max-width: 380px) {
  .proof-gallery { margin-left: 0.5rem; }
  .proof-address, .proof-gallery-head { padding-left: 0.6rem; padding-right: 0.6rem; }
  .proof-gallery .media-grid { padding: 0 0.6rem; }
  .proof-gallery .tile-attribution { letter-spacing: 0; padding: 0.3rem; }
}

/* Static under reduced motion, even if the preference changes mid-play. */
@media (prefers-reduced-motion: reduce) {
  .landing .product-proof.is-armed :is(.proof-step-sent, .proof-step-reply, .proof-step-new) { animation: none; opacity: 1; }
  .landing .product-proof.is-armed .proof-count-before { animation: none; visibility: hidden; }
  .landing .product-proof.is-armed .proof-count-after { animation: none; visibility: visible; }
}
.landing .steps { border-top: 1px solid var(--line); list-style: none; margin: 2rem 0 0; padding: 0; }
.landing .steps li { border-bottom: 1px solid var(--line); display: grid; gap: 1rem; grid-template-columns: 2.4rem 1fr; padding: clamp(1.6rem, 3vw, 2.6rem) 0; }
.landing .step-number { color: var(--accent-dark); font-family: var(--font-display); font-size: 1.7rem; font-weight: 800; letter-spacing: 0; padding-top: 0; }
.landing .steps h3 { font-size: clamp(1.8rem, 3vw, 2.8rem); letter-spacing: 0; line-height: 0.95; margin: 0 0 0.7rem; }
.landing .steps p { color: var(--muted); font-size: 1rem; line-height: 1.55; margin: 0; max-width: var(--measure); }

.pricing-section {
  background: #ddd4c6;
  border-bottom: 1px solid var(--line);
  border-top: 1px solid var(--line);
  padding: clamp(7rem, 11vw, 11rem) max(1rem, calc((100vw - 1400px) / 2 + 2.5rem)) clamp(7rem, 12vw, 12rem);
}
.pricing-heading { align-items: end; display: grid; gap: clamp(2rem, 8vw, 8rem); grid-template-columns: minmax(0, 1fr) minmax(280px, 0.58fr); }
.pricing-heading h2 { font-size: clamp(3.2rem, 7vw, 7.2rem); letter-spacing: -0.015em; line-height: 0.86; margin: 0; max-width: 4.56em; }
.pricing-heading > p { color: var(--muted); font-size: clamp(1rem, 0.9rem + 0.3vw, 1.125rem); line-height: 1.55; margin: 0; max-width: var(--measure); }
.pricing-grid { align-items: stretch; display: grid; grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.14fr); margin-top: clamp(4rem, 7vw, 7rem); }
.plan { display: flex; flex-direction: column; min-height: 650px; padding: clamp(2rem, 4.5vw, 4.5rem); position: relative; }
.plan-free { border: 1px solid var(--line); border-right: 0; }
.plan-plus { background: var(--ink); color: var(--surface); margin: -1.5rem 0 0; }
.plan-plus .plan-price { color: var(--fiesta); }
/* Plus wears the accent: its flag is a marigold label in the display face
   and its price is set in marigold. */
.plan-flag { background: var(--fiesta); color: var(--ink); font-family: var(--font-display); font-size: 1.25rem; font-weight: 800; letter-spacing: 0.04em; line-height: 1; padding: 0.55rem 0.8rem 0.5rem; position: absolute; right: 0; text-transform: uppercase; top: 0; }
.plan-heading { min-height: 215px; }
.plan-heading > p { font-size: 0.75rem; letter-spacing: 0.08em; margin: 0 0 1.6rem; text-transform: uppercase; }
.plan-heading h3 { font-size: clamp(2.6rem, 5vw, 5rem); letter-spacing: -0.005em; line-height: 0.86; margin: 0 0 1.2rem; }
.plan-price { display: inline-block; font-family: var(--font-display); font-size: clamp(2rem, 4.5vw, 4.2rem); font-variant-numeric: tabular-nums; letter-spacing: 0; line-height: 1; text-transform: uppercase; }
.plan-period { display: block; font-size: 0.75rem; margin-top: 0.55rem; text-transform: uppercase; }
.plan-description { font-size: 1rem; line-height: 1.55; margin: 1.2rem 0 2rem; max-width: var(--measure); min-height: 4em; }
.plan-features { border-top: 1px solid currentColor; list-style: none; margin: 0 0 2.5rem; padding: 0; }
.plan-features li { border-bottom: 1px solid color-mix(in srgb, currentColor, transparent 60%); font-size: 1rem; line-height: 1.4; padding: 0.8rem 0 0.75rem 1.4rem; position: relative; }
.plan-features li::before { content: "✳"; font-size: 0.75rem; left: 0; position: absolute; top: 0.95rem; }
.plan .button { align-self: flex-start; margin-top: auto; }
/* One line under the Plus card (its column on desktop) on how to upgrade later. */
.plan-upgrade-note { color: var(--muted); font-size: 1rem; grid-column: 2; line-height: 1.55; margin: 1.5rem 0 0; max-width: var(--measure); }

/* The toast photo is the finale's own (nbodorrio-2ay.17): no other section
   shows it. Phones get the 768px file in the 560px block below. */
.landing .landing-finale {
  align-items: center;
  background-image: linear-gradient(90deg, rgba(22, 18, 12, 0.2), rgba(22, 18, 12, 0.72)), url("/assets/bodorrio-toast-1280-231727b7.webp");
  background-position: center;
  background-size: cover;
  border-radius: 0;
  color: var(--ink);
  display: flex;
  justify-content: flex-end;
  margin: 0;
  max-width: none;
  min-height: 680px;
  padding: clamp(3rem, 8vw, 8rem) max(1.5rem, calc((100vw - 1400px) / 2 + 2.5rem));
}
.finale-panel { background: var(--surface); max-width: 520px; padding: clamp(2.5rem, 5vw, 5rem); position: relative; }
.finale-panel::before { border: 1px solid var(--ink); content: ""; inset: 0.7rem; pointer-events: none; position: absolute; }
.landing .finale-panel h2 { font-size: clamp(3rem, 5.5vw, 5.5rem); letter-spacing: -0.01em; line-height: 0.86; margin: 0; max-width: 4.1em; }
.finale-panel > p { color: var(--muted); font-size: 1rem; line-height: 1.55; margin: 1.2rem 0 1.6rem; max-width: var(--measure); }
.landing .site-footer { align-items: flex-end; display: flex; justify-content: space-between; margin: 0 auto; max-width: 1400px; padding: 4rem clamp(1rem, 3vw, 2.5rem) 2rem; }
.landing .site-footer .wordmark { display: block; font-size: 1.8rem; }
.landing .site-footer p { color: var(--muted); font-size: 0.875rem; margin: 0.5rem 0 0; }
.landing .site-footer nav { display: flex; gap: 1.5rem; }
.landing .site-footer nav a { font-size: 0.75rem; text-transform: uppercase; text-underline-offset: 0.3em; }

@media (max-width: 860px) {
  /* The stamp sits on top and the photo is a full-width band below it, so the
     huddle is never under the stamp. The bottom padding is the band height minus
     the strip of papel picado the stamp may overlap. */
  .landing .hero { min-height: 0; padding: 7rem 1rem 50vw; place-items: start center; }
  .landing .hero-image-wrap { aspect-ratio: 3 / 2; inset: auto 0 0; -webkit-mask-image: linear-gradient(transparent, #000 22%); mask-image: linear-gradient(transparent, #000 22%); }
  .hero-stamp-shell { width: min(620px, calc(100vw - 2.5rem)); }
  .postal-intro { gap: 4rem; grid-template-columns: 1fr; }
  .postcard { margin-left: clamp(0rem, 8vw, 4rem); }
  .landing .section-heading, .pricing-heading { align-items: start; grid-template-columns: 1fr; }
  .landing .steps { margin-left: clamp(0rem, 8vw, 4rem); }
  .pricing-grid { grid-template-columns: 1fr; }
  .plan { min-height: auto; }
  .plan-free { border-right: 1px solid var(--line); }
  .plan-plus { margin: -1px 0 0; }
  .plan-heading { min-height: 0; }
  .plan-description { min-height: 0; }
  .plan .button { margin-top: 0; }
  .plan-upgrade-note { grid-column: auto; }
  .landing .landing-finale { min-height: 620px; }
}

@media (max-width: 560px) {
  .landing .site-nav { height: 66px; padding: 0 1rem; }
  .site-nav-links > a:not(.button) { display: none; }
  .landing .site-nav .wordmark { font-size: 1.3rem; }
  .landing .button-compact { font-size: 0.75rem; min-height: 40px; padding: 0.7rem 0.75rem; }
  .landing .hero { padding: 6.5rem 1rem 78vw; }
  .landing .hero-image-wrap { aspect-ratio: 820 / 800; }
  .landing .hero-image { object-position: center; }
  .hero-stamp-shell { max-width: calc(100vw - 2.5rem); width: 100%; }
  .hero-stamp { padding: 2.4rem 1.25rem 2.3rem; }
  /* Close to the top edge so the 44px hit area stays clear of the h1. */
  .hero-cycle-toggle { right: 0.6rem; top: 0.4rem; }
  .landing .hero h1 { font-size: clamp(2.45rem, 12.3vw, 3.5rem); }
  .hero-actions { flex-direction: column; gap: 1rem; width: 100%; }
  .hero-actions .button { width: 100%; }
  .postmark-lines { display: none; }
  .hero-caption { max-width: 16em; }
  /* Phones: 4.5rem above and below each section instead of the 7rem floor
     of the desktop clamp, and the decorative postcard steps aside. */
  .postal-intro { padding: 4.5rem 1rem; }
  .postal-copy h2 { font-size: clamp(2.75rem, 13vw, 4.8rem); }
  .postcard { display: none; }
  .landing .how-it-works { padding: 4.5rem 1rem; }
  /* No em max-width on phones: it scales with the size and would keep the
     same line breaks. */
  .landing .section-heading h2, .pricing-heading h2 { font-size: clamp(2.75rem, 13vw, 5rem); max-width: none; }
  .landing .product-story { gap: 2.5rem; margin-top: 2.5rem; }
  .landing .steps { margin-left: 0; }
  .landing .steps li { padding: 1.25rem 0; }
  .pricing-section { padding: 4.5rem 1rem; }
  .pricing-grid { margin-top: 2.5rem; }
  .plan { padding: 2rem 1.35rem; }
  .plan-plus { padding-top: 3.5rem; }
  .plan-heading > p { margin-bottom: 1rem; }
  .plan-heading h3 { font-size: 3.4rem; margin-bottom: 0.8rem; }
  .plan-description { margin: 1rem 0 1.5rem; }
  .plan-features { margin-bottom: 1.75rem; }
  .plan-price { font-size: 2.8rem; }
  .landing .landing-finale { background-image: linear-gradient(90deg, rgba(22, 18, 12, 0.2), rgba(22, 18, 12, 0.72)), url("/assets/bodorrio-toast-768-199feae4.webp"); margin: 0; min-height: 610px; padding: 2rem 1rem; }
  .finale-panel { padding: 2.5rem 1.5rem; }
  .landing .site-footer { align-items: flex-start; flex-direction: column; gap: 2rem; padding-left: 1rem; padding-right: 1rem; }
  /* 26px between wrapped rows: two 18px links whose 44px hit areas touch
     without overlapping. */
  .landing .site-footer nav { flex-wrap: wrap; row-gap: 1.625rem; }
}

@media (max-width: 860px) {
  .hero { grid-template-columns: 1fr; min-height: auto; padding: 1rem; }
  .hero-copy { padding: 3rem 0; }
  .hero h1 { font-size: clamp(3.2rem, 14vw, 5rem); }
  .hero-image-wrap { min-height: 460px; }
  .product-story { grid-template-columns: 1fr; }
  .landing-finale { align-items: flex-start; flex-direction: column; }
}

@media (max-width: 560px) {
  .site-nav { height: 64px; padding: 0 1rem; }
  .hero-image-wrap { min-height: 380px; }
  .how-it-works { padding-left: 1rem; padding-right: 1rem; }
  .landing-finale { margin: 0 1rem; }
  .site-footer { align-items: flex-start; flex-direction: column; gap: 1rem; padding-left: 1rem; padding-right: 1rem; }
}

/* Postal application surfaces: the event gallery (public and organizer), its
   dialogs, and the export, billing-return, legal and error pages. One rule per
   component, with one 860px and one 560px block below. */
:is(.gallery-page, .export-page, .error-page) {
  background-color: var(--canvas);
  color: var(--ink);
  position: relative;
}
:is(.gallery-page, .export-page, .error-page) :is(h1, h2, h3, .wordmark),
:is(.gallery-page, .export-page, .error-page) .event-code strong {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
}
:is(.gallery-page, .export-page, .error-page) :is(h1, h2, h3) { text-wrap: balance; }
:is(.gallery-page, .export-page, .error-page) p { text-wrap: pretty; }
:is(.gallery-page, .export-page, .error-page) .wordmark { letter-spacing: 0; }
/* The one small line above a billing-return or legal title (the payment's
   status, "Aviso legal"): sentence case, not a tracked capital eyebrow. */
:is(.gallery-page, .export-page, .error-page) .eyebrow {
  color: var(--accent-dark);
  font-size: 0.9375rem;
  font-weight: 750;
  letter-spacing: 0;
  text-transform: none;
}
:is(.gallery-page, .export-page, .error-page) .button {
  border-radius: 0;
  font-size: 0.9375rem;
}
:is(.gallery-page, .export-page, .error-page) .button-secondary { background: transparent; }

.gallery-page { margin: 0 auto; max-width: 1500px; min-height: 100dvh; padding: 0 clamp(1rem, 3vw, 3rem) 8rem; }
.gallery-header {
  align-items: flex-end;
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem 3rem;
  justify-content: space-between;
  padding: 1.5rem 0 2rem;
  position: relative;
}
/* The name takes what the actions leave, but never less than 36rem: where
   the organizer's three buttons would squeeze it to three lines (861-1150px)
   the actions wrap under it instead, at the start of their own line. */
.gallery-header > div:first-child { flex: 1 1 36rem; max-width: 900px; position: relative; z-index: 1; }
/* Photos first: the header is one compact band (no minimum height, the
   actions beside the name) that keeps under about 40% of a phone or laptop
   screen. The name is its one display-size element; the date is a plain
   sentence-case line, which event_date_range keeps short. */
.gallery-header .wordmark { display: inline-block; font-size: 1.45rem; margin-bottom: clamp(1rem, 2.5vw, 2rem); }
.gallery-header h1 { font-size: clamp(2.6rem, 5vw, 4.5rem); letter-spacing: -0.01em; line-height: 0.92; margin: 0 0 0.6rem; overflow-wrap: anywhere; }
.gallery-header p { color: var(--muted); font-size: 1rem; margin: 0; }
.header-note { color: var(--muted); font-size: 0.875rem; font-weight: 650; margin-left: 0.75rem; }
/* A long event name (GalleryHelper#gallery_header_class) takes the full
   width, and the actions follow under it, left-aligned with the name. */
.gallery-header.has-long-name > div:first-child { flex-basis: 100%; }
.gallery-header.has-long-name .header-actions { justify-content: flex-start; }
.header-actions { align-items: center; display: flex; flex-shrink: 0; flex-wrap: wrap; gap: 0.55rem; justify-content: flex-end; max-width: 610px; position: relative; z-index: 1; }
.header-actions form { margin: 0; }
.header-actions .button { min-height: 46px; }
/* The public page's side of the header: its actions, then how guests add
   photos (PUB-007), a quiet muted note under them. On desktop the side is
   shorter than the name beside it, so the note adds no height; on phones it
   is one short paragraph under the buttons. The join link keeps to one line,
   and its ::after makes its hit area 44px (2.75rem) tall, as the landing's
   text links do, without spacing the lines apart. */
.gallery-aside { align-items: flex-end; display: flex; flex: 0 1 24rem; flex-direction: column; gap: 0.75rem; position: relative; z-index: 1; }
.gallery-header .gallery-note { font-size: 0.875rem; line-height: 1.45; max-width: 24rem; text-align: right; }
.gallery-note a { color: var(--ink); font-weight: 700; position: relative; text-underline-offset: 0.2em; white-space: nowrap; }
.gallery-note a::after { content: ""; height: 100%; inset-inline: -0.25rem; min-height: 2.75rem; position: absolute; top: 50%; translate: 0 -50%; }
.gallery-header.has-long-name .gallery-aside { align-items: flex-start; }
.gallery-header.has-long-name .gallery-note { text-align: left; }
/* The public count and the filters share one line; the organizer page has
   only the filters (its tabs carry the counts). */
.gallery-toolbar { align-items: center; display: flex; gap: 0.75rem 1.5rem; padding: 1.25rem 0; }
.gallery-toolbar > strong { flex: none; font-size: 0.9375rem; font-weight: 750; }
/* The filters are one row of 44px chip links, grouped by dimension with a
   hairline between groups. When the row does not fit it scrolls sideways
   inside itself, never the page: the right edge fades, so a chip cut there
   shows that more follow, and the end padding clears the fade once the row
   is scrolled to its end. Once the row is scrolled from its start
   (.is-scrolled, set by filter_row_controller.js, which also scrolls a
   focused or active chip clear of both fades) the left edge fades too. The 6px inset (cancelled by the negative margin)
   keeps the focus ring, 3px wide 3px out, from being clipped by the
   scroller. Pointer users get a thin scrollbar; touch users swipe. */
.gallery-filters { flex: 1 1 auto; min-width: 0; }
.filter-row { display: flex; gap: 0.5rem; margin: -6px; -webkit-mask-image: linear-gradient(to left, transparent, #000 2rem); mask-image: linear-gradient(to left, transparent, #000 2rem); overflow-x: auto; overscroll-behavior-x: contain; padding: 6px 2.5rem 6px 6px; scrollbar-color: var(--surface-soft) transparent; scrollbar-width: thin; }
.filter-row.is-scrolled { -webkit-mask-image: linear-gradient(to right, transparent, #000 2rem, #000 calc(100% - 2rem), transparent); mask-image: linear-gradient(to right, transparent, #000 2rem, #000 calc(100% - 2rem), transparent); }
.filter-group { display: flex; flex: none; gap: 0.5rem; }
.filter-group + .filter-group { border-left: 1px solid var(--surface-soft); padding-left: 0.5rem; }
select, input[type="password"], input[type="text"] { background: transparent; border: 1px solid var(--line); border-radius: 0; color: var(--ink); font-size: 1rem; min-height: 44px; padding: 0.65rem 0.8rem; }
.filter-chip { align-items: center; background: transparent; border: 1px solid var(--line); border-radius: 0; color: var(--ink); display: inline-flex; font-size: 0.9375rem; font-weight: 650; gap: 0.4rem; min-height: 44px; padding: 0 0.95rem; text-decoration: none; transition: background-color 180ms ease, color 180ms ease; white-space: nowrap; }
.filter-chip:hover { background: var(--surface-soft); }
.filter-chip.is-active { background: var(--ink); border-color: var(--ink); color: var(--surface); }
.filter-sort svg { flex: none; }
@media (pointer: coarse) { .filter-row { scrollbar-width: none; } }

/* The landing proof's grid is multi-column (.proof-gallery); the gallery's
   own pages are .gallery-grid below. */
.media-grid { column-gap: 0.75rem; columns: 4 260px; }
/* align-self and min-width matter where a .media-grid is a CSS grid: each
   gallery page and the landing proof's phone row. Gallery tiles stand
   straight: a fraction of a degree read as soft edges, not scattered prints. */
.media-tile {
  align-self: start;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 0;
  break-inside: avoid;
  display: inline-block;
  margin: 0 0 0.75rem;
  min-width: 0;
  position: relative;
  width: 100%;
}
/* Each gallery page is a grid of up to four columns at least 260px wide (two
   from 860px down), filled row by row, newest first, so the newest items run
   across the top. Without JavaScript the rows are top-aligned and keep that
   order; masonry_controller.js then packs each tile under the column that
   ends highest (.is-masonry: fine rows, each tile spans its height plus the
   gap), still in reading order. */
.gallery-grid { align-items: start; columns: auto; display: grid; gap: 0.75rem; grid-template-columns: repeat(auto-fill, minmax(max(260px, calc((100% - 2.25rem) / 4)), 1fr)); }
.gallery-grid.is-masonry { grid-auto-rows: var(--masonry-row, 1px); row-gap: 0; }
.gallery-grid > .media-tile { margin: 0; }
.media-open { background: var(--surface-soft); border: 0; border-radius: 0; cursor: zoom-in; display: block; min-height: 160px; overflow: hidden; padding: 0; position: relative; width: 100%; }
.media-open img, .media-open video { height: auto; min-height: 160px; object-fit: cover; transition: transform 280ms ease; width: 100%; }
/* A quiet zoom on hover; the photo keeps its own colour (no filter). */
.media-open:hover img, .media-open:hover video { transform: scale(1.012); }
/* A video's mark: a small ink play sign in the photo's bottom-left corner,
   with the length when it is known, so it covers little of the poster. It is
   decorative; the tile's button and its image name the video. */
.video-mark { align-items: center; background: var(--ink); bottom: 0.5rem; color: var(--surface); display: inline-flex; font-size: 0.75rem; font-variant-numeric: tabular-nums; font-weight: 750; gap: 0.3rem; justify-content: center; left: 0.5rem; line-height: 1; min-height: 1.75rem; min-width: 1.75rem; padding: 0 0.45rem; pointer-events: none; position: absolute; }
.video-mark svg { flex: none; }
/* The sender line (PUB-023, ORG-020): who sent the item and when it
   arrived, the gallery's one postal element, in the typewriter face and
   terracotta on the tile's paper (4.65:1). No strip and no capitals: the name
   reads as the guest wrote it. Like a return address, the name sits over the
   time on every tile, so the rows line up across the grid; a long name wraps
   rather than being cut, and the time keeps to one line. */
.sender-line { color: var(--accent); display: grid; font-family: var(--font-postal); font-size: 0.8125rem; gap: 0.1rem; line-height: 1.35; margin: 0; padding: 0.7rem 0.85rem 0.8rem; }
.sender-name { font-weight: 600; overflow-wrap: anywhere; }
.sender-line time { white-space: nowrap; }
/* The public tile select control only shows on events with ZIP exports
   (nbodorrio-0hm.3), and the whole label is the hit area, at least 44px
   square at every width (on phones only the checkbox shows). */
.select-control { align-items: center; background: var(--surface); border: 1px solid var(--line); cursor: pointer; display: flex; font-size: 0.8125rem; font-weight: 750; gap: 0.4rem; justify-content: center; left: 0.55rem; min-height: 44px; min-width: 44px; padding: 0.45rem 0.55rem; position: absolute; top: 0.55rem; }
.select-control input { accent-color: var(--accent); height: 19px; margin: 0; width: 19px; }
/* Select controls, public and organizer, show once gallery_controller.js has
   connected (data-selection-ready): without JavaScript there is no bar to act
   on the selection. The organizer's (ORG-034) is a 44px square on the photo's
   top left, opposite the actions button and matching it. A selected tile gets an
   ink frame, so the choice reads at a glance and not only from the box. */
.gallery-page:not([data-selection-ready]) .select-control { display: none; }
/* It comes before the photo's button in the tab order, so it is lifted over it. */
.organizer-select { height: 44px; padding: 0; width: 44px; z-index: 1; }
.organizer-select input { accent-color: var(--ink); height: 22px; width: 22px; }
.organizer-select:has(input:focus-visible) { box-shadow: 0 0 0 3px var(--surface); outline: 3px solid var(--focus); outline-offset: 3px; }
.organizer-select input:focus-visible { outline: 0; }
.organizer-grid .media-tile:has(.organizer-select input:checked) { box-shadow: 0 0 0 3px var(--ink); }
.organizer-grid .media-tile:has(.organizer-select input:checked) .organizer-select { background: var(--ink); border-color: var(--ink); }
.organizer-grid .media-tile:has(.organizer-select input:checked) .organizer-select input { accent-color: var(--surface); }
/* The organizer tile's actions: a 44px button on the photo's top right
   corner (the public select control holds the top left) and a menu below it,
   right-aligned and never wider than the tile, so it stays on screen in either
   column. Tiles are not transformed, so they make no stacking context of
   their own and the menu's z-index lifts it over the tiles after it (and
   their 44px controls). */
.tile-actions-toggle { align-items: center; background: var(--surface); border: 1px solid var(--line); color: var(--ink); cursor: pointer; display: flex; font-size: 1.375rem; font-weight: 800; height: 44px; justify-content: center; line-height: 1; list-style: none; position: absolute; right: 0.55rem; top: 0.55rem; transition: background-color 180ms ease, color 180ms ease; width: 44px; }
.tile-actions-toggle::-webkit-details-marker { display: none; }
.tile-actions-toggle:hover, .tile-actions[open] > .tile-actions-toggle { background: var(--ink); color: var(--surface); }
/* On a photo the ring gets a cream gap, so it reads on dark and busy images. */
.tile-actions-toggle:focus-visible { box-shadow: 0 0 0 3px var(--surface); outline: 3px solid var(--focus); outline-offset: 3px; }
.tile-actions-menu { background: var(--surface); border: 1px solid var(--line); box-shadow: 0 12px 28px rgba(43, 46, 41, 0.22); display: grid; padding: 0.25rem 0; position: absolute; right: 0.55rem; top: calc(0.55rem + 44px + 0.3rem); width: min(12rem, 100% - 1.1rem); z-index: 3; }
.tile-actions-menu > * { align-items: center; background: none; border: 0; color: var(--ink); cursor: pointer; display: flex; font: inherit; font-size: 0.9375rem; font-weight: 700; min-height: 44px; padding: 0.5rem 0.85rem; text-align: left; text-decoration: none; }
.tile-actions-menu > *:hover { background: var(--canvas); }
.tile-actions-menu > *:focus-visible { outline-offset: -3px; }
/* Delete is set apart by a rule and the danger colour; it keeps its confirm. */
.tile-actions-menu > .tile-actions-danger { border-top: 1px solid var(--line); color: var(--danger); margin-top: 0.25rem; }
/* Each gallery page is its own .gallery-grid, so a page loading below never
   moves the tiles of the pages above it. */
.gallery-more { display: block; }
.gallery-more-link { display: flex; margin: 0.75rem auto 1.5rem; width: fit-content; }

/* The organizer's plan, as one slim line above the tabs: a meter, the count
   and the plan, with a quiet upgrade link on Free, and the date until which
   everything is kept. It turns ink, with a
   cream button, only near the limit (80%) or when it is full. An archived
   event keeps a full message instead. */
.usage-line { --meter-fill: var(--ink); --meter-track: var(--surface-soft); align-items: center; column-gap: 1rem; display: flex; flex-wrap: wrap; margin: 0 0 1rem; padding: 0.85rem 0; row-gap: 0.4rem; }
.usage-line p { margin: 0; }
.usage-line meter { appearance: none; background: var(--meter-track); border: 0; border-radius: 0; display: block; flex: none; height: 0.5rem; width: 8rem; }
.usage-line meter::-webkit-meter-bar { background: var(--meter-track); border: 0; border-radius: 0; height: 0.5rem; }
.usage-line meter::-webkit-meter-optimum-value, .usage-line meter::-webkit-meter-suboptimum-value, .usage-line meter::-webkit-meter-even-less-good-value { background: var(--meter-fill); border-radius: 0; }
.usage-line meter::-moz-meter-bar { background: var(--meter-fill); border-radius: 0; }
.usage-count { font-size: 0.9375rem; font-weight: 750; }
.usage-plan { color: var(--muted); font-size: 0.9375rem; }
/* Always the last row of the line, under the button, in the same muted tone. */
.usage-retention { color: var(--muted); flex-basis: 100%; font-size: 0.875rem; order: 1; }
.usage-note { font-size: 0.9375rem; font-weight: 750; }
.usage-upgrade { margin: 0 0 0 auto; }
.usage-upgrade .text-button { font-size: 0.9375rem; min-height: 44px; padding: 0.35rem 0; }
/* On ink the track is a mid grey, so the cream fill keeps 3:1 against it. */
.usage-alert { --meter-fill: var(--surface); --meter-track: #6b6e66; --muted: #c9c3b6; background: var(--ink); color: var(--surface); margin-top: 1.25rem; padding: 0.85rem clamp(1rem, 2vw, 1.5rem); }
.usage-line.is-archived { background: var(--surface); border: 1px solid var(--line); display: block; margin-top: 1.25rem; padding: clamp(1.5rem, 4vw, 2.5rem); }
.usage-line.is-archived h2 { font-size: clamp(2rem, 4vw, 3rem); letter-spacing: 0; line-height: 0.95; margin: 0 0 0.6rem; }
.usage-line.is-archived p { color: var(--muted); font-size: 1rem; line-height: 1.55; max-width: var(--measure); }
.tabs { border-bottom: 1px solid var(--line); display: flex; gap: 0; }
.tabs a { border: 1px solid transparent; border-bottom: 0; color: var(--muted); font-size: 0.9375rem; font-weight: 750; padding: 0.8rem 1rem; text-decoration: none; transition: color 180ms ease, background-color 180ms ease; }
.tabs a:hover { color: var(--ink); }
.tabs a.is-active { background: var(--ink); border: 1px solid var(--ink); border-bottom-width: 3px; color: var(--surface); }

.empty-state { align-items: flex-start; border: 1px solid var(--line); display: flex; flex-direction: column; margin: 2rem 0; min-height: 390px; overflow: hidden; padding: clamp(2rem, 6vw, 5rem); position: relative; }
.empty-state h2 { font-family: var(--font-display); font-size: clamp(2.8rem, 6vw, 6rem); letter-spacing: -0.01em; line-height: 0.88; margin: auto 0 0.8rem; max-width: 6.38em; text-transform: uppercase; }
.empty-state p { color: var(--muted); font-size: 1rem; line-height: 1.55; margin: 0 0 1.5rem; max-width: var(--measure); }
/* An event without media leads with its join QR (ORG-013): on phones the
   headline, then the QR and code at full width, then the line and the
   download; from 720px the QR and code stand left of the words. The QR is not capped by the viewport height as in the
   dialog, so it stays large enough to scan from a phone screen. */
.empty-invite { align-items: start; display: grid; gap: 1.25rem; grid-template-columns: minmax(0, 1fr); min-height: 0; }
.empty-invite h2 { font-size: clamp(2.25rem, 4.5vw, 4rem); line-height: 0.92; margin: 0; max-width: 9em; }
.empty-invite-qr { display: grid; gap: 0.75rem; width: min(100%, 300px); }
.empty-invite .qr-code { margin: 0; width: 100%; }
.empty-invite .event-code { background: var(--surface); column-gap: 1rem; flex-wrap: wrap; margin: 0; }
.empty-invite .configuration-warning { border-left: 4px solid var(--accent-dark); color: var(--ink); font-weight: 700; margin: 0; padding: 0.25rem 0 0.25rem 0.85rem; }
.empty-invite .empty-invite-body { color: var(--ink); font-size: 1.0625rem; margin: 0; }
.empty-invite .button { justify-self: start; }
@media (min-width: 720px) {
  .empty-invite { column-gap: clamp(2rem, 5vw, 4.5rem); grid-template-columns: 300px minmax(0, 1fr); grid-template-rows: auto auto 1fr; }
  .empty-invite-qr { grid-column: 1; grid-row: 1 / span 3; }
  .empty-invite h2, .empty-invite-body, .empty-invite .button { grid-column: 2; }
  .empty-invite h2 { align-self: end; }
}
.selection-bar { background: var(--surface); border: 1px solid var(--line); bottom: 1rem; box-shadow: 0 16px 40px rgba(55, 45, 34, 0.24); left: 50%; max-width: calc(100% - 2rem); padding: 0.7rem; position: fixed; transform: translateX(-50%); width: max-content; z-index: 20; }
.selection-form { align-items: center; display: flex; font-size: 0.8125rem; gap: 0.8rem; }
.selection-form .text-button { min-height: 44px; }
/* The organizer's bar has two actions: Ocultar or Mostrar, then Descargar
   (or, without ZIP exports, the upgrade). */
.selection-actions { display: flex; gap: 0.5rem; }

dialog { background: transparent; border: 0; color: inherit; height: 100%; max-height: none; max-width: none; padding: 1rem; width: 100%; }
dialog::backdrop { backdrop-filter: blur(8px); background: rgba(26, 22, 16, 0.92); }
/* The lightbox: a bar with the position, download and close on top, the photo
   between previous and next, and who sent it and when underneath. The media
   area takes horizontal swipes (gallery_controller.js) and leaves vertical pans
   and pinch-zoom to the browser; once the page is zoomed it pans freely. */
.lightbox-panel { align-items: center; column-gap: 1rem; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; grid-template-rows: auto minmax(0, 1fr) auto; height: 100%; margin: auto; max-width: 1400px; }
.lightbox-bar { align-items: center; display: flex; gap: 0.5rem; grid-column: 1 / -1; padding-bottom: 0.5rem; }
.lightbox-position { color: #c9bda9; font-size: 0.875rem; font-variant-numeric: tabular-nums; font-weight: 700; margin: 0; }
.lightbox-download { align-items: center; display: inline-flex; margin-left: auto; text-decoration: none; }
.lightbox-media { align-items: center; display: flex; grid-column: 2; grid-row: 2; height: 100%; justify-content: center; min-height: 0; touch-action: pan-y pinch-zoom; }
.lightbox-media.is-zoomed { touch-action: auto; }
.lightbox-media img, .lightbox-media video { max-height: 100%; max-width: 100%; object-fit: contain; transition: transform 200ms ease; }
.lightbox-media .is-dragged { transition: none; }
.lightbox-enter-next { animation: lightbox-enter-next 220ms ease-out; }
.lightbox-enter-previous { animation: lightbox-enter-previous 220ms ease-out; }
@keyframes lightbox-enter-next { from { opacity: 0; transform: translateX(3rem); } }
@keyframes lightbox-enter-previous { from { opacity: 0; transform: translateX(-3rem); } }
.lightbox-nav, .lightbox-download, .modal-close { background: var(--surface); border: 1px solid var(--line); border-radius: 0; color: var(--ink); cursor: pointer; font-size: 0.875rem; font-weight: 750; min-height: 44px; padding: 0.7rem; transition: background-color 180ms ease, transform 180ms ease; }
.lightbox-nav:hover, .lightbox-download:hover, .modal-close:hover { background: #efe6d4; }
.lightbox-nav:active, .lightbox-download:active, .modal-close:active { transform: translateY(1px); }
.lightbox-nav { align-items: center; display: inline-flex; gap: 0.35rem; grid-row: 2; justify-content: center; min-width: 44px; }
.lightbox-nav.previous { grid-column: 1; }
.lightbox-nav.next { grid-column: 3; }
.lightbox-nav svg { flex: none; }
.modal-close { position: absolute; right: 0.5rem; top: 0.5rem; z-index: 2; }
.lightbox-bar .modal-close { position: static; }
.lightbox-meta { color: var(--surface); display: grid; font-size: 0.875rem; gap: 0.2rem; grid-column: 2; grid-row: 3; justify-items: center; margin: 0; min-width: 0; padding-top: 0.6rem; text-align: center; }
.lightbox-meta strong, .lightbox-meta span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lightbox-meta span { color: #c9bda9; font-size: 0.8125rem; }
.share-modal { align-items: center; justify-content: center; }
.share-modal[open] { display: flex; }
/* The share dialogs fit a 375x812 phone and a 1440x900 screen without
   scrolling to the download: the title is a plain sentence, not a poster, and
   the QR shrinks with a short viewport. */
/* A plain panel with no inner frame: a frame drawn on the scroller stayed put
   and cut across the content of a panel scrolled on a short screen. Close
   floats in the panel's top-right padding and sticks there while the panel
   scrolls, so it is always in reach; the title wraps beside it. */
.share-panel { --panel-padding: clamp(1.5rem, 4vw, 2.75rem); background: var(--surface); box-shadow: 0 28px 80px rgba(28, 21, 12, 0.42); max-height: calc(100dvh - 2rem); max-width: 520px; overflow: auto; padding: var(--panel-padding); position: relative; width: 100%; }
.share-panel > .modal-close { float: right; margin: calc(0.5rem - var(--panel-padding)) calc(0.5rem - var(--panel-padding)) 0.5rem 0.75rem; position: sticky; right: auto; top: calc(0.5rem - var(--panel-padding)); }
/* Beats the layer's display-face heading rule, whose :is() counts .wordmark. */
.share-modal .share-panel h2 { font-family: var(--font-body); font-size: clamp(1.5rem, 1.25rem + 1vw, 1.875rem); font-weight: 800; letter-spacing: -0.02em; line-height: 1.15; margin: 0 0 0.5rem; text-transform: none; }
.share-panel > p { color: var(--muted); font-size: 1rem; line-height: 1.5; margin: 0 0 1rem; }
.qr-code { aspect-ratio: 1; background: #fff; border: 1px solid var(--line); height: auto; margin: 1rem auto; padding: 0; width: min(100%, 270px, 34dvh); }
.copy-field { display: grid; flex: 1 1 12rem; gap: 0.35rem; min-width: 0; }
.copy-field span { color: var(--ink); font-size: 0.875rem; font-weight: 700; }
.copy-field input { min-width: 0; width: 100%; }
.copy-field-group { align-items: flex-end; display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 1rem; }
.copy-field-group.is-private { display: grid; margin: 0; }
.copy-field-warning { color: var(--danger); font-size: 0.9375rem; font-weight: 700; line-height: 1.4; margin: 0; }
.modal-actions { display: flex; gap: 0.7rem; margin-top: 1rem; }
.event-code { align-items: center; border: 1px solid var(--line); display: flex; justify-content: space-between; margin: 0 0 1rem; padding: 0.55rem 1rem; }
.event-code strong { font-size: 1.6rem; letter-spacing: 0.15em; }
.share-download { margin: 0 0 1rem; width: 100%; }
/* The organizer's private link: collapsed, tinted and ruled in the danger
   colour, so it never reads as a second copy of the guests' link. */
.share-private { background: #f3e2d8; border: 1px solid var(--line); border-left: 4px solid var(--danger); margin-top: 1rem; }
.share-private summary { cursor: pointer; display: grid; gap: 0.1rem; list-style: none; min-height: 44px; padding: 0.7rem 3rem 0.7rem 1rem; position: relative; }
.share-private summary::-webkit-details-marker { display: none; }
.share-private summary::after { content: "+"; font-size: 1.5rem; font-weight: 700; line-height: 1; position: absolute; right: 1rem; top: 50%; transform: translateY(-50%); }
.share-private[open] summary::after { content: "\2212"; }
.share-private summary:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
.share-private summary span { color: var(--muted); font-size: 0.875rem; }
.share-private > .copy-field-group { gap: 0.6rem; padding: 0 1rem 1rem; }

.flash-stack { display: grid; gap: 0.5rem; max-width: min(520px, calc(100% - 2rem)); position: fixed; right: 1rem; top: 1rem; width: 100%; z-index: 50; }
.flash { align-items: center; background: var(--surface); border: 1px solid var(--line); border-left: 5px solid var(--accent); box-shadow: 0 12px 32px rgba(55, 45, 34, 0.2); display: flex; font-size: 1rem; gap: 1rem; justify-content: space-between; padding: 0.8rem 1rem; }
.flash button { background: none; border: 0; color: var(--muted); cursor: pointer; font-size: 0.875rem; }
.flash.alert { border-left-color: var(--danger); }

.export-page, .error-page {
  background-image: linear-gradient(90deg, rgba(24, 20, 14, 0.5), rgba(24, 20, 14, 0.82)), url("/assets/bodorrio-hero-1280-9893374a.webp");
  background-position: center;
  background-size: cover;
  color: var(--surface);
  display: grid;
  min-height: 100dvh;
  padding: clamp(1rem, 3vw, 2.5rem);
}
.export-page > .wordmark, .error-page > .wordmark { align-self: start; color: var(--surface); font-size: 1.45rem; }
.export-card, .error-card {
  align-self: center;
  background: var(--surface);
  box-shadow: 0 28px 80px rgba(28, 21, 12, 0.38);
  color: var(--ink);
  padding: clamp(2.5rem, 6vw, 5.5rem);
  position: relative;
  text-align: left;
}
.export-card { margin: 2rem auto; max-width: 820px; width: 100%; }
.error-card { margin: auto; max-width: 760px; }
.export-card::before, .error-card::before { border: 1px solid var(--line); content: ""; inset: 0.7rem; pointer-events: none; position: absolute; }
.export-card > *, .error-card > * { position: relative; z-index: 1; }
.export-card h1, .error-card h1 { font-size: clamp(3.2rem, 7vw, 7rem); letter-spacing: -0.01em; line-height: 0.86; margin: 0; max-width: 5.02em; }
.export-card > p:not(.eyebrow), .error-card > p:not(.eyebrow) { color: var(--muted); font-size: 1rem; line-height: 1.55; margin: 1.5rem 0 2.5rem; max-width: var(--measure); }
.export-card > p.export-hint { margin-top: -1.25rem; }
.export-summary { border-bottom: 1px solid var(--line); border-top: 1px solid var(--line); display: grid; gap: 0; grid-template-columns: repeat(3, 1fr); margin: 0 0 2rem; overflow: hidden; padding: 0; }
.export-summary div { border-left: 1px solid var(--line); display: grid; gap: 0.8rem; padding: 1rem; }
.export-summary div:first-child { border-left: 0; }
.export-summary dt { color: var(--muted); font-size: 0.875rem; }
.export-summary dd { font-size: 1.05rem; font-variant-numeric: tabular-nums; font-weight: 750; margin: 0; }
.export-actions { display: flex; flex-wrap: wrap; gap: 0.7rem; }

.legal-card { margin: 2rem auto; }
.legal-body { border-bottom: 1px solid var(--line); display: grid; gap: 0.4rem; margin: 0 0 2.5rem; padding-bottom: 1rem; }
.legal-body p { color: var(--muted); font-size: 1rem; line-height: 1.6; margin: 0 0 1.2rem; max-width: var(--measure); }
.legal-body h2 { font-size: clamp(1.6rem, 3vw, 2.4rem); letter-spacing: 0; line-height: 0.95; margin: 1.8rem 0 0.9rem; }
.legal-body ul { list-style: none; margin: 0 0 1.2rem; padding: 0; }
.legal-body li { border-top: 1px solid color-mix(in srgb, var(--line), transparent 70%); color: var(--muted); font-size: 1rem; line-height: 1.55; max-width: var(--measure); padding: 0.85rem 0 0.8rem 1.4rem; position: relative; }
.legal-body li:last-child { border-bottom: 1px solid color-mix(in srgb, var(--line), transparent 70%); }
.legal-body li::before { content: "✳"; font-size: 0.75rem; left: 0; position: absolute; top: 1.05rem; }


@media (max-width: 860px) {
  .gallery-header { align-items: flex-start; flex-direction: column; gap: 1.25rem; padding: 1.25rem 0 1.75rem; }
  .gallery-header > div:first-child { flex: none; max-width: 100%; }
  .header-actions { justify-content: flex-start; max-width: none; }
  /* Under the name, the note reads left-aligned under the buttons. */
  .gallery-aside { align-items: flex-start; align-self: stretch; flex: none; }
  .gallery-header .gallery-note { text-align: left; }
  /* Two columns on tablets and phones, whatever their width. */
  .gallery-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .export-summary { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
  /* The bottom padding clears the two-row selection bar (about 122px with
     its offset), so the last row of tiles can scroll out from under it. */
  .gallery-page { padding: 0 1rem 8.5rem; }
  .gallery-header { padding-bottom: 1.25rem; }
  /* One row of equal buttons that always spans the column: a guest's lone
     Compartir is a full-width button, not half of an empty grid. A label
     that does not fit breaks onto a second line inside its button. */
  .header-actions { align-items: stretch; display: grid; gap: 0.4rem; grid-auto-columns: minmax(0, 1fr); grid-auto-flow: column; width: 100%; }
  .header-actions .button { font-size: 0.8125rem; height: 100%; line-height: 1.15; padding: 0.5rem 0.35rem; text-align: center; text-wrap: balance; white-space: normal; width: 100%; }
  /* The count above the chips, which get the full width. */
  .gallery-toolbar { align-items: stretch; flex-direction: column; gap: 0.5rem; padding: 1rem 0; }
  .gallery-grid { gap: 0.5rem; }
  .media-open, .media-open img, .media-open video { min-height: 120px; }
  .sender-line { padding: 0.55rem 0.6rem 0.65rem; }
  .select-control span { display: none; }
  /* The meter takes the first line; on ink the upgrade button takes the last. */
  .usage-line meter { width: 100%; }
  .usage-upgrade { margin: 0; }
  .usage-alert .usage-upgrade { margin-top: 0.35rem; width: 100%; }
  .usage-alert .usage-upgrade .button { width: 100%; }
  /* Narrower padding so the QR keeps its width on a 320px screen, and a
     tighter frame so the whole QR is above the fold on a 375x812 phone. */
  .empty-invite { gap: 1rem; margin-top: 1rem; padding: 1.25rem; }
  .empty-invite .button { justify-self: stretch; }
  /* The count and Limpiar share the first line, the download takes the second. */
  .selection-bar { bottom: 0.5rem; left: 0.5rem; max-width: none; padding: 0.5rem; right: 0.5rem; transform: none; width: auto; }
  .selection-form { display: grid; gap: 0.35rem 0.5rem; grid-template-columns: minmax(0, 1fr) auto; }
  .selection-form .text-button { padding: 0.35rem 0.5rem; }
  .selection-form .button { grid-column: 1 / -1; }
  /* The organizer's two actions share the second line, so the bar stays two
     rows tall; a long upgrade label breaks inside its button. */
  .selection-actions { grid-column: 1 / -1; }
  .selection-actions .button { flex: 1 1 0; font-size: 0.875rem; line-height: 1.15; min-width: 0; padding: 0.5rem 0.6rem; text-align: center; white-space: normal; }
  /* Edge to edge: the photo takes the full width and every row the bars leave
     it; previous and next become 44px arrows either side of the sender line,
     where a thumb reaches them. The bars keep 8px from the screen edge so the
     focus ring is not clipped. */
  .lightbox { padding: 0; }
  .lightbox-panel { column-gap: 0.25rem; }
  .lightbox-bar { padding: 0.5rem 0.5rem 0.5rem 1rem; }
  .lightbox-media { grid-column: 1 / -1; }
  .lightbox-nav { grid-row: 3; margin: 0.5rem; }
  .lightbox-nav span { display: none; }
  .lightbox-meta { padding: 0.5rem 0; }
  .modal-actions { align-items: stretch; flex-direction: column; }
  .export-page, .error-page { padding: 1rem; }
  .export-card, .error-card { padding: 3.5rem 1.5rem 2.5rem; }
  .export-card h1, .error-card h1 { font-size: clamp(3rem, 15vw, 4.7rem); }
  .export-summary div { border-left: 0; border-top: 1px solid var(--line); }
  .export-summary div:first-child { border-top: 0; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { scroll-behavior: auto !important; transition-duration: 0.01ms !important; }
  .hero-cycle-word.is-glitching .hero-cycle-current,
  .hero-cycle-word.is-glitching .hero-cycle-current::before,
  .hero-cycle-word.is-glitching .hero-cycle-current::after { animation: none !important; }
  .hero-cycle-toggle { display: none; }
  .lightbox-enter-next, .lightbox-enter-previous { animation: none; }
}
