/* Generado por el build desde Styles/tokens.json y Styles/guest.css. No lo edites: cambia esos archivos. */
:root {
    --color-plum-900: #3B1B34;
    --color-plum: #5A2A4F;
    --color-plum-100: #EFE2EB;
    --color-magenta: #E0147F;
    --color-magenta-700: #B00F63;
    --color-surface-base: #FBF8F5;
    --color-surface-raised: #FFFFFF;
    --color-ink: #241C22;
    --color-ink-muted: #6B6068;
    --color-line: #E6DCD9;
    --color-border-control: #8F7A86;
    --color-veil-clear: #00000000;
    --color-veil-light-06: #FFFFFF0F;
    --color-veil-light-07: #FFFFFF12;
    --color-veil-light-50: #FFFFFF80;
    --color-veil-shade-06: #0000000F;
    --color-veil-shade-18: #0000002E;
    --color-veil-shade-20: #00000033;
    --color-gold: #E8C87A;
    --color-forest-900: #1C3A2B;
    --color-forest: #2E5740;
    --color-forest-accent: #A8C97A;
    --color-champagne: #F5EFE0;
    --color-champagne-ink: #2C2418;
    --color-champagne-gold: #7A5C28;
    --color-coral-800: #8B3A2E;
    --color-coral-accent: #F2B89A;
    --color-slasher-bg: #0B0708;
    --color-slasher-ink: #F1E6E0;
    --color-slasher-accent: #FF4B5C;
    --color-slasher-blood: #B0001A;
    --color-slasher-blood-deep: #7D0012;
    --color-slasher-blood-wet-top: #D1061F;
    --color-slasher-blood-wet-mid: #8F0013;
    --color-slasher-blood-wet-bottom: #4D0009;
    --color-slasher-cover-top: #4A0510;
    --color-slasher-cover-mid: #150709;
    --color-slasher-cover-bottom: #080405;
    --color-slasher-unit-surface: #1A0A0D;
    --color-slasher-unit-border: #6B0F1B;
    --color-slasher-divider: #3A0F15;
    --color-slasher-specular: #FFE3E3;
    --color-slasher-rsvp-bg: #B0001A;
    --color-slasher-rsvp-ink: #FFFFFF;
    --color-slasher-rsvp-loading: #970117;
    --color-infection-bg: #050A06;
    --color-infection-ink: #D9F5C8;
    --color-infection-toxic: #9DFF2E;
    --color-infection-toxic-deep: #5DB800;
    --color-infection-hazard: #F2D200;
    --color-infection-hazard-stripe: #111111;
    --color-infection-cover-glow: #17400F;
    --color-infection-unit-surface: #0E1F0B;
    --color-infection-vein-top: #B6FF4A;
    --color-infection-vein-bottom: #4F8A14;
    --color-infection-glitch-a: #FF2E63;
    --color-infection-glitch-b: #00E5FF;
    --color-infection-wound-core: #120002;
    --color-infection-wound-mid: #5A0008;
    --color-infection-wound-rim: #A30F1E;
    --color-infection-input-border: #9DFF2E88;
    --color-infection-scanline: #00000066;
    --color-infection-rsvp-bg: #9DFF2E;
    --color-infection-rsvp-ink: #050A06;
    --color-infection-rsvp-loading: #86DA28;
    --color-orchard-bg: #1A0F24;
    --color-orchard-ink: #F3E4D8;
    --color-orchard-title: #FFF1DE;
    --color-orchard-sky-top: #1B0C2C;
    --color-orchard-sky-mid: #5A2255;
    --color-orchard-sky-low: #C4572F;
    --color-orchard-sky-horizon: #F0A04A;
    --color-orchard-hill: #160B12;
    --color-orchard-accent: #FF9A4A;
    --color-orchard-kicker: #FFD9A8;
    --color-orchard-border: #C4572F;
    --color-orchard-firefly: #EAFF9A;
    --color-orchard-rsvp-bg: #F2791A;
    --color-orchard-rsvp-ink: #1A0F24;
    --color-orchard-rsvp-loading: #D2691C;
    --color-orchard-pumpkin-light: #FFBC62;
    --color-orchard-pumpkin-dusk: #7A2A08;
    --color-orchard-flame-core: #FFF6B0;
    --color-orchard-flame-edge: #FF5A00;
    --color-witch-bg: #0F0A1C;
    --color-witch-ink: #E4DCF5;
    --color-witch-title: #F5EEFF;
    --color-witch-sky-top: #0B0720;
    --color-witch-sky-mid: #1D1244;
    --color-witch-sky-low: #3A2268;
    --color-witch-accent: #B79CFF;
    --color-witch-kicker: #C3A9FF;
    --color-witch-border: #6F55B8;
    --color-witch-glow: #A98BFF;
    --color-witch-fog: #C9C0E0;
    --color-witch-rsvp-bg: #8F6BFF;
    --color-witch-rsvp-ink: #0F0A1C;
    --color-witch-rsvp-loading: #8563ED;
    --color-on-accent: #FFFFFF;
    --color-focus-ring: #E0147F;
    --color-success: #1E6B4E;
    --color-success-surface: #E3F3EC;
    --color-warning: #8A5300;
    --color-warning-surface: #FDF1DC;
    --color-danger: #A3122B;
    --color-danger-surface: #FCE8EC;
    --color-disabled-surface: #D9CCD4;
    --color-disabled-text: #574A52;
    --color-field-surface: #FFFFFF;
    --color-limit-surface: #F7F3F5;
    --color-limit-text: #8F7A86;
    --font-family-display: 'Fraunces', Georgia, serif;
    --font-family-guest-body: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --font-family-slasher-title: Georgia, serif;
    --font-family-infection-title: Impact, 'Arial Narrow Bold', sans-serif;
    --font-family-infection-mono: 'Courier New', monospace;
    --font-family-infection-button: Impact, sans-serif;
    --font-family-orchard-title: Georgia, serif;
    --font-family-witch-title: Georgia, serif;
    --font-size-invite-title: 38px;
    --font-size-invite-kicker: 11px;
    --font-size-invite-date: 13px;
    --font-size-body: 15px;
    --font-size-body-input: 16px;
    --font-size-label: 13px;
    --font-size-helper: 12px;
    --font-size-legal: 11px;
    --font-size-eyebrow: 11px;
    --font-size-stepper-value: 20px;
    --font-size-slasher-title: 44px;
    --font-size-infection-title: 50px;
    --font-size-orchard-title: 44px;
    --font-size-witch-title: 42px;
    --font-size-countdown: 22px;
    --font-weight-invite-title: 400;
    --font-weight-invite-kicker: 400;
    --font-weight-label: 600;
    --font-weight-eyebrow: 700;
    --font-weight-button: 600;
    --font-weight-stepper-value: 700;
    --font-weight-countdown: 700;
    --line-height-invite-title: 1.08;
    --line-height-body: 1.55;
    --line-height-helper: 1.45;
    --line-height-countdown: 1.1;
    --letter-spacing-invite-title: 0;
    --letter-spacing-invite-kicker: 0.22em;
    --letter-spacing-invite-date: 0.06em;
    --letter-spacing-eyebrow: 0.08em;
    --letter-spacing-infection-button: 0.06em;
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --space-8: 32px;
    --space-10: 40px;
    --space-12: 48px;
    --space-gutter: 16px;
    --space-card-padding: 24px;
    --space-touch-min: 44px;
    --space-control-height: 48px;
    --space-focus-ring-offset: 2px;
    --border-width-control: 1px;
    --border-width-focus-ring: 3px;
    --border-width-emphasis: 2px;
    --duration-slasher-drip: 7s;
    --duration-infection-glitch: 4s;
    --duration-orchard-tw-1: 3s;
    --duration-orchard-tw-2: 4.2s;
    --duration-orchard-tw-3: 5s;
    --duration-orchard-fly: 16s;
    --duration-orchard-flap: 0.32s;
    --duration-orchard-firefly: 6s;
    --duration-orchard-flick: 1.3s;
    --duration-orchard-sway: 6s;
    --duration-witch-tw-1: 3s;
    --duration-witch-tw-2: 4.2s;
    --duration-witch-tw-3: 5s;
    --duration-witch-fly: 14s;
    --duration-witch-flap: 0.32s;
    --duration-witch-drift: 10s;
    --shadow-slasher-title: 0 3px 0 var(--color-slasher-blood), 0 0 18px var(--color-slasher-blood-wet-top), 0 4px 14px var(--color-slasher-bg);
    --shadow-slasher-text: 0 1px 3px var(--color-slasher-bg), 0 0 10px var(--color-slasher-bg);
    --shadow-slasher-rsvp: 0 0 18px color-mix(in srgb, var(--color-slasher-accent) 40%, transparent);
    --shadow-infection-title: 2px 0 var(--color-infection-glitch-a), -2px 0 var(--color-infection-glitch-b), 0 0 24px color-mix(in srgb, var(--color-infection-toxic) 53%, transparent);
    --shadow-infection-glitch-1: -5px 0 var(--color-infection-glitch-a), 5px 0 var(--color-infection-glitch-b), 0 0 24px color-mix(in srgb, var(--color-infection-toxic) 53%, transparent);
    --shadow-infection-glitch-2: 6px 1px var(--color-infection-glitch-a), -4px -1px var(--color-infection-glitch-b), 0 0 24px color-mix(in srgb, var(--color-infection-toxic) 53%, transparent);
    --shadow-infection-text: 0 1px 3px var(--color-infection-bg), 0 0 10px var(--color-infection-bg);
    --shadow-infection-rsvp: 0 0 22px color-mix(in srgb, var(--color-infection-toxic) 40%, transparent);
    --shadow-orchard-title: 0 2px 12px var(--color-orchard-hill), 0 0 24px var(--color-orchard-sky-mid);
    --shadow-orchard-text: 0 1px 3px var(--color-orchard-bg), 0 0 10px var(--color-orchard-bg);
    --shadow-orchard-rsvp: 0 0 20px color-mix(in srgb, var(--color-orchard-rsvp-bg) 40%, transparent);
    --shadow-witch-title: 0 2px 12px var(--color-witch-bg), 0 0 24px var(--color-witch-glow);
    --shadow-witch-text: 0 1px 3px var(--color-witch-bg), 0 0 10px var(--color-witch-bg);
    --shadow-witch-rsvp: 0 0 20px color-mix(in srgb, var(--color-witch-rsvp-bg) 40%, transparent);
    --size-infection-scanline-line: 3px;
    --size-infection-scanline-pitch: 4px;
    --size-orchard-fly-start: -140px;
    --size-orchard-fly-end: 420px;
    --size-orchard-crow-rest: 120px;
    --size-orchard-firefly-drift-x: 16px;
    --size-orchard-firefly-drift-y: -12px;
    --size-witch-fly-start: -140px;
    --size-witch-fly-end: 420px;
    --size-witch-rest: 160px;
    --size-witch-fog-drift: 28px;
    --size-brand-logo: 4.5em;
    --radius-sm: 8px;
    --radius-md: 10px;
    --radius-lg: 12px;
    --radius-xl: 16px;
    --radius-full: 9999px;
    --layout-guest-max-width: 480px;
}

/* Reglas base de la raíz del invitado. Propias: no comparten nada con las del panel.
   Solo usan var(--…): los valores viven en tokens.json y el build los antepone al generar
   wwwroot/css/guest.css. Sin modo oscuro, con la fuente del sistema (0 KB) para el texto y
   Fraunces solo para títulos. Ni Inter ni Varela Round se descargan aquí: el logo va en SVG. */

/* Fraunces variable, solo el eje de peso y el subconjunto latino (36.6 KB, SIL OFL 1.1). */
@font-face {
    font-family: "Fraunces";
    src: url("../fonts/fraunces-latin-wght-normal.woff2") format("woff2");
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

html {
    box-sizing: border-box;
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

*,
*::before,
*::after {
    box-sizing: inherit;
}

/* Sin relleno propio: la portada de la invitación va de orilla a orilla en el teléfono. Una palabra
   larga (un título sin espacios) se parte antes que empujar la página de lado. */
body {
    margin: 0;
    background: var(--color-surface-base);
    color: var(--color-ink);
    font-family: var(--font-family-guest-body);
    font-size: var(--font-size-body);
    line-height: var(--line-height-body);
    overflow-wrap: anywhere;
}

/* Una sola columna, incluso en pantallas grandes. */
main {
    max-width: var(--layout-guest-max-width);
    margin: 0 auto;
    padding: var(--space-8) var(--space-gutter);
}

h1 {
    margin: 0 0 var(--space-4);
    font-family: var(--font-family-display);
    font-size: var(--font-size-invite-title);
    font-weight: var(--font-weight-invite-title);
    line-height: var(--line-height-invite-title);
    letter-spacing: var(--letter-spacing-invite-title);
}

p {
    margin: 0 0 var(--space-4);
}

/* Controles: altura mínima, nunca fija, para que el texto escalado del sistema no se recorte;
   16 px de letra para que iOS no haga zoom al enfocar. */
button,
input:not([type="checkbox"]):not([type="radio"]),
select,
textarea {
    min-height: var(--space-control-height);
    font: inherit;
    font-size: var(--font-size-body-input);
}

button {
    min-width: var(--space-touch-min);
}

/* Lo que solo oyen los lectores de pantalla. Recortado a un punto, nunca display: none, que lo
   borraría también del árbol de accesibilidad. Duplicado a propósito de la hoja del panel. */
.visually-hidden {
    position: absolute;
    width: var(--border-width-control);
    max-height: var(--border-width-control);
    margin: 0;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* --- Plantilla posada-clasica: portada ciruela profunda con letras doradas --- */

.invitation {
    padding: 0;
}

.template-posada-clasica .invitation-cover {
    padding-bottom: var(--space-8);
    background:
        linear-gradient(160deg, var(--color-veil-light-07) 0%, var(--color-veil-clear) 45%, var(--color-veil-shade-18) 100%),
        var(--color-plum-900);
    color: var(--color-surface-base);
    text-align: center;
}

/* La foto va a lo ancho de la columna; su alto sale de su proporción. */
.invitation-photo {
    display: block;
    width: 100%;
    max-width: 100%;
}

.invitation-cover-text {
    padding: var(--space-10) var(--space-6) 0;
}

.template-posada-clasica .invitation-kicker {
    margin: 0 0 var(--space-3);
    color: var(--color-gold);
    font-size: var(--font-size-invite-kicker);
    font-weight: var(--font-weight-invite-kicker);
    letter-spacing: var(--letter-spacing-invite-kicker);
    text-transform: uppercase;
}

.invitation-title {
    margin: 0;
}

.template-posada-clasica .invitation-date {
    margin: var(--space-3) 0 0;
    color: var(--color-gold);
    font-size: var(--font-size-invite-date);
    letter-spacing: var(--letter-spacing-invite-date);
    text-transform: uppercase;
}

/* La cuenta regresiva: tres cajas (días, horas, minutos) calculadas en el servidor; no corre en
   vivo. La etiqueta va a 11 px, nunca menos. */
.countdown {
    margin: var(--space-5) 0 0;
}

.countdown-units {
    display: flex;
    justify-content: center;
    gap: var(--space-2);
}

.countdown-unit {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: var(--space-12);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
    background: var(--color-plum);
}

.countdown-number {
    color: var(--color-surface-base);
    font-size: var(--font-size-countdown);
    font-weight: var(--font-weight-countdown);
    line-height: var(--line-height-countdown);
}

.template-posada-clasica .countdown-label {
    margin-top: var(--space-1);
    color: var(--color-gold);
    font-size: var(--font-size-eyebrow);
    font-weight: var(--font-weight-eyebrow);
    letter-spacing: var(--letter-spacing-eyebrow);
    text-transform: uppercase;
}

.template-posada-clasica .countdown-status {
    margin: var(--space-5) 0 0;
    color: var(--color-gold);
    font-weight: var(--font-weight-label);
}

.invitation-body {
    padding: var(--space-6) var(--space-gutter) 0;
}

/* El mensaje respeta los saltos de línea que escribió el anfitrión, sin interpretar nada. */
.invitation-message {
    white-space: pre-line;
}

.invitation-section {
    margin: var(--space-6) 0 0;
    padding-top: var(--space-4);
    border-top: var(--border-width-control) solid var(--color-line);
}

.invitation-heading {
    margin: 0 0 var(--space-2);
    color: var(--color-plum);
    font-size: var(--font-size-eyebrow);
    font-weight: var(--font-weight-eyebrow);
    letter-spacing: var(--letter-spacing-eyebrow);
    text-transform: uppercase;
}

.invitation-section p {
    margin: 0;
}

/* El botón del mapa: un enlace real, con borde de 2 px en ciruela (el magenta es solo relleno y
   aquí no hay botón primario). Tocable de 48 px. */
.invitation-map {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    min-height: var(--space-control-height);
    margin: var(--space-3) 0 0;
    padding: var(--space-3) var(--space-4);
    border: var(--border-width-emphasis) solid var(--color-plum);
    border-radius: var(--radius-lg);
    color: var(--color-plum);
    font-weight: var(--font-weight-label);
    text-decoration: none;
}

.itinerary {
    margin: 0;
    padding: 0;
    list-style: none;
}

.itinerary-item {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-2) 0;
    border-top: var(--border-width-control) solid var(--color-line);
}

.itinerary-item:first-child {
    border-top: 0;
}

.itinerary-time {
    flex: none;
    color: var(--color-plum);
    font-size: var(--font-size-label);
    font-weight: var(--font-weight-label);
    white-space: nowrap;
}

/* --- Plantilla bosque-verde: portada verde oscura con acento lima --- */

.template-bosque-verde .invitation-cover {
    padding-bottom: var(--space-8);
    background:
        linear-gradient(160deg, var(--color-veil-light-06) 0%, var(--color-veil-clear) 45%, var(--color-veil-shade-20) 100%),
        var(--color-forest-900);
    color: var(--color-surface-base);
    text-align: center;
}

.template-bosque-verde .invitation-kicker {
    margin: 0 0 var(--space-3);
    color: var(--color-forest-accent);
    font-size: var(--font-size-invite-kicker);
    font-weight: var(--font-weight-invite-kicker);
    letter-spacing: var(--letter-spacing-invite-kicker);
    text-transform: uppercase;
}

.template-bosque-verde .invitation-date {
    margin: var(--space-3) 0 0;
    color: var(--color-forest-accent);
    font-size: var(--font-size-invite-date);
    letter-spacing: var(--letter-spacing-invite-date);
    text-transform: uppercase;
}

.template-bosque-verde .countdown-unit {
    background: var(--color-forest);
}

.template-bosque-verde .countdown-label {
    margin-top: var(--space-1);
    color: var(--color-forest-accent);
    font-size: var(--font-size-eyebrow);
    font-weight: var(--font-weight-eyebrow);
    letter-spacing: var(--letter-spacing-eyebrow);
    text-transform: uppercase;
}

.template-bosque-verde .countdown-status {
    margin: var(--space-5) 0 0;
    color: var(--color-forest-accent);
    font-weight: var(--font-weight-label);
}

.template-bosque-verde .invitation-heading {
    color: var(--color-forest);
}

.template-bosque-verde .invitation-map {
    border-color: var(--color-forest);
    color: var(--color-forest);
}

.template-bosque-verde .itinerary-time {
    color: var(--color-forest);
}

/* --- Plantilla champan-claro: portada crema cálida con acento dorado --- */

.template-champan-claro .invitation-cover {
    padding-bottom: var(--space-8);
    background:
        linear-gradient(160deg, var(--color-veil-light-50) 0%, var(--color-veil-clear) 50%, var(--color-veil-shade-06) 100%),
        var(--color-champagne);
    color: var(--color-champagne-ink);
    text-align: center;
}

.template-champan-claro .invitation-kicker {
    margin: 0 0 var(--space-3);
    color: var(--color-champagne-gold);
    font-size: var(--font-size-invite-kicker);
    font-weight: var(--font-weight-invite-kicker);
    letter-spacing: var(--letter-spacing-invite-kicker);
    text-transform: uppercase;
}

.template-champan-claro .invitation-date {
    margin: var(--space-3) 0 0;
    color: var(--color-champagne-gold);
    font-size: var(--font-size-invite-date);
    letter-spacing: var(--letter-spacing-invite-date);
    text-transform: uppercase;
}

/* La caja de la cuenta regresiva invierte: fondo oscuro sobre portada clara. */
.template-champan-claro .countdown-unit {
    background: var(--color-champagne-ink);
}

.template-champan-claro .countdown-label {
    margin-top: var(--space-1);
    color: var(--color-champagne-gold);
    font-size: var(--font-size-eyebrow);
    font-weight: var(--font-weight-eyebrow);
    letter-spacing: var(--letter-spacing-eyebrow);
    text-transform: uppercase;
}

.template-champan-claro .countdown-status {
    margin: var(--space-5) 0 0;
    color: var(--color-champagne-gold);
    font-weight: var(--font-weight-label);
}

.template-champan-claro .invitation-heading {
    color: var(--color-champagne-gold);
}

.template-champan-claro .invitation-map {
    border-color: var(--color-champagne-gold);
    color: var(--color-champagne-gold);
}

.template-champan-claro .itinerary-time {
    color: var(--color-champagne-gold);
}

/* --- Plantilla festiva-coral: portada coral profunda con acento melocotón --- */

.template-festiva-coral .invitation-cover {
    padding-bottom: var(--space-8);
    background:
        linear-gradient(160deg, var(--color-veil-light-07) 0%, var(--color-veil-clear) 45%, var(--color-veil-shade-18) 100%),
        var(--color-coral-800);
    color: var(--color-surface-base);
    text-align: center;
}

.template-festiva-coral .invitation-kicker {
    margin: 0 0 var(--space-3);
    color: var(--color-coral-accent);
    font-size: var(--font-size-invite-kicker);
    font-weight: var(--font-weight-invite-kicker);
    letter-spacing: var(--letter-spacing-invite-kicker);
    text-transform: uppercase;
}

.template-festiva-coral .invitation-date {
    margin: var(--space-3) 0 0;
    color: var(--color-coral-accent);
    font-size: var(--font-size-invite-date);
    letter-spacing: var(--letter-spacing-invite-date);
    text-transform: uppercase;
}

.template-festiva-coral .countdown-unit {
    background: var(--color-coral-800);
    border: var(--border-width-control) solid var(--color-coral-accent);
}

.template-festiva-coral .countdown-label {
    margin-top: var(--space-1);
    color: var(--color-coral-accent);
    font-size: var(--font-size-eyebrow);
    font-weight: var(--font-weight-eyebrow);
    letter-spacing: var(--letter-spacing-eyebrow);
    text-transform: uppercase;
}

.template-festiva-coral .countdown-status {
    margin: var(--space-5) 0 0;
    color: var(--color-coral-accent);
    font-weight: var(--font-weight-label);
}

.template-festiva-coral .invitation-heading {
    color: var(--color-coral-800);
}

.template-festiva-coral .invitation-map {
    border-color: var(--color-coral-800);
    color: var(--color-coral-800);
}

.template-festiva-coral .itinerary-time {
    color: var(--color-coral-800);
}

/* --- Privacidad (Historia 2.6) --- */

/* El bloque de privacidad de la página del invitado, después del contenido y antes de la marca: en
   el cuerpo del texto, en ink y a 15 px, nunca en gris claro ni más chico. El enlace va subrayado,
   como se reconoce un enlace, y mide al menos 44 px de alto para tocarlo. Aquí la 3.2 pondrá el
   acceso a confirmar. */
.guest-privacy {
    max-width: var(--layout-guest-max-width);
    margin: var(--space-8) auto 0;
    padding: var(--space-4) var(--space-gutter) 0;
    border-top: var(--border-width-control) solid var(--color-line);
    color: var(--color-ink);
    font-size: var(--font-size-body);
}

.guest-privacy p {
    margin: 0;
}

.guest-privacy-link,
.legal a {
    color: var(--color-ink);
    text-decoration: underline;
}

.guest-privacy-link {
    display: inline-flex;
    align-items: center;
    min-height: var(--space-touch-min);
}

/* Las páginas legales: una sola columna de texto corrido, legible a 375 px. */
.legal h1 {
    color: var(--color-plum);
}

/* El aviso de borrador va primero y no se confunde con el texto: superficie de advertencia, con
   su texto del mismo matiz. */
.legal-provisional {
    padding: var(--space-3) var(--space-4);
    border-left: var(--border-width-emphasis) solid var(--color-warning);
    border-radius: var(--radius-md);
    background: var(--color-warning-surface);
    color: var(--color-warning);
}

.legal-meta {
    margin: 0 0 var(--space-6);
    font-size: var(--font-size-label);
}

.legal-meta div {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
}

.legal-meta dt {
    font-weight: var(--font-weight-label);
}

.legal-meta dt::after {
    content: ":";
}

.legal-meta dd {
    margin: 0;
}

.legal-section {
    margin: var(--space-6) 0 0;
}

.legal-section h2 {
    margin: 0 0 var(--space-2);
    color: var(--color-plum);
    font-size: var(--font-size-body);
    font-weight: var(--font-weight-label);
}

.legal-section ul {
    margin: 0 0 var(--space-4);
    padding-left: var(--space-6);
}

.legal-section li {
    margin: 0 0 var(--space-2);
}

.legal-links {
    margin: var(--space-8) 0 0;
    padding-top: var(--space-4);
    border-top: var(--border-width-control) solid var(--color-line);
}

.legal-links ul {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

.legal-links a {
    display: inline-flex;
    align-items: center;
    min-height: var(--space-touch-min);
}

.legal-links a[aria-current="page"] {
    font-weight: var(--font-weight-label);
    text-decoration: none;
}

/* --- Acceso a confirmar (Historia 3.2) --- */

/* Después del aviso de privacidad, con el mismo ancho de columna. El título va en la fuente del
   sistema: Fraunces va una sola vez por pantalla, en el título de la invitación. */
.rsvp {
    max-width: var(--layout-guest-max-width);
    margin: var(--space-6) auto 0;
    padding: var(--space-4) var(--space-gutter) 0;
    border-top: var(--border-width-control) solid var(--color-line);
}

.rsvp-heading {
    margin: 0 0 var(--space-4);
    color: var(--color-plum);
    font-size: var(--font-size-countdown);
    font-weight: var(--font-weight-countdown);
    line-height: var(--line-height-countdown);
}

.rsvp-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.rsvp-form p,
.rsvp-acknowledgement p {
    margin: 0;
}

.rsvp-acknowledgement p {
    margin-bottom: var(--space-4);
}

/* Los radios van en su fieldset, sin el marco de fábrica. Cada opción se toca en 44 px. */
.rsvp-attendance {
    min-width: 0;
    margin: 0;
    padding: 0;
    border: 0;
}

legend.field-label {
    padding: 0;
}

.rsvp-choice {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.rsvp-choice input {
    flex: none;
    width: var(--space-5);
    min-height: var(--space-5);
    margin: 0;
    accent-color: var(--color-plum);
}

.rsvp-choice label {
    display: flex;
    flex: 1;
    align-items: center;
    min-height: var(--space-touch-min);
}

/* El grupo/grado y los contadores solo se piden si ya eligieron "Sí, ahí estaremos": antes de
   elegir, o si eligen "No podemos ir", se esconden sin script. Sin :has se ven siempre, y el
   servidor no los exige si no van (el grupo nunca es obligatorio). */
.rsvp-attending-only {
    display: none;
}

.rsvp-form:has(#rsvp-attending-yes:checked) .rsvp-attending-only {
    display: block;
}

.rsvp-counts {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    margin-top: var(--space-5);
}

/* input-text de la raíz del invitado; duplicado a propósito del del panel. */
.field-label {
    display: block;
    margin: 0 0 var(--space-1);
    color: var(--color-ink);
    font-size: var(--font-size-label);
    font-weight: var(--font-weight-label);
}

.input-text {
    display: block;
    width: 100%;
    padding: 0 var(--space-3);
    border: var(--border-width-control) solid var(--color-border-control);
    border-radius: var(--radius-md);
    background: var(--color-field-surface);
    color: var(--color-ink);
}

/* En error, el borde sube a 2 px en danger; el padding baja lo mismo para que el texto no brinque. */
.input-text[aria-invalid="true"] {
    padding: 0 calc(var(--space-3) - (var(--border-width-emphasis) - var(--border-width-control)));
    border: var(--border-width-emphasis) solid var(--color-danger);
}

.rsvp-form .field-hint,
.rsvp-form .field-error {
    margin: var(--space-1) 0 0;
    font-size: var(--font-size-helper);
    line-height: var(--line-height-helper);
}

.field-hint {
    color: var(--color-ink-muted);
}

.field-error {
    color: var(--color-danger);
}

/* El contador: botones de 48×48 que muestra el script, y la cifra en un campo numérico que
   funciona solo sin él. */
.counter-controls {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.counter-input {
    max-width: calc(var(--space-12) * 2);
    text-align: center;
}

.counter-button {
    min-width: var(--space-control-height);
    min-height: var(--space-control-height);
    padding: 0;
    border: var(--border-width-control) solid var(--color-border-control);
    border-radius: var(--radius-md);
    background: var(--color-field-surface);
    color: var(--color-ink);
    font-size: var(--font-size-stepper-value);
    font-weight: var(--font-weight-stepper-value);
    cursor: pointer;
}

.counter-button[hidden] {
    display: none;
}

/* En el tope: marcado, no deshabilitado. Sigue enfocable. */
.counter-button[aria-disabled="true"] {
    border-color: var(--color-line);
    background: var(--color-limit-surface);
    color: var(--color-limit-text);
    cursor: not-allowed;
}

/* button-primary: ancho completo, magenta con texto blanco; el único de la pantalla. */
.button-primary {
    display: block;
    width: 100%;
    padding: 0 var(--space-4);
    border: 0;
    border-radius: var(--radius-lg);
    background: var(--color-magenta);
    color: var(--color-on-accent);
    font-weight: var(--font-weight-button);
    cursor: pointer;
}

/* Enviando: magenta-700 con el texto blanco declarado aparte, nunca el gris del apagado. */
.button-primary[aria-busy="true"] {
    background: var(--color-magenta-700);
    color: var(--color-on-accent);
    cursor: progress;
}

/* Apagado: solo en la vista previa, donde no se guardan respuestas. */
.button-primary:disabled {
    background: var(--color-disabled-surface);
    color: var(--color-disabled-text);
    cursor: not-allowed;
}

.rsvp-purpose {
    color: var(--color-ink);
    font-size: var(--font-size-body);
}

/* No se pudo recibir: superficie de advertencia, con su texto del mismo matiz. */
.rsvp-retry {
    padding: var(--space-3) var(--space-4);
    border-left: var(--border-width-emphasis) solid var(--color-warning);
    border-radius: var(--radius-md);
    background: var(--color-warning-surface);
    color: var(--color-warning);
}

.rsvp-summary {
    margin: 0;
    padding: var(--space-3) var(--space-4);
    border-left: var(--border-width-emphasis) solid var(--color-success);
    border-radius: var(--radius-md);
    background: var(--color-success-surface);
}

.rsvp-summary div {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
}

.rsvp-summary dt {
    font-weight: var(--font-weight-label);
}

.rsvp-summary dt::after {
    content: ":";
}

.rsvp-summary dd {
    margin: 0;
}

/* --- Cambiar la respuesta (Historia 3.3) --- */

/* El enlace de edición en el acuse: completo y visible, subrayado como enlace, y partido donde haga
   falta para que a 375 px ni al 200 % haya scroll horizontal. Sin botón de compartir. */
.rsvp-edit {
    margin-top: var(--space-4);
    padding: var(--space-3) var(--space-4);
    border: var(--border-width-control) solid var(--color-line);
    border-radius: var(--radius-md);
}

.rsvp-acknowledgement .rsvp-edit p {
    margin: 0;
}

.rsvp-edit-link {
    display: inline-flex;
    align-items: center;
    min-height: var(--space-touch-min);
    color: var(--color-ink);
    text-decoration: underline;
    overflow-wrap: anywhere;
}

.rsvp-saved {
    margin: var(--space-4) 0 var(--space-2);
}

/* "Ya respondiste": cambiarla o responder como otra persona, dos enlaces de 44 px de alto. */
.rsvp-actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: var(--space-4) 0 0;
    padding: 0;
    list-style: none;
}

.rsvp-action {
    display: inline-flex;
    align-items: center;
    min-height: var(--space-touch-min);
    color: var(--color-plum);
    font-weight: var(--font-weight-label);
    text-decoration: underline;
}

/* brand-badge: "Hecha con envait", centrada al pie, a 11 px en ink-muted, con enlace a envait. La
   marca es el logo SVG en ciruela (currentColor); su alto sale de la proporción del viewBox, sin
   fijar altura. El enlace mide al menos 44 px de alto para tocarlo. */
.brand-badge {
    max-width: var(--layout-guest-max-width);
    margin: var(--space-8) auto 0;
    padding: 0 var(--space-gutter) var(--space-8);
    text-align: center;
}

.brand-badge-text {
    margin: 0;
    color: var(--color-ink-muted);
    font-size: var(--font-size-legal);
}

.brand-badge-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: var(--space-touch-min);
    color: inherit;
    text-decoration: none;
}

.brand-badge-logo {
    display: inline-block;
    width: var(--size-brand-logo);
    color: var(--color-plum);
    fill: currentColor;
    vertical-align: baseline;
}

/* La única regla de foco de la hoja: todo lo enfocable, no solo los campos. */
:focus-visible {
    outline: var(--border-width-focus-ring) solid var(--color-focus-ring);
    outline-offset: var(--space-focus-ring-offset);
}

/* --- Base de las plantillas de temporada (HW-1.1) ---
   Las cuatro de Halloween son oscuras a propósito: rellenan el fondo de toda la página y no tienen
   modo oscuro. Aquí solo van tokens genéricos; la paleta, el arte y la animación propios de cada
   una los agregan HW-1.2 a HW-1.5. La columna (375 a 480 px, centrada, con gutter) es la de
   siempre: <main> ya la fija. */

body:has(.seasonal-template) {
    background: var(--color-plum-900);
    color: var(--color-surface-base);
}

/* El texto que vive fuera de <main> (aviso de privacidad, confirmación y marca) hereda un fondo
   oscuro: se pinta con la tinta clara y no con la del fondo crema. */
body:has(.seasonal-template) .guest-privacy,
body:has(.seasonal-template) .guest-privacy-link,
body:has(.seasonal-template) .rsvp-purpose,
body:has(.seasonal-template) .rsvp-edit-link,
body:has(.seasonal-template) .field-label,
body:has(.seasonal-template) .field-hint {
    color: var(--color-surface-base);
}

body:has(.seasonal-template) .rsvp-heading,
body:has(.seasonal-template) .rsvp-action,
body:has(.seasonal-template) .brand-badge-logo {
    color: var(--color-gold);
}

body:has(.seasonal-template) .brand-badge-text {
    color: var(--color-surface-base);
}

.seasonal-template .invitation-cover {
    position: relative;
    overflow: hidden;
    padding-bottom: var(--space-8);
    background:
        linear-gradient(160deg, var(--color-veil-light-07) 0%, var(--color-veil-clear) 45%, var(--color-veil-shade-18) 100%),
        var(--color-plum-900);
    color: var(--color-surface-base);
    text-align: center;
}

/* La capa de arte va detrás del contenido: absoluta, sin reacomodarse y sin recibir toques ni
   foco. El SVG lleva preserveAspectRatio="slice" (llena todo el marco, sin barras nunca; recorta
   el borde del arte según la proporción de la portada — xMidYMid en Slasher/Infección, xMidYMax
   en Huerto/Brujas para anclar al suelo). Revertido 2026-10-09: un intento con "meet" dejaba
   barras visibles del fondo de la portada a los lados en producción.

   Corregido 2026-10-09: un <svg> es un elemento reemplazado; con solo width:100% e inset:0 (sin
   height, que esta hoja no usa nunca), el navegador calculaba su alto a partir de la razón del
   viewBox en vez de llenar el contenedor, así que el arte se desbordaba hacia abajo y
   .invitation-cover (overflow:hidden) le cortaba la base — justo donde viven las calabazas o la
   bruja. display:flex en .seasonal-art con align-items:stretch fuerza a su único hijo (el SVG) al
   alto exacto del contenedor sin escribir height en ningún lado; preserveAspectRatio ya hace el
   resto dentro de esa caja correcta.

   Pensado para un solo hijo: si algún día se agrega una segunda capa (p. ej. un <div> de brillo)
   habría que ponerle position:absolute, como ya tiene .seasonal-art::after (scanlines de Infección,
   más abajo) — si no, caería al layout de fila de flex en vez de superponerse. */
.seasonal-art {
    position: absolute;
    inset: 0;
    z-index: 0;
    display: flex;
    align-items: stretch;
    pointer-events: none;
}

.seasonal-art svg {
    display: block;
    width: 100%;
    pointer-events: none;
}

.seasonal-template .invitation-photo,
.seasonal-template .invitation-cover-text {
    position: relative;
    z-index: 1;
}

.seasonal-template .invitation-kicker {
    margin: 0 0 var(--space-3);
    color: var(--color-gold);
    font-size: var(--font-size-invite-kicker);
    font-weight: var(--font-weight-invite-kicker);
    letter-spacing: var(--letter-spacing-invite-kicker);
    text-transform: uppercase;
}

.seasonal-template .invitation-date {
    margin: var(--space-3) 0 0;
    color: var(--color-gold);
    font-size: var(--font-size-invite-date);
    letter-spacing: var(--letter-spacing-invite-date);
    text-transform: uppercase;
}

.seasonal-template .countdown-unit {
    border: var(--border-width-emphasis) solid var(--color-gold);
    border-radius: var(--radius-sm);
    background: var(--color-plum-900);
}

.seasonal-template .countdown-label {
    margin-top: var(--space-1);
    color: var(--color-gold);
    font-size: var(--font-size-eyebrow);
    font-weight: var(--font-weight-eyebrow);
    letter-spacing: var(--letter-spacing-eyebrow);
    text-transform: uppercase;
}

.seasonal-template .countdown-status {
    margin: var(--space-5) 0 0;
    color: var(--color-gold);
    font-weight: var(--font-weight-label);
}

.seasonal-template .invitation-heading {
    color: var(--color-gold);
}

.seasonal-template .invitation-map {
    border-color: var(--color-gold);
    border-radius: var(--radius-sm);
    color: var(--color-gold);
}

.seasonal-template .itinerary-time {
    color: var(--color-gold);
}

/* El botón de pausar/reanudar animación: lo agrega guest.js, arriba a la derecha de la portada y
   sobre el arte. Solo ícono (decisión 2026-10-09: ya no es texto visible); 44 px cuadrado, el
   mínimo táctil, para tapar lo menos posible del arte. El nombre accesible va en aria-label, el
   estado en aria-pressed y el tooltip de mouse/trackpad en title. */
.pause-animation-button {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--space-touch-min);
    min-height: var(--space-touch-min);
    padding: 0;
    border: var(--border-width-emphasis) solid var(--color-gold);
    border-radius: var(--radius-sm);
    background: var(--color-plum-900);
    color: var(--color-surface-base);
}

.pause-animation-button svg {
    width: var(--space-5);
    min-height: var(--space-5);
}

/* El cuadro estático compuesto: con la pausa activada (guest.js pone is-still en <main>) o con
   movimiento reducido, se apaga toda animación del arte y del título, y cada plantilla deja su
   reposo (gotas completas, personajes dentro del cuadro) en su propia hoja. No se congela a media
   animación: animation: none vuelve cada elemento a su estado base. */
.seasonal-template.is-still .seasonal-art *,
.seasonal-template.is-still .seasonal-art *::before,
.seasonal-template.is-still .seasonal-art *::after,
.seasonal-template.is-still .invitation-title {
    animation: none;
    transition: none;
}

@media (prefers-reduced-motion: reduce) {
    .seasonal-template .seasonal-art *,
    .seasonal-template .seasonal-art *::before,
    .seasonal-template .seasonal-art *::after,
    .seasonal-template .invitation-title {
        animation: none;
        transition: none;
    }

    .pause-animation-button {
        display: none;
    }
}

/* --- Plantilla halloween-slasher (HW-1.2) ---
   Casi negro cálido con rojo sangre. Sustituye los tokens genéricos de la base de temporada, también
   fuera de <main> (privacidad, RSVP y marca). Sin modo oscuro: es oscura a propósito. */

body:has(.template-halloween-slasher) {
    background: var(--color-slasher-bg);
    color: var(--color-slasher-ink);
}

body:has(.template-halloween-slasher) .guest-privacy,
body:has(.template-halloween-slasher) .guest-privacy-link,
body:has(.template-halloween-slasher) .rsvp-purpose,
body:has(.template-halloween-slasher) .rsvp-edit-link,
body:has(.template-halloween-slasher) .field-label,
body:has(.template-halloween-slasher) .field-hint,
body:has(.template-halloween-slasher) .brand-badge-text {
    color: var(--color-slasher-ink);
}

body:has(.template-halloween-slasher) .rsvp-heading,
body:has(.template-halloween-slasher) .rsvp-action,
body:has(.template-halloween-slasher) .brand-badge-logo {
    color: var(--color-slasher-accent);
}

body:has(.template-halloween-slasher) .rsvp {
    border-top-color: var(--color-slasher-divider);
}

/* El foco conserva la única regla global de :focus-visible; aquí solo cambia el color del anillo,
   que solo se dibuja cuando el foco es visible. */
body:has(.template-halloween-slasher) :focus {
    outline-color: var(--color-slasher-accent);
}

/* Campo: el borde va en el realce (6.12:1); slasher-unit-border nunca es borde de un control. */
body:has(.template-halloween-slasher) .field-error {
    color: var(--color-slasher-accent);
}

/* El resumen y el reintento no pueden quedarse con el fondo claro genérico: aquí texto claro
   sobre fondo claro es ilegible. Misma superficie que la cuenta regresiva. El resumen hereda la
   tinta del cuerpo (ya verificada contra esa superficie); el reintento fija la suya porque la
   regla base trae color propio y, si no, se queda con el marrón de warning. */
body:has(.template-halloween-slasher) .rsvp-summary,
body:has(.template-halloween-slasher) .rsvp-retry {
    background: var(--color-slasher-unit-surface);
    border-left-color: var(--color-slasher-accent);
}

body:has(.template-halloween-slasher) .rsvp-retry {
    color: var(--color-slasher-accent);
}

body:has(.template-halloween-slasher) .input-text {
    border-color: var(--color-slasher-accent);
    background: var(--color-slasher-unit-surface);
    color: var(--color-slasher-ink);
}

body:has(.template-halloween-slasher) .rsvp-choice input {
    accent-color: var(--color-slasher-accent);
}

/* Botón de RSVP: sangre sólida con tinta blanca y resplandor de acento. En Enviando, el fondo
   se atenúa y el texto blanco se declara aparte. */
body:has(.template-halloween-slasher) .button-primary {
    border-radius: var(--radius-md);
    background: var(--color-slasher-rsvp-bg);
    color: var(--color-slasher-rsvp-ink);
    font-weight: var(--font-weight-countdown);
    box-shadow: var(--shadow-slasher-rsvp);
}

body:has(.template-halloween-slasher) .button-primary[aria-busy="true"] {
    background: var(--color-slasher-rsvp-loading);
    color: var(--color-slasher-rsvp-ink);
}

.template-halloween-slasher .invitation-cover {
    background:
        radial-gradient(ellipse at 50% 20%, var(--color-slasher-cover-top) 0%, var(--color-slasher-cover-mid) 55%, var(--color-slasher-cover-bottom) 100%);
    color: var(--color-slasher-ink);
}

.template-halloween-slasher .invitation-title {
    font-family: var(--font-family-slasher-title);
    font-style: italic;
    font-weight: 900;
    font-size: var(--font-size-slasher-title);
    line-height: 1;
    overflow-wrap: anywhere;
    color: var(--color-slasher-ink);
    text-shadow: var(--shadow-slasher-title);
}

/* El texto de la portada pasa sobre las gotas: un halo del color del fondo lo despega del arte. */
.template-halloween-slasher .invitation-cover-text {
    text-shadow: var(--shadow-slasher-text);
}

.template-halloween-slasher .invitation-kicker,
.template-halloween-slasher .invitation-date,
.template-halloween-slasher .countdown-label,
.template-halloween-slasher .countdown-status,
.template-halloween-slasher .invitation-heading,
.template-halloween-slasher .itinerary-time {
    color: var(--color-slasher-accent);
}

.template-halloween-slasher .countdown-number {
    color: var(--color-slasher-ink);
}

/* La cuenta regresiva: el borde de sus unidades es solo decoración; no son controles. */
.template-halloween-slasher .countdown-unit {
    border-color: var(--color-slasher-unit-border);
    background: var(--color-slasher-unit-surface);
}

.template-halloween-slasher .invitation-section,
.template-halloween-slasher .itinerary-item {
    border-top-color: var(--color-slasher-divider);
}

.template-halloween-slasher .itinerary-item:first-child {
    border-top: 0;
}

.template-halloween-slasher .invitation-map {
    border-color: var(--color-slasher-accent);
    color: var(--color-slasher-accent);
}

.template-halloween-slasher .pause-animation-button {
    border-color: var(--color-slasher-accent);
    background: var(--color-slasher-unit-surface);
    color: var(--color-slasher-ink);
}

/* El arte: las clases pintan con los tokens; el SVG no lleva colores. */
.slasher-stop-top {
    stop-color: var(--color-slasher-blood-wet-top);
}

.slasher-stop-mid {
    stop-color: var(--color-slasher-blood-wet-mid);
}

.slasher-stop-bottom {
    stop-color: var(--color-slasher-blood-wet-bottom);
}

.slasher-specular {
    lighting-color: var(--color-slasher-specular);
}

.slasher-claws {
    fill: var(--color-slasher-blood-deep);
    opacity: 0.85;
}

/* Cada gota nace en su borde superior. En reposo (pausa o movimiento reducido) se queda a largo
   completo: scaleY(1) es el estado final de drip, y las reglas de reposo de la base apagan la
   animación sin dejar la gota a medias. */
.slasher-drip {
    transform: scaleY(1);
    transform-box: fill-box;
    transform-origin: 50% 0;
    animation: drip var(--duration-slasher-drip) ease-in infinite backwards;
}

.slasher-delay-1 {
    animation-delay: 0.5s;
}

.slasher-delay-2 {
    animation-delay: 0.9s;
}

.slasher-delay-3 {
    animation-delay: 1.4s;
}

.slasher-delay-4 {
    animation-delay: 1.9s;
}

.slasher-delay-5 {
    animation-delay: 2.3s;
}

.slasher-delay-6 {
    animation-delay: 2.8s;
}

@keyframes drip {
    0% {
        transform: scaleY(0.12);
    }

    70%,
    100% {
        transform: scaleY(1);
    }
}

/* --- Plantilla halloween-infection (HW-1.3) ---
   Negro verdoso con verde tóxico y amarillo de peligro. Sustituye los tokens genéricos de la base de
   temporada, también fuera de <main> (privacidad, RSVP y marca). Sin modo oscuro: es oscura a propósito.
   El magenta y el cian existen solo en el glitch del título. */

body:has(.template-halloween-infection) {
    background: var(--color-infection-bg);
    color: var(--color-infection-ink);
}

body:has(.template-halloween-infection) .guest-privacy,
body:has(.template-halloween-infection) .guest-privacy-link,
body:has(.template-halloween-infection) .rsvp-purpose,
body:has(.template-halloween-infection) .rsvp-edit-link,
body:has(.template-halloween-infection) .field-label,
body:has(.template-halloween-infection) .field-hint,
body:has(.template-halloween-infection) .brand-badge-text {
    color: var(--color-infection-ink);
}

body:has(.template-halloween-infection) .rsvp-heading {
    color: var(--color-infection-toxic);
    font-family: var(--font-family-infection-mono);
}

body:has(.template-halloween-infection) .rsvp-action,
body:has(.template-halloween-infection) .brand-badge-logo {
    color: var(--color-infection-toxic);
}

body:has(.template-halloween-infection) .rsvp {
    border-top-color: var(--color-infection-toxic-deep);
}

/* El foco conserva la única regla global de :focus-visible; aquí solo cambia el color del anillo. */
body:has(.template-halloween-infection) :focus {
    outline-color: var(--color-infection-toxic);
}

/* El error va en amarillo de peligro: el verde ya es el realce de títulos, enlaces y foco. */
body:has(.template-halloween-infection) .field-error {
    color: var(--color-infection-hazard);
}

/* El resumen (verde, igual que el realce) y el reintento (amarillo, igual que field-error) no
   pueden quedarse con el fondo claro genérico: aquí texto claro sobre fondo claro es ilegible. */
body:has(.template-halloween-infection) .rsvp-summary,
body:has(.template-halloween-infection) .rsvp-retry {
    background: var(--color-infection-unit-surface);
}

body:has(.template-halloween-infection) .rsvp-summary {
    border-left-color: var(--color-infection-toxic);
}

body:has(.template-halloween-infection) .rsvp-retry {
    border-left-color: var(--color-infection-hazard);
    color: var(--color-infection-hazard);
}

/* Campo: borde verde al 53 % (la prueba mide su contraste mezclado sobre el fondo, mínimo 3:1). */
body:has(.template-halloween-infection) .input-text {
    border-color: var(--color-infection-input-border);
    background: var(--color-infection-unit-surface);
    color: var(--color-infection-ink);
}

body:has(.template-halloween-infection) .rsvp-choice input {
    accent-color: var(--color-infection-toxic);
}

/* Botón de RSVP: verde tóxico con tinta casi negra, Impact y resplandor. En Enviando, el fondo se
   atenúa y la tinta se declara aparte. */
body:has(.template-halloween-infection) .button-primary {
    border-radius: var(--radius-md);
    background: var(--color-infection-rsvp-bg);
    color: var(--color-infection-rsvp-ink);
    font-family: var(--font-family-infection-button);
    font-weight: var(--font-weight-countdown);
    letter-spacing: var(--letter-spacing-infection-button);
    text-transform: uppercase;
    box-shadow: var(--shadow-infection-rsvp);
}

body:has(.template-halloween-infection) .button-primary[aria-busy="true"] {
    background: var(--color-infection-rsvp-loading);
    color: var(--color-infection-rsvp-ink);
}

.template-halloween-infection .invitation-cover {
    background:
        radial-gradient(100% 70% at 50% 100%, var(--color-infection-cover-glow) 0, var(--color-infection-bg) 70%);
    color: var(--color-infection-ink);
}

/* Líneas de barrido sobre toda la portada, detrás del texto y sin recibir toques. */
/* position:absolute la saca del layout de fila de .seasonal-art (display:flex); por eso no le
   importa ser hija de un contenedor flex y puede superponerse al SVG en vez de ir a su lado. */
.template-halloween-infection .seasonal-art::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
        repeating-linear-gradient(0deg, var(--color-veil-clear) 0 var(--size-infection-scanline-line), var(--color-infection-scanline) var(--size-infection-scanline-line) var(--size-infection-scanline-pitch));
    pointer-events: none;
}

/* El título es lo único que se mueve: glitch de 4 s, sin retardo. En reposo (pausa o movimiento
   reducido) las reglas de la base apagan la animación y queda la sombra base sin desplazar. */
.template-halloween-infection .invitation-title {
    font-family: var(--font-family-infection-title);
    font-weight: 400;
    font-size: var(--font-size-infection-title);
    line-height: 0.95;
    text-transform: uppercase;
    overflow-wrap: anywhere;
    color: var(--color-infection-toxic);
    text-shadow: var(--shadow-infection-title);
}

/* El glitch solo corre si el sistema no pide movimiento reducido; así ninguna regla posterior de la
   hoja lo vuelve a encender. La pausa (is-still) lo apaga con una regla más específica. */
@media (prefers-reduced-motion: no-preference) {
    .template-halloween-infection .invitation-title {
        animation: glitch var(--duration-infection-glitch) infinite;
    }
}

/* El texto de la portada cruza el arte: un halo del color del fondo lo despega. */
.template-halloween-infection .invitation-cover-text {
    text-shadow: var(--shadow-infection-text);
}

.template-halloween-infection .invitation-kicker,
.template-halloween-infection .itinerary-time,
.template-halloween-infection .countdown-label {
    color: var(--color-infection-hazard);
}

.template-halloween-infection .invitation-kicker,
.template-halloween-infection .invitation-date,
.template-halloween-infection .invitation-heading {
    font-family: var(--font-family-infection-mono);
}

.template-halloween-infection .invitation-date,
.template-halloween-infection .countdown-status,
.template-halloween-infection .invitation-heading {
    color: var(--color-infection-toxic);
}

.template-halloween-infection .countdown-number {
    color: var(--color-infection-toxic);
    font-family: var(--font-family-infection-button);
    font-weight: var(--font-weight-invite-title);
}

/* La cuenta regresiva: el borde de sus unidades es solo decoración; no son controles. */
.template-halloween-infection .countdown-unit {
    border-color: var(--color-infection-toxic-deep);
    background: var(--color-infection-unit-surface);
}

.template-halloween-infection .invitation-section,
.template-halloween-infection .itinerary-item {
    border-top-color: var(--color-infection-toxic-deep);
}

.template-halloween-infection .itinerary-item:first-child {
    border-top: 0;
}

.template-halloween-infection .invitation-map {
    border-color: var(--color-infection-toxic);
    color: var(--color-infection-toxic);
}

.template-halloween-infection .pause-animation-button {
    border-color: var(--color-infection-toxic);
    background: var(--color-infection-unit-surface);
    color: var(--color-infection-ink);
}

/* El arte: las clases pintan con los tokens; el SVG no lleva colores. */
.infection-stop-top {
    stop-color: var(--color-infection-vein-top);
}

.infection-stop-bottom {
    stop-color: var(--color-infection-vein-bottom);
}

.infection-wound-core {
    stop-color: var(--color-infection-wound-core);
}

.infection-wound-mid {
    stop-color: var(--color-infection-wound-mid);
}

.infection-wound-rim {
    stop-color: var(--color-infection-wound-rim);
}

.infection-veins {
    fill: none;
    stroke: url(#infection-vein);
}

.infection-vein-glow {
    fill: none;
    stroke: var(--color-infection-toxic);
    opacity: 0.16;
}

.infection-teeth {
    fill: var(--color-infection-ink);
}

.infection-hazard-yellow {
    fill: var(--color-infection-hazard);
}

.infection-hazard-black {
    fill: var(--color-infection-hazard-stripe);
}

.infection-biohazard {
    fill: none;
    stroke: var(--color-infection-toxic);
    opacity: 0.18;
}

.infection-biohazard-core {
    fill: var(--color-infection-toxic);
    stroke: none;
}

/* Dos desplazamientos breves (92 % y 95 % del ciclo); el resto del tiempo, la sombra base. */
@keyframes glitch {
    0%,
    90%,
    100% {
        text-shadow: var(--shadow-infection-title);
    }

    92% {
        text-shadow: var(--shadow-infection-glitch-1);
    }

    95% {
        text-shadow: var(--shadow-infection-glitch-2);
    }
}

/* --- Plantilla halloween-pumpkin-orchard (HW-1.4) ---
   Crepúsculo ciruela y naranja con calabazas, cuervos y luciérnagas. Sustituye los tokens genéricos de
   la base de temporada, también fuera de <main> (privacidad, RSVP y marca). Sin modo oscuro: es oscura
   a propósito. Sin sangre ni elementos de otras plantillas. */

body:has(.template-halloween-pumpkin-orchard) {
    background: var(--color-orchard-bg);
    color: var(--color-orchard-ink);
}

body:has(.template-halloween-pumpkin-orchard) .guest-privacy,
body:has(.template-halloween-pumpkin-orchard) .guest-privacy-link,
body:has(.template-halloween-pumpkin-orchard) .rsvp-purpose,
body:has(.template-halloween-pumpkin-orchard) .rsvp-edit-link,
body:has(.template-halloween-pumpkin-orchard) .field-label,
body:has(.template-halloween-pumpkin-orchard) .field-hint,
body:has(.template-halloween-pumpkin-orchard) .brand-badge-text {
    color: var(--color-orchard-ink);
}

body:has(.template-halloween-pumpkin-orchard) .rsvp-heading,
body:has(.template-halloween-pumpkin-orchard) .rsvp-action,
body:has(.template-halloween-pumpkin-orchard) .brand-badge-logo {
    color: var(--color-orchard-accent);
}

body:has(.template-halloween-pumpkin-orchard) .rsvp {
    border-top-color: var(--color-orchard-border);
}

/* El foco conserva la única regla global de :focus-visible; aquí solo cambia el color del anillo. */
body:has(.template-halloween-pumpkin-orchard) :focus {
    outline-color: var(--color-orchard-accent);
}

/* El error va en el durazno del kicker: el naranja ya es el realce de encabezados, enlaces y foco. */
body:has(.template-halloween-pumpkin-orchard) .field-error {
    color: var(--color-orchard-kicker);
}

/* El resumen (realce) y el reintento (kicker, igual que field-error) no pueden quedarse con el
   fondo claro genérico: aquí texto claro sobre fondo claro es ilegible. Huerto no tiene una
   superficie de unidad propia; usa el mismo fondo oscuro de toda la página. */
body:has(.template-halloween-pumpkin-orchard) .rsvp-summary,
body:has(.template-halloween-pumpkin-orchard) .rsvp-retry {
    background: var(--color-orchard-bg);
}

body:has(.template-halloween-pumpkin-orchard) .rsvp-summary {
    border-left-color: var(--color-orchard-accent);
}

body:has(.template-halloween-pumpkin-orchard) .rsvp-retry {
    border-left-color: var(--color-orchard-kicker);
    color: var(--color-orchard-kicker);
}

/* Campo: el borde va en orchard-border (4.18:1 sobre el fondo, límite de control). */
body:has(.template-halloween-pumpkin-orchard) .input-text {
    border-color: var(--color-orchard-border);
    background: var(--color-orchard-bg);
    color: var(--color-orchard-ink);
}

body:has(.template-halloween-pumpkin-orchard) .rsvp-choice input {
    accent-color: var(--color-orchard-accent);
}

/* Botón de RSVP: naranja de calabaza con tinta ciruela y resplandor. En Enviando, el fondo se
   atenúa y la tinta se declara aparte. */
body:has(.template-halloween-pumpkin-orchard) .button-primary {
    border-radius: var(--radius-md);
    background: var(--color-orchard-rsvp-bg);
    color: var(--color-orchard-rsvp-ink);
    font-weight: var(--font-weight-countdown);
    box-shadow: var(--shadow-orchard-rsvp);
}

body:has(.template-halloween-pumpkin-orchard) .button-primary[aria-busy="true"] {
    background: var(--color-orchard-rsvp-loading);
    color: var(--color-orchard-rsvp-ink);
}

.template-halloween-pumpkin-orchard .invitation-cover {
    background:
        linear-gradient(180deg, var(--color-orchard-sky-top) 0%, var(--color-orchard-sky-mid) 45%, var(--color-orchard-sky-low) 72%, var(--color-orchard-hill) 100%);
    color: var(--color-orchard-ink);
}

.template-halloween-pumpkin-orchard .invitation-title {
    font-family: var(--font-family-orchard-title);
    font-style: italic;
    font-weight: 900;
    font-size: var(--font-size-orchard-title);
    line-height: 1;
    overflow-wrap: anywhere;
    color: var(--color-orchard-title);
    text-shadow: var(--shadow-orchard-title);
}

/* El texto de la portada cruza el arte: un halo del color del fondo lo despega. */
.template-halloween-pumpkin-orchard .invitation-cover-text {
    text-shadow: var(--shadow-orchard-text);
}

.template-halloween-pumpkin-orchard .invitation-kicker,
.template-halloween-pumpkin-orchard .invitation-date,
.template-halloween-pumpkin-orchard .countdown-label,
.template-halloween-pumpkin-orchard .countdown-status {
    color: var(--color-orchard-kicker);
}

.template-halloween-pumpkin-orchard .invitation-heading,
.template-halloween-pumpkin-orchard .itinerary-time {
    color: var(--color-orchard-accent);
}

.template-halloween-pumpkin-orchard .countdown-number {
    color: var(--color-orchard-ink);
}

/* La cuenta regresiva: el borde de sus unidades es solo decoración; no son controles. */
.template-halloween-pumpkin-orchard .countdown-unit {
    border-color: var(--color-orchard-border);
    background: var(--color-orchard-bg);
}

.template-halloween-pumpkin-orchard .invitation-section,
.template-halloween-pumpkin-orchard .itinerary-item {
    border-top-color: var(--color-orchard-border);
}

.template-halloween-pumpkin-orchard .itinerary-item:first-child {
    border-top: 0;
}

.template-halloween-pumpkin-orchard .invitation-map {
    border-color: var(--color-orchard-border);
    color: var(--color-orchard-accent);
}

.template-halloween-pumpkin-orchard .pause-animation-button {
    border-color: var(--color-orchard-accent);
    background: var(--color-orchard-bg);
    color: var(--color-orchard-ink);
}

/* El arte: las clases pintan con los tokens; el SVG no lleva colores. */
.orchard-sky-top {
    stop-color: var(--color-orchard-sky-top);
}

.orchard-sky-mid {
    stop-color: var(--color-orchard-sky-mid);
}

.orchard-sky-low {
    stop-color: var(--color-orchard-sky-low);
}

.orchard-sky-horizon {
    stop-color: var(--color-orchard-sky-horizon);
}

.orchard-halo-core,
.orchard-halo-edge {
    stop-color: var(--color-orchard-accent);
}

.orchard-halo-core {
    stop-opacity: 0.75;
}

.orchard-halo-edge {
    stop-opacity: 0;
}

.orchard-moon-glow-core,
.orchard-moon-glow-edge {
    stop-color: var(--color-orchard-kicker);
}

.orchard-moon-glow-core {
    stop-opacity: 0.45;
}

.orchard-moon-glow-edge {
    stop-opacity: 0;
}

.orchard-moon {
    fill: var(--color-orchard-title);
}

.orchard-hill-shape {
    fill: var(--color-orchard-hill);
}

/* El cuerpo de la calabaza es un degradado radial (claro arriba-izquierda a oscuro), referenciado con
   fill="url(#orchard-pumpkin)"/"url(#orchard-pumpkin-shade)" en el arte, igual que orchard-sky; aquí solo
   van los colores de sus paradas. orchard-pumpkin es el degradado claro de las costillas externas e
   internas y del gajo frontal (clase orchard-pumpkin en el arte); orchard-pumpkin-shade es el degradado
   oscuro de la costilla central, de atrás. */
.orchard-pumpkin-stop-light {
    stop-color: var(--color-orchard-pumpkin-light);
}

.orchard-pumpkin-stop-mid {
    stop-color: var(--color-orchard-rsvp-bg);
}

.orchard-pumpkin-stop-dusk,
.orchard-pumpkin-shade-stop-dusk {
    stop-color: var(--color-orchard-pumpkin-dusk);
}

.orchard-pumpkin-shade-stop-mid {
    stop-color: var(--color-orchard-rsvp-loading);
}

.orchard-flame-stop-core {
    stop-color: var(--color-orchard-flame-core);
}

.orchard-flame-stop-edge {
    stop-color: var(--color-orchard-flame-edge);
}

.orchard-pumpkin-ground {
    fill: var(--color-orchard-hill);
    opacity: 0.45;
}

.orchard-pumpkin-highlight {
    fill: var(--color-orchard-kicker);
    opacity: 0.25;
}

.orchard-stem {
    fill: var(--color-orchard-border);
}

.orchard-face,
.orchard-firefly {
    fill: var(--color-orchard-firefly);
}

.orchard-stars {
    fill: var(--color-orchard-title);
}

.orchard-crow {
    fill: var(--color-orchard-hill);
}

/* El reposo de cada elemento es su propia regla base: estrellas, luciérnagas y halos a opacidad plena,
   el cuervo desplazado hacia dentro del cuadro y las alas abiertas. La animación solo lo sobrescribe:
   con animation: none (pausa o movimiento reducido) vuelve solo a este estado. */
.orchard-stars,
.orchard-firefly,
.orchard-halo {
    opacity: 1;
}

.orchard-crow {
    transform: translate(var(--size-orchard-crow-rest), 0);
}

.orchard-wing {
    transform: scaleY(1);
    transform-box: fill-box;
    transform-origin: 50% 100%;
}

.orchard-sway {
    transform: rotate(0deg);
    transform-box: fill-box;
    transform-origin: 50% 100%;
}

/* Las animaciones solo corren si el sistema no pide movimiento reducido; así ninguna regla posterior
   las vuelve a encender. La pausa (is-still) las apaga con una regla más específica. Toda animación con
   retardo lleva backwards para no mostrar el estado de reposo antes de empezar. */
@media (prefers-reduced-motion: no-preference) {
    .orchard-tw-1 {
        animation: orchard-tw var(--duration-orchard-tw-1) ease-in-out infinite backwards;
        animation-delay: 0.4s;
    }

    .orchard-tw-2 {
        animation: orchard-tw var(--duration-orchard-tw-2) ease-in-out infinite backwards;
        animation-delay: 1.1s;
    }

    .orchard-tw-3 {
        animation: orchard-tw var(--duration-orchard-tw-3) ease-in-out infinite backwards;
        animation-delay: 2s;
    }

    .orchard-crow {
        animation: orchard-fly var(--duration-orchard-fly) linear infinite backwards;
    }

    .orchard-fly-1 {
        animation-delay: 0.5s;
    }

    .orchard-fly-2 {
        animation-delay: 5.5s;
    }

    .orchard-fly-3 {
        animation-delay: 10.5s;
    }

    .orchard-wing {
        animation: orchard-flap var(--duration-orchard-flap) ease-in-out infinite alternate backwards;
    }

    .orchard-firefly {
        animation: orchard-fly2 var(--duration-orchard-firefly) ease-in-out infinite backwards;
    }

    .orchard-fly2-1 {
        animation-delay: 0s;
    }

    .orchard-fly2-2 {
        animation-delay: 0.8s;
    }

    .orchard-fly2-3 {
        animation-delay: 1.6s;
    }

    .orchard-fly2-4 {
        animation-delay: 2.4s;
    }

    .orchard-fly2-5 {
        animation-delay: 3.2s;
    }

    .orchard-fly2-6 {
        animation-delay: 4s;
    }

    .orchard-flick {
        animation: orchard-flick var(--duration-orchard-flick) ease-in-out infinite backwards;
    }

    .orchard-flick-2 {
        animation-delay: 0.6s;
    }

    .orchard-sway {
        animation: orchard-sway var(--duration-orchard-sway) ease-in-out infinite backwards;
    }
}

/* Estrellas: bajan y vuelven a la opacidad plena. */
@keyframes orchard-tw {
    0%,
    100% {
        opacity: 1;
    }

    50% {
        opacity: 0.25;
    }
}

/* Cuervo: cruza la portada de izquierda a derecha, de fuera del cuadro a fuera del cuadro. */
@keyframes orchard-fly {
    0% {
        transform: translate(var(--size-orchard-fly-start), 0);
    }

    100% {
        transform: translate(var(--size-orchard-fly-end), 0);
    }
}

/* Aleteo: el ala se cierra a la mitad y vuelve; alterna cada 0.32 s. */
@keyframes orchard-flap {
    0% {
        transform: scaleY(1);
    }

    100% {
        transform: scaleY(0.4);
    }
}

/* Luciérnaga: deriva y se apaga a medias en el punto lejano; empieza y termina a opacidad plena. */
@keyframes orchard-fly2 {
    0%,
    100% {
        transform: translate(0, 0);
        opacity: 1;
    }

    50% {
        transform: translate(var(--size-orchard-firefly-drift-x), var(--size-orchard-firefly-drift-y));
        opacity: 0.4;
    }
}

/* Calabaza: se mece de lado a lado como si la tocara el viento, desde su base; nunca una vuelta
   completa. */
@keyframes orchard-sway {
    0%,
    100% {
        transform: rotate(-4deg);
    }

    50% {
        transform: rotate(4deg);
    }
}

/* Vela: el halo baja un poco y vuelve; empieza y termina a opacidad plena. */
@keyframes orchard-flick {
    0%,
    100% {
        opacity: 1;
    }

    40% {
        opacity: 0.6;
    }

    70% {
        opacity: 0.85;
    }
}

/* --- Plantilla halloween-witch-moon (HW-1.5) ---
   Cielo violeta con luna, bruja en escoba, murciélagos, bosque seco y neblina. Sustituye los tokens
   genéricos de la base de temporada, también fuera de <main> (privacidad, RSVP y marca). Sin modo
   oscuro: es oscura a propósito. Sin sangre ni elementos de otras plantillas. */

body:has(.template-halloween-witch-moon) {
    background: var(--color-witch-bg);
    color: var(--color-witch-ink);
}

body:has(.template-halloween-witch-moon) .guest-privacy,
body:has(.template-halloween-witch-moon) .guest-privacy-link,
body:has(.template-halloween-witch-moon) .rsvp-purpose,
body:has(.template-halloween-witch-moon) .rsvp-edit-link,
body:has(.template-halloween-witch-moon) .field-label,
body:has(.template-halloween-witch-moon) .field-hint,
body:has(.template-halloween-witch-moon) .brand-badge-text {
    color: var(--color-witch-ink);
}

body:has(.template-halloween-witch-moon) .rsvp-heading,
body:has(.template-halloween-witch-moon) .rsvp-action,
body:has(.template-halloween-witch-moon) .brand-badge-logo {
    color: var(--color-witch-accent);
}

body:has(.template-halloween-witch-moon) .rsvp {
    border-top-color: var(--color-witch-border);
}

/* El foco conserva la única regla global de :focus-visible; aquí solo cambia el color del anillo. */
body:has(.template-halloween-witch-moon) :focus {
    outline-color: var(--color-witch-accent);
}

/* El error va en el lavanda claro del kicker: el lavanda del realce ya es el de encabezados, enlaces y foco. */
body:has(.template-halloween-witch-moon) .field-error {
    color: var(--color-witch-kicker);
}

/* El resumen (realce) y el reintento (kicker, igual que field-error) no pueden quedarse con el
   fondo claro genérico: aquí texto claro sobre fondo claro es ilegible. Brujas no tiene una
   superficie de unidad propia; usa el mismo fondo oscuro de toda la página. */
body:has(.template-halloween-witch-moon) .rsvp-summary,
body:has(.template-halloween-witch-moon) .rsvp-retry {
    background: var(--color-witch-bg);
}

body:has(.template-halloween-witch-moon) .rsvp-summary {
    border-left-color: var(--color-witch-accent);
}

body:has(.template-halloween-witch-moon) .rsvp-retry {
    border-left-color: var(--color-witch-kicker);
    color: var(--color-witch-kicker);
}

/* Campo: el borde va en witch-border (3.39:1 sobre el fondo, límite de control). */
body:has(.template-halloween-witch-moon) .input-text {
    border-color: var(--color-witch-border);
    background: var(--color-witch-bg);
    color: var(--color-witch-ink);
}

body:has(.template-halloween-witch-moon) .rsvp-choice input {
    accent-color: var(--color-witch-accent);
}

/* Botón de RSVP: violeta con tinta casi negra y resplandor. En Enviando, el fondo se
   atenúa y la tinta se declara aparte. */
body:has(.template-halloween-witch-moon) .button-primary {
    border-radius: var(--radius-md);
    background: var(--color-witch-rsvp-bg);
    color: var(--color-witch-rsvp-ink);
    font-weight: var(--font-weight-countdown);
    box-shadow: var(--shadow-witch-rsvp);
}

body:has(.template-halloween-witch-moon) .button-primary[aria-busy="true"] {
    background: var(--color-witch-rsvp-loading);
    color: var(--color-witch-rsvp-ink);
}

.template-halloween-witch-moon .invitation-cover {
    background:
        linear-gradient(180deg, var(--color-witch-sky-top) 0%, var(--color-witch-sky-mid) 50%, var(--color-witch-sky-low) 88%, var(--color-witch-bg) 100%);
    color: var(--color-witch-ink);
}

.template-halloween-witch-moon .invitation-title {
    font-family: var(--font-family-witch-title);
    font-style: italic;
    font-weight: 800;
    font-size: var(--font-size-witch-title);
    line-height: 1;
    overflow-wrap: anywhere;
    color: var(--color-witch-title);
    text-shadow: var(--shadow-witch-title);
}

/* El texto de la portada cruza el arte (la bruja pasa detrás): un halo del color del fondo despega el
   título, el kicker y la fecha, y las unidades de la cuenta regresiva llevan fondo opaco propio. El
   contraste se mide contra ese halo o ese fondo, no contra la bruja (UX-DR-11). */
.template-halloween-witch-moon .invitation-cover-text {
    text-shadow: var(--shadow-witch-text);
}

.template-halloween-witch-moon .invitation-kicker,
.template-halloween-witch-moon .invitation-date,
.template-halloween-witch-moon .countdown-label,
.template-halloween-witch-moon .countdown-status {
    color: var(--color-witch-kicker);
}

.template-halloween-witch-moon .invitation-heading,
.template-halloween-witch-moon .itinerary-time {
    color: var(--color-witch-accent);
}

.template-halloween-witch-moon .countdown-number {
    color: var(--color-witch-ink);
}

/* La cuenta regresiva: fondo opaco propio para sostener el contraste cuando la bruja pasa detrás; el
   borde de sus unidades es solo decoración, no son controles. */
.template-halloween-witch-moon .countdown-unit {
    border-color: var(--color-witch-border);
    background: var(--color-witch-bg);
}

.template-halloween-witch-moon .invitation-section,
.template-halloween-witch-moon .itinerary-item {
    border-top-color: var(--color-witch-border);
}

.template-halloween-witch-moon .itinerary-item:first-child {
    border-top: 0;
}

.template-halloween-witch-moon .invitation-map {
    border-color: var(--color-witch-border);
    color: var(--color-witch-accent);
}

.template-halloween-witch-moon .pause-animation-button {
    border-color: var(--color-witch-accent);
    background: var(--color-witch-bg);
    color: var(--color-witch-ink);
}

/* El arte: las clases pintan con los tokens; el SVG no lleva colores. */
.witch-sky-top {
    stop-color: var(--color-witch-sky-top);
}

.witch-sky-mid {
    stop-color: var(--color-witch-sky-mid);
}

.witch-sky-low {
    stop-color: var(--color-witch-sky-low);
}

.witch-glow-core,
.witch-glow-edge {
    stop-color: var(--color-witch-glow);
}

.witch-moon-glow-core {
    stop-opacity: 0.4;
}

.witch-aura-core {
    stop-opacity: 0.7;
}

.witch-glow-edge {
    stop-opacity: 0;
}

.witch-moon {
    fill: var(--color-witch-title);
}

.witch-crater {
    fill: var(--color-witch-border);
    opacity: 0.55;
}

.witch-stars {
    fill: var(--color-witch-title);
}

.witch-figure,
.witch-bat-wing,
.witch-bat-body,
.witch-forest {
    fill: var(--color-witch-bg);
}

.witch-fog {
    fill: var(--color-witch-fog);
    opacity: 0.28;
}

/* El reposo de cada elemento es su propia regla base: estrellas y halo a opacidad plena, la bruja
   desplazada hacia dentro del cuadro, las alas abiertas y la neblina sin desplazar. La animación solo
   lo sobrescribe: con animation: none (pausa o movimiento reducido) vuelve solo a este estado. */
.witch-stars,
.witch-halo {
    opacity: 1;
}

.witch-fly {
    transform: translate(var(--size-witch-rest), 0);
}

.witch-bat-wing {
    transform: scaleY(1);
    transform-box: fill-box;
    transform-origin: 50% 100%;
}

.witch-fog {
    transform: translate(0, 0);
}

/* Las animaciones solo corren si el sistema no pide movimiento reducido; así ninguna regla posterior
   las vuelve a encender. La pausa (is-still) las apaga con una regla más específica. Toda animación con
   retardo lleva backwards para no mostrar el estado de reposo antes de empezar. */
@media (prefers-reduced-motion: no-preference) {
    .witch-tw-1 {
        animation: witch-tw var(--duration-witch-tw-1) ease-in-out infinite backwards;
        animation-delay: 0.3s;
    }

    .witch-tw-2 {
        animation: witch-tw var(--duration-witch-tw-2) ease-in-out infinite backwards;
        animation-delay: 1.2s;
    }

    .witch-tw-3 {
        animation: witch-tw var(--duration-witch-tw-3) ease-in-out infinite backwards;
        animation-delay: 2.1s;
    }

    .witch-fly {
        animation: witch-fly var(--duration-witch-fly) linear infinite backwards;
        animation-delay: 0.5s;
    }

    .witch-bat-wing {
        animation: witch-flap var(--duration-witch-flap) ease-in-out infinite alternate backwards;
    }

    .witch-bat-2 .witch-bat-wing {
        animation-delay: 0.12s;
    }

    .witch-bat-3 .witch-bat-wing {
        animation-delay: 0.2s;
    }

    .witch-fog {
        animation: witch-drift var(--duration-witch-drift) ease-in-out infinite alternate backwards;
    }

    .witch-drift-2 {
        animation-delay: 2.5s;
    }
}

/* Estrellas: bajan y vuelven a la opacidad plena. */
@keyframes witch-tw {
    0%,
    100% {
        opacity: 1;
    }

    50% {
        opacity: 0.25;
    }
}

/* Bruja: cruza la portada de izquierda a derecha, de fuera del cuadro a fuera del cuadro; su halo
   lavanda viaja con ella. */
@keyframes witch-fly {
    0% {
        transform: translate(var(--size-witch-fly-start), 0);
    }

    100% {
        transform: translate(var(--size-witch-fly-end), 0);
    }
}

/* Aleteo: el ala se cierra a la mitad y vuelve; alterna cada 0.32 s. */
@keyframes witch-flap {
    0% {
        transform: scaleY(1);
    }

    100% {
        transform: scaleY(0.4);
    }
}

/* Neblina: va y viene en horizontal; empieza en su lugar de reposo. */
@keyframes witch-drift {
    0% {
        transform: translate(0, 0);
    }

    100% {
        transform: translate(var(--size-witch-fog-drift), 0);
    }
}
