/* Inter self-hosted. Removed 2026-08-30 the @import that fetched the font
   CSS + binary from fonts.googleapis.com / fonts.gstatic.com — every EU
   visitor was sending their IP to Google servers just to load a font, a
   real GDPR concern with actual case-law precedent (multiple German court
   decisions have sanctioned Google Fonts hotlinking).
   Now: two .woff2 files served from /fonts/ (same-origin), one variable
   binary per unicode subset. Covers weights 100-900 for latin + latin-ext
   in ~130 KB total. See public/fonts/LICENSE.txt for OFL attribution. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/inter-latin.woff2') format('woff2');
  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;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* Components that set their own `display` silently defeat the hidden
   attribute; this keeps `el.hidden = true` meaning what it says. */
[hidden] { display: none !important; }

:root {
  /* Surfaces — never pure black; it smears on OLED and kills depth */
  --bg:        #0a0a0f;
  --surface:   #121218;
  --surface-2: #191922;
  --surface-3: #22222d;
  --line:      rgba(255,255,255,.06);
  --line-2:    rgba(255,255,255,.12);

  /* Shared page gutter · header, footer AND body all consume it.
     One token, three surfaces, no drift. Adjusts at breakpoints below. */
  --page-gutter: 20px;

  /* Type */
  --text:   #f4f4f7;
  --muted:  #9a9aa8;
  --muted-2:#83838f;

  /* Brand: violet → cyan. Green is reserved for savings, so a number in
     green always means "you gained something".
     --brand is the ONE canonical brand hex. Every other token, gradient,
     shadow, and rgba() derived from it consumes --brand (or --brand-rgb
     when transparency is needed) instead of duplicating the literal.
     Changing --brand alone shifts the entire violet accent across every
     surface — that is the single-source contract of the UI Constitution. */
  --brand:     #7c5cff;
  --brand-rgb: 124, 92, 255;
  --brand-2:   #4dd9ff;
  --brand-soft: rgba(var(--brand-rgb), .14);
  --grad:      linear-gradient(135deg, var(--brand) 0%, var(--brand-2) 100%);

  --good: #00e5a0;
  --warn: #ffb74a;
  --bad:  #ff5c6e;

  --sidebar: 340px;
  --header: 58px;

  --ease: cubic-bezier(.16, 1, .3, 1);
  --r-sm: 8px;
  --r:    12px;
  --r-lg: 18px;
  --r-xl: 26px;

  /* BATCH-UI-1 fix (2026-08-26): tokens de espaciado y sizing GLOBALES.
     Antes vivían solo en `.sidebar` — cualquier vista fuera del sidebar
     (Batch, landing, futuras páginas) los recibía como cadena vacía y
     `gap: var(--sp-4)` caía silenciosamente a `normal` = 0. Root scope
     evita ese fallo en toda la app. */
  --sp-0: 4px; --sp-1: 8px; --sp-2: 12px; --sp-3: 20px; --sp-4: 28px;
  --h-ctl: 44px;
  --pm-h-sm: 32px; --pm-h-md: 36px; --pm-h-lg: 48px;
  --pm-r-md: 8px;  --pm-r-lg: 10px;
  --pm-fs-11: 11px; --pm-fs-12: 12px; --pm-fs-13: 13px; --pm-fs-14: 14px;

  /* ═══ pm-menu · canonical menu-component tokens (ACCOUNT-UX-2, 2026-08-30) ══
     Consumed by every menu-shaped surface: avatar dropdown, account rail,
     mobile section selector, admin tabs, filter chips, footer language
     switcher. Change a value here and every menu in the product moves in
     lockstep — no per-surface drift. Documented in UI_CONSTITUTION.md.        */
  --pm-menu-item-h: 40px;
  --pm-menu-item-py: 10px;
  --pm-menu-item-px: 14px;
  --pm-menu-item-gap: 12px;
  --pm-menu-item-fs: 14px;
  --pm-menu-item-fw: 500;
  --pm-menu-item-fw-on: 600;
  --pm-menu-item-r: 8px;
  --pm-menu-icon-size: 18px;
  --pm-menu-container-p: 8px;
  --pm-menu-container-r: 12px;
  --pm-menu-hover-bg: color-mix(in oklab, var(--brand) 8%, transparent);
  --pm-menu-active-bg: color-mix(in oklab, var(--brand) 12%, transparent);
  --pm-menu-active-icon: var(--brand-2);
}

html {
  scroll-behavior: smooth;
  /* MOBILE-0: iOS auto-adjusta el tamaño del texto en rotación landscape.
     Fijarlo en 100% deja el diseño responsivo con `clamp()` en control. */
  -webkit-text-size-adjust: 100%;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-feature-settings: 'cv11', 'ss01';
  -webkit-font-smoothing: antialiased;
  letter-spacing: -.011em;
  min-height: 100vh;
  /* MOBILE-0 · iOS polish global:
     · `-webkit-tap-highlight-color: transparent` elimina el flash gris opaco
       que Safari mobile pinta encima de cualquier elemento tocado — cada
       componente ya provee su propio feedback (hover/active), el flash
       nativo empeora la sensación de app nativa.
     · `overscroll-behavior-y: contain` corta el "rubber-band" del body que
       en iOS revela el background del navegador cuando el usuario sigue
       arrastrando al llegar al tope — aísla el scroll a la vista actual.
     · `touch-action: manipulation` (aplicado a controles interactivos abajo)
       elimina el retardo de 300ms del doble-tap heredado de iOS. */
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: contain;
}
button, a, input, label, summary, [role="button"] {
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
body[data-view="editor"] { overflow: hidden; }
/* MOBILE-1: en desktop la sidebar tiene su propio scroll interno y bloqueamos
   el body para que solo esa columna scrollee. En mobile el patrón se
   invierte: la sidebar deja de scrollear (overflow:visible), el body
   scrollea la página entera, y .sb-foot flota anclado como sticky bar. */
@media (max-width: 940px) {
  body[data-view="editor"] { overflow: visible; }
}

/* Static gradient mesh — pure paint, no animation, no blur filters. */
.bg-mesh {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(58vw 46vh at 14% -8%,  rgba(var(--brand-rgb),.20), transparent 62%),
    radial-gradient(52vw 42vh at 88% 4%,   rgba(77,217,255,.13), transparent 60%),
    radial-gradient(46vw 40vh at 50% 108%, rgba(0,229,160,.07),  transparent 62%);
}
body[data-view="editor"] .bg-mesh { opacity: .45; }

::selection { background: rgba(var(--brand-rgb),.35); }

:focus-visible {
  outline: 2px solid var(--brand-2);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ══ Header ═══════════════════════════════════════════════════════════════ */
.site-header {
  position: sticky; top: 0; z-index: 50;
  height: var(--header);
  display: flex; align-items: center; gap: 16px;
  /* MOBILE-0: safe-area padding para iPhones notched. En landscape también
     protege del botón home/gesture bar lateral. viewport-fit=cover en el meta
     activa env(safe-area-inset-*) correctamente. */
  padding: 0 max(var(--page-gutter), env(safe-area-inset-right)) 0 max(var(--page-gutter), env(safe-area-inset-left));
  /* Owner decision 2026-08-30 (revised): true crystalline fade, no hard
     line at the bottom. The tint + backdrop-blur live on a ::before
     pseudo-element that extends 24px BELOW the header rect and gets a
     mask-image gradient — opaque behind the logo/avatar area, softly
     transparent at the bottom edge. Content sits at z-index 1 on top
     so text stays crisp. Both themes use the same technique with a
     different tint colour (overrides further down flip the ::before
     background). */
  background: transparent;
  border-bottom: 0;
  isolation: isolate;    /* keeps ::before z-index local to the header */
}
.site-header::before {
  content: '';
  position: absolute;
  inset: 0 0 -28px 0;    /* extends 28px below the header for the fade tail */
  z-index: -1;
  background: color-mix(in oklab, var(--bg, #0a0a0f) 82%, transparent);
  backdrop-filter: blur(22px) saturate(180%);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 62%, transparent 100%);
          mask-image: linear-gradient(180deg, #000 0%, #000 62%, transparent 100%);
  pointer-events: none;
}
.site-header > * { position: relative; z-index: 1; }

.brand { display: flex; align-items: center; gap: 10px; cursor: pointer; background: none; border: 0; padding: 0; text-decoration: none; color: inherit; }
/* PUBLIC-SHELL-0: en el header de contenido (Legal, futuras public pages),
   el spacer flex-grow empuja account/theme al extremo derecho — misma
   geometría que .header-file del editor. Sin él, brand+account+theme
   quedaban comprimidos a la izquierda. */
.header-spacer { flex: 1; min-width: 0; }
/* El logo es un SVG con los trazados de la tipografía incrustados: no depende
   de que cargue Inter, no se puede seleccionar y es idéntico en todo sistema.
   "Pixel" hereda currentColor y sigue al tema; "Minify" lleva el degradado. */
.brand-mark { display: none; height: 26px; width: auto;
  color: var(--text); overflow: visible; }

.brand-logo {
  height: 26px; width: auto;
  display: block; color: var(--text); overflow: visible;
  transition: transform .25s var(--ease);
}
.hero-logo {
  height: clamp(46px, 6.4vw, 64px); width: auto;
  display: block; margin: 0 auto 22px; color: var(--text); overflow: visible;
}
.brand:hover .brand-logo { transform: scale(1.04); }
.brand-name {
  font-size: 15px; font-weight: 700; letter-spacing: -.02em; color: var(--text);
  white-space: nowrap;
}
.brand-name em {
  font-style: normal;
  background: var(--grad); -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* SIDEBAR-DS-1: el nav Studio/Batch del header fue retirado — el modo lo
   decide el software (1 imagen → Studio, ≥2 → Batch). Las reglas se dejan
   vacías para no reintroducir estilos huérfanos si alguna extensión futura
   reutiliza el selector. */

.header-file { margin-left: auto; display: flex; align-items: center; gap: 8px; min-width: 0; }
.header-file .fname {
  font-size: 12.5px; font-weight: 600;
  max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chip {
  font-size: 11px; font-weight: 600; color: var(--muted);
  border: 1px solid var(--line-2); border-radius: 100px; padding: 3px 9px; white-space: nowrap;
}
.chip.warn { color: var(--warn); border-color: rgba(255,183,74,.35); }
@media (max-width: 720px) { .header-file .chip { display: none; } }

/* Landing (desktop) keeps the giant wordmark + dropzone as the whole front
   door: header is transparent + zero-height, and #account docks fixed in
   the top-right so the CTA pair paints on the first byte without adding
   any visual weight over the hero. Mobile inherits none of this — see the
   mobile media query below, which restores a stable app chrome instead. */
/* Landing AT THE TOP: header is invisible chrome — the hero logo owns
   the identity. Height 0, no tint, #account docks fixed top-right. */
body[data-view="landing"] .site-header {
  background: none;
  border-bottom: 0;
  height: 0;
  padding: 0;
  transition: height .28s cubic-bezier(.2,.7,.3,1);
}
body[data-view="landing"] .site-header::before {
  opacity: 0;
  transition: opacity .28s cubic-bezier(.2,.7,.3,1);
}
body[data-view="landing"] .site-header > .brand,
body[data-view="landing"] .site-header > .header-file,
body[data-view="landing"] .site-header > .header-spacer { display: none; }
body[data-view="landing"] .site-header > #account {
  position: fixed;
  top:   max(14px, env(safe-area-inset-top));
  right: max(16px, env(safe-area-inset-right));
  z-index: 60;
  transition: top .28s cubic-bezier(.2,.7,.3,1), right .28s cubic-bezier(.2,.7,.3,1);
}
/* Landing PAST THE HERO: JS adds body.past-hero once the hero logo
   scrolls off. The header animates into the same blurred fade chrome
   every other page uses — brand back, #account back in the flex flow. */
body[data-view="landing"].past-hero .site-header {
  height: var(--header);
  padding: 0 max(var(--page-gutter), env(safe-area-inset-right)) 0 max(var(--page-gutter), env(safe-area-inset-left));
}
body[data-view="landing"].past-hero .site-header::before { opacity: 1; }
body[data-view="landing"].past-hero .site-header > .brand   { display: flex; }
body[data-view="landing"].past-hero .site-header > .header-spacer { display: block; }
/* #account stays fixed top-right ALWAYS on landing — do NOT re-position
   it when the header wakes. Moving the avatar mid-scroll is jarring;
   the fixed pill sits at the same coordinates whether the header is
   invisible (top of page) or blurred-visible (scrolled). Owner rule. */

/* Legacy floating-controls slot is unused now that #account itself becomes
   the fixed top-right dock on landing. Kept in the template for backward
   compat with older cached HTML; hidden so nothing paints in the corner. */
.floating-controls { display: none; }

.hero-brand {
  /* flex, not inline-flex: the eyebrow badge that follows is itself inline and
     would otherwise sit on the same line as the wordmark. */
  display: flex; align-items: center; justify-content: center; gap: 14px;
  margin-bottom: 22px;
}
.hero-brand img { width: clamp(46px, 7vw, 64px); height: auto; display: block; }
.hero-brand span {
  font-size: clamp(28px, 4.4vw, 40px); font-weight: 800; letter-spacing: -.035em;
}
.hero-brand em {
  font-style: normal;
  background: var(--grad); -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* ══ Views ════════════════════════════════════════════════════════════════ */
.view { display: none; }
body[data-view="landing"] #landing { display: block; }
body[data-view="editor"]  #editor  { display: grid; }
body[data-view="batch"]   #batch   { display: block; }

/* ══ Landing ══════════════════════════════════════════════════════════════ */
.hero {
  max-width: 1080px; margin: 0 auto;
  /* padding lateral usa clamp: 16px en mobile pequeño, 24 en tablet/desktop.
     Da 40-70px extra a locales con palabras largas sin desbordar. */
  padding: clamp(40px, 7vh, 80px) clamp(16px, 4vw, 24px) 0;
  text-align: center;
}

.eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 12px; font-weight: 600; color: var(--muted);
  border: 1px solid var(--line-2); border-radius: 100px;
  padding: 6px 14px; margin-bottom: 26px;
  background: rgba(255,255,255,.02);
}
.eyebrow .dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--good);
  box-shadow: 0 0 0 3px rgba(0,229,160,.18);
}

.hero h1 {
  /* clamp min 20px para que compuestos alemanes ("komprimieren",
     "schützen", "Hunderte") quepan en 375 con padding 16px lateral. */
  font-size: clamp(20px, 6.2vw, 62px);
  font-weight: 800; line-height: 1.08; letter-spacing: -.03em;
  margin-bottom: 18px;
  overflow-wrap: break-word;
  hyphens: auto;
  -webkit-hyphens: auto;
}
.hero h1 em {
  font-style: normal;
  background: var(--grad); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.hero .sub {
  font-size: clamp(15px, 1.7vw, 18px); line-height: 1.6; color: var(--muted);
  max-width: 620px; margin: 0 auto 38px;
}

/* Drop zone — the hero's centrepiece, so it gets the only moving accent. */
.dropzone {
  position: relative;
  border-radius: var(--r-xl);
  background: linear-gradient(180deg, rgba(255,255,255,.045), rgba(255,255,255,.015));
  border: 1px solid var(--line-2);
  padding: clamp(38px, 6vw, 62px) 28px;
  cursor: pointer;
  /* Landing dropzone hereda text-align:center via .hero; el batch dropzone
     vive fuera de .hero y por eso nacía alineado a la izquierda. Centrado
     como propiedad del dropzone soluciona el batch sin cambiar landing. */
  text-align: center;
  transition: transform .3s var(--ease), border-color .3s var(--ease), box-shadow .3s var(--ease);
  overflow: hidden;
}
.dropzone::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  padding: 1px;
  background: var(--grad);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  opacity: 0; transition: opacity .3s var(--ease);
}
.dropzone:hover, .dropzone.drag-over {
  transform: translateY(-2px);
  border-color: transparent;
  box-shadow: 0 18px 50px -18px rgba(var(--brand-rgb),.55);
}
.dropzone:hover::before, .dropzone.drag-over::before { opacity: 1; }
.dropzone.drag-over { transform: scale(1.012); }

.dz-icon {
  width: 60px; height: 60px; margin: 0 auto 20px;
  border-radius: 18px; display: grid; place-items: center;
  background: var(--brand-soft); color: var(--brand-2);
  transition: transform .3s var(--ease);
}
.dropzone:hover .dz-icon { transform: translateY(-3px) scale(1.05); }
.dropzone h2 { font-size: clamp(17px, 2.2vw, 21px); font-weight: 700; letter-spacing: -.02em; margin-bottom: 8px; }
.dropzone p  { font-size: 13.5px; color: var(--muted); }

.dz-formats { display: flex; gap: 6px; justify-content: center; margin-top: 22px; flex-wrap: wrap; }
.dz-formats span {
  font-size: 10.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted-2); border: 1px solid var(--line); border-radius: 100px; padding: 4px 10px;
}
input[type=file] { display: none; }

/* ══ Homepage capabilities card ═══════════════════════════════════════════
   Sits directly below the samples row on the homepage, replaces the old
   `.hero-facts` strip. Owner-approved 2026-08-29 as post-M1 polish.
   Homepage-only: no other route consumes this markup or CSS. Uses ONLY
   existing PixelMinify tokens (--surface*, --line*, --brand, --brand-rgb,
   --brand-2, --grad, --text, --muted, --r-lg). No new palette. */
.pm-capabilities {
  max-width: 720px;
  margin: 40px auto 0;
  text-align: left;
}
.pm-cap-card {
  background: linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,.015));
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  overflow: hidden;
  container-type: inline-size;   /* stats + bullets reflow based on card width */
}
.pm-cap-top {
  padding: clamp(24px, 4vw, 40px);
  display: flex; flex-direction: column; gap: 18px;
}
.pm-cap-h2 {
  font-size: clamp(24px, 3vw, 32px);
  font-weight: 800; letter-spacing: -.03em; line-height: 1.15;
  color: var(--text); margin: 0;
  text-wrap: balance;
}
.pm-cap-h2-muted { color: var(--muted); font-weight: 700; }
.pm-cap-body {
  font-size: 15px; line-height: 1.6; color: var(--muted);
  margin: 0; max-width: 560px;
}
.pm-cap-bullets {
  list-style: none; padding: 0; margin: 4px 0 0;
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px 24px;
}
.pm-cap-bullets li {
  display: flex; align-items: flex-start; gap: 10px;
  font-size: 13.5px; color: var(--text); line-height: 1.4;
  position: relative; padding-left: 26px;
}
.pm-cap-bullets li::before {
  content: '';
  position: absolute; left: 0; top: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  background: rgba(var(--brand-rgb), .16);
  border: 1px solid rgba(var(--brand-rgb), .34);
}
.pm-cap-bullets li::after {
  content: '';
  position: absolute; left: 4px; top: 5px;
  width: 8px; height: 4px;
  border-left: 1.6px solid var(--brand-2);
  border-bottom: 1.6px solid var(--brand-2);
  transform: rotate(-45deg);
}
.pm-cap-stats {
  display: grid; grid-template-columns: repeat(3, 1fr);
  border-top: 1px solid var(--line);
  background: rgba(255,255,255,.015);
}
.pm-cap-stat {
  padding: 22px clamp(14px, 2.5vw, 24px);
  border-right: 1px solid var(--line);
  text-align: left;
  min-width: 0;
}
.pm-cap-stat:last-child { border-right: none; }
.pm-cap-stat-n {
  display: block;
  font-size: clamp(24px, 3vw, 32px);
  font-weight: 800; letter-spacing: -.03em; line-height: 1;
  margin-bottom: 6px;
  font-variant-numeric: tabular-nums;
  background: var(--grad);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.pm-cap-stat-l { font-size: 12.5px; color: var(--muted); line-height: 1.4; }

/* Container-fit responsive: stack bullets + stats when the card gets
   narrow (matches the samples-carousel approach — decision by measured
   fit, not viewport). */
@container (max-width: 480px) {
  .pm-cap-bullets { grid-template-columns: 1fr; }
  .pm-cap-stats { grid-template-columns: 1fr; }
  .pm-cap-stat {
    border-right: none;
    border-top: 1px solid var(--line);
    padding: 18px 20px;
  }
  .pm-cap-stat:first-child { border-top: none; }
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .pm-cap-card {
    background: linear-gradient(180deg, rgba(0,0,0,.02), rgba(0,0,0,.01));
  }
  :root:not([data-theme="dark"]) .pm-cap-stats {
    background: rgba(0,0,0,.015);
  }
}

/* Feature cards */
.features {
  max-width: 1080px; margin: 0 auto;
  padding: clamp(64px, 11vh, 110px) 24px;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px;
}
@media (max-width: 820px) { .features { grid-template-columns: 1fr; } }

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 26px 24px;
  transition: transform .3s var(--ease), border-color .3s var(--ease), background .3s var(--ease);
}
.card:hover { transform: translateY(-3px); border-color: var(--line-2); background: var(--surface-2); }
.card .ic {
  width: 38px; height: 38px; border-radius: 11px; display: grid; place-items: center;
  background: var(--brand-soft); color: var(--brand-2); margin-bottom: 16px;
}
.card h3 { font-size: 15px; font-weight: 700; letter-spacing: -.02em; margin-bottom: 7px; }
.card p  { font-size: 13.5px; line-height: 1.6; color: var(--muted); }

/* PUBLIC-SHELL-0 · Global multi-column footer. Serious grid con room to
   grow: BRAND / LEGAL / LANGUAGE hoy; añadir Product/Resources/Integrations
   cuando existan páginas reales sin redesign. Mobile: stack ordenado. */
.site-footer {
  border-top: 1px solid var(--line);
  padding: 42px 32px 28px;
  color: var(--muted-2);
  background: var(--surface);
}
.site-footer a { color: var(--muted); text-decoration: none; transition: color .18s var(--ease); }
.site-footer a:hover { color: var(--brand-2); }
.footer-grid {
  display: grid;
  /* 3 columnas hoy: brand (fluida y ancha), legal (fija natural), language
     (fija). Cuando existan Product/Resources se añaden columnas SIN redesign. */
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 56px;
  max-width: 1120px;
  margin: 0 auto;
  align-items: start;
}
.footer-col { min-width: 0; }
/* Brand tagline queda ancho controlado por la columna (2fr), sin cap
   arbitrario: cuando existan Product/Resources la columna se recorta sola. */
.footer-col-brand .footer-tagline { max-width: 42ch; }
.footer-col-lang { justify-self: start; }
.footer-col-title {
  font-size: 11px; font-weight: 700; letter-spacing: .11em;
  text-transform: uppercase; color: var(--text);
  margin: 0 0 14px;
}
.footer-tagline {
  font-size: 13px; line-height: 1.6; color: var(--muted);
  margin: 0;
}
.footer-links { list-style: none; padding: 0; margin: 0; }
.footer-links li { margin-bottom: 8px; }
.footer-links a { font-size: 13px; line-height: 1.4; }
.footer-legal-line {
  max-width: 1120px; margin: 34px auto 0;
  padding-top: 22px; border-top: 1px solid var(--line);
  font-size: 12px; color: var(--muted-2); text-align: left;
}

/* Variant minimal (para superficies como error pages). */
.site-footer-minimal {
  padding: 22px 24px calc(22px + env(safe-area-inset-bottom));
  text-align: center;
}
.site-footer-minimal .footer-lang { justify-content: center; }

/* I18N-1B.3 · Language selector — mismo componente, ahora vive en la columna
   Language del footer grid. Popover se despliega HACIA ARRIBA porque estamos
   al final de la página. */
.footer-lang {
  display: inline-flex; align-items: center; gap: 10px;
}
.footer-lang-label {
  font-size: 11.5px; font-weight: 600; letter-spacing: .06em;
  color: var(--muted-2); text-transform: uppercase;
}
.footer-col-lang .footer-lang { margin-top: 0; }
.footer-col-lang .footer-lang-label { display: none; }  /* la columna ya tiene su título */
.footer-lang .pm-lang-menu {
  top: auto; bottom: calc(100% + 8px);
}

/* footer-col-fold es un <details> que actúa como acordeón EN MOBILE. En
   desktop lo forzamos abierto vía CSS (override del UA que colapsa
   :not([open])), preservando la lectura escaneable en pantallas anchas.
   Estado por defecto: cerrado en HTML → mobile arranca colapsado y reduce
   drásticamente la altura del footer. */
.footer-col-fold > summary {
  cursor: default;
  list-style: none;
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
}
.footer-col-fold > summary::-webkit-details-marker,
.footer-col-fold > summary::marker { display: none; content: ''; }
.footer-col-chev {
  display: none;
  width: 8px; height: 8px; flex-shrink: 0;
  border-right: 2px solid var(--muted-2); border-bottom: 2px solid var(--muted-2);
  transform: rotate(45deg);
  transition: transform .18s var(--ease);
}
.footer-col-fold[open] .footer-col-chev { transform: rotate(-135deg); }
/* Desktop: the <details>/<summary> collapse mechanism must be visually
   INVISIBLE — owner rule 2026-08-30, "desktop footer stays as it was".
   display:contents removes the <summary> box from the layout tree so its
   children (h4 + chev) reparent to the <details> as if the summary were
   never there. The chev already has display:none at this breakpoint. */
@media (min-width: 721px) {
  /* pointer-events:none on the summary prevents an accidental click on the
     h4 from toggling the details closed. lang-switcher.js sets the `open`
     attribute on desktop viewports so the content renders regardless of
     browser default collapse (::details-content uses content-visibility
     in modern Chromium — a plain `display:block` on the ul cannot
     override that; forcing `open` is the reliable path). */
  .footer-col-fold > summary { display: contents; cursor: default; pointer-events: none; }
  .footer-col-fold > *:not(summary) { display: block !important; }
  .footer-col-fold > ul { padding: 0; margin: 0; list-style: none; }
}

/* Mobile: LEGAL colapsa como acordeón (44px tap target en summary), BRAND
   y LANGUAGE siempre visibles. Reduce altura vertical drásticamente. */
@media (max-width: 720px) {
  .site-footer { padding: 32px 20px calc(24px + env(safe-area-inset-bottom)); }
  .footer-grid {
    grid-template-columns: 1fr;
    gap: 24px;
  }
  .footer-col-brand { max-width: none; }
  .footer-col-lang { justify-self: stretch; }
  .footer-col-title { margin-bottom: 10px; }
  .footer-legal-line { margin-top: 22px; padding-top: 18px; text-align: center; }
  .footer-col-fold {
    border-top: 1px solid var(--line);
    padding-top: 4px;
  }
  .footer-col-fold:not([open]) { padding-bottom: 4px; }
  .footer-col-fold > summary {
    cursor: pointer;
    padding: 12px 0;
    min-height: 44px;
    -webkit-tap-highlight-color: transparent;
  }
  .footer-col-fold .footer-col-chev { display: block; }
  .footer-col-fold[open] > summary { margin-bottom: 6px; }
}

/* Entrance choreography — one pass on load, transform+opacity only.
   The resting state is VISIBLE and the animation plays *from* hidden with
   fill-mode backwards. If the animation never runs — reduced motion, a
   backgrounded tab, a stylesheet that half-loaded — the content is simply
   there. Content must never depend on an animation to become visible. */
.reveal { opacity: 1; }
body.ready .reveal { animation: rise .55s var(--ease) backwards; }
body.ready .reveal[data-d="1"] { animation-delay: .06s; }
body.ready .reveal[data-d="2"] { animation-delay: .12s; }
body.ready .reveal[data-d="3"] { animation-delay: .19s; }
body.ready .reveal[data-d="4"] { animation-delay: .26s; }
body.ready .reveal[data-d="5"] { animation-delay: .33s; }
@keyframes rise { from { opacity: 0; transform: translateY(18px); } }

/* ══ Editor ═══════════════════════════════════════════════════════════════ */
#editor {
  grid-template-columns: 1fr var(--sidebar);
  height: calc(100vh - var(--header));
  min-height: 0;
}
@media (max-width: 940px) {
  /* MOBILE-1: grid layout de mobile crece con contenido. `height: auto`
     + `min-height` garantiza que el editor ocupe al menos el viewport
     visible, pero pueda crecer si el usuario abre múltiples folds — el
     body scrollea la página entera y el CTA queda anclado como sticky. */
  #editor { grid-template-columns: 1fr; grid-template-rows: minmax(340px, auto) auto; height: auto; min-height: calc(100vh - var(--header)); }
  #stage { min-height: 240px; }
}

.stage-wrap { display: flex; flex-direction: column; min-width: 0; min-height: 0; }

.stage-bar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 9px 14px; border-bottom: 1px solid var(--line); background: var(--surface);
}
.seg { display: flex; background: var(--surface-2); padding: 2.5px; border-radius: 9px; }
.seg button {
  background: transparent; border: 0; color: var(--muted); cursor: pointer;
  font: inherit; font-size: 11.5px; font-weight: 600;
  padding: 6px 12px; border-radius: 6.5px; white-space: nowrap;
  transition: color .2s var(--ease), background .2s var(--ease), box-shadow .2s var(--ease);
}
.seg button:hover { color: var(--text); }
.seg button[aria-selected="true"] {
  /* iOS-style UISegmentedControl active pill: solid fill + sombra sutil de
     1px + hairline luminoso arriba. Da la sensación característica de
     "cápsula flotante" sin ruido visual. */
  background: var(--surface-3); color: var(--text);
  box-shadow: 0 1px 2px rgba(0,0,0,.22), inset 0 1px 0 rgba(255,255,255,.04);
}

/* Sidebar-scoped: los segmented del panel de settings (Layout Tile/Corner/Center,
   modos futuros) rellenan el ancho de la sección con items equitativos —
   patrón nativo iOS/macOS UISegmentedControl. En la stage-bar el .seg vive
   en una fila flex con otros controles y se auto-dimensiona; ahí NO queremos
   stretch (mantiene la densidad del toolbar). */
.sb-field .seg { width: 100%; }
.sb-field .seg button { flex: 1; text-align: center; }

.icon-btn {
  background: var(--surface-2); border: 1px solid var(--line); color: var(--muted);
  width: 30px; height: 30px; border-radius: 8px; cursor: pointer;
  display: grid; place-items: center;
  transition: color .2s var(--ease), border-color .2s var(--ease), transform .12s var(--ease);
}
.icon-btn:hover { color: var(--text); border-color: var(--line-2); }
.icon-btn:active { transform: scale(.94); }
.zoom-label {
  font-size: 11.5px; font-weight: 600; color: var(--muted);
  min-width: 44px; text-align: center; font-variant-numeric: tabular-nums;
}

/* ── MOBILE-2 · Stage-bar rediseñada tipo editor nativo ─────────────────────
   Investigación (ui-ux-pro-max + benchmark contra VSCO/Snapseed/Lightroom
   Mobile/Photoshop Express/Apple Photos): en mobile los editores serios
   colapsan el toolbar del canvas a máximo 3 grupos, todos icon-only,
   confiando en el pinch/double-tap nativo. Aplicamos exactamente eso.

   Desktop: los SVG están hidden por defecto (queremos texto). Los
   labels .txt se muestran y los botones +/- de zoom siguen usables con
   mouse. Nada cambia en desktop. */
.seg-icons button svg,
.stage-icon-btn svg { display: none; }

@media (max-width: 940px) {
  .stage-bar {
    flex-wrap: nowrap; gap: 6px;
    padding: 8px 10px;
    min-height: 56px;
  }
  /* Text labels dentro de los botones de la stage-bar → hidden en mobile.
     El aria-label del botón preserva el nombre accesible. */
  .stage-bar .txt { display: none; }
  /* SVG icons visibles solo en mobile. */
  .seg-icons button svg,
  .stage-icon-btn svg { display: block; }
  /* Botones redundantes con pinch/double-tap: fuera. */
  .stage-bar .stage-zoom-btn { display: none; }
  .stage-bar .spacer { display: none; }
  /* btn-new (start-over) va a la izquierda como icono +, patrón Apple
     Photos edit view (top-left Cancel/Close). */
  #btn-new { order: -1; }
  /* Botones icon-only del stage-bar: 44×44 táctil, misma geometría que
     .theme-btn / .account-btn del header para coherencia visual. */
  .stage-bar .stage-icon-btn {
    width: 44px; height: 44px; padding: 0;
    border-radius: 10px;
    display: grid; place-items: center;
  }
  /* Segmented compacto con solo iconos — 3 pills de 40px de ancho. */
  .seg-icons button {
    padding: 8px 10px; min-width: 40px;
    display: grid; place-items: center;
  }
  /* Empuja zoom-label + zoom-fit al extremo derecho. */
  .zoom-label { margin-left: auto; min-width: 40px; font-size: 12px; }
}

.stage {
  flex: 1; position: relative; overflow: hidden; min-height: 0;
  /* Arrastrar el comparador no debe pintar la imagen de azul como si
     estuvieras seleccionando texto. */
  user-select: none; -webkit-user-select: none;
  background-color: #08080c;
  background-image:
    linear-gradient(45deg, #141419 25%, transparent 25%),
    linear-gradient(-45deg, #141419 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #141419 75%),
    linear-gradient(-45deg, transparent 75%, #141419 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0;
  cursor: grab; touch-action: none;
}
.stage.panning { cursor: grabbing; }

.canvas { position: absolute; top: 50%; left: 50%; }
.canvas img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  display: block; user-select: none; -webkit-user-drag: none;
}
.canvas img.result { z-index: 2; }
.stage[data-view="side"] .canvas { display: flex; gap: 10px; }
.stage[data-view="side"] .canvas img { position: relative; inset: auto; width: 50%; }

.handle {
  position: absolute; top: 0; bottom: 0; z-index: 3;
  width: 2px; margin-left: -1px; background: var(--brand-2);
  cursor: ew-resize; display: none;
  box-shadow: 0 0 14px rgba(77,217,255,.55);
}
.stage[data-view="curtain"] .handle { display: block; }
.handle::after {
  content: ''; position: absolute; top: 50%; left: 50%;
  width: 32px; height: 32px; margin: -16px 0 0 -16px;
  background: var(--grad); border-radius: 50%;
  box-shadow: 0 3px 16px rgba(0,0,0,.6);
  transition: transform .2s var(--ease);
}
.handle:hover::after { transform: scale(1.12); }

.handle-label {
  position: absolute; top: 12px; z-index: 4; pointer-events: none;
  font-size: 10px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  background: rgba(0,0,0,.62); color: var(--text);
  padding: 4px 9px; border-radius: 100px;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.handle-label.left  { left: 12px; }
.handle-label.right { right: 12px; }
.stage[data-view="result"] .handle-label.left { display: none; }

.stage-busy {
  position: absolute; top: 12px; left: 50%; transform: translateX(-50%);
  z-index: 5; display: none; align-items: center; gap: 8px;
  background: rgba(0,0,0,.68); backdrop-filter: blur(10px);
  padding: 6px 13px; border-radius: 100px;
  font-size: 11px; font-weight: 600; color: var(--muted);
}
.stage-busy.on { display: flex; }
.mini-spinner {
  width: 11px; height: 11px; border: 2px solid rgba(77,217,255,.25);
  border-top-color: var(--brand-2); border-radius: 50%; animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ══ Stats bar ════════════════════════════════════════════════════════════ */
.stats {
  display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
  padding: 11px 16px; border-top: 1px solid var(--line); background: var(--surface);
}
/* MOBILE-0: en pantallas estrechas priorizamos las tres métricas que
   describen el resultado (Original · Result · Saved). Dimensions y Render
   son telemetría útil en desktop pero saturan el pie del canvas en móvil.
   Se ocultan bajo 620px; regresan en tablet/desktop. */
@media (max-width: 620px) {
  .stats .stat:nth-child(4),
  .stats .stat:nth-child(5) { display: none; }
  .stats { gap: 16px; padding: 10px 14px; }
}
.stat { display: flex; flex-direction: column; gap: 2px; }
.stat .k { font-size: 9.5px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--muted-2); }
.stat .v {
  font-size: 13.5px; font-weight: 700; font-variant-numeric: tabular-nums;
  transition: opacity .2s var(--ease);
}
.stat .v.good { color: var(--good); }
.stat .v.bad  { color: var(--bad); }
.stats .spacer { margin-left: auto; }

.btn {
  background: var(--grad); border: 0; color: #0a0a0f; cursor: pointer;
  font: inherit; font-size: 13px; font-weight: 700;
  /* Un <a class="btn"> es un botón, no un enlace de texto: sin subrayado. */
  text-decoration: none;
  padding: 10px 20px; border-radius: 10px;
  display: inline-flex; align-items: center; gap: 8px;
  box-shadow: 0 4px 18px -4px rgba(var(--brand-rgb),.6);
  transition: transform .15s var(--ease), box-shadow .25s var(--ease), filter .2s var(--ease);
}
.btn:hover { transform: translateY(-1px); box-shadow: 0 8px 26px -6px rgba(var(--brand-rgb),.7); }
.btn:active { transform: scale(.975); }
.btn:disabled { opacity: .4; cursor: not-allowed; transform: none; box-shadow: none; }

.btn.ghost {
  background: transparent; border: 1px solid var(--line-2); color: var(--muted);
  box-shadow: none; font-weight: 600;
}
.btn.ghost:hover { color: var(--text); border-color: var(--brand); transform: none; }
.btn.sm { padding: 6px 12px; font-size: 11.5px; border-radius: 8px; }

/* ══ Sidebar ══════════════════════════════════════════════════════════════ */
.sidebar {
  border-left: 1px solid var(--line); background: var(--surface);
  min-height: 0; overflow: hidden;
  display: flex; flex-direction: column;
  /* Familias de token separadas: el espaciado marca el ritmo, la dimensión no.
     Los 44px de un objetivo táctil no son una excepción a la escala. */
  --sp-0: 4px; --sp-1: 8px; --sp-2: 12px; --sp-3: 20px; --sp-4: 28px;
  --h-ctl: 44px;

  /* SIDEBAR-DS-1 — design system canónico. Todos los controles del sidebar
     y del stage-bar seleccionan una de estas tres alturas y uno de estos
     dos radios; la tipografía sale de la escala de 4 tamaños y 3 pesos.
     Ningún componente inventa geometría ad hoc: `padding: 6px 12px;
     font-size: 11.5px; border-radius: 8px;` NO se escribe en un botón —
     se hereda del rol. */
  --pm-h-sm: 32px;   /* icon buttons, secondary sm, seg items sm             */
  --pm-h-md: 36px;   /* seg items lg (format picker), stronger sidebar CTAs  */
  --pm-h-lg: 48px;   /* única CTA primaria de la vista (Download image)      */
  --pm-r-md: 8px;    /* controles estándar y icon squares                    */
  --pm-r-lg: 10px;   /* contenedores segmented, primary CTA                  */
  --pm-fs-11: 11px;  /* metadata muy secundaria (batch item metrics)          */
  --pm-fs-12: 12px;  /* helper, status, hint, filename metadata              */
  --pm-fs-13: 13px;  /* controles, labels, section titles, filenames         */
  --pm-fs-14: 14px;  /* primary CTA, valor principal de resultado            */
  --t-fast: 140ms; --t-fold: 200ms;
}
.sb-scroll {
  flex: 1; min-height: 0; overflow-y: auto;
  scrollbar-width: thin; scrollbar-color: var(--surface-3) transparent;
}
.sb-scroll::-webkit-scrollbar { width: 9px; }
.sb-scroll::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 100px; border: 3px solid var(--surface); }
.sidebar::-webkit-scrollbar { width: 9px; }
.sidebar::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 100px; border: 3px solid var(--surface); }
@media (max-width: 940px) {
  /* MOBILE-1: en mobile la sidebar deja de tener cap (44vh) porque el
     pie con Download image se ancla via `position: fixed` — el usuario
     puede desplazar la página completa y siempre tiene el CTA a mano
     (mismo patrón que .batch-total). Además, con los grupos plegables
     cerrados por defecto la sidebar arranca compacta. */
  .sidebar { border-left: 0; border-top: 1px solid var(--line); max-height: none; overflow: visible; }
  .sb-scroll { overflow-y: visible; padding-bottom: 120px; }
  .sb-foot {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    background: rgba(18,18,24,.92); backdrop-filter: blur(14px);
    border-top: 1px solid var(--line-2);
  }
  :root[data-theme="light"] .sb-foot { background: rgba(255,255,255,.92); }
}
@media (max-width: 940px) and (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .sb-foot { background: rgba(255,255,255,.92); }
}

.group { border-bottom: 1px solid var(--line); padding: 16px; }
.group > h3 {
  display: flex; align-items: center; gap: 8px;
  font-size: 9.5px; font-weight: 700; letter-spacing: .11em; text-transform: uppercase;
  color: var(--muted-2); margin-bottom: 13px;
}
.group > h3 .spacer { margin-left: auto; }

.field { margin-bottom: 14px; }
.field:last-child { margin-bottom: 0; }
.field > label {
  display: flex; align-items: baseline; gap: 6px;
  font-size: 12px; font-weight: 600; color: var(--muted); margin-bottom: 7px;
}
.field > label .val {
  margin-left: auto; font-size: 11.5px; font-weight: 700;
  color: var(--brand-2); font-variant-numeric: tabular-nums;
}
.field.disabled { opacity: .35; pointer-events: none; }

/* Sliders — the primary instrument of this app, so they get real styling. */
input[type=range] {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 20px; background: transparent; cursor: pointer;
}
input[type=range]::-webkit-slider-runnable-track {
  height: 4px; border-radius: 100px; background: var(--surface-3);
}
input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 15px; height: 15px; margin-top: -5.5px;
  border-radius: 50%; background: #fff;
  box-shadow: 0 1px 5px rgba(0,0,0,.55);
  transition: transform .15s var(--ease), box-shadow .15s var(--ease);
}
input[type=range]:hover::-webkit-slider-thumb  { transform: scale(1.18); }
input[type=range]:active::-webkit-slider-thumb { transform: scale(1.3); box-shadow: 0 0 0 6px rgba(var(--brand-rgb),.22); }
input[type=range]::-moz-range-track { height: 4px; border-radius: 100px; background: var(--surface-3); }
input[type=range]::-moz-range-thumb {
  width: 15px; height: 15px; border: 0; border-radius: 50%; background: #fff;
  box-shadow: 0 1px 5px rgba(0,0,0,.55);
}

/* SIZE-1 — porcentaje slider al estilo UISlider iOS/macOS.
   El track se rellena a la izquierda del thumb con el color de marca. En
   WebKit no existe `::-*-range-progress`, así que usamos un linear-gradient
   sobre el track con una CSS custom property `--pm-pct` que JS actualiza en
   cada input (0-100% del ancho del track). Firefox usa el pseudo nativo. */
#maxpx::-webkit-slider-runnable-track {
  height: 4px; border-radius: 100px;
  background: linear-gradient(to right,
    var(--brand) 0,
    var(--brand) var(--pm-pct, 100%),
    var(--surface-3) var(--pm-pct, 100%),
    var(--surface-3) 100%);
}
#maxpx::-moz-range-progress {
  height: 4px; border-radius: 100px; background: var(--brand);
}

/* Título de sección con valor a la derecha (Size · 85%). Reutiliza .sb-title
   pero flexea para alinear el valor a la derecha en la misma baseline. */
.sb-title-with-value {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-1);
}
.sb-title-with-value .val {
  font-size: var(--pm-fs-12); font-weight: 600; color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* Hitos visibles bajo el slider — labels 10/25/50/75/100 posicionados
   exactamente donde el thumb caería en cada valor. Para un slider min=10
   max=100, el thumb en value=V está a (V-10)/90 del ancho: 25→16.67%,
   50→44.44%, 75→72.22%. Se centran con translateX, salvo los extremos que
   se alinean flush para no cortarse. */
.size-field { margin-top: 0; }
.size-ticks {
  position: relative;
  height: 14px;
  margin-top: 6px;
  font-size: 10px;
  font-weight: 500;
  color: var(--muted-2);
  font-variant-numeric: tabular-nums;
  user-select: none;
}
.size-ticks span { position: absolute; transform: translateX(-50%); white-space: nowrap; }
.size-ticks span:nth-child(1) { left: 0;      transform: translateX(0); }
.size-ticks span:nth-child(2) { left: 16.67%; }
.size-ticks span:nth-child(3) { left: 44.44%; }
.size-ticks span:nth-child(4) { left: 72.22%; }
.size-ticks span:nth-child(5) { left: 100%;   transform: translateX(-100%); }

/* Helper de dimensiones live — mismo peso visual que otros .hint pero con
   números tabulares para que no baile mientras se arrastra el slider. */
.size-dims {
  font-variant-numeric: tabular-nums;
  margin-top: var(--sp-1);
}

/* BATCH-UI-0: familia canónica de sliders. Quality, Size y Opacity usan el
   mismo track/thumb — un solo slider visual en toda la sección Batch.
   iOS-style track fill via --pm-pct (JS actualiza en cada input); Firefox
   usa ::-moz-range-progress nativo. */
.pct-slider { width: 100%; }
.pct-slider::-webkit-slider-runnable-track {
  height: 4px; border-radius: 100px;
  background: linear-gradient(to right,
    var(--brand) 0,
    var(--brand) var(--pm-pct, 100%),
    var(--surface-3) var(--pm-pct, 100%),
    var(--surface-3) 100%);
}
.pct-slider::-moz-range-progress {
  height: 4px; border-radius: 100px; background: var(--brand);
}
.b-size-opt { min-width: 0; }

/* iOS-style toggle switch para Watermark. Reemplaza el checkbox nativo por
   una pastilla horizontal con thumb deslizante. Accent-color no basta para
   dar el look "iOS 26" — este set de reglas construye la pastilla completa
   y respeta prefers-reduced-motion via transición suave. */
.pm-switch-row {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  font-size: var(--pm-fs-13); font-weight: 600; color: var(--muted);
}
.pm-switch {
  appearance: none; -webkit-appearance: none;
  width: 40px; height: 24px;
  background: var(--surface-3); border: 1px solid var(--line);
  border-radius: 999px; cursor: pointer; position: relative;
  transition: background .2s var(--ease), border-color .2s var(--ease);
  flex-shrink: 0;
  margin: 0;
}
.pm-switch::before {
  content: ''; position: absolute;
  width: 18px; height: 18px; top: 2px; left: 2px;
  background: #fff; border-radius: 50%;
  box-shadow: 0 1px 3px rgba(0,0,0,.35);
  transition: transform .2s var(--ease);
}
.pm-switch:checked {
  background: var(--brand); border-color: transparent;
}
.pm-switch:checked::before { transform: translateX(16px); }
.pm-switch:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.presets { display: flex; gap: var(--sp-1); flex-wrap: wrap; margin-top: var(--sp-1); }
.presets button {
  background: var(--surface-2); border: 1px solid var(--line); color: var(--muted);
  font: inherit; font-size: var(--pm-fs-12); font-weight: 600; cursor: pointer;
  padding: 0 12px; min-height: var(--pm-h-sm); border-radius: var(--pm-r-md);
  transition: color .18s var(--ease), border-color .18s var(--ease), background .18s var(--ease);
}
.presets button:hover { color: var(--text); border-color: var(--line-2); }
.presets button[aria-selected="true"] {
  background: var(--brand-soft); border-color: var(--brand); color: #cbbcff;
}

.switch {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  font-size: var(--pm-fs-13); font-weight: 600; color: var(--muted);
}
.switch input { accent-color: var(--brand); width: 15px; height: 15px; cursor: pointer; }

.hint { font-size: var(--pm-fs-12); line-height: 1.5; color: var(--muted-2); margin-top: var(--sp-1); }

/* ── Custom watermark slot ─────────────────────────────────────────────── */
.logo-slot {
  position: relative;
  display: flex; align-items: center; gap: 11px;
  padding: 10px 11px;
  border: 1px dashed var(--line-2); border-radius: var(--r);
  background: var(--surface-2); cursor: pointer;
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
.logo-slot:hover, .logo-slot.drag-over { border-color: var(--brand); background: rgba(var(--brand-rgb),.07); }

/* Checkerboard so a transparent logo reads as transparent, not as white. */
#logo-preview {
  width: 42px; height: 42px; flex-shrink: 0;
  object-fit: contain; border-radius: 7px;
  background-color: #0d0d11;
  background-image:
    linear-gradient(45deg, #1c1c22 25%, transparent 25%),
    linear-gradient(-45deg, #1c1c22 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #1c1c22 75%),
    linear-gradient(-45deg, transparent 75%, #1c1c22 75%);
  background-size: 10px 10px;
  background-position: 0 0, 0 5px, 5px -5px, -5px 0;
}
/* Account-1.6.1: EMPTY state. El SVG placeholder se sirve como src del <img>
   en JS (evita el broken-image del navegador y el alt sobrepuesto). Aquí
   solo neutralizamos el fondo de checkerboard porque encima ya no hay logo. */
.logo-slot.is-empty #logo-preview {
  background: transparent;
  padding: 8px;
}
.logo-slot.is-empty #logo-name { color: var(--muted); font-weight: 500; }
/* DEMO: tratamiento sutilmente distinto para que se lea como "ejemplo",
   no como marca del usuario. */
.logo-slot.is-demo #logo-name { color: var(--muted); font-weight: 500; font-style: italic; }
/* Pulso breve cuando el usuario intenta activar la marca de agua sin logo. */
@keyframes logo-slot-attention {
  0%   { border-color: var(--line-2); box-shadow: 0 0 0 0 rgba(var(--brand-rgb),0); }
  30%  { border-color: var(--brand);  box-shadow: 0 0 0 4px rgba(var(--brand-rgb),.22); }
  100% { border-color: var(--line-2); box-shadow: 0 0 0 0 rgba(var(--brand-rgb),0); }
}
.logo-slot.attention { animation: logo-slot-attention .9s cubic-bezier(.16,1,.3,1) 1; }
.logo-meta { min-width: 0; flex: 1; }
.logo-name {
  display: block; font-size: 12px; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.logo-dims { display: block; font-size: 11px; color: var(--muted-2); margin-top: 1px; }
/* Account-1.6 corrective: icon-only remove control dentro del flujo flex de
   la tarjeta; el align-items:center del padre lo centra en el mismo eje que
   el nombre y la preview. Cuadrado con esquinas redondeadas que combina con
   el rectángulo punteado. Discreto en reposo, intención destructiva sutil
   en hover. Easing y jerarquía alineados con el sistema. */
.logo-clear {
  flex-shrink: 0;
  width: 28px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  border-radius: 8px;
  background: rgba(255,255,255,.06);
  border: 1px solid rgba(255,255,255,.08);
  color: var(--muted-2);
  cursor: pointer;
  transition: background .12s cubic-bezier(.16,1,.3,1),
              color      .12s cubic-bezier(.16,1,.3,1),
              border-color .12s cubic-bezier(.16,1,.3,1),
              transform  .12s cubic-bezier(.16,1,.3,1);
}
.logo-clear:hover {
  background: rgba(239,68,68,.14);
  border-color: rgba(239,68,68,.35);
  color: #fca5a5;
  transform: scale(1.05);
}
.logo-clear:active { transform: scale(.95); }
.logo-clear:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* ══ Feedback ═════════════════════════════════════════════════════════════ */
.error-box {
  display: none; margin: 14px auto; max-width: 620px;
  padding: 13px 15px; border-radius: var(--r);
  border: 1px solid rgba(255,92,110,.32); background: rgba(255,92,110,.08);
  font-size: 12.5px; line-height: 1.55;
}
.error-box strong { display: block; margin-bottom: 3px; color: var(--bad); }

#uploading { display: none; text-align: center; padding: clamp(48px, 12vh, 110px) 24px; }
.spinner {
  width: 40px; height: 40px; margin: 0 auto 20px;
  border: 3px solid rgba(var(--brand-rgb),.2); border-top-color: var(--brand);
  border-radius: 50%; animation: spin .8s linear infinite;
}
#uploading p { font-size: 14px; color: var(--muted); }
#uploading strong { color: var(--text); }
.progress-track {
  width: 280px; height: 5px; margin: 18px auto 9px;
  background: var(--surface-3); border-radius: 100px; overflow: hidden;
}
.progress-bar { height: 100%; width: 0%; background: var(--grad); border-radius: 100px; transition: width .15s linear; }

/* ══ Batch ════════════════════════════════════════════════════════════════ */
.batch-inner {
  max-width: 980px; margin: 0 auto;
  /* MOBILE-0: 120px de fondo evita que el sticky batch-total tape el
     último item de la lista. Sumamos safe-area-inset-bottom para iPhone
     notched — sin esto, el home-indicator suma otros 34px y la última
     row queda parcialmente oculta. */
  padding: 30px max(24px, env(safe-area-inset-right))
           calc(120px + env(safe-area-inset-bottom))
           max(24px, env(safe-area-inset-left));
}
.batch-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 20px; }
.batch-head h2 { font-size: 22px; font-weight: 700; letter-spacing: -.03em; }
.batch-head p { font-size: 13px; color: var(--muted); margin-top: 3px; }

/* BATCH-UI-1 (2026-08-26): control surface por grupos conceptuales, NO
   spreadsheet 2×3. Tres grupos: Output · Image · Watermark. Cada grupo
   organiza sus controles en pares 2-col cuando aplica; Watermark es
   collapsible (config oculta cuando OFF). Regla canónica:
     "Group by MEANING, not by CSS auto-layout convenience."
     "Consistency ≠ uniformity — hierarchy over symmetry."
   El sistema hereda automáticamente los defaults de My Brand cuando
   existe — Studio y Batch comparten UNA sola verdad de configuración. */
.batch-opts {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 20px 24px; margin-bottom: 16px;
}
.batch-group { padding: var(--sp-3) 0; }
.batch-group:first-child { padding-top: 0; }
.batch-group:last-child { padding-bottom: 0; }
.batch-group + .batch-group { border-top: 1px solid var(--line); }

/* MOBILE-1: Batch usa el patrón `.sb-fold` (details/summary) para los 3
   grupos. El contenedor `.batch-opts` ya aporta padding/border/radius,
   así que anulamos el padding y borde superior del sb-fold canónico. */
.batch-opts .sb-fold { border-top: none; }
.batch-opts .sb-fold + .sb-fold { border-top: 1px solid var(--line); }
.batch-opts .sb-fold > summary { padding: 0; min-height: 48px; }
.batch-opts .sb-fold[open] > summary { border-bottom: 1px solid var(--line); margin-bottom: var(--sp-3); }
.batch-opts .sb-fold-body { padding: 0 0 var(--sp-3); }
.opt-row {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: var(--sp-4);
  align-items: start;
}
.opt-row + .opt-row { margin-top: var(--sp-3); }
@media (max-width: 680px) {
  .opt-row { grid-template-columns: 1fr; gap: var(--sp-3); }
  .batch-opts { padding: 16px; }
}
.opt { min-width: 0; }
.opt.opt-full { grid-column: 1 / -1; }
.opt > label {
  display: flex; align-items: baseline; gap: var(--sp-1);
  font-size: var(--pm-fs-13); font-weight: 600; color: var(--muted);
  margin: 0 0 var(--sp-1);
}
.opt > label .val {
  margin-left: auto; color: var(--text); font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.opt.disabled { opacity: .35; pointer-events: none; }

/* Watermark head + collapsible config. La cabecera es una fila con toggle
   + estado a la derecha; la hint aparece SOLO cuando WM está ON y el user
   tiene brand guardada; la config se expande debajo. */
.batch-wm-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-2);
}
.batch-wm-head .val { font-size: var(--pm-fs-13); font-weight: 700; color: var(--text); }
.batch-wm-hint { margin: var(--sp-1) 0 0; }
.batch-wm-hint[hidden] { display: none; }
.batch-wm-config { display: flex; flex-direction: column; margin-top: var(--sp-3); }
.batch-wm-config[hidden] { display: none; }
.batch-wm-config .opt-row + .opt-row { margin-top: var(--sp-3); }

.dropzone.compact { padding: 26px 24px; margin-bottom: 16px; border-radius: var(--r-lg); }
.dropzone.compact .dz-icon { width: 44px; height: 44px; border-radius: 13px; margin-bottom: 12px; }
.dropzone.compact h2 { font-size: 15px; margin-bottom: 4px; }
.dropzone.compact p { font-size: 12.5px; }

/* ── Result rows ──────────────────────────────────────────────────────────── */
.row {
  display: flex; align-items: center; gap: 14px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: 10px 14px; margin-bottom: 8px;
  position: relative; overflow: hidden;
}
.row.done { cursor: pointer; }
.row.done:hover { border-color: var(--line-2); }
.row.failed { border-color: rgba(255,92,110,.35); }

.row .thumb {
  width: 44px; height: 44px; flex-shrink: 0; border-radius: 8px;
  object-fit: cover; background: var(--surface-3);
}
.row .meta { flex: 1; min-width: 0; }
.row .nm { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row .sz { font-size: 11.5px; color: var(--muted-2); margin-top: 2px; font-variant-numeric: tabular-nums; }
.row .sz b { color: var(--muted); font-weight: 600; }

.row .save {
  font-size: 14px; font-weight: 700; color: var(--good);
  font-variant-numeric: tabular-nums; min-width: 54px; text-align: right;
}
.row .save.bad { color: var(--bad); }
.row .state { font-size: 11.5px; color: var(--muted-2); min-width: 80px; text-align: right; }

.row .dl {
  flex-shrink: 0; width: 34px; height: 34px; border-radius: 9px;
  display: grid; place-items: center; text-decoration: none;
  background: var(--brand-soft); border: 1px solid rgba(var(--brand-rgb),.3); color: var(--brand-2);
  transition: background .18s var(--ease);
}
.row .dl:hover { background: rgba(var(--brand-rgb),.24); }

/* Progress runs as a hairline under the row rather than as a separate widget. */
.row .prog {
  position: absolute; left: 0; bottom: 0; height: 2px;
  background: var(--grad); transition: width .2s linear;
}

/* ── Sticky totals ────────────────────────────────────────────────────────── */
.batch-total {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  display: flex; align-items: center; gap: 22px; flex-wrap: wrap;
  /* MOBILE-0: home-indicator bar de iOS ocupa 34px bottom en portrait.
     Sin este padding, el CTA Download queda debajo del gesture bar y el
     usuario o toca gesto en lugar del botón, o no puede tocarlo del todo. */
  padding: 12px max(24px, calc((100vw - 980px) / 2))
           calc(12px + env(safe-area-inset-bottom))
           max(24px, calc((100vw - 980px) / 2));
  background: rgba(18,18,24,.92); backdrop-filter: blur(14px);
  border-top: 1px solid var(--line-2);
}
:root[data-theme="light"] .batch-total { background: rgba(255,255,255,.92); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .batch-total { background: rgba(255,255,255,.92); }
}
.batch-total .spacer { margin-left: auto; }

.variants-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin: 10px 0 2px; }
@media (max-width: 780px) { .variants-grid { grid-template-columns: 1fr; } }
.variant-card { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.variant-card .variant-info { padding: 10px 12px; }
.variant-card h4 { font-size: 12px; font-weight: 700; }
.variant-dims { font-size: 10.5px; color: var(--muted-2); margin-bottom: 8px; }
.size-row { display: flex; gap: 6px; }
.size-pill { flex: 1; background: var(--surface-3); border-radius: 6px; padding: 5px 7px; text-align: center; }
.size-pill .fmt { display: block; font-size: 8.5px; font-weight: 700; letter-spacing: .08em; color: var(--muted-2); }
.size-pill .kb { font-size: 11.5px; font-weight: 700; font-variant-numeric: tabular-nums; }
.size-pill .kb.good { color: var(--good); }
.size-pill .kb.warn { color: var(--warn); }
.size-pill .kb.over { color: var(--bad); }
.dl-row { display: flex; gap: 6px; margin-top: 8px; }
.btn-dl {
  flex: 1; display: flex; align-items: center; justify-content: center;
  background: var(--brand-soft); border: 1px solid rgba(var(--brand-rgb),.3);
  color: var(--brand-2); font-size: 11px; font-weight: 700;
  padding: 6px; border-radius: 7px; text-decoration: none;
}
.btn-dl:hover { background: rgba(var(--brand-rgb),.24); }
.btn-dl.alt { background: var(--surface-3); border-color: var(--line); color: var(--muted); }

/* ── Light theme ──────────────────────────────────────────────────────────
   Three states, and all three must be covered: an explicit choice stamps
   data-theme on <html>, while the default "system" setting stamps nothing and
   only prefers-color-scheme separates the two. Every token is defined on bare
   :root (dark) and redefined here, so no colour has its only definition inside
   a media query. Contrast verified at WCAG AA against all three surfaces. */
:root[data-theme="light"],
:root[data-theme="light"] body { color-scheme: light; }

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) { color-scheme: light; }
}

:root[data-theme="light"] {
  --bg:        #f6f7fb;
  --surface:   #ffffff;
  --surface-2: #eff0f6;
  --surface-3: #e3e4ee;
  --line:      rgba(16,16,28,.09);
  --line-2:    rgba(16,16,28,.16);

  --text:    #15151c;
  --muted:   #55555f;
  --muted-2: #6b6b78;

  --brand:      #6a45ff;
  --brand-2:    #0a6d88;
  --brand-soft: rgba(106,69,255,.10);
  --grad:       linear-gradient(135deg, #6a45ff 0%, #12a5cf 100%);

  --good: #00714f;
  --warn: #9a6100;
  --bad:  #d32d43;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg:        #f6f7fb;
    --surface:   #ffffff;
    --surface-2: #eff0f6;
    --surface-3: #e3e4ee;
    --line:      rgba(16,16,28,.09);
    --line-2:    rgba(16,16,28,.16);

    --text:    #15151c;
    --muted:   #55555f;
    --muted-2: #6b6b78;

    --brand:      #6a45ff;
    --brand-2:    #0a6d88;
    --brand-soft: rgba(106,69,255,.10);
    --grad:       linear-gradient(135deg, #6a45ff 0%, #12a5cf 100%);

    --good: #00714f;
    --warn: #9a6100;
    --bad:  #d32d43;
  }
}

/* Surfaces that were hand-tuned for dark need their light counterparts.
   Light-mode header keeps the same blur + gradient-fade mask; only the
   tint colour flips (translucent white instead of near-black bg). */
:root[data-theme="light"] .site-header::before { background: rgba(255,255,255,.72); }
:root[data-theme="light"] .bg-mesh {
  background:
    radial-gradient(58vw 46vh at 14% -8%,  rgba(106,69,255,.13), transparent 62%),
    radial-gradient(52vw 42vh at 88% 4%,   rgba(18,165,207,.11), transparent 60%),
    radial-gradient(46vw 40vh at 50% 108%, rgba(0,113,79,.06),   transparent 62%);
}
:root[data-theme="light"] .stage {
  background-color: #eceef4;
  background-image:
    linear-gradient(45deg, #dcdfe9 25%, transparent 25%),
    linear-gradient(-45deg, #dcdfe9 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #dcdfe9 75%),
    linear-gradient(-45deg, transparent 75%, #dcdfe9 75%);
}
:root[data-theme="light"] #logo-preview {
  background-color: #eceef4;
  background-image:
    linear-gradient(45deg, #dcdfe9 25%, transparent 25%),
    linear-gradient(-45deg, #dcdfe9 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #dcdfe9 75%),
    linear-gradient(-45deg, transparent 75%, #dcdfe9 75%);
}
:root[data-theme="light"] .btn:not(.ghost) { color: #fff; }
:root[data-theme="light"] .handle-label,
:root[data-theme="light"] .variant-badge,
:root[data-theme="light"] .stage-busy { background: rgba(20,20,30,.72); color: #fff; }
:root[data-theme="light"] input[type=range]::-webkit-slider-thumb,
:root[data-theme="light"] input[type=range]::-moz-range-thumb { background: #2a2a38; }
:root[data-theme="light"] .presets button[aria-selected="true"] { color: #4a29d6; }
:root[data-theme="light"] .btn-dl { color: #4a29d6; }

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .site-header::before { background: rgba(255,255,255,.72); }
  :root:not([data-theme="dark"]) .bg-mesh {
    background:
      radial-gradient(58vw 46vh at 14% -8%,  rgba(106,69,255,.13), transparent 62%),
      radial-gradient(52vw 42vh at 88% 4%,   rgba(18,165,207,.11), transparent 60%),
      radial-gradient(46vw 40vh at 50% 108%, rgba(0,113,79,.06),   transparent 62%);
  }
  :root:not([data-theme="dark"]) .stage,
  :root:not([data-theme="dark"]) #logo-preview {
    background-color: #eceef4;
    background-image:
      linear-gradient(45deg, #dcdfe9 25%, transparent 25%),
      linear-gradient(-45deg, #dcdfe9 25%, transparent 25%),
      linear-gradient(45deg, transparent 75%, #dcdfe9 75%),
      linear-gradient(-45deg, transparent 75%, #dcdfe9 75%);
  }
  :root:not([data-theme="dark"]) .btn:not(.ghost) { color: #fff; }
  :root:not([data-theme="dark"]) .handle-label,
  :root:not([data-theme="dark"]) .variant-badge,
  :root:not([data-theme="dark"]) .stage-busy { background: rgba(20,20,30,.72); color: #fff; }
  :root:not([data-theme="dark"]) input[type=range]::-webkit-slider-thumb,
  :root:not([data-theme="dark"]) input[type=range]::-moz-range-thumb { background: #2a2a38; }
  :root:not([data-theme="dark"]) .presets button[aria-selected="true"] { color: #4a29d6; }
  :root:not([data-theme="dark"]) .btn-dl { color: #4a29d6; }
}

/* ── Account ──────────────────────────────────────────────────────────────── */
#account { display: flex; align-items: center; gap: 8px; }

.btn-google {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--surface-2); border: 1px solid var(--line-2); color: var(--text);
  font: inherit; font-size: 12.5px; font-weight: 600;
  padding: 7px 13px; border-radius: 9px; cursor: pointer; text-decoration: none;
  transition: border-color .2s var(--ease), background .2s var(--ease);
  white-space: nowrap;
}
.btn-google:hover { border-color: var(--brand); background: var(--surface-3); }

.avatar {
  display: flex; align-items: center; gap: 8px;
  background: none; border: 0; padding: 3px; cursor: pointer; border-radius: 100px;
}
.avatar img {
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--surface-3); border: 1px solid var(--line-2);
}
/* AUTH-CTA-SSR · Initials fallback for signed-in users whose avatar URL
   is not in the CSP allowlist (e.g. GitHub) or is missing entirely. Flat
   PixelMinify gradient disc with 1–2 initials — reads as a deliberate
   product decision, not a broken image. Same 28 px footprint as the
   <img> so the dropdown geometry never shifts between the two states. */
.avatar-initials {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--grad);
  color: #0a0a0f;
  font: 700 12px/1 Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  letter-spacing: -.01em;
  user-select: none;
  border: 1px solid rgba(0,0,0,.10);
}
.avatar .nm {
  font-size: 12.5px; font-weight: 600; color: var(--muted);
  max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Signed-in dropdown logout: SSR uses a <form method="post"> for a JS-free
   fallback. The form is a flex row so the button inside behaves like the
   other .menu-row entries. */
.menu form.menu-form { margin: 0; padding: 0; }
@media (max-width: 860px) { .btn-google span { display: none; } }

/* ═══ Avatar dropdown · consumes the canonical pm-menu tokens ═══
   Owner rule 2026-08-30: icon and text must breathe (12px gap standard),
   comfortable row height, hover discreet but evident, same icon stroke/size
   as the rest of the product. Every value below routes through --pm-menu-*
   so changing the token elsewhere moves this menu with it. */
.menu {
  position: absolute; top: calc(var(--header) - 6px); right: 14px; z-index: 60;
  min-width: 260px;
  padding: var(--pm-menu-container-p);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--pm-menu-container-r);
  box-shadow: 0 12px 32px rgba(0,0,0,.28), 0 2px 6px rgba(0,0,0,.22);
}
:root[data-theme="light"] .menu { box-shadow: 0 12px 32px rgba(15,16,25,.08), 0 2px 6px rgba(15,16,25,.05); }
.menu[hidden] { display: none; }
.menu .who {
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px 14px 12px;
}
.menu .who b { font-size: 13.5px; font-weight: 600; line-height: 1.2; }
.menu .who span {
  font-size: 12px; color: var(--muted); line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.menu a, .menu button {
  display: flex; align-items: center;
  gap: var(--pm-menu-item-gap);
  min-height: var(--pm-menu-item-h);
  padding: var(--pm-menu-item-py) var(--pm-menu-item-px);
  border-radius: var(--pm-menu-item-r);
  font-size: var(--pm-menu-item-fs); font-weight: var(--pm-menu-item-fw);
  color: var(--text);
  background: none; border: 0; cursor: pointer;
  font-family: inherit; text-align: left; text-decoration: none; width: 100%;
  transition: background .16s var(--ease), color .16s var(--ease);
}
.menu a:hover, .menu button:hover { background: var(--pm-menu-hover-bg); }
.menu-row { display: flex; align-items: center; gap: var(--pm-menu-item-gap); }
.menu-row .menu-ico {
  width: var(--pm-menu-icon-size); height: var(--pm-menu-icon-size);
  color: var(--muted); flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  transition: color .16s var(--ease);
}
.menu-row .menu-ico svg { width: 100%; height: 100%; display: block; }
.menu-row:hover .menu-ico { color: var(--pm-menu-active-icon); }
.menu-div {
  border: 0; height: 1px; background: var(--line); margin: 6px 8px;
}
.menu-form { padding: 0; margin: 0; }
.menu-row:focus-visible { outline: 2px solid var(--brand-2); outline-offset: -2px; }

/* AUTH-CTA-PAIR · Header auth control cluster, geometry aligned to the
   ChatGPT top-right pattern: 44px pill height, 18px horizontal padding,
   9999 radius, 14px/600 Inter, 8px gap. PixelMinify differs from ChatGPT
   only in brand color, not in anatomy — so the pair reads as one crisp,
   flat, disciplined auth affordance rather than two decorative pills.
   Server-rendered inside #account by src/shell.js (see AUTH-CTA-SSR),
   so the buttons paint with the first byte of HTML. */
.pm-cta-pair {
  display: inline-flex; align-items: center; gap: 8px;
}
.pm-cta {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 18px;
  border-radius: 9999px;
  font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-size: 14px; font-weight: 600; line-height: 1;
  letter-spacing: -.005em;
  white-space: nowrap;
  cursor: pointer;
  border: 1px solid transparent;
  box-shadow: none;
  transform: none;
  transition:
    background .18s var(--ease),
    color .18s var(--ease),
    border-color .18s var(--ease),
    filter .18s var(--ease);
}
.pm-cta:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 3px; }

/* Secondary — high-contrast light pill, mirroring ChatGPT's Log in button.
   Solid white on dark, near-black text, hairline border for edge crispness.
   Deliberately assertive so first-time visitors read it as an equal peer of
   the primary CTA rather than a hidden transparent outline. */
.pm-cta-log-in {
  background: #fff;
  color: #0d0d0d;
  border-color: rgba(0, 0, 0, .08);
}
.pm-cta-log-in:hover { background: #f2f2f5; }

/* Primary — TRANSLUCENT pill, matching ChatGPT's Sign up more literally:
   the button is not a solid dark color; it's a low-opacity white overlay
   that lets whatever is behind (page bg + `.bg-mesh` violet/cyan radial
   glows) show through. The result is a subtle "brand-tinted glass" pill
   that stays distinct from the crisp white Log in without competing
   with the hero H1 gradient or any other identity surface. Owner-
   approved 2026-08-29 as the final iteration of the auth pair. */
.pm-cta-sign-up {
  background: rgba(255, 255, 255, .06);
  color: #fff;
  border-color: rgba(255, 255, 255, .14);
  box-shadow: none;
}
.pm-cta-sign-up:hover  { background: rgba(255, 255, 255, .12); }
.pm-cta-sign-up:active { background: rgba(255, 255, 255, .04); }

/* Light theme — Log in keeps its white/near-black look (works on both
   surfaces); tune only the hover fill so it doesn't flicker on a bright
   page. Sign up mirrors the dark-theme translucent pattern with a black
   overlay instead of a white one, so the light page background does the
   coloring the same way the dark bg does in dark mode. */
:root[data-theme="light"] .pm-cta-log-in { border-color: rgba(0, 0, 0, .12); }
:root[data-theme="light"] .pm-cta-log-in:hover { background: #ececee; }
:root[data-theme="light"] .pm-cta-sign-up {
  background: rgba(0, 0, 0, .06);
  color: #0a0a0f;
  border-color: rgba(0, 0, 0, .14);
}
:root[data-theme="light"] .pm-cta-sign-up:hover  { background: rgba(0, 0, 0, .12); }
:root[data-theme="light"] .pm-cta-sign-up:active { background: rgba(0, 0, 0, .04); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .pm-cta-log-in { border-color: rgba(0, 0, 0, .12); }
  :root:not([data-theme="dark"]) .pm-cta-log-in:hover { background: #ececee; }
  :root:not([data-theme="dark"]) .pm-cta-sign-up {
    background: rgba(0, 0, 0, .06);
    color: #0a0a0f;
    border-color: rgba(0, 0, 0, .14);
  }
  :root:not([data-theme="dark"]) .pm-cta-sign-up:hover  { background: rgba(0, 0, 0, .12); }
  :root:not([data-theme="dark"]) .pm-cta-sign-up:active { background: rgba(0, 0, 0, .04); }
}

/* Narrow mobile — trim horizontal padding only. Height stays 44px (comfort
   + WCAG touch target), gap tightens to 6px so both pills still fit above
   the giant hero wordmark on ~360px screens. */
@media (max-width: 480px) {
  .pm-cta-pair { gap: 6px; }
  .pm-cta { padding: 0 14px; }
}

/* ── I18N-1B.3 · Language switcher ───────────────────────────────────────────
   Peer visual de .theme-btn: mismos tokens (32h/44h coarse, radius 9-10,
   border-color, hover). Diferencia semántica: es un DISCLOSURE (chevron
   giratorio) — no un toggle instantáneo como el theme. Popover iOS/Sonoma:
   backdrop-blur 18px, radius 12, hairline border, sombra dual. */
.pm-lang-wrap { position: relative; display: inline-flex; }
.pm-lang {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 10px 0 12px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 9px;
  color: var(--muted);
  font: 600 12px/1 inherit;
  cursor: pointer; letter-spacing: .04em;
  flex-shrink: 0;
  transition: color .18s var(--ease), border-color .18s var(--ease), background .18s var(--ease);
}
.pm-lang:hover { color: var(--text); border-color: var(--line-2); }
.pm-lang[aria-expanded="true"] { color: var(--text); border-color: var(--line-2); }
.pm-lang[hidden] { display: none; }
.pm-lang-code { font-variant-numeric: tabular-nums; }
.pm-lang-chev {
  width: 6px; height: 6px; flex-shrink: 0;
  border-right: 1.6px solid currentColor; border-bottom: 1.6px solid currentColor;
  transform: rotate(45deg); margin-top: -3px; opacity: .7;
  transition: transform .18s var(--ease), margin-top .18s var(--ease);
}
.pm-lang[aria-expanded="true"] .pm-lang-chev { transform: rotate(-135deg); margin-top: 1px; opacity: 1; }

.pm-lang-menu {
  position: absolute; top: calc(100% + 8px); right: 0;
  min-width: 180px; padding: 6px;
  background: rgba(18,18,24,.94);
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
  border: 1px solid var(--line-2);
  border-radius: 12px;
  box-shadow: 0 14px 44px rgba(0,0,0,.45), 0 2px 6px rgba(0,0,0,.30);
  z-index: 50; text-align: left;
}
.pm-lang-menu[hidden] { display: none; }
:root[data-theme="light"] .pm-lang-menu { background: rgba(255,255,255,.96); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .pm-lang-menu { background: rgba(255,255,255,.96); }
}

.pm-lang-item {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 9px 12px 9px 10px;
  background: transparent; border: 0; border-radius: 8px;
  color: var(--text);
  font: 500 13.5px/1.1 inherit;
  text-align: left; cursor: pointer; letter-spacing: 0;
  transition: background .12s var(--ease);
}
.pm-lang-item:hover,
.pm-lang-item:focus-visible { background: rgba(255,255,255,.06); outline: none; }
:root[data-theme="light"] .pm-lang-item:hover,
:root[data-theme="light"] .pm-lang-item:focus-visible { background: rgba(0,0,0,.05); }
.pm-lang-check { width: 14px; flex: 0 0 14px; opacity: 0; color: var(--brand-2); }
.pm-lang-item[aria-checked="true"] .pm-lang-check { opacity: 1; }
.pm-lang-native { flex: 1; }
.pm-lang-code2 {
  font-size: 10.5px; font-weight: 700; letter-spacing: .08em;
  color: var(--muted-2); font-variant-numeric: tabular-nums;
}

/* Coarse pointer: 44×44 canónico como account/theme (MOBILE-0). */
@media (pointer: coarse) {
  .pm-lang { height: 44px; min-width: 44px; padding: 0 12px; border-radius: 10px; font-size: 13px; }
}

/* ══ Touch targets ════════════════════════════════════════════════════════ */
/* MOBILE-0: HIG iOS min 44×44pt. Keyed on pointer type — un laptop narrowed
   a 700px sigue queriendo densidad compacta, y un tablet grande sigue
   necesitando controles thumb-friendly. */
@media (pointer: coarse) {
  .nav button,
  .seg button       { padding: 12px 16px; }
  .presets button   { padding: 11px 14px; }
  .btn.sm           { padding: 11px 15px; font-size: 12.5px; }
  .icon-btn         { width: 44px; height: 44px; }
  .theme-btn,
  .btn-google:not(.btn-google-lg) { width: 44px; height: 44px; }
  .switch           { padding: 8px 0; }
  .switch input     { width: 20px; height: 20px; }
  /* iOS-native switch para watermark: escalar hit-area en touch sin
     inflar la pastilla visual — padding invisible via ::before. */
  .pm-switch        { width: 44px; height: 26px; }
  .pm-switch::before { width: 20px; height: 20px; }
  .pm-switch:checked::before { transform: translateX(18px); }
  /* Footer inline text links: expandir hit-area vertical sin cambiar
     el look (line-height ya provee ancho). */
  .site-footer a    { padding: 6px 0; display: inline-block; }

  input[type=range] { height: 40px; }
  input[type=range]::-webkit-slider-thumb { width: 22px; height: 22px; margin-top: -9px; }
  input[type=range]::-moz-range-thumb     { width: 22px; height: 22px; }
}

/* ══ Motion preferences ═══════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .reveal { opacity: 1; transform: none; }
}

/* ── Mobile chrome ≤720px ──────────────────────────────────────────────────
   MOBILE-CHROME-0 (2026-08-28): reference contract is ChatGPT's mobile app
   chrome discipline — a persistent, disciplined header with a brand anchor
   and ONE dominant action, not a landing that tries to sell two auth CTAs
   plus a giant hero logo in the top 150 px of the viewport.

   Rules on mobile:
   · Header stays visible on every view (landing included) — restores app
     feel; no more `height: 0` collapse.
   · Brand is perceptually centered via a 3-column grid (empty-left /
     center-brand / right-account). Even without a menu button today, the
     brand reads as anchored rather than floated in the corner.
   · Only Log in shows in the header. `Sign up for free` is hidden — the
     Log in modal already exposes Google + GitHub + Email + Try it first,
     so a second CTA in the most expensive viewport pixel is pure noise.
   · The oversized hero `.hero-logo` is hidden — the brand now lives in
     the header, so repeating the wordmark inside the hero would duplicate
     identity and push the H1 down. */
/* Impact-standard header positioning: on every viewport narrower than
   full desktop (≥1150), the header is a 3-column grid — menu/hamburger
   column on the left (reserved), centered PixelMinify wordmark in the
   middle, account/cart controls on the right. Above 1150 the base flex
   rules take over and the brand sits left-aligned with the auth pair on
   the right. This mirrors the pattern used by Impact (Shopify) so the
   logo behavior stays predictable across small, medium, and large
   screens without per-page overrides. */
@media (max-width: 1149px) {
  .site-header {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 8px;
  }
  /* Explicit grid placement so the layout survives when the SPA landing
     header only contains {brand, account} (header-file is hidden here).
     Without explicit column assignment, auto-placement drops account into
     column 2 and the pill floats near the middle of the header. */
  .brand {
    grid-column: 2;
    justify-self: center;
    flex-shrink: 0;
  }
  .brand-mark   { display: none; }
  .header-file  { display: none; }
  .header-spacer { display: none; }
  #account {
    grid-column: 3;
    justify-self: end;
    flex-shrink: 0;
    margin-left: 0;
  }
}

@media (max-width: 720px) {
  /* Mobile-only overrides layered on top of the tablet+mobile grid above:
     tighter horizontal padding for narrow viewports, wordmark size trimmed
     to match the auth-modal's mobile brand treatment
     (`.pm-auth-brand svg { height: 24px }` in auth-modal.css @520px), the
     mobile-only landing hero-fix, one-dominant-CTA discipline, and 44px
     touch-target compliance for the signed-in avatar. */
  /* Mobile · shrink the shared gutter · site-header + site-footer + any
     wrap-style body container all follow it. Removes the previous per-
     surface override that let /account body drift wider than the shell. */
  :root { --page-gutter: 12px; }
  .site-header { height: var(--header); }
  .brand-logo { height: 24px; }

  /* Landing on mobile: owner explicit preference is to keep the large
     PixelMinify hero logo as the identity anchor and NOT move a tiny
     wordmark into the header. So the landing header collapses to a
     transparent zero-height rail (same discipline as desktop landing);
     the giant hero logo owns the top of the screen; and #account docks
     fixed top-right with the single Log in pill. The stable-header
     mobile chrome above still applies on every non-landing view
     (account/profile, legal, editor, batch), where there is no hero
     logo to anchor identity. */
  body[data-view="landing"] .site-header {
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-bottom: 0;
    height: 0;
    padding: 0;
  }
  body[data-view="landing"] .site-header > .brand { display: none; }
  body[data-view="landing"] .site-header > #account {
    position: fixed;
    top:   max(14px, env(safe-area-inset-top));
    right: max(16px, env(safe-area-inset-right));
    justify-self: auto;
    z-index: 60;
  }

  /* One dominant action: Log in only. Sign up lives inside the auth modal
     which Log in opens (Continue with Google/GitHub/Email — same account-
     entry outcome). */
  .pm-cta-pair { gap: 0; }
  .pm-cta-sign-up { display: none; }

  /* Touch-target compliance for the signed-in avatar. */
  .btn-google { width: 44px; height: 44px; padding: 0; gap: 0; justify-content: center; }
  .avatar     { padding: 4px; }
  .avatar img { width: 32px; height: 32px; }
  .avatar-initials { width: 32px; height: 32px; font-size: 13px; }

  .floating-controls { display: none; }
}

/* Pantallas muy estrechas: se recorta el texto de las pestanas antes que
   permitir scroll horizontal, que es el peor resultado posible. */
@media (max-width: 380px) {
  .site-header { gap: 8px; padding: 0 10px; }
  .nav button  { padding: 10px 10px; font-size: 12px; }
}

/* ── Muestras ────────────────────────────────────────────────────────────────
   Avatares redondos, debajo de la zona de arrastre y con menos peso visual:
   son un atajo para probar, no el camino principal. Desaparecen en cuanto hay
   una imagen cargada. */
.samples {
  max-width: 620px;
  margin: 30px auto 0;
  /* Enable container-query so the carousel rule below can react to the
     samples component's OWN available width rather than a viewport
     breakpoint. Tablets, split-screen windows, narrow desktop columns,
     and phones all get the correct behavior based on measured fit. */
  container-type: inline-size;
}
.samples[hidden] { display: none !important; }
.samples-lead { font-size: 13px; color: var(--muted-2); text-align: center; margin: 0 0 16px; }
.samples-grid { display: flex; justify-content: center; gap: 26px; flex-wrap: wrap; }

/* Fit-responsive carousel: the samples row switches to a horizontal
   scroll-snap carousel ONLY when its container is too narrow to hold
   all cards in one comfortable centered row. Threshold ≈ 4 typical
   cards × ~95px + 3 × 26px gap ≈ 464px, rounded to 500px for
   translated-title headroom (DE/ES). No viewport-name assumption:
   phones, tablets, split-screen, and future intermediate widths all
   get the right behavior from the same rule. Owner-approved scope
   correction 2026-08-29.

   When ACTIVE (container ≤500px):
     · single horizontal row (`flex-wrap: nowrap`)
     · card width fixed to 96px so titles wrap inside the card instead
       of stretching the row — long translations flow onto a natural
       second line without breaking layout
     · CSS `scroll-snap` with proximity so a swipe softly centers each
       card without forcing snap on tiny gestures
     · subtle `mask-image` edge fade signals off-screen content — no
       arrow, no dot, no JS carousel library
     · no per-item positioning, no :nth-child hacks — 4/5/8/10+ cards
       all fit the same rule

   When INACTIVE (container >500px):
     · base `.samples-grid` wrap-center layout applies, unchanged
     · no fade, no snap, no scroll — cards use natural width, same
       appearance as prior production
     · state transitions cleanly if the container resizes across the
       threshold (no residual mask/scroll) */
@container (max-width: 500px) {
  .samples-grid {
    flex-wrap: nowrap;
    justify-content: flex-start;
    gap: 20px;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding: 4px 24px 12px;
    mask-image: linear-gradient(to right, transparent 0, #000 24px, #000 calc(100% - 24px), transparent 100%);
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 24px, #000 calc(100% - 24px), transparent 100%);
  }
  .samples-grid::-webkit-scrollbar { display: none; }
  .sample {
    scroll-snap-align: center;
    flex: 0 0 auto;
    width: 96px;
  }
  .sample .t {
    text-align: center;
    line-height: 1.25;
    word-wrap: break-word;
    overflow-wrap: break-word;
  }
  .sample .w { text-align: center; }
}

.sample {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  background: none; border: 0; padding: 4px; cursor: pointer;
  font: inherit; color: inherit; border-radius: 12px;
  transition: transform .18s var(--ease);
}
.sample:hover { transform: translateY(-3px); }
.sample:disabled { opacity: .5; cursor: progress; transform: none; }

.sample-av {
  position: relative; display: block; width: 64px; height: 64px;
  border-radius: 50%; overflow: hidden;
  background: var(--surface-2);
  box-shadow: 0 0 0 1px var(--line-2);
  transition: box-shadow .18s var(--ease);
}
.sample:hover .sample-av { box-shadow: 0 0 0 2px var(--brand); }
.sample-av img { width: 100%; height: 100%; object-fit: cover; display: block; }

.sample-dot {
  position: absolute; right: 1px; bottom: 1px;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--grad); border: 2px solid var(--bg);
}

.sample .t { font-size: 11.5px; font-weight: 600; }
.sample .w { font-size: 10.5px; color: var(--muted-2); }

/* Distintivo de muestra: aparece en el editor, no solo en la portada. Alguien
   puede compartir una captura del editor y esa captura no debe mentir. */
.badge-demo {
  background: rgba(255,183,74,.16); color: var(--warn);
  font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 6px; white-space: nowrap;
}
.demo-note {
  display: flex; gap: 9px; align-items: flex-start;
  background: rgba(255,183,74,.09); border: 1px solid rgba(255,183,74,.25);
  border-radius: 10px; padding: 10px 13px; margin: 0 0 14px;
  font-size: 12px; color: var(--warn); line-height: 1.5;
}
.demo-note[hidden] { display: none !important; }

/* ═══ INSPECTOR DEL EDITOR ════════════════════════════════════════════════════
   Nivel 1 siempre visible: formato, calidad, tamaño. El resto plegado, con el
   estado escrito en el resumen para no perder contexto al cerrarlo. */

.sb-sec { padding: var(--sp-4) var(--sp-3); }
.sb-sec + .sb-sec { border-top: 1px solid var(--line); }

.sb-title {
  font-size: var(--pm-fs-13); font-weight: 600; letter-spacing: -.01em; line-height: 1;
  color: var(--text); text-transform: none;
  margin: 0 0 var(--sp-2);
}

.sb-field { margin-top: var(--sp-2); }
.sb-field:first-child { margin-top: 0; }
.sb-field > label {
  display: flex; align-items: center; gap: var(--sp-1);
  font-size: var(--pm-fs-13); font-weight: 600; color: var(--muted);
  margin: 0 0 var(--sp-1);
}
.sb-field > label .val {
  margin-left: auto; color: var(--text); font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.sb-field > label.switch { margin-bottom: 0; font-size: var(--pm-fs-13); color: var(--text); }
.sb-field .hint { margin-top: var(--sp-1); }

/* Format picker: mismo lenguaje visual que Layout (Tile/Corner/Center) y
   que el resto de segmented controls del sidebar. Antes .seg-lg imponía
   min-height 44 y font 13, dándole peso desproporcionado; se retiró para
   respetar simetría sidebar. `flex: 1` mantiene el reparto equitativo. */
.seg-lg { width: 100%; }
.seg-lg button { flex: 1; }

.sb-field .presets {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px;
  margin-top: var(--sp-2);
}
.sb-field .presets button { min-height: 34px; }

/* ── Plegables ───────────────────────────────────────────────────────────── */
.sb-fold { border-top: 1px solid var(--line); }
.sb-fold > summary {
  display: flex; align-items: center; gap: var(--sp-2);
  min-height: 56px; padding: 0 var(--sp-3);
  cursor: pointer; list-style: none; -webkit-user-select: none; user-select: none;
  transition: background var(--t-fast) ease-out;
}
.sb-fold > summary::-webkit-details-marker,
.sb-fold > summary::marker { display: none; content: ''; }
.sb-fold > summary:hover { background: var(--surface-2); }
.sb-fold[open] > summary { border-bottom: 1px solid var(--line); }

.sb-fold-name { font-size: var(--pm-fs-13); font-weight: 600; color: var(--text); }
.sb-fold-state {
  margin-left: auto; font-size: var(--pm-fs-12); color: var(--muted-2);
  text-align: right; transition: color var(--t-fast) ease-out;
}
/* Estado distinto del de por defecto: se colorea para poder escanear la
   columna y ver qué está tocado sin abrir ninguna sección. */
.sb-fold-state[data-on="1"] { color: var(--brand-2); font-weight: 600; }

.sb-chev {
  width: 7px; height: 7px; flex-shrink: 0;
  border-right: 2px solid var(--muted-2); border-bottom: 2px solid var(--muted-2);
  transform: rotate(-45deg);
  transition: transform var(--t-fold) ease-out;
}
.sb-fold[open] .sb-chev { transform: rotate(45deg); }

.sb-fold-body { padding: var(--sp-3); }

/* Avanzado: un revelador de texto, no una sección. */
.sb-adv { margin-top: var(--sp-3); }
.sb-adv > summary {
  list-style: none; cursor: pointer; font-size: var(--pm-fs-12); color: var(--muted-2);
  padding: var(--sp-0) 0; transition: color var(--t-fast) ease-out;
}
.sb-adv > summary::-webkit-details-marker,
.sb-adv > summary::marker { display: none; content: ''; }
.sb-adv > summary:hover { color: var(--text); }

/* ── Pie: la acción principal ────────────────────────────────────────────── */
.sb-foot {
  flex-shrink: 0;
  /* MOBILE-0: en móvil el sidebar colapsa a bottom y este pie contiene el
     Download image. env(safe-area-inset-bottom) evita que el CTA quede
     debajo del home-indicator/gesture bar del iPhone. */
  padding: var(--sp-3) var(--sp-3) calc(var(--sp-3) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line-2); background: var(--surface);
}
.sb-out {
  /* Métrica final del sidebar: label discreta + valor prominente + delta.
     Ahora es la ÚNICA presentación de bytes/savings en la vista — la barra
     de canvas inferior con ORIGINAL/RESULT/SAVED/DIMS/RENDER se retiró
     (SIDEBAR-DS-1). */
  display: flex; align-items: baseline; gap: var(--sp-1);
  font-size: var(--pm-fs-12); color: var(--muted); margin-bottom: var(--sp-2);
}
.sb-out strong {
  margin-left: auto; font-size: var(--pm-fs-14); font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums;
  transition: opacity .2s var(--ease);
}
.sb-out em { font-style: normal; font-size: var(--pm-fs-13); font-weight: 600; transition: opacity .2s var(--ease); }
.btn-lg { width: 100%; min-height: 48px; font-size: 14px; justify-content: center; }

/* ── Foco visible en todo el inspector ───────────────────────────────────── */
.sidebar :is(button, input, summary, a, [tabindex]):focus-visible {
  outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 8px;
}

/* ── Botón oficial de Google ─────────────────────────────────────────────── */
.btn-google-lg {
  width: 100%; min-height: var(--h-ctl); justify-content: center;
  gap: 10px; font-size: 13.5px; border-radius: 10px; padding: 0 16px;
}

@media (max-width: 940px) {
  .sb-sec, .sb-fold-body, .sb-foot { padding: var(--sp-3) var(--sp-2); }
  .sb-fold > summary { padding: 0 var(--sp-2); }
}

/* Cabecera de la cuenta: foto + nombre + correo, alineado como el modelo. */
.acct-head {
  display: flex; align-items: center; gap: 16px;
  padding-bottom: 20px; border-bottom: 1px solid var(--line); margin-bottom: 20px;
}
#acct-pic {
  width: 56px; height: 56px; border-radius: 50%;
  object-fit: cover; background: var(--surface-2);
  box-shadow: 0 0 0 1px var(--line-2); flex-shrink: 0;
}
.acct-name { font-size: 15px; font-weight: 600; color: var(--text); }
.acct-mail { font-size: 13px; color: var(--muted); margin-top: 2px; }
.acct-since { font-size: 12px; color: var(--muted-2); margin-top: 4px; }

.settings-danger {}
.settings-danger p { margin: 4px 0 10px; }

.sheet-legal {
  display: flex; gap: 20px; flex-wrap: wrap;
  padding: 18px 24px;
}
.sheet-legal a {
  font-size: 12.5px; color: var(--muted-2); text-decoration: none;
}
.sheet-legal a:hover { color: var(--brand-2); text-decoration: underline; }

.linkish.danger {
  /* SIDEBAR-DS-1: acción destructiva del sistema. Alineada al mismo
     alto/padding/tipografía que .btn.ghost para que Update y Delete brand
     compartan geometría en una misma fila sin que Delete lea como una
     categoría distinta. Sin borde ni fondo por defecto — la naturaleza
     destructiva se comunica por color (--bad). En hover, un fondo suave
     rojizo refuerza la afordancia sin invadir jerarquía. */
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--pm-h-sm);
  padding: 0 12px;
  border-radius: var(--pm-r-md);
  background: none;
  border: 0;
  color: var(--bad);
  font: inherit; font-size: var(--pm-fs-13); font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background .15s var(--ease);
}
.linkish.danger:hover { background: rgba(239,68,68,.10); }

/* Panel "Mi marca" — sistema canónico SIDEBAR-DS-1. Alineado al ritmo
   vertical del sidebar (12px = --sp-2), sin gaps ad hoc. Filename como
   identidad principal (13/600), hint muted (12/400), acciones primaria +
   destructiva en fila con 8px de gap y misma altura (32) para leerse como
   pareja. */
.brand-user-stack {
  display: flex; flex-direction: column;
  gap: var(--sp-2);
}
.brand-user-saved {
  display: flex; flex-direction: column;
  gap: var(--sp-0);          /* 4px entre filename y hint */
  min-width: 0;
}
.brand-user-filename {
  font-size: var(--pm-fs-13); font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  min-width: 0;
}
.brand-user-hint {
  font-size: var(--pm-fs-12); line-height: 1.5; color: var(--muted-2);
}
.brand-user-stack .hint { margin: 0; }
.brand-user-actions {
  display: flex; align-items: center;
  gap: var(--sp-1);          /* 8px entre Update y Delete brand */
  flex-wrap: wrap;
}

/* SIDEBAR-DS-1 utilities. .push-right se retiró tras el revert quirúrgico
   de la stage-bar (el <div class="spacer"> volvió a su forma original).
   .demo-note-icon y .brand-anon-stack sobreviven — reemplazan inline
   styles del sidebar sin cambiar visual. */
.demo-note-icon { flex-shrink: 0; margin-top: 1px; }
.brand-anon-stack {
  display: flex; flex-direction: column; gap: var(--sp-2);
}
.brand-anon-stack .hint { margin: 0; }

@media (max-width: 620px) {
  .settings-page { padding: 24px 16px 64px; }
  .settings-card { padding: 20px 18px; }
}



/* Cuenta: solo la foto, sin nombre al lado. */
.avatar { padding: 0; }
.avatar img { width: 34px; height: 34px; border-width: 0; box-shadow: 0 0 0 1px var(--line-2); transition: box-shadow .18s var(--ease); }
.avatar:hover img, .avatar[aria-expanded="true"] img { box-shadow: 0 0 0 2px var(--brand); }


/* ═══ HOMEPAGE — CAPACIDADES Y FAQ ══════════════════════════════════════════
   Añadido en Semantic Expansion. Reutiliza .card y tokens existentes SIN
   modificarlos. Cualquier ajuste específico vive en las clases cap-* y faq-*
   para no arrastrar a otros componentes que también usan .card. */

.cap-block, .faq-block {
  max-width: 1080px; margin: 40px auto 0; padding: 0 24px;
}

.cap-head, .faq-head { text-align: center; margin-bottom: 28px; }
.cap-title, .faq-title {
  font-size: clamp(20px, 2.6vw, 26px); font-weight: 700; letter-spacing: -.02em;
  color: var(--text); margin: 0 0 8px;
}
.cap-sub, .faq-sub {
  font-size: 14px; color: var(--muted); margin: 0 auto; max-width: 560px; line-height: 1.55;
}

.cap-grid {
  display: grid; gap: 16px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (max-width: 700px) {
  .cap-grid { grid-template-columns: 1fr; }
}

.cap-item .cap-name {
  font-size: 14.5px; font-weight: 600; color: var(--text);
  margin: 0 0 8px; letter-spacing: -.01em;
}
.cap-item .cap-text {
  font-size: 13.5px; color: var(--muted); line-height: 1.55; margin: 0;
}

/* PRICING · Checkpoint B · Free vs Pro, mismo lenguaje visual que la
   sección Billing del /account rebuild. Se resuelve como grid 2×1 en
   desktop (Pro con borde brand) y stack 1×2 en mobile. No usa clases
   .ah-* para no acoplar el landing a account-hub.css: éstas viven en
   app.css y se hidratan solo desde el template. */
.pm-pricing {
  max-width: 1080px; margin: 40px auto 0; padding: 0 24px;
}
.pm-pricing-head { text-align: center; margin-bottom: 28px; }
.pm-pricing-title {
  font-size: clamp(20px, 2.6vw, 26px); font-weight: 700; letter-spacing: -.02em;
  color: var(--text); margin: 0 0 8px;
}
.pm-pricing-sub {
  font-size: 14px; color: var(--muted); margin: 0 auto; max-width: 560px; line-height: 1.55;
}
.pm-pricing-grid {
  display: grid; gap: 16px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: stretch;
}
@media (max-width: 700px) {
  .pm-pricing-grid { grid-template-columns: 1fr; }
}
.pm-pricing-col {
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r, 14px);
  padding: 24px;
}
.pm-pricing-col.pro {
  border-color: color-mix(in oklab, var(--brand) 42%, var(--line));
  background: color-mix(in oklab, var(--brand) 4%, var(--surface));
}
.pm-pricing-col-head { margin-bottom: 20px; }
.pm-pricing-name {
  font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted-2); margin: 0 0 8px;
}
.pm-pricing-col.pro .pm-pricing-name { color: var(--brand-2); }
.pm-pricing-price {
  font-size: 28px; font-weight: 700; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; color: var(--text);
  margin-bottom: 4px;
}
.pm-pricing-price em {
  font-style: normal; color: var(--muted); font-size: 13px; font-weight: 500;
  letter-spacing: 0; margin-left: 2px;
}
.pm-pricing-note {
  font-size: 12.5px; color: var(--muted-2); margin: 0;
}
.pm-pricing-feats {
  list-style: none; padding: 0; margin: 0 0 20px;
  display: flex; flex-direction: column; gap: 10px;
  flex: 1;
}
/* Check pattern replicated 1:1 desde .pm-cap-bullets (owner rule
   2026-08-31 · "los checks deben ser los mismos que en la tarjeta de
   Más que compresión"). Círculo tinted en ::before + palito brand-2 en
   ::after — sin svg inline, single source of truth visual con la
   card de capacidades. Si un día se cambia el look del check, se toca
   en un solo sitio (el estilo compartido). */
.pm-pricing-feats li {
  font-size: 13.5px; color: var(--text); line-height: 1.4;
  position: relative; padding-left: 26px; min-height: 20px;
}
.pm-pricing-feats li::before {
  content: '';
  position: absolute; left: 0; top: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  background: rgba(var(--brand-rgb), .16);
  border: 1px solid rgba(var(--brand-rgb), .34);
}
.pm-pricing-feats li::after {
  content: '';
  position: absolute; left: 4px; top: 5px;
  width: 8px; height: 4px;
  border-left: 1.6px solid var(--brand-2);
  border-bottom: 1.6px solid var(--brand-2);
  transform: rotate(-45deg);
}
.pm-pricing-cta {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 16px;
  font-size: 14px; font-weight: 600; letter-spacing: -.005em;
  border-radius: var(--r, 14px); text-decoration: none;
  transition: background .14s var(--ease, ease), border-color .14s var(--ease, ease), color .14s var(--ease, ease);
}
.pm-pricing-cta.ghost {
  background: transparent; color: var(--text);
  border: 1px solid var(--line);
}
.pm-pricing-cta.ghost:hover { background: var(--surface-2); border-color: var(--line-2, var(--line)); }
.pm-pricing-cta.primary {
  background: var(--brand); color: #fff; border: 1px solid transparent;
}
.pm-pricing-cta.primary:hover {
  background: color-mix(in oklab, var(--brand) 88%, #000);
}
.pm-pricing-cta:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 2px;
}
@media (max-width: 700px) {
  .pm-pricing-price { font-size: 24px; }
}

/* FAQ: <details> nativo, sin JS. */
.faq-block { max-width: 780px; margin-bottom: 56px; }
.faq-list {
  border: 1px solid var(--line); border-radius: var(--r, 14px);
  background: var(--surface); overflow: hidden;
}
.faq-item + .faq-item { border-top: 1px solid var(--line); }
.faq-item > summary {
  display: flex; align-items: center; gap: 12px;
  padding: 16px 18px; cursor: pointer;
  font-size: 14px; font-weight: 500; color: var(--text);
  list-style: none; -webkit-user-select: none; user-select: none;
  transition: background .14s var(--ease, ease);
}
.faq-item > summary::-webkit-details-marker,
.faq-item > summary::marker { display: none; content: ''; }
.faq-item > summary:hover { background: var(--surface-2); }

.faq-chev {
  width: 7px; height: 7px; margin-left: auto; flex-shrink: 0;
  border-right: 2px solid var(--muted-2); border-bottom: 2px solid var(--muted-2);
  transform: rotate(-45deg);
  transition: transform .18s var(--ease, ease);
}
.faq-item[open] .faq-chev { transform: rotate(45deg); }

.faq-a {
  padding: 0 18px 18px; font-size: 13.5px; color: var(--muted);
  line-height: 1.6;
}

.faq-item :is(summary):focus-visible {
  outline: 2px solid var(--brand); outline-offset: -2px;
}
