/* Generado por el build desde Styles/tokens.json y Styles/dashboard.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-gold: #E8C87A;
    --color-forest-900: #1C3A2B;
    --color-forest-accent: #A8C97A;
    --color-champagne: #F5EFE0;
    --color-champagne-gold: #7A5C28;
    --color-coral-800: #8B3A2E;
    --color-coral-accent: #F2B89A;
    --color-slasher-bg: #0B0708;
    --color-slasher-accent: #FF4B5C;
    --color-infection-bg: #050A06;
    --color-infection-toxic: #9DFF2E;
    --color-orchard-bg: #1A0F24;
    --color-orchard-accent: #FF9A4A;
    --color-witch-bg: #0F0A1C;
    --color-witch-accent: #B79CFF;
    --color-on-accent: #FFFFFF;
    --color-focus-ring: #E0147F;
    --color-accent-text: #B00F63;
    --color-success: #1E6B4E;
    --color-success-surface: #E3F3EC;
    --color-warning: #8A5300;
    --color-warning-surface: #FDF1DC;
    --color-danger: #A3122B;
    --color-danger-surface: #FCE8EC;
    --color-action-secondary: #5A2A4F;
    --color-action-secondary-border: #5A2A4F;
    --color-disabled-surface: #D9CCD4;
    --color-disabled-text: #574A52;
    --color-field-surface: #FFFFFF;
    --color-limit-surface: #F7F3F5;
    --color-limit-text: #8F7A86;
    --color-neutral: #3B1B34;
    --color-metric: #5A2A4F;
    --color-pill-surface: #EFE2EB;
    --color-pill-text: #3B1B34;
    --color-skeleton-base: #EDE4E8;
    --color-skeleton-sweep: #F7F3F5;
    --color-lock-fade-top: #FFFFFF33;
    --color-lock-fade-bottom: #FFFFFFF5;
    --color-qr-surface: #FFFFFF;
    --color-brand-logo: #5A2A4F;
    --color-qr-ink: #241C22;
    --font-family-display: 'Fraunces', Georgia, serif;
    --font-family-ui: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --font-family-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
    --font-size-panel-title: 21px;
    --font-size-panel-metric: 28px;
    --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-notice: 13px;
    --font-size-stepper-value: 20px;
    --font-size-list-row: 14px;
    --font-weight-panel-title: 600;
    --font-weight-panel-metric: 600;
    --font-weight-label: 600;
    --font-weight-eyebrow: 700;
    --font-weight-button: 600;
    --font-weight-button-ghost: 500;
    --font-weight-notice-title: 700;
    --font-weight-stepper-value: 700;
    --line-height-panel-title: 1.3;
    --line-height-panel-metric: 1.1;
    --line-height-body: 1.55;
    --line-height-helper: 1.45;
    --letter-spacing-eyebrow: 0.08em;
    --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;
    --space-pill-inline: 10px;
    --space-tab-rail: 3px;
    --border-width-control: 1px;
    --border-width-focus-ring: 3px;
    --border-width-emphasis: 2px;
    --border-width-notice: 3px;
    --blur-lock: 3px;
    --duration-tab: 150ms;
    --duration-skeleton-delay: 200ms;
    --duration-skeleton-sweep: 1.4s;
    --size-qr: 168px;
    --size-template-card: 180px;
    --size-template-swatch: 8px;
    --size-switch-track-width: 44px;
    --size-switch-track-height: 24px;
    --size-switch-thumb: 20px;
    --size-dashboard-logo: 112px;
    --radius-sm: 8px;
    --radius-md: 10px;
    --radius-lg: 12px;
    --radius-xl: 16px;
    --radius-full: 9999px;
    --layout-dashboard-max-width: 720px;
    --layout-preview-width: 90%;
    --layout-preview-height: 467px;
}

@media (prefers-color-scheme: dark) {
    :root {
        --color-plum-100: #331F2C;
        --color-surface-base: #1B1218;
        --color-surface-raised: #271A24;
        --color-ink: #F4EAF0;
        --color-ink-muted: #B4A3AE;
        --color-line: #3E2C38;
        --color-border-control: #B98BA8;
        --color-focus-ring: #FF5CA8;
        --color-accent-text: #FF5CA8;
        --color-success: #8FD9B4;
        --color-success-surface: #12301F;
        --color-warning: #F5CE7E;
        --color-warning-surface: #3A2C12;
        --color-danger: #FFB3C0;
        --color-danger-surface: #3A141C;
        --color-action-secondary: #FFC2E0;
        --color-action-secondary-border: #B98BA8;
        --color-disabled-surface: #3E2C38;
        --color-disabled-text: #C8B7C1;
        --color-field-surface: #1B1218;
        --color-limit-surface: #3E2C38;
        --color-limit-text: #C8B7C1;
        --color-neutral: #F4EAF0;
        --color-metric: #FF5CA8;
        --color-pill-surface: #3A2033;
        --color-pill-text: #FFD0E6;
        --color-skeleton-base: #331F2C;
        --color-skeleton-sweep: #3E2C38;
        --color-lock-fade-top: #271A2433;
        --color-lock-fade-bottom: #271A24F5;
        --color-brand-logo: #FFFFFF;
    }
}

/* Reglas base de la raíz del panel. Propias: no comparten nada con las del invitado.
   Solo usan var(--…): los valores viven en tokens.json y el build los antepone al generar
   wwwroot/css/dashboard.css, con el modo oscuro en prefers-color-scheme. Inter para la
   interfaz y Fraunces para títulos y cifras. Los componentes de la parte B de la historia 1.3
   (botones, campo, aviso y stepper) van abajo, seguidos de los de la parte C (cifra, fila de
   lista, pastilla, pestañas, esqueleto, candado y QR) y del bloque de prefers-reduced-motion. */

/* Fraunces variable, solo el eje de peso y el subconjunto latino (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;
}

/* Inter variable, eje de peso y subconjunto latino (SIL OFL 1.1). Solo en el panel. */
@font-face {
    font-family: "Inter";
    src: url("../fonts/inter-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;
}

:root {
    color-scheme: light dark;
}

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

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

body {
    margin: 0;
    background: var(--color-surface-base);
    color: var(--color-ink);
    font-family: var(--font-family-ui);
    font-size: var(--font-size-body);
    line-height: var(--line-height-body);
}

/* La marca a la izquierda y "Salir" a la derecha; en pantallas angostas el aviso baja. */
.dashboard-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-gutter);
    border-bottom: var(--border-width-control) solid var(--color-line);
}

/* La navegación del panel, pegada a la marca; "Salir" y el saludo quedan a la derecha. Cada
   enlace es una pastilla tocable (mínimo 44 px); la sección activa lleva el tinte plum-100 y
   el color de acento, igual que la pestaña activa. */
.dashboard-nav {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    margin-right: auto;
    margin-left: var(--space-4);
}

.dashboard-nav-link {
    display: inline-flex;
    align-items: center;
    min-height: var(--space-touch-min);
    padding: 0 var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--color-ink-muted);
    font-size: var(--font-size-label);
    font-weight: var(--font-weight-label);
    text-decoration: none;
    transition: background-color var(--duration-tab) ease, color var(--duration-tab) ease;
}

.dashboard-nav-link:hover {
    background: var(--color-plum-100);
    color: var(--color-ink);
}

.dashboard-nav-link.is-active {
    background: var(--color-plum-100);
    color: var(--color-accent-text);
}

/* El saludo y "Salir", en una fila; el aviso de error baja a su propia línea. */
.sign-out {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
}

.sign-out-greeting {
    color: var(--color-ink-muted);
    font-size: var(--font-size-label);
    overflow-wrap: anywhere;
}

.sign-out-greeting strong {
    color: var(--color-ink);
    font-weight: var(--font-weight-label);
}

/* El logotipo de envait (img/logo-envait.svg con <use>): toma el color por currentColor, ciruela
   en claro y blanco en oscuro. El alto sale de la proporción del SVG. */
.brand {
    display: block;
    width: var(--size-dashboard-logo);
    color: var(--color-brand-logo);
    fill: currentColor;
}

/* Una columna centrada, con ancho máximo de 720 px. */
.dashboard-content {
    max-width: var(--layout-dashboard-max-width);
    margin: 0 auto;
    padding: var(--space-6) var(--space-gutter);
}

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

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

/* 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 (prefijos de los avisos). Recortado a un punto,
   nunca display: none, que lo borraría también del árbol de accesibilidad. Sin altura fija ni
   longitudes a mano: el ancho y el alto máximo salen del token de borde de 1 px. */
.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;
}

/* --- Botones (componentes PrimaryButton, SecondaryButton y GhostButton) --- */

/* button-primary: ancho completo, magenta con texto blanco; uno solo por 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;
}

/* En carga: magenta-700 y el texto blanco declarado aparte. Si heredara el gris del estado
   deshabilitado, "Enviando…" caería a 1.35:1 (revisión C-2). */
.button-primary[aria-busy="true"] {
    background: var(--color-magenta-700);
    color: var(--color-on-accent);
    cursor: progress;
}

/* button-secondary: borde de 2 px (a 1 px desaparece en pantallas mate), fondo transparente. */
.button-secondary {
    display: block;
    width: 100%;
    padding: 0 var(--space-4);
    border: var(--border-width-emphasis) solid var(--color-action-secondary-border);
    border-radius: var(--radius-lg);
    background: none;
    color: var(--color-action-secondary);
    font-weight: var(--font-weight-button);
    cursor: pointer;
}

/* button-danger (Historia 2.7): la forma de button-secondary con el borde y el texto en danger,
   solo para lo que no se puede deshacer. Va antes de button-disabled para que, apagado, se vea
   como cualquier botón apagado. */
.button-danger {
    border-color: var(--color-danger);
    color: var(--color-danger);
}

/* button-ghost: texto subrayado, sin fondo ni borde; tocable de al menos 44 px. */
.button-ghost {
    min-height: var(--space-touch-min);
    padding: 0 var(--space-2);
    border: 0;
    background: none;
    color: var(--color-action-secondary);
    font-size: var(--font-size-body);
    font-weight: var(--font-weight-button-ghost);
    text-decoration: underline;
    cursor: pointer;
}

/* Un botón cuyo texto pueda desbordar (Guardar, Agregar renglón, Actualizar vista previa,
   Compartir, Ver mi invitación…) se trunca con elipsis; nunca salta de línea. */
.button-primary,
.button-secondary,
.button-ghost {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Anclas con forma de botón: height, font y sin subrayado que los botones obtienen del reset. */
a.button-primary,
a.button-secondary {
    min-height: var(--space-control-height);
    font: inherit;
    font-size: var(--font-size-body-input);
    text-decoration: none;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Encabezado de Contactos: título, enlace de importar y botón de agregar en una sola línea. */
.contacts-header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.contacts-header h2 {
    margin: 0;
    margin-right: auto;
    white-space: nowrap;
}

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

/* "Importar archivo" es un enlace: texto subrayado, con la misma altura tocable que button-ghost. */
.contacts-import-link {
    display: inline-flex;
    align-items: center;
    min-height: var(--space-touch-min);
    color: var(--color-action-secondary);
    font-size: var(--font-size-body);
    text-decoration: underline;
    white-space: nowrap;
    cursor: pointer;
}

/* "Agregar contacto" conserva la forma de button-secondary pero a su ancho natural, con ícono. */
.contacts-actions .button-secondary {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    width: auto;
    min-height: var(--space-control-height);
    white-space: nowrap;
}

/* Equipo: el título comparte el encabezado de Contactos; las acciones de cada fila tienen un ancho
   fijo para que los selectores y los botes de basura queden alineados en columna. */
.team-header h2 {
    margin: 0 0 var(--space-4);
}

.team-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.team-role-select {
    width: 7.5rem;
}

.team-role-label {
    width: 7.5rem;
    color: var(--color-ink-muted);
    text-align: center;
}

.team-action-spacer {
    flex: none;
    width: var(--space-touch-min);
}

/* Formulario "Nuevo contacto": una tarjeta con borde, separada del encabezado y de la lista. */
.contacts-add-form {
    margin: var(--space-4) 0;
    padding: var(--space-4);
    border: var(--border-width-control) solid var(--color-line);
    border-radius: var(--radius-lg);
}

.contacts-add-form h3 {
    margin: 0 0 var(--space-3);
}

/* Guardar ocupa el ancho; Cancelar queda centrado debajo. */
.contacts-form-buttons {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-4);
}

.contacts-form-buttons .button-primary {
    width: 100%;
}

.contacts-import-result {
    margin: var(--space-4) 0;
}

.contacts-empty {
    padding: var(--space-6) 0;
    text-align: center;
}

/* El buscador ocupa todo el ancho del contenedor. */
.contacts-search {
    margin-top: var(--space-4);
    margin-bottom: var(--space-4);
}

/* Borrar es un ícono de bote de basura, del tamaño tocable mínimo y en danger. */
.contacts-delete-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    color: var(--color-danger);
}

/* button-disabled: el mismo estado para las tres variantes. En oscuro, los tokens bajan a un
   tono oscuro para que el botón inactivo no se vea más que el primario (revisión M-5). */
.button-disabled {
    border-color: var(--color-disabled-surface);
    background: var(--color-disabled-surface);
    color: var(--color-disabled-text);
    text-decoration: none;
    cursor: not-allowed;
}

/* --- Campo de texto (componente TextField) --- */

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

.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 mensaje va debajo del campo. El padding baja lo
   mismo que crece el borde, para que el texto no se recorra. */
.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);
}

.field-hint,
.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);
}

.field-error span {
    display: block;
}

/* --- Aviso (componente Notice) --- */

/* Superficie tintada, texto del mismo matiz oscuro y borde izquierdo de 3 px en el color del
   tono. Nunca fondo saturado con texto blanco. */
/* position: relative da ancestro posicionado al prefijo .visually-hidden del aviso. */
.notice {
    position: relative;
    display: flex;
    gap: var(--space-3);
    margin: 0 0 var(--space-4);
    padding: var(--space-3) var(--space-4);
    border-inline-start: var(--border-width-notice) solid;
    border-radius: var(--radius-md);
    font-size: var(--font-size-notice);
    line-height: var(--line-height-helper);
}

.notice-icon {
    flex: none;
    font-weight: var(--font-weight-notice-title);
}

.notice-body {
    flex: 1;
    min-width: 0;
}

.notice-body p {
    margin: 0;
    color: inherit;
}

.notice-title {
    font-weight: var(--font-weight-notice-title);
}

.notice-ok {
    border-inline-start-color: var(--color-success);
    background: var(--color-success-surface);
    color: var(--color-success);
}

.notice-warning {
    border-inline-start-color: var(--color-warning);
    background: var(--color-warning-surface);
    color: var(--color-warning);
}

.notice-error {
    border-inline-start-color: var(--color-danger);
    background: var(--color-danger-surface);
    color: var(--color-danger);
}

.notice-neutral {
    border-inline-start-color: var(--color-neutral);
    background: var(--color-plum-100);
    color: var(--color-neutral);
}

/* --- Stepper (componente Stepper) --- */

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

.stepper-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);
}

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

/* Botones de 48×48: min-width y min-height, nunca una altura fija. */
.stepper-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);
    cursor: pointer;
}

/* En el tope: marcado, no deshabilitado. Sigue enfocable y el anillo de foco sigue siendo el
   de la regla global. */
.stepper-button[aria-disabled="true"] {
    border-color: var(--color-line);
    background: var(--color-limit-surface);
    color: var(--color-limit-text);
    cursor: not-allowed;
}

.stepper-value {
    min-width: var(--space-10);
    color: var(--color-ink);
    font-size: var(--font-size-stepper-value);
    font-weight: var(--font-weight-stepper-value);
    text-align: center;
}

.stepper-limit {
    margin: var(--space-1) 0 0;
    color: var(--color-ink-muted);
    font-size: var(--font-size-helper);
    line-height: var(--line-height-helper);
}

/* --- Cifra (componente MetricCard) --- */

/* Borde sutil y la cifra en Fraunces; --color-metric pasa de ciruela a magenta-dark en oscuro. */
.metric-card {
    padding: var(--space-3) var(--space-4);
    border: var(--border-width-control) solid var(--color-line);
    border-radius: var(--radius-lg);
    background: var(--color-surface-raised);
}

.metric-card-value {
    margin: 0;
    color: var(--color-metric);
    font-family: var(--font-family-display);
    font-size: var(--font-size-panel-metric);
    font-weight: var(--font-weight-panel-metric);
    line-height: var(--line-height-panel-metric);
}

.metric-card-label {
    margin: 0;
    color: var(--color-ink-muted);
    font-size: var(--font-size-eyebrow);
}

/* --- Fila de lista (componente ListRow) --- */

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

/* El nombre envuelve siempre, aunque sea una sola palabra larguísima: min-width: 0 deja que la
   columna se encoja y overflow-wrap: anywhere corta donde haga falta. Nunca elipsis. */
.list-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) 0;
    border-top: var(--border-width-control) solid var(--color-line);
    color: var(--color-ink);
    font-size: var(--font-size-list-row);
}

.list-row-main {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

.list-row-end {
    flex: none;
}

/* --- Pastilla (componente Pill) --- */

.pill {
    position: relative;
    display: inline-block;
    padding: var(--space-1) var(--space-pill-inline);
    border-radius: var(--radius-full);
    background: var(--color-pill-surface);
    color: var(--color-pill-text);
    font-size: var(--font-size-helper);
    font-weight: var(--font-weight-label);
    line-height: var(--line-height-helper);
}

/* Único uso hoy: la pastilla "Actual" del espacio de la sesión en /panel/espacios, que enlaza a
   /panel. El subrayado al pasar el mouse es la única pista de que es un enlace (la pastilla, como
   insignia, no lo tiene en los demás usos). */
a.pill:hover {
    text-decoration: underline;
}

/* --- Pestañas (componentes TabBar y TabPanel) --- */

/* Riel en plum-100. La pestaña activa se distingue por su borde de 1 px en border-control (3:1
   contra el riel), no por el fondo blanco (1.25:1) ni por una sombra (revisión C-6). Las
   inactivas llevan el borde del color del riel, para que el cambio no mueva el texto. */
.tab-bar {
    display: flex;
    gap: var(--space-tab-rail);
    margin: 0 0 var(--space-4);
    padding: var(--space-tab-rail);
    border-radius: var(--radius-md);
    background: var(--color-plum-100);
}

.tab-bar-tab {
    flex: 1 1 0;
    min-height: var(--space-10);
    padding: 0 var(--space-2);
    border: var(--border-width-control) solid var(--color-plum-100);
    border-radius: var(--radius-sm);
    background: none;
    color: var(--color-neutral);
    font-size: var(--font-size-label);
    font-weight: var(--font-weight-label);
    overflow-wrap: anywhere;
    cursor: pointer;
    transition: background-color var(--duration-tab) ease, border-color var(--duration-tab) ease, color var(--duration-tab) ease;
}

.tab-bar-tab[aria-current="true"] {
    border-color: var(--color-border-control);
    background: var(--color-surface-raised);
    color: var(--color-action-secondary);
}

/* --- Esqueleto (componente Skeleton) --- */

/* Invisible durante los primeros 200 ms, sin JavaScript: la animación de 0 s con retraso y
   relleno "both" lo mantiene oculto hasta que termina el retraso. */
.skeleton {
    margin: 0 0 var(--space-4);
    animation: skeleton-reveal 0s linear var(--duration-skeleton-delay) both;
}

@keyframes skeleton-reveal {
    from {
        visibility: hidden;
    }

    to {
        visibility: visible;
    }
}

.skeleton-metrics {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-3);
    margin: 0 0 var(--space-4);
}

/* El barrido va de derecha a izquierda, en tonos de tokens.json que en oscuro no deslumbran. */
.skeleton-block {
    border-radius: var(--radius-sm);
    background: linear-gradient(to right, var(--color-skeleton-base) 0%, var(--color-skeleton-sweep) 50%, var(--color-skeleton-base) 100%);
    background-size: 200% 100%;
    animation: skeleton-sweep var(--duration-skeleton-sweep) linear infinite;
}

@keyframes skeleton-sweep {
    from {
        background-position: 100% 0;
    }

    to {
        background-position: -100% 0;
    }
}

/* Del alto de una metric-card: 12 + 31 + 17 + 12 px, redondeado a la escala de 8. */
.skeleton-metric {
    min-height: calc(var(--space-12) + var(--space-6));
}

.skeleton-line {
    min-height: var(--space-5);
    margin: 0 0 var(--space-3);
}

/* --- Candado (componente LockOverlay) --- */

/* La lista, el degradado y el mensaje comparten la misma celda de la rejilla: el mensaje queda
   abajo y, si crece, la celda crece con él en vez de desbordarse. El mensaje lleva fondo opaco:
   con una lista corta subiría a la parte translúcida del degradado y su contraste no se mediría. */
.lock-overlay {
    position: relative;
    display: grid;
    margin: 0 0 var(--space-4);
    overflow: hidden;
}

.lock-overlay-content,
.lock-overlay-message,
.lock-overlay::after {
    grid-area: 1 / 1;
}

.lock-overlay-content {
    filter: blur(var(--blur-lock));
    user-select: none;
}

.lock-overlay::after {
    content: "";
    background: linear-gradient(to bottom, var(--color-lock-fade-top) 0%, var(--color-lock-fade-bottom) 55%);
    pointer-events: none;
}

.lock-overlay-message {
    position: relative;
    z-index: 1;
    align-self: end;
    padding: var(--space-4);
    background: var(--color-surface-raised);
    text-align: center;
}

.lock-overlay-reason {
    margin: 0 0 var(--space-3);
    color: var(--color-ink);
    font-weight: var(--font-weight-label);
}

/* --- Barra de avance (componente ProgressBar) --- */

/* Riel en la superficie de alerta y relleno en el color de alerta (más de 3:1 entre los dos, en
   claro y en oscuro). Extremos redondos (rounded.full). El ancho del relleno lo pone el
   componente; no se anima: la cifra cambia al recargar. */
.progress-bar {
    margin: var(--space-3) 0 var(--space-4);
}

.progress-bar-track {
    min-height: var(--space-2);
    overflow: hidden;
    border-radius: var(--radius-full);
    background: var(--color-warning-surface);
}

.progress-bar-fill {
    display: block;
    min-height: var(--space-2);
    border-radius: var(--radius-full);
    background: var(--color-warning);
}

.progress-bar-text {
    margin: var(--space-1) 0 0;
    color: var(--color-ink-muted);
    font-size: var(--font-size-helper);
    line-height: var(--line-height-helper);
}

/* --- QR (componente QrCard) --- */

/* Negro sobre blanco puro también en oscuro: qr-surface y qr-ink no tienen valor oscuro. Sin
   redondeo en el código, que baja la tasa de lectura; el redondeo es del marco. aspect-ratio
   reserva el cuadro del QR mientras carga (el alto ya es automático; la hoja no escribe height). */
.qr-card {
    margin: 0 0 var(--space-4);
    padding: var(--space-5);
    border: var(--border-width-control) solid var(--color-line);
    border-radius: var(--radius-xl);
    background: var(--color-qr-surface);
    text-align: center;
}

.qr-card-image {
    display: block;
    width: var(--size-qr);
    max-width: 100%;
    aspect-ratio: 1 / 1;
    margin: 0 auto;
    border-radius: 0;
    image-rendering: pixelated;
}

.qr-card-link {
    margin: var(--space-3) 0 0;
    color: var(--color-qr-ink);
    font-family: var(--font-family-mono);
    font-size: var(--font-size-body);
    overflow-wrap: anywhere;
}

/* --- Movimiento reducido --- */

/* Las dos únicas animaciones del producto se apagan: el barrido del esqueleto (queda el tono
   base, quieto) y la transición de las pestañas. El retraso de 200 ms no es movimiento y sigue. */
@media (prefers-reduced-motion: reduce) {
    .skeleton-block {
        background: var(--color-skeleton-base);
        animation: none;
    }

    .tab-bar-tab {
        transition: none;
    }

    .dashboard-nav-link {
        transition: none;
    }
}

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

/* --- Entrada (historia 1.4) --- */

.sign-in-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-4);
}

/* --- Campo de varias líneas (TextField con Rows, historia 2.2) --- */

/* El mismo input-text con aire arriba y abajo; crece hacia abajo si la persona lo estira. El
   min-height del control sigue saliendo de la regla de los controles. */
.input-text-multiline {
    padding-block: var(--space-3);
    resize: vertical;
}

.input-text-multiline[aria-invalid="true"] {
    padding-block: calc(var(--space-3) - (var(--border-width-emphasis) - var(--border-width-control)));
}

/* --- Editor de la invitación (historia 2.2; tira horizontal: historia "panel-orden-invitacion") --- */

/* Los grupos del editor (plantilla e itinerario) son fieldset sin el marco del navegador. */
.template-picker,
.itinerary {
    min-width: 0;
    margin: 0 0 var(--space-4);
    padding: 0;
    border: 0;
}

.template-picker>legend,
.itinerary>legend {
    padding: 0;
}

/* Leyenda del grupo "De temporada", antes de la tira: el punto de muestra enseña el código de
   color que luego repite cada tarjeta, como la leyenda de un mapa. */
.template-group-legend {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: var(--space-3) 0 var(--space-2);
    font-weight: var(--font-weight-label);
}

/* El punto de "temporada": magenta de relleno (nunca texto, como manda DESIGN.md), con un anillo
   del color de la superficie para que se distinga sobre cualquier franja de color de abajo. En
   la leyenda es parte del flujo; en la tarjeta, flota en la esquina sin ocupar espacio propio. */
.template-seasonal-marker {
    display: inline-block;
    flex: none;
    width: 10px;
    height: 10px;
    border-radius: var(--radius-full);
    background: var(--color-magenta);
    box-shadow: 0 0 0 2px var(--color-surface-raised);
}

.template-option .template-seasonal-marker {
    position: absolute;
    bottom: var(--space-2);
    right: var(--space-2);
    z-index: 1;
}

/* Tira horizontal con scroll-snap, sin librería de carrusel: el radiogroup nativo sigue siendo
   el mismo (mismo input radio, mismas clases .template-option*), solo cambia su contenedor de
   columna a fila. padding-bottom deja aire para el indicador de scroll del sistema. */
.template-strip {
    display: flex;
    gap: var(--space-3);
    margin: 0 0 var(--space-2);
    padding-bottom: var(--space-2);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
}

/* Tarjeta de radio: franja de color arriba, el radio, el nombre como etiqueta y la descripción
   debajo. La elegida lleva el borde de la acción secundaria; overflow: hidden recorta la franja
   a las esquinas redondeadas de la tarjeta. */
.template-option {
    display: flex;
    flex-direction: column;
    flex: 0 0 auto;
    width: var(--size-template-card);
    scroll-snap-align: start;
    overflow: hidden;
    border: var(--border-width-emphasis) solid var(--color-border-control);
    border-radius: var(--radius-lg);
    background: var(--color-surface-raised);
}

/* La franja de dos tonos (fondo y acento) que esa plantilla ya pinta en la página del invitado;
   cada clase sale del mismo patrón de nombre que usa guest.css. Sin franja propia, el color por
   omisión es el borde de control: nunca queda sin ningún tono. */
.template-swatch {
    flex: none;
    height: var(--size-template-swatch);
    background: linear-gradient(to right, var(--color-border-control) 50%, var(--color-line) 50%);
}

.template-posada-clasica .template-swatch {
    background: linear-gradient(to right, var(--color-plum-900) 50%, var(--color-gold) 50%);
}

.template-bosque-verde .template-swatch {
    background: linear-gradient(to right, var(--color-forest-900) 50%, var(--color-forest-accent) 50%);
}

.template-champan-claro .template-swatch {
    background: linear-gradient(to right, var(--color-champagne) 50%, var(--color-champagne-gold) 50%);
}

.template-festiva-coral .template-swatch {
    background: linear-gradient(to right, var(--color-coral-800) 50%, var(--color-coral-accent) 50%);
}

.template-halloween-slasher .template-swatch {
    background: linear-gradient(to right, var(--color-slasher-bg) 50%, var(--color-slasher-accent) 50%);
}

.template-halloween-infection .template-swatch {
    background: linear-gradient(to right, var(--color-infection-bg) 50%, var(--color-infection-toxic) 50%);
}

.template-halloween-pumpkin-orchard .template-swatch {
    background: linear-gradient(to right, var(--color-orchard-bg) 50%, var(--color-orchard-accent) 50%);
}

.template-halloween-witch-moon .template-swatch {
    background: linear-gradient(to right, var(--color-witch-bg) 50%, var(--color-witch-accent) 50%);
}

.template-option-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 0;
    padding: var(--space-3);
}

.template-option-body input {
    min-width: var(--space-6);
    min-height: var(--space-6);
    margin: 0;
    accent-color: var(--color-magenta);
}

.template-option-name {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--space-touch-min);
    color: var(--color-ink);
    font-weight: var(--font-weight-label);
    cursor: pointer;
}

/* La palabra "Elegida": nunca solo el color del borde marca cuál está elegida (revisión de
   accesibilidad). */
.template-option-chosen {
    font-size: var(--font-size-helper);
    font-weight: var(--font-weight-label);
    color: var(--color-action-secondary-border);
}

.template-option-description {
    margin: 0;
    font-size: var(--font-size-helper);
    line-height: var(--line-height-helper);
}

/* Pastilla de etiqueta de contenido: debajo del nombre, antes de la descripción (Slasher e
   Infección); informativa, no bloquea. */
.content-label-pill {
    width: fit-content;
}

/* La plantilla de temporada que ya tenía la invitación pero dejó de estar vigente: se conserva,
   con el aviso de que ya no se ofrece para eventos nuevos. */
.template-option-note {
    margin: 0;
    color: var(--color-ink-muted);
    font-size: var(--font-size-helper);
    line-height: var(--line-height-helper);
}

/* Toda la tarjeta es tocable (el <label> envuelve la tarjeta completa): se oculta el círculo del
   radio sin quitarlo del recorrido de foco ni del árbol de accesibilidad. */
.template-option {
    position: relative;
    cursor: pointer;
}

.template-option input[type="radio"] {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
    margin: 0;
    padding: 0;
    border: 0;
}

/* Resaltar la tarjeta seleccionada: mismo borde de la acción secundaria (ciruela) que ya usa el
   resto del panel, con un halo tenue del mismo tono. */
.template-option:has(input:checked) {
    border-color: var(--color-action-secondary-border);
    box-shadow: 0 0 0 2px rgb(90 42 79 / 15%);
}

.template-option:hover {
    border-color: var(--color-action-secondary-border);
}

/* El radio queda invisible (0×0): el foco de teclado se marca en la tarjeta, no en el radio,
   con el mismo anillo de foco global (línea 836). */
.template-option:has(input:focus-visible) {
    outline: var(--border-width-focus-ring) solid var(--color-focus-ring);
    outline-offset: var(--space-focus-ring-offset);
}

.template-option:has(input:disabled) {
    cursor: not-allowed;
    opacity: 0.6;
}

/* Cada renglón del itinerario: fieldset sin marco, con un div interior en grid. */
.itinerary-item {
    min-width: 0;
    margin: 0 0 var(--space-3);
    border: 0;
    padding: 0;
}

/* Grid: 65 % "Qué pasa" | 25 % "Hora" | auto botón de borrar. */
.itinerary-row {
    display: grid;
    grid-template-columns: 65fr 25fr auto;
    gap: var(--space-3);
    align-items: end;
}

/* El botón de borrar no tiene margen inferior propio; lo hereda del .field del texto. */
.itinerary-row>.button-ghost {
    margin-bottom: var(--space-4);
    text-decoration: none;
    flex-shrink: 0;
}

.itinerary>.field-error {
    margin: 0 0 var(--space-3);
}

/* --- Foto del evento (PhotoPicker, historia 2.3) --- */

/* La miniatura ocupa el ancho de la columna y nunca la desborda; sin sombra, con el radio de las
   tarjetas. El control de archivo toma el min-height de la regla de los controles. */
.photo-picker-thumbnail {
    display: block;
    width: 100%;
    max-width: 100%;
    margin: 0 0 var(--space-3);
    border: var(--border-width-control) solid var(--color-line);
    border-radius: var(--radius-lg);
    background: var(--color-surface-raised);
}

.photo-picker-empty {
    margin: 0 0 var(--space-3);
    padding: var(--space-4);
    border: var(--border-width-control) dashed var(--color-border-control);
    border-radius: var(--radius-lg);
    color: var(--color-ink-muted);
    font-size: var(--font-size-helper);
    line-height: var(--line-height-helper);
}

.photo-picker-input {
    display: block;
    width: 100%;
    max-width: 100%;
}

.photo-picker-progress {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin: 0 0 var(--space-3);
    color: var(--color-ink);
    font-size: var(--font-size-helper);
}

.photo-picker-progress progress {
    flex: 1;
    min-width: 0;
    accent-color: var(--color-magenta);
}

.photo-picker-notice {
    display: block;
    margin: 0 0 var(--space-3);
    color: var(--color-ink);
    font-size: var(--font-size-helper);
    line-height: var(--line-height-helper);
}

/* La pantalla del evento (Historia 6.0): el encabezado y, debajo, las pestañas. El nombre y la
   píldora van en una fila que envuelve; cuándo, dónde y hasta cuándo, en tinta tenue (el <p> ya lo
   pinta así); "Compartir" y "Ver mi invitación", debajo, como acciones de segundo nivel. */
.event-header {
    margin: 0 0 var(--space-4);
}

.event-header-title {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    margin: 0 0 var(--space-2);
}

.event-header-title h1 {
    margin: 0;
}

.event-header-detail {
    margin: 0 0 var(--space-1);
}

.event-header-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-3);
}

/* Son enlaces con la forma de button-secondary: centrados, de la altura de un botón, sin subrayado.
   Uno al lado del otro si caben; si no, uno debajo del otro, a lo ancho. */
.event-header-actions .button-secondary {
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    justify-content: center;
    width: auto;
    min-height: var(--space-control-height);
    text-decoration: none;
}

/* Pestaña Ajustes: el enlace arriba, y debajo el formulario del evento con su zona de borrar. */
.event-link-settings {
    margin: 0 0 var(--space-8);
}

.event-link-settings h2 {
    margin: 0 0 var(--space-2);
    font-size: var(--font-size-body);
    font-weight: var(--font-weight-label);
}

/* Regenerar el enlace (Historia 3.1): un segundo toque en la misma página, sin zona de peligro,
   porque aquí no se pierde ningún dato. */
.regenerate-link {
    margin-top: var(--space-4);
}

.regenerate-link button+button {
    margin-top: var(--space-2);
}

/* La consecuencia va en cuerpo, no en el texto de ayuda: es la frase que hay que leer antes de
   romper algo que ya está allá afuera. */
.regenerate-link-consequence {
    margin: 0 0 var(--space-3);
    color: var(--color-ink);
    font-size: var(--font-size-body);
    line-height: var(--line-height-body);
}

/* Fila del botón Guardar con el indicador de auto-guardado. */
.save-row {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
}

.save-status {
    font-size: var(--font-size-label);
    color: var(--color-ink-muted);
}

.save-status--ok {
    color: var(--color-success);
}

/* Vista previa (Historia 2.5), ahora dentro del mismo <EditForm>, entre la plantilla y Guardar;
   sin margen, relleno ni línea propios: queda pegada a la tira de plantillas. */

.invitation-preview h2 {
    margin: 0 0 var(--space-2);
    font-size: var(--font-size-body);
    font-weight: var(--font-weight-label);
}

.invitation-preview-pending {
    margin: 0 0 var(--space-3);
    color: var(--color-ink);
    font-size: var(--font-size-helper);
    line-height: var(--line-height-helper);
}

/* La página real del invitado, al ancho de un celular; en pantallas más angostas, el ancho que haya. */
.invitation-preview-frame {
    display: block;
    width: var(--layout-preview-width);
    max-width: 100%;
    min-height: var(--layout-preview-height);
    margin: 0 0 var(--space-3);
    border: var(--border-width-control) solid var(--color-line);
    border-radius: var(--radius-lg);
}

/* --- Encabezado de la pestaña Invitación y el switch de publicar (historia "panel-orden-invitacion") --- */

/* El título y el switch en una fila; el estado y el aviso de error quedan debajo, a lo ancho. */
.invitation-tab-header {
    margin: 0 0 var(--space-6);
}

.invitation-tab-header-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-3);
    margin: 0 0 var(--space-2);
}

.invitation-tab-header-row h2 {
    margin: 0;
}

.publish-switch-group {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.publish-switch-label {
    color: var(--color-ink);
    font-size: var(--font-size-label);
    font-weight: var(--font-weight-label);
}

.invitation-publication-state {
    margin: 0 0 var(--space-3);
}

/* switch (componente nativo, role="switch"): pista de 44×24, pulgar de 20. El botón en sí mide
   el mínimo táctil de 44 px (DESIGN.md: hitArea); la pista visual es más chica y se dibuja con
   el pseudo-elemento ::before del propio botón para no depender de más marcado. */
.switch {
    position: relative;
    display: inline-flex;
    align-items: center;
    min-width: var(--space-touch-min);
    min-height: var(--space-touch-min);
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
}

.switch::before {
    content: "";
    display: block;
    width: var(--size-switch-track-width);
    height: var(--size-switch-track-height);
    border-radius: var(--radius-full);
    background: var(--color-border-control);
    transition: background-color var(--duration-tab) ease;
}

.switch[aria-checked="true"]::before {
    background: var(--color-magenta);
}

.switch-thumb {
    position: absolute;
    top: 50%;
    left: calc((var(--space-touch-min) - var(--size-switch-track-width)) / 2 + 2px);
    width: var(--size-switch-thumb);
    height: var(--size-switch-thumb);
    border-radius: var(--radius-full);
    background: var(--color-on-accent);
    transform: translateY(-50%);
    transition: left var(--duration-tab) ease;
}

.switch[aria-checked="true"] .switch-thumb {
    left: calc((var(--space-touch-min) - var(--size-switch-track-width)) / 2 + var(--size-switch-track-width) - var(--size-switch-thumb) - 2px);
}

.switch[disabled] {
    cursor: not-allowed;
}

/* --- Modal de publicar: <dialog> nativo (showModal()/close(), sin librería de terceros) --- */

.publish-modal {
    width: min(92vw, 28rem);
    padding: var(--space-6) var(--space-4);
    border: 0;
    border-radius: var(--radius-xl);
    background: var(--color-surface-raised);
    color: var(--color-ink);
}

.publish-modal::backdrop {
    background: var(--color-ink);
    opacity: 0.5;
}

.publish-modal h2 {
    margin: 0 0 var(--space-2);
    font-size: var(--font-size-body);
    font-weight: var(--font-weight-label);
}

.publish-modal-actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-top: var(--space-4);
}

/* La casilla de los términos de uso (Historia 2.6), ahora dentro del modal: la casilla y su
   etiqueta visible en un renglón tocable de 44 px, el enlace a los términos debajo y el error
   después. */
.terms-option {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: var(--space-touch-min);
}

.terms-option input {
    flex: none;
    min-width: var(--space-6);
    min-height: var(--space-6);
    margin: 0;
    accent-color: var(--color-magenta);
}

.terms-option-label {
    display: flex;
    align-items: center;
    min-height: var(--space-touch-min);
    color: var(--color-ink);
    font-weight: var(--font-weight-label);
    cursor: pointer;
}

.terms-link {
    display: inline-flex;
    align-items: center;
    min-height: var(--space-touch-min);
    color: var(--color-accent-text);
}

/* --- Borrar el evento (DeleteEventZone, historia 2.7) --- */

/* Al final de la edición del evento, separada del formulario como las secciones del editor. */
.danger-zone {
    margin: var(--space-8) 0 0;
    padding: var(--space-6) 0 0;
    border-top: var(--border-width-control) solid var(--color-line);
}

.danger-zone-title {
    margin: 0 0 var(--space-2);
    font-size: var(--font-size-body);
    font-weight: var(--font-weight-label);
}

/* --- Confirmaciones (RsvpOverview, historia 4.1) --- */

/* Las cifras en dos columnas, igual que el esqueleto: al cargar no brinca nada. */
.rsvp-metrics {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-3);
    margin: 0 0 var(--space-4);
}

/* El total ocupa la fila completa: es el número con el que se pide la comida. */
.rsvp-metrics .rsvp-metric-total {
    grid-column: 1 / -1;
}

.rsvp-list-title {
    margin: var(--space-6) 0 var(--space-2);
    font-size: var(--font-size-body);
    font-weight: var(--font-weight-label);
}

.rsvp-row-detail {
    display: block;
    color: var(--color-ink-muted);
    font-size: var(--font-size-helper);
    line-height: var(--line-height-helper);
}

.rsvp-overview>a+a,
.rsvp-overview .notice+a {
    margin-top: var(--space-2);
}

.rsvp-retry {
    margin-top: var(--space-3);
}

/* --- Buscar, filtrar y ajustar (RsvpOverview, historia 4.2) --- */

.rsvp-closed,
.rsvp-feedback {
    margin: 0 0 var(--space-4);
}

/* Descargar Excel (Historia 4.3): junto a las cifras, a lo ancho. Es un enlace, así que toma aquí
   la altura y el centrado que los botones reciben de la regla de los controles. */
.rsvp-export {
    margin: 0 0 var(--space-4);
}

.rsvp-export .button-secondary {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--space-control-height);
    text-decoration: none;
}

/* Las pestañas del filtro miden al menos 44 px: aquí se tocan con el pulgar. */
.rsvp-filter .tab-bar-tab {
    min-height: var(--space-touch-min);
}

.rsvp-clear {
    margin-top: var(--space-2);
}

/* Las acciones van debajo del nombre, no a su lado: así el nombre conserva el ancho y envuelve a
   dos líneas en vez de apretarse (UX-DR14). */
.rsvp-row-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-2);
}

.rsvp-row-actions .button-ghost,
.rsvp-row-actions .button-secondary {
    min-width: var(--space-touch-min);
    min-height: var(--space-touch-min);
}

/* Íconos de ajustar y borrar en el slot End de la fila, en línea con el nombre. */
.rsvp-row-icons {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.rsvp-row-icons .button-ghost {
    min-width: var(--space-touch-min);
    min-height: var(--space-touch-min);
    padding: var(--space-1);
}

.rsvp-adjust,
.rsvp-confirm-delete {
    margin-top: var(--space-3);
}

.rsvp-confirm-delete p {
    margin: 0;
    font-size: var(--font-size-helper);
    line-height: var(--line-height-helper);
}

.rsvp-attendance {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    margin: 0 0 var(--space-4);
    padding: 0;
    border: 0;
}

.rsvp-attendance legend {
    width: 100%;
    padding: 0;
}

/* Cada opción es una zona táctil completa de 44 px, con la etiqueta incluida. */
.rsvp-attendance-option {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--space-touch-min);
    min-width: var(--space-touch-min);
    cursor: pointer;
}

/* --- El plan gratis (RsvpOverview, historia 4.4) --- */

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

.rsvp-plan-actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

/* "Activar el plan" es un enlace con forma de botón: toma aquí la altura y el centrado que los
   botones reciben de la regla de los controles, igual que "Descargar Excel". */
.rsvp-plan-actions .button-primary {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--space-control-height);
    text-decoration: none;
}

/* El correo es la salida secundaria: un enlace de texto con zona táctil de 44 px. */
.rsvp-plan-actions .rsvp-plan-email:not(.button-primary) {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--space-touch-min);
}

/* Compartir (Historia 5.1): WhatsApp arriba, copiar debajo, y el mensaje en un cuadro que se puede
   seleccionar a mano si el portapapeles no está. */
.share-overview .button-primary {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--space-control-height);
    text-decoration: none;
}

.share-lead {
    margin: 0 0 var(--space-4);
}

.share-copy {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-top: var(--space-2);
}

.share-message {
    margin-top: var(--space-4);
}

.share-message label {
    display: block;
    margin-bottom: var(--space-1);
    font-weight: 700;
}

.share-message textarea {
    box-sizing: border-box;
    width: 100%;
    padding: var(--space-3);
    font: inherit;
    color: var(--color-ink);
    background: var(--color-field-surface);
    border: var(--border-width-control) solid var(--color-border-control);
    border-radius: var(--radius-md);
    resize: vertical;
}

/* El enlace escrito va en monoespaciada y se rompe donde haga falta: nunca se corta. */
.share-link {
    margin: var(--space-3) 0 0;
    font-family: var(--font-family-mono);
    overflow-wrap: anywhere;
}

/* QR y hoja imprimible (Historia 5.2). En pantalla la hoja no se ve; al imprimir, es lo único que
   se ve: tamaño carta, con el QR de 12 cm (el mínimo de la spec es 4 cm) en negro sobre blanco
   puros. Sin PDF: el navegador imprime esta misma página. */
.share-heading {
    margin: var(--space-5) 0 var(--space-2);
}

.share-overview .button-secondary {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--space-touch-min);
    text-decoration: none;
}

.print-sheet {
    display: none;
}

@page {
    size: letter;
    margin: 1.5cm;
}

@media print {

    /* visibility no libera el espacio de lo oculto: sin recortar la altura, un panel largo
       imprimiría hojas en blanco después de la del QR. Y en modo oscuro el papel sale blanco. */
    html,
    body {
        max-height: 100vh;
        overflow: hidden;
        color: var(--color-qr-ink);
        background: var(--color-qr-surface);
    }

    body * {
        visibility: hidden;
    }

    .print-sheet,
    .print-sheet * {
        visibility: visible;
    }

    .print-sheet {
        display: block;
        position: absolute;
        inset: 0 0 auto 0;
        color: var(--color-qr-ink);
        background: var(--color-qr-surface);
        text-align: center;
    }

    .print-sheet-title {
        margin: 0 0 0.4cm;
        font-size: 28pt;
        line-height: 1.15;
    }

    .print-sheet-when {
        margin: 0 0 1cm;
        font-size: 18pt;
    }

    .print-sheet-lead {
        margin: 0 0 0.6cm;
        font-size: 20pt;
        font-weight: 700;
    }

    .print-sheet-qr {
        margin: 0 auto;
        padding: 0;
        border: 0;
        background: var(--color-qr-surface);
    }

    .print-sheet-qr .qr-card-image {
        width: 12cm;
        max-width: none;
    }

    .print-sheet-qr .qr-card-link {
        margin-top: 0.6cm;
        color: var(--color-qr-ink);
        font-size: 16pt;
    }
}