/* ════════════════════════════════════════════════════════════════════════
   kreawave — Sistema de diseño · tema oscuro violeta-tinta
   Tokens canónicos (ver DESIGN_SYSTEM.md). Cargado por TODAS las páginas.
   ════════════════════════════════════════════════════════════════════════ */

/* ── Fuentes auto-alojadas (CSP default-src 'self' → sin Google Fonts) ──── */
@font-face {
  font-family: "Space Grotesk Var";
  src: url("/fonts/space-grotesk-var.woff2") format("woff2");
  font-weight: 300 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Inter Var";
  src: url("/fonts/inter-var.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap;
}

:root {
  /* Superficies */
  --bg: #0c0a12;
  --bg-soft: #131019;
  --bg-card: #18141f;
  --bg-card-hover: #1f1929;
  --border: #2a2335;
  --border-soft: #211b2b;
  /* Texto */
  --text: #f2eef7;
  --muted: #a89db8;
  --muted-2: #8b7fa0;   /* ≥4.5:1 sobre las cards (WCAG AA texto pequeño) */
  --on-accent: #160c1f; /* texto sobre acentos brillantes */
  /* Marca / acentos */
  --accent: #a06bff;
  --accent-2: #ff5d8f;
  --accent-grad: linear-gradient(135deg, #a06bff, #ff5d8f);
  --wave-grad: linear-gradient(115deg, #a06bff 0%, #ff5d8f 50%, #ffb84d 100%);
  /* Funcionales */
  --green: #38d39f;
  --red: #ff5d73;
  --amber: #ffb84d;
  --gold: #ffc34d;
  --gold-soft: rgba(255, 195, 77, .16);
  --heart: #ff5d8f;
  /* Tipografía */
  --font-display: "Space Grotesk Var", "Space Grotesk", "Segoe UI", system-ui, sans-serif;
  --font-text: "Inter Var", "Inter", "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, "Cascadia Code", "SF Mono", Menlo, Consolas, monospace;
  /* Espaciado (base-4) */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px;
  --sp-6: 32px; --sp-7: 48px; --sp-8: 64px; --sp-9: 96px;
  /* Radios */
  --radius-sm: 10px;
  --radius: 16px;
  --radius-lg: 22px;
  --radius-pill: 999px;
  /* Sombras y brillo de marca */
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, .3);
  --shadow: 0 14px 44px rgba(0, 0, 0, .5);
  --shadow-glow: 0 0 40px rgba(160, 107, 255, .25);
  /* Movimiento */
  --t-fast: .12s ease;
  --t: .18s ease;
  --t-slow: .3s ease;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
[hidden] { display: none !important; }

html, body {
  background: var(--bg); color: var(--text);
  font-family: var(--font-text);
  font-size: 15px; line-height: 1.55; min-height: 100vh;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* Titulares en la display por defecto (el catálogo y legales heredan) */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700; letter-spacing: -.02em; line-height: 1.15;
}

/* Foco visible — accesibilidad */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #3a3048; }

/* Animaciones decorativas: respetar prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
  .wb, .hero-wave span, .demo-progress-bar, .card.playing::before { animation: none !important; }
}

/* ── Waveform logo ─────────────────────────────────────────────────────── */
.waveform-logo {
  display: flex; align-items: center; justify-content: center; gap: 3px;
  width: 52px; height: 52px; flex-shrink: 0;
  background: linear-gradient(145deg, #221a33, #15101f);
  border: 1px solid var(--border); border-radius: 14px;
}
.wb { display: block; width: 4px; border-radius: 2px; background: var(--accent-grad); animation: wave 1.56s ease-in-out infinite; }
.wb:nth-child(1) { height: 12px; animation-delay: 0s; }
.wb:nth-child(2) { height: 22px; animation-delay: .15s; }
.wb:nth-child(3) { height: 32px; animation-delay: .3s; }
.wb:nth-child(4) { height: 22px; animation-delay: .45s; }
.wb:nth-child(5) { height: 12px; animation-delay: .6s; }
@keyframes wave { 0%,100% { transform:scaleY(1); opacity:.9; } 50% { transform:scaleY(.4); opacity:.45; } }

/* ── Topbar ─────────────────────────────────────────────────────────────── */
.topbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; padding: 14px 28px;
  background: linear-gradient(180deg, #130f1b 0%, #0c0a12 100%);
  border-bottom: 1px solid var(--border-soft);
  /* NO pegajosa (3-ago): clavada arriba, las tarjetas de voz pasaban por debajo
     cortadas y se comia el tercio superior de la pantalla a cambio de ensenar el
     logotipo. Los botones utiles estan en la barra de encima, que tampoco se queda. */
  z-index: 20; backdrop-filter: blur(10px);
}
.brand { display: flex; align-items: center; gap: 14px; }
.brand-title {
  font-family: var(--font-display);
  font-size: 21px; font-weight: 700; letter-spacing: -.4px;
  background: var(--wave-grad);
  -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}
.brand-sub { font-weight: 400; opacity: .7; }
.sub { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
.stats { display: flex; gap: 7px; flex-wrap: wrap; align-items: center; }
.pill {
  background: var(--bg-card); border: 1px solid var(--border);
  padding: 6px 12px; border-radius: 999px; font-size: 12.5px; color: var(--muted); white-space: nowrap;
}
.pill b { color: var(--text); }
.pill.premium-pill { border-color: rgba(255,195,77,.35); color: var(--gold); }
.pill.fav-pill { border-color: rgba(240,101,117,.4); color: var(--heart); cursor: pointer; transition: background .1s; }
.pill.fav-pill:hover { background: rgba(240,101,117,.1); }
.pill.fav-pill.active { background: rgba(240,101,117,.15); }

/* ── Layout ─────────────────────────────────────────────────────────────── */
.layout {
  display: grid; grid-template-columns: 252px 1fr; gap: 20px;
  max-width: 1440px; margin: 0 auto; padding: 20px 24px 130px;
}

/* ── Filters sidebar ────────────────────────────────────────────────────── */
.filters {
  /* Los filtros SI siguen pegajosos: son utiles mientras se recorre la lista. El `top`
     era 82 px para esquivar la cabecera pegajosa; como ya no se queda, sube a 12 px y
     recupera esos 70 px de alto util. */
  position: sticky; top: 12px; align-self: start;
  background: var(--bg-soft); border: 1px solid var(--border-soft);
  border-radius: var(--radius); padding: 14px 13px;
  max-height: calc(100vh - 36px); overflow: auto;
}
.filters-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.filters h2 { font-size: 10.5px; text-transform: uppercase; letter-spacing: 1.3px; color: var(--muted-2); font-weight: 600; }
.link-btn { background: none; border: none; color: var(--accent); cursor: pointer; font-size: 11.5px; padding: 0; }
.link-btn:hover { text-decoration: underline; }

.field { margin: 11px 0; }
.field > label { display: block; font-size: 11px; color: var(--muted); margin-bottom: 5px; font-weight: 500; text-transform: uppercase; letter-spacing: .6px; }
.field.check label { display: flex; align-items: center; gap: 8px; color: var(--text); font-size: 12.5px; text-transform: none; letter-spacing: 0; }

input[type="search"], select {
  width: 100%; background: var(--bg-card); color: var(--text);
  border: 1px solid var(--border); border-radius: 9px; padding: 7px 10px; font-size: 13px;
}
input[type="search"]:focus, select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px rgba(160,107,255,.12); }

.chips { display: flex; flex-wrap: wrap; gap: 5px; }
.chip {
  background: var(--bg-card); border: 1px solid var(--border); color: var(--muted);
  border-radius: 999px; padding: 4px 9px; font-size: 11.5px; cursor: pointer;
  transition: all .12s; user-select: none; white-space: nowrap;
}
.chip:hover { background: var(--bg-card-hover); color: var(--text); border-color: var(--accent); }
.chip .c { color: var(--muted-2); margin-left: 3px; font-size: 10px; }
.chip.on { background: var(--accent-grad); border-color: transparent; color: #0c0a12; font-weight: 600; }
.chip.on .c { color: rgba(10,14,24,.5); }
.chip.premium-chip:not(.on) { border-color: rgba(255,195,77,.4); color: var(--gold); }
.chip.premium-chip.on { background: linear-gradient(135deg,#ffc34d,#e6a817); border-color: transparent; color: #1a1200; }

/* ── Results bar ────────────────────────────────────────────────────────── */
.results-bar {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 14px; gap: 10px; flex-wrap: wrap;
}
.results-bar-left { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.count { color: var(--muted); font-size: 13px; }
.count b { color: var(--text); }

.bar-controls { display: flex; align-items: center; gap: 8px; }
.sort { color: var(--muted); font-size: 13px; display: flex; align-items: center; gap: 6px; }
.sort select { width: auto; padding: 6px 9px; }

/* View toggle */
.view-toggle { display: flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.view-btn {
  background: none; border: none; color: var(--muted); cursor: pointer;
  padding: 6px 10px; font-size: 14px; transition: all .1s; line-height: 1;
}
.view-btn:hover { color: var(--text); background: var(--bg-card); }
.view-btn.active { background: var(--accent-grad); color: #0c0a12; }

/* ── Grid modes ─────────────────────────────────────────────────────────── */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(265px, 1fr));
  gap: 12px;
  transition: grid-template-columns .2s;
}
.grid.compact {
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 8px;
}
.grid.list {
  grid-template-columns: 1fr;
  gap: 6px;
}

/* ── Card — base ────────────────────────────────────────────────────────── */
.card {
  background: var(--bg-card); border: 1px solid var(--border-soft);
  border-radius: var(--radius); padding: 14px;
  display: flex; flex-direction: column; gap: 9px;
  transition: border-color .15s, transform .15s, box-shadow .15s;
  position: relative;
}
.card:hover { border-color: var(--border); transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,.3); }
.card.playing { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset, 0 0 20px rgba(160,107,255,.1); }
.card.premium-card { border-color: rgba(255,195,77,.2); }
.card.premium-card:hover { border-color: rgba(255,195,77,.5); box-shadow: 0 8px 24px rgba(0,0,0,.3), 0 0 18px rgba(255,195,77,.05); }
.card.fav-card { border-color: rgba(240,101,117,.25); }

/* now-playing dot */
.card.playing::before {
  content: ""; position: absolute; top: 10px; right: 12px;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 6px var(--accent);
  animation: pulse 1.4s ease-in-out infinite;
}
@keyframes pulse { 0%,100%{opacity:1;transform:scale(1);} 50%{opacity:.4;transform:scale(.6);} }

/* ── Card header ────────────────────────────────────────────────────────── */
.card-header { display: flex; align-items: center; gap: 9px; }
.voice-avatar {
  width: 38px; height: 38px; border-radius: 10px; flex-shrink: 0;
  display: grid; place-items: center;
  background: linear-gradient(145deg, #221a33, #16121d);
  border: 1px solid var(--border); font-size: 18px; line-height: 1;
}
.voice-avatar.premium-av { background: linear-gradient(145deg, #2a2010, #1a1500); border-color: rgba(255,195,77,.3); }
.card-meta { flex: 1; min-width: 0; }
.card-lang { font-weight: 600; font-size: 14px; line-height: 1.2; }
.card-id { font-size: 11px; color: var(--muted-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 1px; }
.badges { display: flex; gap: 4px; flex-wrap: wrap; }
.badge { font-size: 10px; padding: 2px 7px; border-radius: 999px; white-space: nowrap; font-weight: 600; }
.badge.premium { background: var(--gold-soft); color: var(--gold); border: 1px solid rgba(255,195,77,.3); }

/* ── Traits & tags ──────────────────────────────────────────────────────── */
.traits { display: flex; flex-wrap: wrap; gap: 4px; }
.trait {
  font-size: 11px; background: rgba(160,107,255,.08);
  border: 1px solid rgba(160,107,255,.15); color: var(--accent);
  padding: 2px 7px; border-radius: 6px;
}
/* ── Chip de idioma ─────────────────────────────────────────────────────────
   Sustituye a la bandera-por-idioma (ver /langchip.js: una bandera no es un idioma).
   Se pinta como el primer "trait" de la tarjeta pero en color neutro, para que se lea
   como metadato del idioma y no como un rasgo más de la voz. En la vista compacta es
   el ÚNICO sitio donde se ve el idioma (el CSS oculta .card-id). */
.lang-chip {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; line-height: 1.45;
  background: var(--bg-soft); border: 1px solid var(--border);
  color: var(--muted); padding: 2px 7px; border-radius: 6px;
  white-space: nowrap; max-width: 100%; overflow: hidden;
}
.lang-chip-code { font-weight: 700; letter-spacing: .04em; color: var(--text, #e9e6f2); }
.lang-chip-sep  { opacity: .45; }
.lang-chip-name { overflow: hidden; text-overflow: ellipsis; }
/* Código de idioma dentro del avatar cuadrado cuando NO hay bandera inequívoca.
   Así el idioma se ve siempre, también con un lector de pantalla. */
.avatar-code { font-size: 13px; font-weight: 700; letter-spacing: .03em; color: var(--muted); }
.voice-avatar.premium-av .avatar-code { color: var(--gold, #ffc34d); }
.avatar-flag { font-size: 18px; line-height: 1; }
.grid.compact .avatar-code { font-size: 11.5px; }
.grid.list .avatar-code { font-size: 12px; }

.tags { display: flex; flex-wrap: wrap; gap: 4px; }
.tag { font-size: 10.5px; background: var(--bg-soft); border: 1px solid var(--border-soft); color: var(--muted); padding: 2px 6px; border-radius: 5px; }
.tag.use { color: var(--accent-2); border-color: rgba(255,93,143,.2); }

/* ── Description (hidden in compact/list) ───────────────────────────────── */
.desc { font-size: 12.5px; color: var(--muted); line-height: 1.4; }
.desc.no-desc { font-style: italic; color: var(--muted-2); }

/* ── Quality stars ──────────────────────────────────────────────────────── */
.quality-stars { display: inline-flex; gap: 1px; }
.qs { font-size: 12px; color: var(--amber); }
.qs.empty { color: var(--border); }
.tier-label { font-size: 10.5px; color: var(--muted-2); }
.tier-label.premium { color: rgba(255,195,77,.7); }

/* ── Meta line ──────────────────────────────────────────────────────────── */
.meta-line { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-size: 11px; color: var(--muted-2); }

/* ── Card bottom ────────────────────────────────────────────────────────── */
.card-bottom { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: auto; }

/* ── Buttons ────────────────────────────────────────────────────────────── */
.btn {
  background: var(--accent-grad); border: none; color: #160c1f;
  font-weight: 600; padding: 8px 13px; border-radius: 9px;
  cursor: pointer; font-size: 13px;
  display: inline-flex; align-items: center; gap: 6px;
  transition: filter .12s, transform .1s, box-shadow .15s;
  position: relative; overflow: hidden;
}
/* Shimmer on hover — luz que barre el botón */
.btn::after {
  content: ''; position: absolute; top: 0; left: -80%;
  width: 50%; height: 100%;
  background: linear-gradient(105deg, transparent, rgba(255,255,255,.2), transparent);
  transform: skewX(-18deg);
  transition: left .45s ease;
  pointer-events: none;
}
.btn:not(:disabled):hover::after { left: 140%; }
.btn:hover { filter: brightness(1.1); }
.btn:active { transform: scale(.97); }
.btn:disabled { opacity: .4; cursor: not-allowed; filter: none; }
.btn.ghost { background: transparent; color: var(--text); border: 1px solid var(--border); }
.btn.ghost::after { display: none; }
.btn.ghost:hover { background: var(--bg-card); border-color: var(--accent); }

/* Listen button waveform icon */
.listen-icon { display: inline-flex; align-items: flex-end; gap: 2px; height: 12px; }
.listen-icon span { display: block; width: 3px; border-radius: 1.5px; background: currentColor; }
.listen-icon span:nth-child(1) { height: 5px; }
.listen-icon span:nth-child(2) { height: 9px; }
.listen-icon span:nth-child(3) { height: 12px; }
.listen-icon span:nth-child(4) { height: 9px; }
.listen-icon span:nth-child(5) { height: 5px; }
.card.playing .listen-icon span { animation: barsPlay .6s ease-in-out infinite alternate; }
.card.playing .listen-icon span:nth-child(2) { animation-delay: .1s; }
.card.playing .listen-icon span:nth-child(3) { animation-delay: .2s; }
.card.playing .listen-icon span:nth-child(4) { animation-delay: .3s; }
.card.playing .listen-icon span:nth-child(5) { animation-delay: .4s; }
@keyframes barsPlay { from{transform:scaleY(.25);} to{transform:scaleY(1);} }
/* Estado "bajando el WAV". Es la mitad visual del arreglo del preview: el WAV pesa
   ~630 KB y entre el clic y el primer sonido pasan de 0,3 s a varios segundos en
   móvil. Sin este estado el usuario volvía a clicar creyendo que no funcionaba, y ese
   2º clic PARABA el preview (ver comentario en app.js). Onda en marcha atenuada +
   etiqueta "Loading…" para que el clic tenga respuesta inmediata. */
.card.loading .btn.play-btn { opacity: .72; }
.card.loading .listen-icon span { animation: barsLoad 1s ease-in-out infinite alternate; }
.card.loading .listen-icon span:nth-child(2) { animation-delay: .12s; }
.card.loading .listen-icon span:nth-child(3) { animation-delay: .24s; }
.card.loading .listen-icon span:nth-child(4) { animation-delay: .36s; }
.card.loading .listen-icon span:nth-child(5) { animation-delay: .48s; }
@keyframes barsLoad { from{transform:scaleY(.2);opacity:.45;} to{transform:scaleY(.7);opacity:1;} }
/* Respeta "reducir movimiento": sin animación, la etiqueta ya dice el estado. */
@media (prefers-reduced-motion: reduce) {
  .card.playing .listen-icon span, .card.loading .listen-icon span { animation: none; }
}

/* ── Favorite button ────────────────────────────────────────────────────── */
.fav-btn {
  background: none; border: 1px solid var(--border); color: var(--muted-2);
  border-radius: 8px; padding: 7px 9px; cursor: pointer; font-size: 14px; line-height: 1;
  transition: all .12s; flex-shrink: 0;
}
.fav-btn:hover { border-color: var(--heart); color: var(--heart); background: rgba(240,101,117,.08); }
.fav-btn.active { border-color: rgba(240,101,117,.5); color: var(--heart); background: rgba(240,101,117,.1); }

/* ── Use this voice button (F-01) ───────────────────────────────────────── */
.use-voice-btn {
  display: inline-flex; align-items: center; padding: 5px 9px;
  background: transparent; border: 1px solid var(--border); border-radius: 8px;
  color: var(--muted); font-size: 12px; text-decoration: none; white-space: nowrap;
  transition: all .12s; flex-shrink: 0;
}
.use-voice-btn:hover { border-color: var(--accent); color: var(--accent); background: rgba(160,107,255,.08); }

/* ── Vote ───────────────────────────────────────────────────────────────── */
.vote { display: flex; align-items: center; gap: 3px; }
.vote button {
  background: var(--bg-soft); border: 1px solid var(--border); color: var(--muted);
  border-radius: 7px; padding: 5px 8px; cursor: pointer; font-size: 12px; transition: all .1s;
}
.vote button:hover { color: var(--text); border-color: var(--accent); }
.vote button.voted-up   { background: rgba(61,214,140,.12); border-color: var(--green); color: var(--green); }
.vote button.voted-down { background: rgba(240,101,117,.12); border-color: var(--red);  color: var(--red);  }
.vote .score { font-size: 11.5px; color: var(--muted); min-width: 22px; text-align: center; }

/* ── COMPACT mode overrides ─────────────────────────────────────────────── */
.grid.compact .card { padding: 11px; gap: 7px; border-radius: 11px; }
.grid.compact .card:hover { transform: translateY(-1px); }
.grid.compact .voice-avatar { width: 32px; height: 32px; border-radius: 8px; font-size: 15px; }
.grid.compact .card-lang { font-size: 13px; }
.grid.compact .card-id { display: none; }
.grid.compact .desc { display: none; }
.grid.compact .tags { display: none; }
.grid.compact .tier-label { display: none; }
.grid.compact .meta-line { gap: 5px; }
/* In compact, make listen btn fill full width */
.grid.compact .card-bottom { flex-direction: column; gap: 6px; }
.grid.compact .btn.play-btn { width: 100%; justify-content: center; padding: 7px; font-size: 12.5px; }
.grid.compact .card-actions { display: flex; justify-content: space-between; width: 100%; align-items: center; }
.grid.compact .vote button { padding: 4px 7px; font-size: 11px; }

/* ── LIST mode overrides ─────────────────────────────────────────────────── */
.grid.list .card {
  flex-direction: row; align-items: center; gap: 14px;
  padding: 10px 14px; border-radius: 10px;
}
.grid.list .card:hover { transform: none; }
.grid.list .card-header { min-width: 200px; flex-shrink: 0; }
.grid.list .card-lang { font-size: 13.5px; }
.grid.list .voice-avatar { width: 34px; height: 34px; border-radius: 9px; font-size: 16px; }
.grid.list .traits { flex-shrink: 0; width: 170px; }
.grid.list .desc { display: none; }
.grid.list .tags { flex: 1; }
.grid.list .meta-line { flex-shrink: 0; gap: 6px; }
.grid.list .tier-label { display: none; }
.grid.list .card-bottom { flex-direction: row; margin-top: 0; flex-shrink: 0; gap: 6px; }
.grid.list .btn.play-btn { padding: 7px 12px; font-size: 12.5px; white-space: nowrap; }

/* ── Load more / empty ──────────────────────────────────────────────────── */
.more { display: grid; place-items: center; margin-top: 24px; gap: 12px; }
.more .btn { padding: 10px 22px; }
.empty-state { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 16px 0 40px; }
.empty-state .chips { justify-content: center; }
.empty-wave { display: flex; align-items: flex-end; gap: 4px; height: 32px; }
.empty-wave span { display: block; width: 5px; border-radius: 3px; background: var(--border); opacity: .5; }
.empty-wave span:nth-child(1) { height: 12px; }
.empty-wave span:nth-child(2) { height: 22px; }
.empty-wave span:nth-child(3) { height: 32px; }
.empty-wave span:nth-child(4) { height: 22px; }
.empty-wave span:nth-child(5) { height: 12px; }
.empty-state p { color: var(--muted); font-size: 15px; line-height: 1.5; text-align: center; }

/* ── Skeleton ────────────────────────────────────────────────────────────── */
.skeleton {
  background: var(--bg-card); border: 1px solid var(--border-soft);
  border-radius: var(--radius); height: 200px; overflow: hidden; position: relative;
}
.grid.compact .skeleton { height: 140px; }
.grid.list .skeleton { height: 58px; border-radius: 10px; }
.skeleton::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,.03) 50%, transparent 100%);
  animation: shimmer 1.4s ease-in-out infinite;
}
@keyframes shimmer { 0%{transform:translateX(-100%);} 100%{transform:translateX(100%);} }

/* ── Floating player ─────────────────────────────────────────────────────── */
.player {
  position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); z-index: 40;
  width: min(700px, calc(100% - 32px));
  background: linear-gradient(135deg, #18141f, #120e1a);
  border: 1px solid var(--border); border-radius: 18px;
  box-shadow: var(--shadow), 0 0 0 1px rgba(160,107,255,.07) inset;
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; gap: 14px; padding: 11px 17px;
}
.player-info { display: flex; flex-direction: column; min-width: 0; }
.player-info strong { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.player audio { height: 34px; width: 100%; }
.player-close {
  order: 3; background: var(--bg-soft); border: 1px solid var(--border); color: var(--muted);
  width: 28px; height: 28px; border-radius: 7px; cursor: pointer; font-size: 11px;
  display: grid; place-items: center; transition: all .1s;
}
.player-close:hover { color: var(--text); border-color: var(--accent); }
.muted { color: var(--muted); font-size: 11.5px; margin-top: 1px; }

/* ── Toast ───────────────────────────────────────────────────────────────── */
.toast {
  position: fixed; bottom: 92px; left: 50%; transform: translateX(-50%) translateY(6px);
  background: var(--bg-soft); border: 1px solid var(--border); color: var(--text);
  padding: 9px 16px; border-radius: 10px; font-size: 13px;
  box-shadow: var(--shadow); z-index: 60;
  opacity: 0; transition: opacity .2s, transform .2s; pointer-events: none;
  white-space: normal; max-width: calc(100vw - 32px); text-align: center;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ── Subnav (catalog back-link) ──────────────────────────────────────────── */
.subnav {
  display: flex; align-items: center; gap: 10px; padding: 9px 28px;
  background: var(--bg-soft); border-bottom: 1px solid var(--border-soft);
  font-size: 13px;
}
.subnav-back { color: var(--accent); text-decoration: none; }
.subnav-back:hover { text-decoration: underline; }
.subnav-sep { color: var(--muted-2); }
.subnav-title { color: var(--muted); }
.subnav-cta {
  margin-left: auto; background: var(--accent-grad); color: #160c1f;
  padding: 5px 12px; border-radius: 8px; font-weight: 600;
  text-decoration: none; font-size: 12.5px; white-space: nowrap;
}
.subnav-cta:hover { filter: brightness(1.1); }

/* ── Responsive ──────────────────────────────────────────────────────────── */
@media (max-width: 860px) {
  .layout { grid-template-columns: 1fr; }
  .filters { position: static; max-height: none; }
  .grid.list .card { flex-wrap: wrap; }
  .grid.list .card-header { min-width: unset; }
  .grid.list .traits { width: auto; }
}

/* Móvil estrecho: la barra superior, la subnav y la de resultados no tenían
   reglas → se amontonaban/desbordaban a ~390px. */
@media (max-width: 560px) {
  .topbar { flex-wrap: wrap; gap: 10px; padding: 12px 14px; }
  .subnav { padding: 9px 14px; flex-wrap: wrap; }
  .layout { padding: 16px 12px; }
  .stats { font-size: 12px; gap: 6px; flex-wrap: wrap; }
  .results-bar { flex-wrap: wrap; gap: 10px; }
  .bar-controls { width: 100%; justify-content: space-between; }
  .grid, .grid.compact, .grid.comfortable { grid-template-columns: 1fr; }
}

/* ── Discord CTA (comunidad · dudas · reporte de bugs) ─────────────────────
   Presente en cabecera y pie de TODAS las páginas (orden del dueño 1-ago). Usa el
   blurple oficial de Discord (#5865F2) en lugar del violeta del sitio para que
   DESTAQUE y se lea como "comunidad", no como un enlace más de navegación.
   Cargado por todas las páginas vía styles.css (home, catálogo, generar, cuenta). */
.discord-cta {
  display: inline-flex; align-items: center; gap: 6px;
  background: #5865F2; color: #fff !important;
  padding: 6px 13px; border-radius: 9px;
  font-weight: 600; font-size: 13px; text-decoration: none; white-space: nowrap;
  transition: filter .12s ease, transform .1s ease, box-shadow .15s ease;
}
.discord-cta:hover { filter: brightness(1.08); transform: translateY(-1px); box-shadow: 0 6px 18px rgba(88,101,242,.35); }
.discord-cta:active { transform: scale(.97); }
.discord-cta svg { width: 16px; height: 16px; flex-shrink: 0; }
/* Variante "enlace" para pies con lista de enlaces: sin fondo, el blurple sólo en el
   texto para distinguirse de los enlaces legales sin romper la fila. */
.discord-inline {
  display: inline-flex; align-items: center; gap: 6px;
  color: #8b93f7 !important; text-decoration: none; font-weight: 600;
}
.discord-inline:hover { color: #a9b0f9 !important; text-decoration: underline; }
.discord-inline svg { width: 15px; height: 15px; flex-shrink: 0; }

/* ── Legal: footer compartido + páginas de documentos ──────────────────────
   Añadido para A1 (LSSI/DSA/RGPD): footer legal en todas las páginas + layout
   server-rendered de los documentos. Todo server-side; sin JS (CSP script-src 'self'). */
.site-legal-footer {
  border-top: 1px solid var(--border-soft);
  padding: 28px 20px 36px; margin-top: 56px;
  text-align: center; background: var(--bg-soft);
}
.legal-nav {
  display: flex; flex-wrap: wrap; gap: 6px 18px;
  align-items: center; justify-content: center;
}
.legal-nav a {
  color: var(--muted); text-decoration: none; font-size: 13.5px;
  transition: color .12s ease;
}
.legal-nav a:hover { color: var(--accent); text-decoration: underline; }
.legal-foot-note { color: var(--muted-2); font-size: 12.5px; margin-top: 14px; }

/* Documento legal renderizado */
.legal-body { background: var(--bg); }
.legal-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 16px 24px; border-bottom: 1px solid var(--border-soft);
  background: var(--bg-soft); position: sticky; top: 0; z-index: 10;
}
.legal-header a { color: var(--muted); text-decoration: none; font-size: 14px; }
.legal-home-link { font-weight: 600; color: var(--text) !important; }
.legal-header a:hover { color: var(--accent); }
.legal-main { max-width: 820px; margin: 0 auto; padding: 40px 24px 64px; }
.legal-title { font-size: 28px; font-weight: 700; margin-bottom: 18px; line-height: 1.25; }
.legal-content { color: var(--text); font-size: 15px; line-height: 1.7; }
.legal-content h2 { font-size: 21px; margin: 30px 0 12px; line-height: 1.3; }
.legal-content h3 { font-size: 17px; margin: 24px 0 10px; color: var(--text); }
.legal-content h4 { font-size: 15px; margin: 18px 0 8px; color: var(--muted); }
.legal-content p { margin: 12px 0; }
.legal-content ul, .legal-content ol { margin: 12px 0 12px 26px; }
.legal-content li { margin: 6px 0; }
.legal-content a { color: var(--accent); }
.legal-content code {
  background: var(--bg-card); border: 1px solid var(--border-soft);
  border-radius: 5px; padding: 1px 6px; font-size: 13px;
  font-family: ui-monospace, "Cascadia Code", monospace;
}
.legal-pre {
  background: var(--bg-card); border: 1px solid var(--border-soft);
  border-radius: 8px; padding: 14px 16px; margin: 16px 0; overflow-x: auto;
}
.legal-pre code {
  background: none; border: none; padding: 0; font-size: 13px;
  white-space: pre-wrap; word-break: break-word; color: var(--text);
}
.legal-content blockquote {
  border-left: 3px solid var(--accent); margin: 16px 0; padding: 8px 16px;
  background: var(--bg-soft); color: var(--muted); border-radius: 0 8px 8px 0;
}
.legal-content hr { border: none; border-top: 1px solid var(--border-soft); margin: 28px 0; }
.legal-table { width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 13.5px; }
.legal-table th, .legal-table td {
  border: 1px solid var(--border-soft); padding: 8px 12px; text-align: left; vertical-align: top;
}
.legal-table th { background: var(--bg-card); color: var(--text); font-weight: 600; }
.legal-banner {
  background: rgba(240, 180, 41, .12); border: 1px solid rgba(240, 180, 41, .4);
  border-radius: 10px; padding: 14px 18px; margin: 0 0 24px; font-size: 14px; color: var(--text);
}
.legal-placeholder {
  background: rgba(240, 180, 41, .25); color: var(--amber); border-radius: 4px;
  padding: 0 5px; font-weight: 600; font-style: normal;
}
.legal-index-list { list-style: none; margin: 18px 0; }
.legal-index-list li { margin: 0; border-bottom: 1px solid var(--border-soft); }
.legal-index-list a { display: block; padding: 13px 4px; color: var(--text); text-decoration: none; font-size: 16px; }
.legal-index-list a:hover { color: var(--accent); padding-left: 10px; transition: padding .12s ease; }
@media (max-width: 600px) {
  .legal-header { flex-direction: column; align-items: flex-start; gap: 8px; position: static; }
  .legal-main { padding: 28px 18px 48px; }
}

/* ── Scroll lateral en móvil estrecho ──────────────────────────────────────
   Medido 2-ago con navegador real a 320px: el documento daba scrollWidth 324
   en /catalog/ y 336 en /legal/terminos contra clientWidth 320. Dos causas,
   las dos del mismo tipo ("esto no puede encogerse"):
   1) Los items de .layout son items de grid, y un item de grid lleva
      min-width:auto: no baja de su min-content. El de .results eran 312,5px
      (.sort 211,3 + hueco 8 + .view-toggle 93,2) contra una columna de 296px,
      así que se salía 4px de la pantalla.
   2) En los textos legales las URLs completas (https://kreawave.com/legal/...)
      no tienen ningún punto donde partirse y empujaban el <li> 34px fuera de
      su caja. */
.layout > * { min-width: 0; }
.legal-content { overflow-wrap: break-word; }

@media (max-width: 560px) {
  /* Aquí .bar-controls pasa a width:100% (296px), pero .sort + .view-toggle
     siguen sumando 312,5px de min-content: sin min-width:0 el <select> de
     ordenación no cede y arrastra la barra fuera de la pantalla. */
  .bar-controls, .sort, .sort select { min-width: 0; }
}

/* Las tablas de los documentos legales (p.ej. la de categorías de datos de
   /legal/privacidad) piden 463px de ancho mínimo y no hay forma de encogerlas
   sin dejar el texto ilegible: a 320px sacaban el documento a scrollWidth 488
   (medido 2-ago). Con display:block la tabla pasa a ser su propio contenedor
   con scroll horizontal, así que se desplaza la tabla y no la página entera. */
@media (max-width: 560px) {
  .legal-table { display: block; overflow-x: auto; }
}


/* LIBRO DE CONSUMO de /account (2026-08-02) — historial de movimientos del saldo.
   Cinco columnas no caben en un móvil de 320px: con el scroll horizontal metido en
   su propio contenedor se desplaza LA TABLA y no la página entera (mismo criterio
   que .legal-table de aquí arriba, que ya se midió sacando el documento a 488px). */
.ledger-wrap { overflow-x: auto; margin-top: 8px; }
.ledger { width: 100%; border-collapse: collapse; font-size: 0.84rem; }
.ledger th, .ledger td {
  padding: 8px 12px 8px 0; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--border-soft); white-space: nowrap;
}
.ledger th {
  color: var(--muted-2); font-weight: 600; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.06em;
}
.ledger td { color: var(--text); }
/* El motivo es el único texto largo del libro: se le deja partir para que no estire
   la tabla y empuje la columna del saldo fuera de la vista. */
.ledger td.ledger-reason { white-space: normal; color: var(--muted); min-width: 160px; }
/* Cifras a la derecha y tabulares: así los descuentos se comparan de un vistazo. */
.ledger td.ledger-num { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
.ledger td.neg { color: var(--red); }    /* sale saldo */
.ledger td.pos { color: var(--green); }  /* entra saldo */


/* INTERRUPTOR MENSUAL | ANUAL (2026-08-03) — encargo del dueño: "un boton tipo slider
   que dandole te muestre las tarifas anuales y que desactivandolo vuelva a las
   mensuales". Vive aquí, y no en home.css, porque el mismo control se usa en DOS sitios
   (#pricing de la portada y el panel de cobro de /account) y las dos páginas cargan
   styles.css: una segunda copia acabaría divergiendo.

   El control es un <button role="switch"> real, así que:
     · el foco y el teclado (Tab + Enter/Espacio) los da el navegador, sin JS;
     · el estado visual se pinta desde [aria-checked], que es el MISMO atributo que lee
       el lector de pantalla → no puede quedar un botón que se ve encendido y se anuncia
       apagado. */
.cycle-switch {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 10px; flex-wrap: wrap; max-width: 100%;
  margin: 0 auto 28px; font-size: 14px;
}
/* A 320px "Yearly · 2 months free · save 16.7%" no cabe en la misma línea que el resto:
   se le deja partir DENTRO de la etiqueta (el badge baja) en vez de estirar la fila y
   sacar scroll horizontal al documento, que es un problema que esta web ya tiene. */
.cycle-opt {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  flex-wrap: wrap; color: var(--muted); font-weight: 600;
}
.cycle-opt.is-on { color: var(--text); }
.cycle-track {
  position: relative; flex: 0 0 auto; width: 46px; height: 26px; padding: 0;
  border: 1px solid var(--border); border-radius: 999px; background: var(--bg-card);
  cursor: pointer; transition: background .15s, border-color .15s;
}
.cycle-track:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cycle-knob {
  position: absolute; top: 50%; left: 3px; transform: translateY(-50%);
  width: 18px; height: 18px; border-radius: 50%; background: var(--muted);
  transition: transform .15s, background .15s;
}
.cycle-track[aria-checked="true"] { background: var(--accent); border-color: var(--accent); }
.cycle-track[aria-checked="true"] .cycle-knob {
  transform: translateY(-50%) translateX(20px); background: #160c1f;
}
/* El ahorro va en verde porque es dinero que se queda el cliente. La cifra la calcula el
   SERVIDOR restando los dos precios de Paddle (save_pct / free_months): aquí no hay
   ningún número, ni en el CSS ni en el HTML. */
.cycle-save { color: var(--green); font-weight: 600; font-size: 12.5px; white-space: nowrap; }
@media (prefers-reduced-motion: reduce) {
  .cycle-track, .cycle-knob { transition: none; }
}
/* En /account el interruptor va dentro de una .acc-row (etiqueta a la izquierda, control
   a la derecha): sin esto el inline-flex se pega al borde y el badge desborda la fila. */
.acc-row .cycle-switch { margin: 0; justify-content: flex-end; }


/* ── Aviso de BETA ABIERTA ────────────────────────────────────────────────────
   Orden del dueno (2-ago-2026): que se vea en TODAS las paginas que el servicio
   esta en beta, que puede fallar, y que reportar un fallo se premia con minutos.
   Va en el FLUJO del documento (no fixed): asi empuja el contenido en vez de
   taparlo, que en movil es la diferencia entre un aviso y un estorbo.
   Sin boton de cerrar a proposito: no es una promocion, es una advertencia que
   acompana a un cobro. */
.kw-beta-bar {
  display: flex; align-items: center; justify-content: center; gap: .6rem;
  flex-wrap: wrap; text-align: center;
  padding: .55rem 1rem; font-size: .82rem; line-height: 1.4;
  background: linear-gradient(90deg, rgba(168,85,247,.18), rgba(236,72,153,.18));
  border-bottom: 1px solid rgba(168,85,247,.35);
  color: #e9e4f5;
}
.kw-beta-tag {
  flex: none;
  font-weight: 700; font-size: .68rem; letter-spacing: .08em;
  padding: .16rem .5rem; border-radius: 999px;
  background: rgba(168,85,247,.35); color: #fff;
}
.kw-beta-text { min-width: 0; }           /* sin esto desborda a 320 px */
.kw-beta-bar a { color: #f0abfc; text-decoration: underline; }
.kw-beta-bar a:hover { color: #fff; }

/* Nota de beta dentro de una seccion concreta (demo, precios, catalogo). */
.kw-beta-note {
  margin: .6rem auto 0; max-width: 46rem;
  font-size: .8rem; line-height: 1.5; text-align: center;
  color: var(--muted-2, #a89fc0);
}
.kw-beta-note strong { color: #f0abfc; }

@media (max-width: 480px) {
  .kw-beta-bar { font-size: .76rem; padding: .5rem .7rem; }
}

/* Caja del aviso de beta en la seccion de precios. Es la unica nota de beta que se
   destaca de verdad: aqui es donde el visitante decide pagar, y donde enterarse de que
   reportar un fallo le regala minutos cambia lo que hace a continuacion. */
.kw-beta-box {
  max-width: 44rem; margin: 1.1rem auto 1.4rem;
  display: flex; align-items: center; justify-content: center;
  gap: .7rem; flex-wrap: wrap; text-align: center;
  padding: .8rem 1.1rem; border-radius: 14px;
  background: linear-gradient(90deg, rgba(168,85,247,.12), rgba(236,72,153,.12));
  border: 1px solid rgba(168,85,247,.3);
  font-size: .88rem; line-height: 1.55; color: #d9d2ea;
}
.kw-beta-box .kw-beta-tag { flex: none; }
.kw-beta-box .kw-beta-gift { color: #34d399; font-weight: 700; white-space: nowrap; }
.kw-beta-box .kw-beta-cta {
  flex: none; display: inline-block; white-space: nowrap;
  padding: .34rem .8rem; border-radius: 999px;
  background: rgba(168,85,247,.3); border: 1px solid rgba(168,85,247,.5);
  color: #fff; font-weight: 600; font-size: .82rem; text-decoration: none;
}
.kw-beta-box .kw-beta-cta:hover { background: rgba(168,85,247,.5); color: #fff; }
@media (max-width: 480px) {
  .kw-beta-box { font-size: .82rem; padding: .7rem .8rem; gap: .5rem; }
}

/* La caja de beta del catalogo va suelta bajo la cabecera (no dentro, que es pegajosa):
   necesita su propio margen y ancho maximo para no pegarse a los bordes. */
.kw-beta-box { margin-left: auto; margin-right: auto; }
main + .kw-beta-box, header + .kw-beta-box { margin: 1rem auto 0; }


/* ── Aviso "te quedaste sin minutos" ────────────────────────────────────
   Lo pinta generate.js y SOLO cuando de verdad no queda saldo: /api/auth/me afirma
   has_hours === false al cargar, o el servidor devuelve 402 al pulsar Generate.
   NO es un widget fijo ni un modal: va en el FLUJO del documento, encima de la rejilla.
   El dueno pregunto por una burbuja fija abajo a la derecha y se descarto: en movil se
   queda encima del boton de generar, que es justo lo que el usuario necesita tocar.
   Se cierra con la x y no vuelve en esa pestana (sessionStorage), pero SI reaparece si
   vuelve a chocar con el 402: ahi el aviso deja de ser ruido y es la respuesta.
   El estilo es DECORATIVO a proposito (colores, borde, radio): si este CSS llegase
   cacheado viejo mientras el JS ya es nuevo, el aviso se ve como texto plano con sus
   enlaces vivos — feo, pero legible y utilizable. */
.nobal {
  position: relative;
  margin: 0 0 1.5rem; padding: .95rem 2.4rem .95rem 1.1rem;
  border: 1px solid rgba(255,184,77,.42); border-radius: 14px;
  background: rgba(255,184,77,.08);
  font-size: .88rem; line-height: 1.55; color: var(--text, #f2eef7);
}
.nobal-title { font-weight: 700; margin-bottom: .3rem; color: var(--amber, #ffb84d); }
.nobal-text { margin: 0 0 .45rem; color: var(--muted, #a89db8); }
.nobal-list { margin: 0; padding-left: 1.15rem; display: flex; flex-direction: column; gap: .3rem; }
.nobal-list li { color: var(--muted, #a89db8); }
.nobal-list a { color: #f0abfc; text-decoration: underline; }
.nobal-list a:hover { color: #fff; }
.nobal-close {
  position: absolute; top: .4rem; right: .45rem;
  width: 1.8rem; height: 1.8rem; line-height: 1;
  background: none; border: 0; border-radius: 9px; cursor: pointer;
  color: var(--muted-2, #8b7fa0); font-size: 1.3rem;
}
.nobal-close:hover { color: var(--text, #f2eef7); background: rgba(255,255,255,.07); }
@media (max-width: 480px) {
  .nobal { font-size: .82rem; padding: .85rem 2.2rem .85rem .85rem; }
}


/* ── Popup del límite de la demo (4-ago-2026) ────────────────────────────────
   `<dialog>` centrado. `max-width` en `min()` para que en un móvil de 360 px no se
   coma los bordes: con un ancho fijo, el diálogo se sale y el usuario no llega al
   botón de cerrar. */
.kw-dialog {
  border: 1px solid var(--border); border-radius: 16px; padding: 26px 26px 20px;
  background: var(--bg-card); color: var(--text);
  max-width: min(460px, calc(100vw - 32px)); width: 100%;
  /* Un <dialog> modal se centra solo con `margin:auto`... salvo que una regla previa
     de la hoja ponga `margin: 0` en algo que lo incluya — y aquí pasa: sin esto sale
     pegado a la esquina superior izquierda (visto en captura, 4-ago). Se fija el
     centrado con `inset` + `margin:auto`, que es lo que hace el navegador por defecto,
     y así ninguna regla anterior puede ganarle por orden. */
  inset: 0; margin: auto;
}
.kw-dialog::backdrop { background: rgba(5, 3, 12, .72); backdrop-filter: blur(3px); }
.kw-dialog-h { margin: 0 0 12px; font-size: 19px; font-weight: 700; letter-spacing: -.01em; }
.kw-dialog-p { margin: 0 0 12px; font-size: 14px; line-height: 1.55; color: var(--muted); }
.kw-dialog-p strong { color: var(--text); }
.kw-dialog-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 18px; }
.kw-dialog-btn {
  display: block; text-align: center; text-decoration: none; cursor: pointer;
  padding: 11px 16px; border-radius: 10px; font-size: 14px; font-weight: 600;
  font-family: inherit; border: 1px solid var(--border); background: transparent;
  color: var(--text); transition: all .12s;
}
.kw-dialog-btn:hover { border-color: var(--accent); }
.kw-dialog-btn.primary { background: #5865F2; border-color: #5865F2; color: #fff; }
.kw-dialog-btn.primary:hover { filter: brightness(1.08); }
.kw-dialog-btn.ghost { border-color: transparent; color: var(--muted); }
.kw-dialog-btn.ghost:hover { color: var(--text); border-color: transparent; }
