:root{
  --bg:#F1ECE3;
  --surface:#FBF9F5;
  /* superficie SÓLIDA blanca para el dock y las stat cards (todos los temas claros).
     dark-academia la sobreescribe con su surface oscura. */
  --card-solid:#FFFDFA;
  --ink:#1C1A17;
  --ink-soft:#706A62;
  --ink-faint:#716A63;
  --rose:#D89AA0;
  --rose-deep:#A0555D;
  /* Ancho del bloque de marca: lo comparten el logotipo y el lema, que se estira hasta el.
     Va AQUI y no junto a sus reglas: la version estrecha se declara en un @media mas abajo, y una
     :root posterior con la misma especificidad le habria ganado por orden de aparicion. */
  --brandmark-w:148.5px;   /* 135 + 10% (25-08) */
  /* La tinta que va ENCIMA de un relleno --rose-deep. En tema claro el relleno es oscuro y quiere
     blanco; en tema OSCURO --rose-deep es el color CLARO de los enlaces y un relleno suyo es una
     mancha brillante donde el blanco no se lee.
     ⚑ Este valor es solo el RESPALDO de arranque. Quien manda es applyTheme(), que calcula
     inkOn(--rose-deep) y lo escribe EN LINEA sobre :root — y un estilo en linea gana a cualquier
     regla de aqui. No pongas valores por tema en este archivo: quedarian muertos y, peor, darian
     la impresion de estar mandando. */
  --on-rose-deep-ink:#fff;
  --rose-tint:#F1DEDF;
  --line:#E2DACC;
  --line-strong:rgba(28,26,23,.16);
  --danger:#b94a4a;        /* fill for danger buttons (white text sits on it) */
  --danger-ink:#a83f3f;    /* danger text/border on a surface */
  --success:#3aa760;       /* fill for success badges */
  --success-ink:#1f7a43;   /* success text on a surface */
  --wood:#9c6a44; --wood-dark:#774d2f; --wood-top:#b3825a;   /* bookshelf planks */
  --card-shadow: 0 1px 2px rgba(28,26,23,.04), 0 10px 24px rgba(28,26,23,.05);
  --radius:14px;
  /* Tipografía — se cambia una familia aquí, no en las 47 reglas de abajo.
     display = títulos y cifras; body = toda la UI funcional (labels, botones, datos);
     hand = SOLO las palabras que escribe Laura (momentos, notas). Nunca en UI. */
  --font-display:'Source Serif 4',Georgia,serif;
  /* Misma familia que la display, distinto rol: la cita es un bloque de lectura.
     Se separan por eje óptico (ver .qpick-text/.jrq-text/.quote-text), no por
     familia — Source Serif 4 tiene opsz, así que el mismo diseño afina en
     titular y engorda en texto corrido. */
  --font-quote:'Source Serif 4',Georgia,serif;
  --font-body:'Mulish',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --font-hand:'Caveat','Bradley Hand','Segoe Script',cursive;
  /* El trazo manuscrito es fino y sobre fondo oscuro se adelgaza a la vista;
     los fondos oscuros suben este peso para compensar. */
  --hand-weight:500;
  /* subtle atmospheric background gradient (per theme) + semi-transparent panel fills */
  --app-gradient:
    radial-gradient(ellipse 66% 66% at 0% 0%, rgba(240,184,168,0.62) 0%, transparent 72%),
    radial-gradient(ellipse 60% 60% at 100% 100%, rgba(240,184,168,0.72) 0%, transparent 72%),
    #F1ECE3;
  --card-glass: rgba(255,255,255,0.88);
  --surface-glass: rgba(251,249,245,0.85);
  --topbar-glass: rgba(241,236,227,0.60);
  --topbar-border: rgba(255,255,255,0.2);

  /* ⚑⚑ --ink-soft Y --ink-faint SE MIDEN CONTRA --bg, NO CONTRA --surface. Es el error que se
     coló en siete temas: se comprobaron sobre --surface (la tarjeta), pero el dock «···», las
     pills, los marcadores de portada vacía y los badges de importación se apoyan en --bg, que en
     todos los temas claros es MÁS OSCURO que --surface. Es decir, se estaban validando contra el
     más fácil de los dos fondos. Mínimos: --ink-soft >= 4.5 y --ink-faint >= 3.0 SOBRE --bg,
     comprobando además que no bajen de ahí sobre --surface. Los catorce cumplen: soft >= 4.54,
     faint >= 3.11. Si añades un tema, mide contra --bg o repetirás esto. */

  /* Tonos de las tarjetas de Feels — derivados de la marca de cada tema.
     Las paradas, el texto de dusk y los acentos se redeclaran en cada bloque de tema;
     los degradados compuestos de abajo los recogen solos (mismo elemento :root).
     daylight y lamplight: las paradas se ordenan por LUMINANCIA, no por nombre de token
     (en dark-academia --rose-deep es más claro que --rose), y sus acentos apuntan a >=3.2
     de contraste manteniendo >=60 de distancia al texto.

     ⚑ LAMPLIGHT SE INVIERTE EN LOS TEMAS OSCUROS. En un tema claro es la tarjeta oscura y salta a
     la vista (16:1 contra la superficie en Clásico). En uno oscuro no queda sitio POR DEBAJO de la
     superficie, así que se hundía en el fondo: 1.04:1 en dark-academia, 1.15 en forest, 1.11 en
     slowburn, 1.12 en onemore — invisible. Ahora se LEVANTA por encima de la superficie
     conservando su propio matiz, para que siga leyéndose como noche y no como «un gris distinto».
     Los valores salen de barrer la luminosidad hasta cumplir tres cosas a la vez: tarjeta >=1.75
     contra la superficie, tinta >=6.5 encima, y el acento del tono >=3.2 (el mínimo que fija el
     comentario de arriba). Quedan en 1.88 / 1.94 / 1.95 / 2.08.

     dusk es multi-tono: recrea el viaje de 3 colores del degradado original rotando el
     MATIZ alrededor del --rose del tema (que ancla la parada del 60%), con el extremo frío
     oscurecido -10 de luminosidad y el cálido aclarado +8. La rotación estándar es ±38°,
     pero cada tema la acota o la reorienta para no salirse de su paleta — ver los
     comentarios de cozy, slick, dark-academia y daybreak. Texto blanco fijo en los 6: como
     el original, dusk no alcanza 4.5 en su parada clara (el original estaba en 2.66) y se
     acepta a propósito. El acento reusa el tinte cálido del original. */
  --tone-daylight-1:#FBF9F5;  --tone-daylight-2:#F5E9E8;
  --tone-dusk-1:#B58AA9;      --tone-dusk-2:#D89AA0;      --tone-dusk-3:#E3CFB7;
  --tone-lamplight-1:#201824; --tone-lamplight-2:#19131C;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#AC7479;  --tone-dusk-accent:#FBE3DC;  --tone-lamplight-accent:#F1DEDF;
  --tone-daylight:linear-gradient(160deg, var(--tone-daylight-1) 0%, var(--tone-daylight-2) 100%);
  --tone-dusk:linear-gradient(155deg, var(--tone-dusk-1) 0%, var(--tone-dusk-2) 60%, var(--tone-dusk-3) 100%);
  --tone-lamplight:linear-gradient(165deg, var(--tone-lamplight-1) 0%, var(--tone-lamplight-2) 100%);
}
:root[data-theme="dark-academia"]{
  --bg:#1d1a15; --surface:#272219; --card-solid:#272219; --ink:#ece3d2; --ink-soft:#a99f8a; --ink-faint:#928974;
  --rose:#8f7350; --rose-deep:#c2a063; --rose-tint:#342d20; --line:#39332a; --line-strong:rgba(236,227,210,.18);
  --danger-ink:#e08a85; --success-ink:#6fcf90;   /* lighter ink so danger/success text stays legible on the dark surface */
  --hand-weight:600;
  --card-shadow:0 1px 2px rgba(0,0,0,.35), 0 12px 28px rgba(0,0,0,.42);
  --app-gradient:
    radial-gradient(ellipse 66% 66% at 0% 0%, rgba(74,56,40,0.62) 0%, transparent 72%),
    radial-gradient(ellipse 60% 60% at 100% 100%, rgba(61,42,24,0.72) 0%, transparent 72%),
    #1d1a15;
  --card-glass: rgba(39,34,25,0.88);
  --surface-glass: rgba(39,34,25,0.85);
  --topbar-glass: rgba(29,26,21,0.70);
  --topbar-border: rgba(255,255,255,0.08);
  /* único tema oscuro: daylight se sintetiza aclarando --rose-tint (su --surface es oscuro)
     y es el único que conserva el texto blanco en dusk. */
  --tone-daylight-1:#E7E6E4;  --tone-daylight-2:#C6C4C1;
  --tone-dusk-1:#5B4333;      --tone-dusk-2:#8F7350;      --tone-dusk-3:#B19857;
  --tone-lamplight-1:#664162; --tone-lamplight-2:#583855;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#342D20;  --tone-dusk-accent:#F8DEBF;  --tone-lamplight-accent:#C2A063;
}
:root[data-theme="romance"]{
  --bg:#fbedf0; --surface:#fff7f8; --ink:#3c1f29; --ink-soft:#84636B; --ink-faint:#91606C;
  --rose:#e98a9b; --rose-deep:#C03E5F; --rose-tint:#fbdce3; --line:#f3d8de; --line-strong:rgba(60,31,41,.15);
  --card-shadow:0 1px 2px rgba(150,50,75,.05), 0 10px 24px rgba(150,50,75,.09);
  --app-gradient:
    radial-gradient(ellipse 66% 66% at 0% 0%, rgba(212,176,212,0.62) 0%, transparent 72%),
    radial-gradient(ellipse 60% 60% at 100% 100%, rgba(196,154,196,0.72) 0%, transparent 72%),
    #fbedf0;
  --card-glass: rgba(255,255,255,0.88);
  --surface-glass: rgba(255,247,248,0.85);
  --topbar-glass: rgba(251,237,240,0.60);
  --topbar-border: rgba(255,255,255,0.2);
  --tone-daylight-1:#FFF7F8;  --tone-daylight-2:#FDE7EB;
  --tone-dusk-1:#B58BAD;      --tone-dusk-2:#E98A9B;      --tone-dusk-3:#EFCBAC;
  --tone-lamplight-1:#301A2D; --tone-lamplight-2:#251423;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#C64F6D;  --tone-dusk-accent:#FBE3DC;  --tone-lamplight-accent:#FBDCE3;
}
:root[data-theme="cozy"]{
  --bg:#f1ecdb; --surface:#faf6ea; --ink:#393426; --ink-soft:#716955; --ink-faint:#716952;
  --rose:#c08a68; --rose-deep:#985C3E; --rose-tint:#ecddc8; --line:#e3dabf; --line-strong:rgba(57,52,38,.16);
  --card-shadow:0 1px 2px rgba(80,60,30,.05), 0 10px 24px rgba(80,60,30,.08);
  --app-gradient:
    radial-gradient(ellipse 66% 66% at 0% 0%, rgba(192,138,104,0.62) 0%, transparent 72%),
    radial-gradient(ellipse 60% 60% at 100% 100%, rgba(192,138,104,0.72) 0%, transparent 72%),
    #f1ecdb;
  --card-glass: rgba(255,255,255,0.88);
  --surface-glass: rgba(250,246,234,0.85);
  --topbar-glass: rgba(241,236,219,0.60);
  --topbar-border: rgba(255,255,255,0.2);
  --tone-daylight-1:#FAF6EA;  --tone-daylight-2:#F2E7D6;
  /* rotación acotada a ±28°: su base está en 23°, así que el extremo cálido para en 52°,
     justo antes de cruzar a amarillo. */
  --tone-dusk-1:#AD4850;      --tone-dusk-2:#C08A68;      --tone-dusk-3:#CCC285;
  --tone-lamplight-1:#2F252C; --tone-lamplight-2:#251D22;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#B06A48;  --tone-dusk-accent:#FBE3DC;  --tone-lamplight-accent:#ECDDC8;
}
:root[data-theme="slick"]{
  --bg:#f3f5f8; --surface:#ffffff; --ink:#0f1318; --ink-soft:#58616c; --ink-faint:#697178;
  --rose:#5b8def; --rose-deep:#2A6BDF; --rose-tint:#e6eefb; --line:#e7eaef; --line-strong:rgba(15,19,24,.13);
  --radius:9px; --card-shadow:0 1px 2px rgba(15,19,24,.04), 0 8px 22px rgba(15,19,24,.07);
  --app-gradient:
    radial-gradient(ellipse 66% 66% at 0% 0%, rgba(200,216,240,0.62) 0%, transparent 72%),
    radial-gradient(ellipse 60% 60% at 100% 100%, rgba(176,200,232,0.72) 0%, transparent 72%),
    #f3f5f8;
  --card-glass: rgba(255,255,255,0.88);
  --surface-glass: rgba(255,255,255,0.85);
  --topbar-glass: rgba(243,245,248,0.65);
  --topbar-border: rgba(255,255,255,0.2);
  --tone-daylight-1:#FFFFFF;  --tone-daylight-2:#F0F5FD;
  /* rotación dentro de su propio registro frío (236°→220°→200°); el extremo claro baja
     a 54% de saturación para no salir cian neón. */
  --tone-dusk-1:#1A29E9;      --tone-dusk-2:#5B8DEF;      --tone-dusk-3:#94C6DF;
  --tone-lamplight-1:#1A1425; --tone-lamplight-2:#14101D;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#2F6FE0;  --tone-dusk-accent:#FBE3DC;  --tone-lamplight-accent:#E6EEFB;
}
/* TEMA: daybreak — 6to tema. Amanecer: naranja cálido sobre azul marino.

   ⚑ LA TINTA SE LEÍA GRIS, Y NO ERA CULPA DEL TONO. #12283A ya estaba en 207° con 53% de
   saturación — el tono era correcto. El problema era la LUMINOSIDAD: a 15% queda tan poca luz
   que el ojo no resuelve el croma. Girar el tono no lo arregla; hay que subir croma y luz.

   La medida que de verdad predice si algo "se ve azul" es la separación entre el canal azul y
   el rojo:  #12283A → 40   ·   #051650 → 75.  Casi el doble.

   --ink-soft y --ink-faint se derivan del MISMO tono que --ink para que los tres sean una familia
   y no tres azules distintos. --ink-soft era el peor caso del original (20% de saturación =
   pizarra) y es el que lleva casi todo el texto.

   ⚑⚑ AJUSTE 27-08 A LA FOTO DEL AMANECER. La lectora trajo una foto: cielo azul-verdoso
   sobre una franja naranja y arena encendida. Lo que NO cuadraba con ella era el azul (226°, un
   marino limpio) y la arena (26°, una crema rosada). Los dos se mueven; el naranja no, porque el
   naranja de la foto ya era este.

     tinta   #051650 → #082B57   226° → 213°, hacia el cielo de la foto
     soft    #3D508F → #2E5480   212°, misma familia
     faint   #5E6C9D → #4F6D8E   211°, misma familia
     fondo   #FBEFE6 → #FCEFDF   26° → 33°, arena dorada en vez de crema rosada
     arena   #FFDCC2 → #FBD2A8   el extremo del degradado, la arena iluminada

   ⚑ Y SE RESPETA LA LECCIÓN DE ARRIBA en vez de repetirla: el tono se mueve solo hasta donde
   la separación azul-rojo aguanta. #082B57 la deja en 79, POR ENCIMA de los 75 del marino que
   funcionaba, mientras que ir hasta los ~207° de la foto la habría dejado en 69 y de vuelta al
   gris. Trece grados es lo que da de sí este eje.

   Contraste medido sobre --surface / --bg, no supuesto:
     ink   13.85 / 12.42   (era 16.71 / 15.00 — baja, y sigue sobrando por diez)
     soft   7.67 /  6.88   (era  7.54 /  6.76 — mejora)
     faint  5.29 /  4.74   (era  5.03 /  4.52 — mejora)
   Los suelos del README son 4.5 y 3.0 sobre --bg.

   ⚑ NO SE TOCA --rose ni --rose-deep, y no por pereza: --tone-dusk-2 ES #FF5F00, y los tonos
   de Feels llevan el color de las entradas que las lectoras YA escribieron. Mover el acento las
   repintaría hacia atrás.
   ⚑ Y ojo: el párrafo de abajo describe --rose-deep como #CC4C00 con sus medidas, pero el
   valor real del bloque es #BE4700 desde que rose-deep-audit --apply lo bajó. Las medidas del
   4.564/4.495 son de aquel color, no de este.

   ⚑ EL NARANJA SON DOS COLORES, no uno. --rose es el acento (bordes, hover, el tono dusk);
   --rose-deep es el FONDO de los botones primarios con texto blanco Y el color de los enlaces
   sobre la superficie. #FF5F00 sirve para el primero pero no para el segundo: da 3.05:1 con
   blanco encima y 3.00:1 como texto. Así que --rose-deep se deriva: mismo tono, croma completo,
   40% de luz → #CC4C00, que da 4.564:1 con blanco encima y 4.495:1 como texto sobre --surface.
   ⚑ OJO CON ESE SEGUNDO NÚMERO: son 4.495, no 4.5. Se queda a 0.005 de AA, así que NO se puede
   decir que cumple — aunque a ojo sea indistinguible. #CB4C00 (un solo paso en el canal rojo) da
   4.523:1 y sí cumple, si algún día hace falta poder afirmarlo. Se dejó #CC4C00 porque es el color
   elegido a propósito; la diferencia es de 1/255 y no se ve.
   Importa en los enlaces de las páginas legales y del modal legal, que se leen en pequeño. Y deja
   un escalón claro por debajo de #FF5F00 para que hover y pressed se sigan notando: el acento ya
   carga toda la viveza, así que --rose-deep no necesita competir.

   ⚑ PENDIENTE si algún día se retocan los tonos de Feels: --tone-dusk-1/2/3 se diseñaron como
   ±26° alrededor del ancla (350° · 16° · 42°). El ancla ahora es 22°, así que la rotación
   quedó en −32°/+20°. Se dejó a propósito: mover los extremos cambiaría el color de las
   entradas que las lectoras YA escribieron. */
:root[data-theme="daybreak"]{
  /* --bg es el sólido de respaldo; --bg-gradient es la versión en degradado */
  --bg:#FCEFDF;
  --bg-gradient:linear-gradient(160deg, #FCEFDF 0%, #FBD2A8 100%);
  --surface:#FFFDFA; --ink:#082B57; --ink-soft:#2E5480; --ink-faint:#4F6D8E;
  --rose:#FF5F00; --rose-deep:#BE4700; --rose-tint:#FDE3D6;
  --line:#F2E5D3; --line-strong:rgba(8,43,87,.16);
  --danger:#D54141; --danger-ink:#A02F2F; --success:#3F8F5C; --success-ink:#296B41;
  --radius:14px; --card-shadow:0 6px 18px rgba(8,43,87,.07);
  --app-gradient:
    radial-gradient(circle at 10% 0%, #FBD2A8 0%, transparent 55%),
    radial-gradient(circle at 95% 100%, #FDE3D6 0%, transparent 50%),
    var(--bg);
  --card-glass: rgba(255,253,250,.6);
  --surface-glass: rgba(255,253,250,.75);
  --topbar-glass: rgba(252,239,223,.85);
  --topbar-border: rgba(8,43,87,.08);
  --tone-daylight-1:#FFFDFA;  --tone-daylight-2:#FDEEDE;
  /* rotación acotada a ±26°: con ±34° el extremo cálido llegaba a amarillo y el frío se
     iba a malva. Los dos extremos se atenúan a la vez (45% y 58% frente al 87% del ancla),
     porque atenuar solo el frío lo desconectaba del naranja. Queda rosa → naranja → ámbar. */
  --tone-dusk-1:#BC4E60;      --tone-dusk-2:#FF5F00;      --tone-dusk-3:#DFC587;
  --tone-lamplight-1:#1B1F36; --tone-lamplight-2:#15182A;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#CC4C00;  --tone-dusk-accent:#FBE3DC;  --tone-lamplight-accent:#FDE3D6;
}
/* ═══ TEMAS DE ÁNIMO (grupo "Mood") — nombrados por lo que a la lectora le APETECE LEER, no por
   el sitio ni por la hora. La prueba es la frase: «hoy me apetece un slow burn», «me apetece un
   capítulo más». Un tema llamado Medianoche describe la habitación; Un capítulo más describe a
   quien está leyendo, que es de lo que va la app. ═══ */

/* TEMA: slowburn — brasas. Oscuro CÁLIDO: vino y ceniza, con el rescoldo como acento.
   ⚑ Mismo tono que Romance (347°) y no se confunden: Romance está al 96% de luz y este al 9%. El
   mismo matiz sobre fondos opuestos lee como dos colores distintos — igual que el oro de Mountain
   frente al de Dark Academia. Se deja escrito para que nadie lo "arregle".
   ⚑ Tema OSCURO: --rose-deep es MÁS CLARO que --rose porque es el color de los ENLACES sobre una
   superficie oscura, no un fondo de botón. Ver la nota de forest.
   Contrastes: tinta 12.79:1 / 13.99:1, --ink-soft 6.58:1, --ink-faint 3.56:1, enlaces 6.35:1,
   bordes 3.47:1, peligro 7.32:1, éxito 8.90:1, CTA pública 6.94:1. */
:root[data-theme="slowburn"]{
  --bg:#1E1013;
  --surface:#2A181C; --card-solid:#2A181C; --ink:#F0DCD8; --ink-soft:#C0989A; --ink-faint:#9D7C7E;
  --rose:#BE4B52; --rose-deep:#E8827A; --rose-tint:#3A2126;
  --line:#3D2429; --line-strong:rgba(240,220,216,.18);
  --danger-ink:#F0918A; --success-ink:#86CC96;
  --hand-weight:600;
  --radius:14px; --card-shadow:0 1px 2px rgba(0,0,0,.40), 0 12px 28px rgba(0,0,0,.48);
  --app-gradient:
    radial-gradient(ellipse 58% 52% at 8% 0%, rgba(190,75,82,0.20) 0%, transparent 68%),
    radial-gradient(ellipse 60% 56% at 100% 100%, rgba(90,30,36,0.55) 0%, transparent 72%),
    var(--bg);
  --card-glass: rgba(42,24,28,0.88);
  --surface-glass: rgba(42,24,28,0.85);
  --topbar-glass: rgba(30,16,19,0.72);
  --topbar-border: rgba(255,255,255,0.07);
  --tone-daylight-1:#F3E7E4;  --tone-daylight-2:#DCC4C2;
  --tone-dusk-1:#6E2733;      --tone-dusk-2:#BE4B52;      --tone-dusk-3:#E5B39A;
  --tone-lamplight-1:#723B48; --tone-lamplight-2:#63333E;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#3A2126;  --tone-dusk-accent:#F6DFD9;  --tone-lamplight-accent:#E8827A;
}
:root[data-theme="slowburn"]{
  --tag-yellow:#D9A45C; --tag-yellow-text:#3A2609;
  --tag-teal:#6E9E8C;   --tag-teal-text:#152E26;
  --tag-pink:#D4737C;   --tag-pink-text:#3E141A;
  --tag-lilac:#A48FA8;  --tag-lilac-text:#2E2130;
}

/* TEMA: onemore ("Un capítulo más") — las dos de la madrugada. Índigo profundo con el charco
   cálido de la lámpara de lectura como acento.
   ⚑ El hueco que faltaba entre los oscuros: Dark Academia es negro-marrón, Forest negro-verde,
   este es negro-AZUL. Tema oscuro, misma inversión de papeles.
   Contrastes: tinta 13.54:1 / 14.85:1, --ink-soft 7.63:1, --ink-faint 4.13:1, enlaces 9.07:1,
   bordes 5.72:1, peligro 7.26:1, éxito 8.81:1, CTA pública 9.64:1. */
:root[data-theme="onemore"]{
  --bg:#0F1424;
  --surface:#171D31; --card-solid:#171D31; --ink:#E4E7F2; --ink-soft:#A8AECB; --ink-faint:#7D84A1;
  --rose:#C98A3C; --rose-deep:#F0B45E; --rose-tint:#241C2E;
  --line:#262D45; --line-strong:rgba(228,231,242,.18);
  --danger-ink:#F09189; --success-ink:#84CC98;
  --hand-weight:600;
  --radius:14px; --card-shadow:0 1px 2px rgba(0,0,0,.42), 0 12px 28px rgba(0,0,0,.50);
  /* la lámpara arriba a la izquierda; el resto de la habitación a oscuras */
  --app-gradient:
    radial-gradient(ellipse 52% 46% at 6% 0%, rgba(240,180,94,0.16) 0%, transparent 66%),
    radial-gradient(ellipse 62% 58% at 100% 100%, rgba(20,26,54,0.70) 0%, transparent 72%),
    var(--bg);
  --card-glass: rgba(23,29,49,0.88);
  --surface-glass: rgba(23,29,49,0.85);
  --topbar-glass: rgba(15,20,36,0.74);
  --topbar-border: rgba(255,255,255,0.07);
  --tone-daylight-1:#E7E9F4;  --tone-daylight-2:#C7CBE0;
  --tone-dusk-1:#6B4A86;      --tone-dusk-2:#C98A3C;      --tone-dusk-3:#EFD9A0;
  --tone-lamplight-1:#3A4C90; --tone-lamplight-2:#34447F;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#241C2E;  --tone-dusk-accent:#F7E7C9;  --tone-lamplight-accent:#F0B45E;
}
:root[data-theme="onemore"]{
  --tag-yellow:#E0B45E; --tag-yellow-text:#3E2C06;
  --tag-teal:#6FA3B0;   --tag-teal-text:#152E36;
  --tag-pink:#C98BA8;   --tag-pink-text:#3A1B2C;
  --tag-lilac:#8E8CC4;  --tag-lilac-text:#22214A;
}

/* TEMA: whodunit — casi monocromo, un solo rojo.
   ⚑ EL ÚNICO TEMA QUE NO ESTÁ CONSTRUIDO SOBRE UN MATIZ: su fondo es neutro de verdad (4% de
   saturación, frente al 10% de taiga que era el más bajo hasta ahora). Eso es precisamente lo que
   hace que el rojo único golpee — en cualquier otro tema competiría con el color del fondo.
   Contrastes: tinta 16.83:1 / 14.07:1, --ink-soft 7.04:1, --ink-faint 3.23:1, enlaces 8.87:1,
   bordes 6.55:1, éxito 5.97:1, CTA 9.27:1. */
:root[data-theme="whodunit"]{
  --bg:#E6E6E4;
  --surface:#FAFAF9; --ink:#191919; --ink-soft:#565654; --ink-faint:#676765;
  --rose:#B0201E; --rose-deep:#8E1512; --rose-tint:#F2DEDD;
  --line:#D2D2CF; --line-strong:rgba(25,25,25,.18);
  --danger:#B0201E; --danger-ink:#8E1512; --success:#3A6B44; --success-ink:#2C5335;
  --radius:9px; --card-shadow:0 1px 2px rgba(0,0,0,.06), 0 10px 24px rgba(0,0,0,.10);
  /* sin color en el degradado: sólo luz. Meter un tinte rompería lo único que define el tema. */
  --app-gradient:
    radial-gradient(ellipse 64% 56% at 4% 0%, rgba(255,255,255,0.90) 0%, transparent 68%),
    radial-gradient(ellipse 62% 58% at 100% 100%, rgba(150,150,146,0.30) 0%, transparent 72%),
    var(--bg);
  --card-glass: rgba(250,250,249,.72);
  --surface-glass: rgba(250,250,249,.86);
  --topbar-glass: rgba(230,230,228,.85);
  --topbar-border: rgba(25,25,25,.10);
  --tone-daylight-1:#FAFAF9;  --tone-daylight-2:#E2E2DF;
  --tone-dusk-1:#5E1512;      --tone-dusk-2:#B0201E;      --tone-dusk-3:#D9A8A2;
  --tone-lamplight-1:#1C1C1C; --tone-lamplight-2:#111111;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#8E1512;  --tone-dusk-accent:#F2DEDD;  --tone-lamplight-accent:#D2D2CF;
}
:root[data-theme="whodunit"]{
  --tag-yellow:#C9B25E; --tag-yellow-text:#3A3008;
  --tag-teal:#6E9490;   --tag-teal-text:#183330;
  --tag-pink:#C4726E;   --tag-pink-text:#3E1614;
  --tag-lilac:#9A9A97;  --tag-lilac-text:#232323;
}

/* TEMA: scifi (Ópera espacial) — 15o tema, 7mo de ambiente. Vacío, casco y holograma.

   ⚑ ELEGIDO POR EL HUECO QUE QUEDABA, no por el género. Con catorce temas el mapa era:
     claro-cálido    Clásico · Cozy · Daybreak · Desert · Romance
     claro-frío      Slick · Seashore · Mountain · Taiga
     claro-neutro    Whodunit
     oscuro-cálido   Dark Academia · Slow Burn
     oscuro-frío     Forest · One More Chapter
   NO HABÍA NINGÚN OSCURO NEUTRO, y los acentos dejaban un hueco de 102° entre el verde de Forest
   (91°) y el azul de Taiga (194°). Este tema ocupa las dos cosas a la vez.

   ⚑ VIOLETA Y NO CIAN, que era el instinto. El cian obvio cae en 185°, a 9° del acento de taiga
   (194°); el precedente de mountain dice que el mismo matiz sobre fondos opuestos lee como dos
   colores, así que se podía defender — pero el violeta (253°) tiene 102° libres a cada lado y no
   hay que defender nada. El cian vuelve donde no compite: como tercera parada del degradado dusk
   y como --tag-teal, que es exactamente donde el tema necesita leerse como holograma.

   ⚑ Y NO AZUL MARINO, que es el otro instinto de ciencia ficción: One More Chapter ya es un
   #0F1424 a 226°. El violeta a 249° se separa 27° de él — poco sobre el papel, pero aquel tema
   lleva un ámbar cálido encima y este un violeta eléctrico; lo que los distingue es el acento.

   ⚑ Tema OSCURO: los papeles se invierten. --rose-deep es el color de los ENLACES y va más claro
   que --rose, y --tone-lamplight se levanta POR ENCIMA de --surface porque debajo no queda sitio.

   Contrastes medidos sobre --bg salvo donde se indica: tinta 16.08:1 (AAA), --ink-soft 8.17:1,
   --ink-faint 4.52:1, enlaces 8.33:1 y 7.80:1 sobre --surface, peligro 8.27:1, éxito 10.52:1,
   CTA público 7.93:1. */
:root[data-theme="scifi"]{
  --bg:#0C0B12;
  --surface:#15131F; --card-solid:#15131F; --ink:#E9E7F7; --ink-soft:#A9A3C8; --ink-faint:#7F7A9C;
  --rose:#7A5AF0; --rose-deep:#AE9BFF; --rose-tint:#221E38;
  --line:#282341; --line-strong:rgba(233,231,247,.18);
  /* inks aclarados para que sigan legibles sobre la superficie oscura, igual que forest */
  --danger-ink:#FF8E86; --success-ink:#5FD9B0;
  --hand-weight:600;
  --radius:14px; --card-shadow:0 1px 2px rgba(0,0,0,.42), 0 12px 30px rgba(0,0,0,.52);
  /* una nebulosa violeta arriba a la izquierda y el frío del casco cerrando por abajo */
  --app-gradient:
    radial-gradient(ellipse 58% 52% at 6% 0%, rgba(122,90,240,0.22) 0%, transparent 68%),
    radial-gradient(ellipse 62% 58% at 100% 100%, rgba(24,74,92,0.42) 0%, transparent 72%),
    var(--bg);
  --card-glass: rgba(21,19,31,0.88);
  --surface-glass: rgba(21,19,31,0.85);
  --topbar-glass: rgba(12,11,18,0.72);
  --topbar-border: rgba(255,255,255,0.07);
  /* daylight se sintetiza claro, como en forest y dark-academia: --surface es oscura y el
     degradado tiene que seguir leyéndose como 'de día'. */
  --tone-daylight-1:#EDEBFA;  --tone-daylight-2:#CFCBEC;
  /* dusk REORIENTADO, no acotado: girar ±26° alrededor del violeta sólo da más violeta. El viaje
     es el que tiene el género — sombra, motor, holograma. */
  --tone-dusk-1:#2E1F63;      --tone-dusk-2:#7A5AF0;      --tone-dusk-3:#2FB6C8;
  /* levantado por encima de --surface (#15131F): en un tema oscuro no hay sitio por debajo */
  --tone-lamplight-1:#221E3A; --tone-lamplight-2:#1A1730;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#221E38;  --tone-dusk-accent:#E8E2FF;  --tone-lamplight-accent:#AE9BFF;
}
/* Paleta funcional: señal ámbar, holograma cian, plasma magenta y el gris del casco. */
:root[data-theme="scifi"]{
  --tag-yellow:#D9B44A; --tag-yellow-text:#2A2008;
  --tag-teal:#3FC7D4;   --tag-teal-text:#08282C;
  --tag-pink:#E07AC0;   --tag-pink-text:#330B27;
  --tag-lilac:#9A93C8;  --tag-lilac-text:#1E1B33;
}

/* TEMA: taiga — 11vo tema, 5to de naturaleza. Río de deshielo, abetos, granito y nube.

   ⚑ EL TEMA NO PUEDE VENIR DEL ASUNTO, porque el asunto ya está cogido: coníferas es Forest y
   cumbres es Mountain. Tenía que venir del TONO. Y el hueco que quedaba no era un color: era la
   FALTA de color.

   Saturación del FONDO, que es lo que tiñe la página entera:
     taiga 10% · slick 26% · mountain 28% · classic 33% · seashore 35% · cozy 44% · desert 60%
     · romance 64% · daybreak 72%
   Taiga es el tema callado, y en un diario de lectura poder elegir lo callado es útil.

   ⚑ Corrección de lo que primero di por hecho: pensé que TODOS los acentos existentes eran
   saturados y que el de taiga (36%) sería el más bajo. Falso — el de Clásico está en 30%. Lo que
   sí es cierto, y es lo que sostiene el tema, es el FONDO: 10% frente a 26% del siguiente. La
   diferencia se nota en toda la superficie, no en un botón.

   Contrastes: tinta 13.82:1 sobre --surface y 11.34:1 sobre --bg, --ink-soft 5.10:1, --ink-faint
   3.25:1, enlaces 6.41:1 y 6.77:1 con blanco encima, éxito 7.48:1, peligro 7.95:1.
   ⚑ --ink-faint se ajustó: a 54% de luz daba 2.98:1 y el mínimo es 3. Dos centésimas por debajo
   siguen siendo por debajo. */
:root[data-theme="taiga"]{
  --bg:#DEE4E2;
  --surface:#F7F9F8; --ink:#1E2B2C; --ink-soft:#4A696B; --ink-faint:#53696A;
  --rose:#54808E; --rose-deep:#356171; --rose-tint:#DCE8EB;
  --line:#C9D3D1; --line-strong:rgba(30,43,44,.16);
  --danger:#B4453A; --danger-ink:#8A2E25; --success:#3F7A5F; --success-ink:#2C5A45;
  --radius:14px; --card-shadow:0 6px 18px rgba(30,43,44,.09);
  /* luz fría de nube arriba a la izquierda, la masa oscura del bosque cerrando por abajo */
  --app-gradient:
    radial-gradient(ellipse 64% 56% at 4% 0%, rgba(236,241,242,0.90) 0%, transparent 68%),
    radial-gradient(ellipse 62% 58% at 100% 100%, rgba(96,124,120,0.30) 0%, transparent 72%),
    var(--bg);
  --card-glass: rgba(247,249,248,.72);
  --surface-glass: rgba(247,249,248,.86);
  --topbar-glass: rgba(222,228,226,.85);
  --topbar-border: rgba(30,43,44,.10);
  --tone-daylight-1:#F7F9F8;  --tone-daylight-2:#DDE5E3;
  /* dusk acotado a ±26° alrededor de --rose (194°). Aquí NO se reorienta: el tema entero se
     sostiene sobre la contención, y un degradado de tres tonos vivos lo rompería justo en la
     tarjeta que más se mira. */
  --tone-dusk-1:#3D7167;      --tone-dusk-2:#54808E;      --tone-dusk-3:#98A6C3;
  --tone-lamplight-1:#16232A; --tone-lamplight-2:#0F1920;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#356171;  --tone-dusk-accent:#DCE8EB;  --tone-lamplight-accent:#C9D3D1;
}
/* Paleta funcional de la taiga: abeto, río, liquen y granito — todos contenidos, como el tema. */
:root[data-theme="taiga"]{
  --tag-yellow:#C0B172; --tag-yellow-text:#3E3612;
  --tag-teal:#5E9282;   --tag-teal-text:#17332B;
  --tag-pink:#B98A82;   --tag-pink-text:#40211C;
  --tag-lilac:#8FA0AE;  --tag-lilac-text:#1F2E38;
}
/* TEMA: mountain — 10mo tema, 4to de naturaleza. Alpenglow: la última luz dorada en la cumbre
   mientras la nieve en sombra ya se ha puesto violeta.

   ⚑ ELEGIDO CONTRA LOS NUEVE QUE YA HABÍA, no en abstracto. El conjunto ya cubría:
     claro-cálido   Clásico · Cozy · Daybreak · Desert
     claro-frío     Slick (gris neutro + azul) · Seashore (aguamarina + coral)
     claro-rosa     Romance
     oscuro-cálido  Dark Academia (marrón + oro)
     oscuro-frío    Forest (verde-negro + musgo)
   Ningún tema usa VIOLETA, y ninguno combina fondo frío con acento DORADO. Ese hueco es el tema.
   Una lectura de nieve y granito habría caído encima de Slick; una de alpenglow coral, encima de
   Seashore. Violeta + oro era la única dirección libre.

   ⚑ Honestamente: el acento (37°) está a 2° del oro de Dark Academia (39°). No se confunden
   porque aquel tema es marrón oscuro al 10% de luminosidad y este es violeta pálido al 92% — el
   mismo tono sobre fondos opuestos lee como dos colores distintos.

   Contrastes: tinta 13.76:1 sobre --surface y 11.50:1 sobre --bg (AAA), --ink-soft 6.45:1,
   --ink-faint 3.34:1, enlaces 4.89:1 y 5.06:1 con blanco encima, éxito 7.71:1, peligro 8.40:1.
   Separaciones forzadas: acento vs peligro 31°, éxito vs tinta 81°. */
:root[data-theme="mountain"]{
  --bg:#E7E6F1;
  --surface:#FBFBFE; --ink:#232946; --ink-soft:#515A85; --ink-faint:#606683;
  --rose:#CE9331; --rose-deep:#8C5F18; --rose-tint:#F6E9CE;
  --line:#D8D7E6; --line-strong:rgba(35,41,70,.16);
  --danger:#C0392B; --danger-ink:#8E241C; --success:#3D7A5A; --success-ink:#2A5A41;
  --radius:14px; --card-shadow:0 6px 18px rgba(35,41,70,.09);
  /* el sol bajo entrando por la izquierda, la sombra del valle cerrando por abajo a la derecha */
  --app-gradient:
    radial-gradient(ellipse 62% 55% at 4% 0%, rgba(246,222,180,0.62) 0%, transparent 68%),
    radial-gradient(ellipse 64% 60% at 100% 100%, rgba(150,148,190,0.45) 0%, transparent 72%),
    var(--bg);
  --card-glass: rgba(251,251,254,.72);
  --surface-glass: rgba(251,251,254,.86);
  --topbar-glass: rgba(231,230,241,.85);
  --topbar-border: rgba(35,41,70,.10);
  --tone-daylight-1:#FBFBFE;  --tone-daylight-2:#E4E3F0;
  /* dusk REORIENTADO, no acotado: el alpenglow ES el tema, así que el degradado hace el recorrido
     real — sombra violeta → oro de cumbre → rosa pálido. ⚑ Se comprobó el punto medio antes de
     usarlo: violeta a oro pasa por un topo cálido (140,110,84), no por el verde oliva sucio que
     salía en Seashore al ir de turquesa a coral. Por eso allí se acotó y aquí se reorienta. */
  --tone-dusk-1:#4B4A78;      --tone-dusk-2:#CE9331;      --tone-dusk-3:#E9C9A8;
  --tone-lamplight-1:#1B1E36; --tone-lamplight-2:#131529;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#95651A;  --tone-dusk-accent:#F6E9CE;  --tone-lamplight-accent:#D8D7E6;
}
/* Paleta funcional de montaña: oro de cumbre, pino, granito y el rosa del alpenglow. */
:root[data-theme="mountain"]{
  --tag-yellow:#DCB45C; --tag-yellow-text:#4A3405;
  --tag-teal:#5E9B7E;   --tag-teal-text:#173A28;
  --tag-pink:#D79A96;   --tag-pink-text:#4E2320;
  --tag-lilac:#9A98C4;  --tag-lilac-text:#26244A;
}
/* TEMA: forest — 9no tema, 3ro de naturaleza, y el PRIMER TEMA OSCURO CON COLOR. Dark Academia
   es el único oscuro que había y es negro-marrón; este es negro-verde, y esa es la diferencia.

   ⚑ EN UN TEMA OSCURO LOS PAPELES SE INVIERTEN, y el CSS ya lo da por supuesto:
     .btn.primary es background:var(--ink); color:var(--surface) → botón CLARO con texto oscuro.
   Por tanto --rose-deep NO es fondo de botón aquí: es el color de los ENLACES sobre una
   superficie oscura, así que tiene que ser MÁS CLARO que --rose, no más oscuro. Dark Academia
   hace exactamente eso (--rose #8f7350, --rose-deep #c2a063) y el comentario de :root lo explica.
   Invertirlo al revés da enlaces verde oscuro sobre tarjeta verde oscura.

   Del bosque: la sombra entre troncos es el fondo, el musgo al sol es el acento, la hoja a
   contraluz es la tinta, y el destello del sol entre las ramas es el degradado de la esquina.

   Contrastes medidos: tinta 12.95:1 sobre --surface y 14.86:1 sobre --bg, --ink-soft 7.80:1,
   --ink-faint 4.30:1, enlaces 8.73:1, peligro 6.19:1, éxito 8.32:1, y el botón primario 12.95:1
   con los papeles ya invertidos. */
:root[data-theme="forest"]{
  --bg:#121A13;
  --surface:#1C271D; --card-solid:#1C271D; --ink:#E9EDD9; --ink-soft:#AFBD9C; --ink-faint:#809073;
  --rose:#6FA83A; --rose-deep:#9CD34F; --rose-tint:#22301F;
  --line:#2D3B2D; --line-strong:rgba(233,237,217,.18);
  /* inks aclarados para que sigan legibles sobre la superficie oscura, igual que dark-academia */
  --danger-ink:#E88A85; --success-ink:#7FD08A;
  --hand-weight:600;
  --radius:14px; --card-shadow:0 1px 2px rgba(0,0,0,.38), 0 12px 28px rgba(0,0,0,.45);
  /* el sol atravesando el dosel arriba a la izquierda, y el suelo del bosque abajo a la derecha */
  --app-gradient:
    radial-gradient(ellipse 58% 52% at 6% 0%, rgba(154,186,86,0.22) 0%, transparent 68%),
    radial-gradient(ellipse 62% 58% at 100% 100%, rgba(58,44,26,0.55) 0%, transparent 72%),
    var(--bg);
  --card-glass: rgba(28,39,29,0.88);
  --surface-glass: rgba(28,39,29,0.85);
  --topbar-glass: rgba(18,26,19,0.72);
  --topbar-border: rgba(255,255,255,0.07);
  /* daylight se sintetiza claro, como en dark-academia: su --surface es oscura y el degradado
     tiene que seguir leyéndose como 'de día'. */
  --tone-daylight-1:#E9EFDC;  --tone-daylight-2:#C9D6B6;
  /* dusk REORIENTADO en vez de acotado: la rotación estándar alrededor de un verde (91°) sólo da
     más verde, un degradado de tres tonos que parecen uno. Se cambia por el viaje que la foto sí
     tiene — sombra → musgo → hojarasca de otoño. */
  --tone-dusk-1:#38512C;      --tone-dusk-2:#6FA83A;      --tone-dusk-3:#C9B24E;
  --tone-lamplight-1:#365844; --tone-lamplight-2:#2E4A39;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#22301F;  --tone-dusk-accent:#EAF3D6;  --tone-lamplight-accent:#9CD34F;
}
/* Paleta funcional del bosque: hojarasca, musgo, corteza y la luz fría entre los árboles. */
:root[data-theme="forest"]{
  --tag-yellow:#C9B24E; --tag-yellow-text:#33290A;
  --tag-teal:#6FA83A;   --tag-teal-text:#152A0C;
  --tag-pink:#C97B4E;   --tag-pink-text:#3A1C0B;
  --tag-lilac:#8FA9B5;  --tag-lilac-text:#1D2E36;
}
/* TEMA: seashore — 8vo tema, 2do de la serie de naturaleza. De una foto de la orilla: agua
   turquesa poco profunda, arena pálida, una estrella de mar color albaricoque y una concha.

   ⚑ EL AGUA ES EL SUELO, NO LA ARENA, y esa es la decisión que hace que el tema exista. La foto
   es mitad y mitad, así que había que elegir: si la arena manda, esto es Desert otra vez con otro
   acento. Además ninguno de los siete temas anteriores tiene un fondo FRÍO — Slick es lo más
   cercano y es gris, no de color. La arena vuelve como un lavado cálido en la esquina inferior
   derecha del degradado de página, que es exactamente la diagonal de orilla de la foto.

   La estrella de mar da el acento: cálido sobre frío, el mismo contraste que compone la imagen.

   ⚑ Dos separaciones que hubo que forzar porque la paleta natural las dejaba demasiado juntas:
   --danger se llevó a rojo puro (0°) para que no se confunda con el coral de --rose-deep (19°),
   y --success se empujó a verde (156°) para que no se lea como la tinta turquesa (187°). Quedan
   a 19° y 31°. Un botón destructivo y un botón primario que parecen el mismo color es un fallo
   de producto, no de estética.

   Contrastes medidos: tinta 9.61:1 sobre --surface y 8.42:1 sobre --bg (AAA), --ink-soft 5.12:1,
   --ink-faint 3.11:1, --rose-deep 4.56:1 como enlace y 4.70:1 con blanco encima, éxito 6.97:1,
   peligro 8.61:1. */
:root[data-theme="seashore"]{
  --bg:#E0F0EE;
  --surface:#FAFCFB; --ink:#0F4A52; --ink-soft:#3C7178; --ink-faint:#4E7176;
  --rose:#F09257; --rose-deep:#AF4F24; --rose-tint:#FCE4D5;
  --line:#CBE3DF; --line-strong:rgba(15,74,82,.16);
  --danger:#BE2C2C; --danger-ink:#8E1F1F; --success:#2E9E72; --success-ink:#1A6349;
  --radius:14px; --card-shadow:0 6px 18px rgba(15,74,82,.09);
  /* espuma arriba a la izquierda, arena entrando por abajo a la derecha: la orilla de la foto */
  --app-gradient:
    radial-gradient(ellipse 66% 58% at 0% 0%, rgba(150,220,214,0.55) 0%, transparent 70%),
    radial-gradient(ellipse 62% 58% at 100% 100%, rgba(240,220,186,0.80) 0%, transparent 72%),
    var(--bg);
  --card-glass: rgba(250,252,251,.72);
  --surface-glass: rgba(250,252,251,.86);
  --topbar-glass: rgba(224,240,238,.85);
  --topbar-border: rgba(15,74,82,.10);
  --tone-daylight-1:#FAFCFB;  --tone-daylight-2:#DDEEEB;
  /* dusk = puesta de sol sobre el agua. Rotación acotada a ±26° alrededor de --rose (23°): el
     extremo frío para en 357° y el cálido en 49°, antes de irse a amarillo puro. Se rotó alrededor
     del coral y NO se metió el turquesa en el degradado: un tramo de turquesa a coral pasa por el
     gris oliva a mitad de camino y se ve sucio. */
  --tone-dusk-1:#964044;      --tone-dusk-2:#F09257;      --tone-dusk-3:#ECDB8D;
  /* lamplight = el mar de noche */
  --tone-lamplight-1:#0D2831; --tone-lamplight-2:#081A22;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#BC5527;  --tone-dusk-accent:#FCE4D5;  --tone-lamplight-accent:#DDEEEB;
}
/* Paleta funcional de la orilla: coral, arena, agua y verde de vidrio de mar. */
:root[data-theme="seashore"]{
  --tag-yellow:#EFC978; --tag-yellow-text:#54400C;
  --tag-teal:#4FB8AE;   --tag-teal-text:#123C38;
  --tag-pink:#F0A583;   --tag-pink-text:#6E2E14;
  --tag-lilac:#8FB2C4;  --tag-lilac-text:#1F3E4C;
}
/* TEMA: desert — 7mo tema, el primero de la serie de naturaleza. Tomado de una foto del Erg
   Chebbi: arena al sol ocupando casi todo el encuadre, un cielo que se hace más profundo cuanto
   más arriba miras, y matas de hierba verde salvia.

   El reparto sale de la foto, no del gusto: la ARENA es el fondo, el CIELO es la tinta (es lo más
   oscuro de la imagen), la SOMBRA DE LA DUNA es el acento, y la HIERBA es el color de éxito — una
   firma que ninguno de los otros seis tiene.

   ⚑ Por qué no es un Daybreak repintado: Daybreak es alto en clave (crema casi blanco, naranja
   eléctrico #FF5F00, azul marino). Desert es más caliente y más polvoriento — la arena está
   saturada, el acento es siena en vez de naranja puro, y la tinta es azul de cielo, no de tinta.

   Contrastes medidos, todos por encima del mínimo: tinta 9.09:1 sobre --surface y 7.41:1 sobre
   --bg (AAA en ambos), --ink-soft 5.36:1, --ink-faint 3.04:1, --rose-deep 5.55:1 como enlace y
   6.08:1 con blanco encima, éxito 6.27:1, peligro 7.15:1. */
:root[data-theme="desert"]{
  --bg:#EFDCBE;
  --surface:#FBF4E7; --ink:#17456E; --ink-soft:#406484; --ink-faint:#4E6579;
  --rose:#C2673A; --rose-deep:#9E4A22; --rose-tint:#F5E2D0;
  --line:#E2CDA9; --line-strong:rgba(23,69,110,.16);
  --danger:#C0442F; --danger-ink:#93301F; --success:#7E9455; --success-ink:#4C6130;
  --radius:14px; --card-shadow:0 6px 18px rgba(94,58,24,.10);
  /* el sol pega desde arriba a la izquierda y la sombra cae al fondo derecha, como en la foto */
  --app-gradient:
    radial-gradient(ellipse 70% 60% at 8% 0%, rgba(247,226,190,0.85) 0%, transparent 70%),
    radial-gradient(ellipse 60% 55% at 100% 100%, rgba(196,146,90,0.42) 0%, transparent 72%),
    var(--bg);
  --card-glass: rgba(251,244,231,.72);
  --surface-glass: rgba(251,244,231,.86);
  --topbar-glass: rgba(239,220,190,.85);
  --topbar-border: rgba(23,69,110,.10);
  --tone-daylight-1:#FBF4E7;  --tone-daylight-2:#F0DCBD;
  /* dusk = el atardecer sobre las dunas. Rotación acotada a ±26° alrededor de --rose (20°): el
     extremo frío para en 354° (rojo de barranco) y el cálido en 46°, justo antes del amarillo. */
  --tone-dusk-1:#913B44;      --tone-dusk-2:#C2673A;      --tone-dusk-3:#D8C179;
  /* lamplight = la noche del desierto, que es el mismo cielo sin sol */
  --tone-lamplight-1:#14273F; --tone-lamplight-2:#0E1B2C;
  --tone-dusk-ink:#FFFFFF;
  --tone-daylight-accent:#9E4A22;  --tone-dusk-accent:#F7E4CE;  --tone-lamplight-accent:#F5E2D0;
}
/* Paleta funcional del desierto: los tags salen de la propia foto — arena, salvia, siena y el
   azul del cielo — en vez de heredar los de Clásico, que aquí se verían prestados. */
:root[data-theme="desert"]{
  --tag-yellow:#E3B457; --tag-yellow-text:#4E3608;
  --tag-teal:#7E9455;   --tag-teal-text:#2E3D19;
  --tag-pink:#D98E6A;   --tag-pink-text:#6B3218;
  --tag-lilac:#7A93B5;  --tag-lilac-text:#22364C;
}
/* Paleta funcional — tags, categorías, badges. Nunca para --rose/--danger/--success. */
:root[data-theme="daybreak"]{
  --tag-yellow:#FFC857; --tag-yellow-text:#5C4300;
  --tag-teal:#5FBFC0;   --tag-teal-text:#0E3E3E;
  --tag-pink:#FE979C;   --tag-pink-text:#7A222A;
  --tag-lilac:#C9A0DC;  --tag-lilac-text:#4A2E5C;
}
*{box-sizing:border-box;}
html{font-size:100%;}
html,body{margin:0;padding:0;}
body{
  font-family:var(--font-body);
  background:var(--app-gradient);
  background-attachment:fixed;
  color:var(--ink);
  min-height:100vh;
  -webkit-font-smoothing:antialiased;
  transition:background 0.6s ease;
  font-kerning:normal;
  /* Source Serif 4 trae eje óptico (8–60): el navegador elige el master según el
     tamaño, así los títulos afinan los remates y el texto de cita los engorda.
     Es lo que deja que display y cita compartan familia sin verse iguales. */
  font-optical-sizing:auto;
}
h1,h2,h3{font-family:var(--font-display); font-weight:500; margin:0; text-wrap:balance;}
button{font-family:var(--font-body); cursor:pointer;}
input,textarea,select{font-family:var(--font-body);}
input:not([type=checkbox]):not([type=radio]),textarea,select{color:var(--ink); background-color:var(--surface);}
select option{background:var(--surface); color:var(--ink);}
::placeholder{color:var(--ink-soft);}
/* visible keyboard focus ring (inputs keep their own border-based focus) */
:focus-visible{outline:2px solid var(--rose-deep); outline-offset:2px;}
svg.icon{width:19px; height:19px; stroke:currentColor; fill:none; stroke-width:1.6; stroke-linecap:round; stroke-linejoin:round; flex-shrink:0;}

/* ===== layout ===== */
#app{display:flex; flex-direction:column; max-width:1358px; margin:0 auto; padding:var(--edge); min-height:100vh; --sidebar-w:60px; --dock-icon:34px; --topbar-h:76px; --edge:14px;}
/* fila principal bajo el header: dock (izq) + contenido (der) */
.app-body{display:flex; gap:20px; align-items:flex-start; width:100%;}
/* ===== dock de navegación =====
   Cápsula flotante que se ajusta a su contenido: ni pegada a los bordes ni de
   altura completa. En ≤900px vuelve a ser el drawer de siempre (ver responsive). */
#sidebar{
  position:sticky; top:var(--edge); align-self:flex-start;
  width:var(--sidebar-w); height:min(760px, calc(100vh - var(--edge) * 2)); flex-shrink:0; z-index:50;
  display:flex; flex-direction:column; align-items:center; gap:6px;
  padding:10px 0 12px;
  background:var(--card-solid); border:1px solid var(--line); border-radius:28px;
  box-shadow:var(--card-shadow);
  overflow:visible;            /* los tooltips salen por la derecha */
}
/* separador */
.dock-sep{width:26px; height:1px; background:var(--line); flex-shrink:0; margin:4px 0;}
.dock-spacer{flex:1; min-height:6px;}
/* botón "···" */
.dock-more{display:flex; align-items:center; justify-content:center; width:var(--dock-icon); height:var(--dock-icon);
  border:none; border-radius:50%; background:var(--bg); color:var(--ink-soft);
  flex-shrink:0; cursor:pointer; position:relative; transition:background .15s ease, color .15s ease;}
.dock-more:hover{background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep));}
.dock-more .icon{width:17px; height:17px;}
/* ítems: círculos con icono ink; activo = cuadrado redondeado oscuro (estilo referencia) */
#sidebar .navbtn{
  position:relative; width:var(--dock-icon); height:var(--dock-icon); min-height:0; padding:0; gap:0; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  border:none; border-radius:50%; background:transparent; color:var(--ink-soft);
  opacity:1; transition:color .15s ease, background .15s ease, border-radius .15s ease;
}
/* Settings arrastra min-height:38px de su regla de sidebar (.settings-btn); sin
   esto queda 34×38 = óvalo, porque min-height gana a height. */
#sidebar .navbtn span{display:none;}
#sidebar .navbtn .icon{width:18px; height:18px;}
#sidebar .navbtn:hover{background:var(--bg); color:var(--ink);}
#sidebar .navbtn.active{background:var(--ink); color:var(--surface); border-radius:12px;}
/* tooltips: a la derecha, 4px de desplazamiento, 0.15s */
#sidebar .navbtn::after, .dock-more::after, #sidebar .profile-btn::after{
  content:attr(data-label);
  position:absolute; left:calc(100% + 10px); top:50%;
  transform:translateY(-50%) translateX(-4px);
  background:var(--ink); color:var(--surface); font-family:var(--font-body); font-size:11px; font-weight:600;
  padding:5px 10px; border-radius:7px; white-space:nowrap;
  box-shadow:0 4px 14px rgba(18,40,58,.28);
  opacity:0; pointer-events:none; z-index:60;
  transition:opacity .15s ease, transform .15s ease;
}
#sidebar .navbtn:hover::after, .dock-more:hover::after, #sidebar .profile-btn:hover::after{
  opacity:1; transform:translateY(-50%) translateX(0);
}
/* pie del dock: settings encima del avatar (orden inverso al del DOM, que el
   drawer móvil sigue necesitando tal cual) */
#sidebar .side-bottom{display:flex; flex-direction:column; align-items:center; gap:6px; margin-top:0; padding:0; border:none;}
#sidebar .side-bottom .profile-btn{order:1;}
#sidebar .side-bottom .settings-btn{order:2;}
#sidebar .side-bottom .dock-signout{order:3;}
/* cerrar sesión: mismo lenguaje que un icono del dock, sin estado activo */
.dock-signout{display:flex; align-items:center; justify-content:center; width:var(--dock-icon); height:var(--dock-icon);
  border:none; border-radius:50%; background:transparent; color:var(--ink-soft); opacity:1;
  flex-shrink:0; cursor:pointer; position:relative; transition:color .15s ease, background .15s ease;}
.dock-signout:hover{color:var(--rose-deep); background:var(--bg);}
.dock-signout .icon{width:18px; height:18px;}
.dock-signout::after{
  content:attr(data-label); position:absolute; left:calc(100% + 10px); top:50%;
  transform:translateY(-50%) translateX(-4px);
  background:var(--ink); color:var(--surface); font-family:var(--font-body); font-size:11px; font-weight:600;
  padding:5px 10px; border-radius:7px; white-space:nowrap; box-shadow:0 4px 14px rgba(18,40,58,.28);
  opacity:0; pointer-events:none; z-index:60; transition:opacity .15s ease, transform .15s ease;}
.dock-signout:hover::after{opacity:1; transform:translateY(-50%) translateX(0);}
#sidebar .side-bottom .lang-select, #sidebar .side-bottom .lang-mini{display:none;}
#sidebar .profile-btn{position:relative; width:32px; height:32px; padding:0; margin:0; border:none; border-bottom:none; background:none; justify-content:center;}
#sidebar .profile-btn .profile-name{display:none;}
#sidebar .profile-btn .avatar{width:30px; height:30px; font-size:0.75rem; background:var(--ink); color:var(--surface); border-color:var(--ink);}
/* La cabecera del drawer y los secundarios solo existen en móvil.
   Va con #sidebar: la regla .side-top{display:flex} aparece más abajo en el
   archivo y con la misma especificidad ganaría por orden. */
#sidebar .side-top{display:none;}
#sidebar .navbtn.drawer-only{display:none;}
/* ===== panel "More" + los submenús del dock (Stories, Feels, Games) =====
   Comparten caja e ítems a propósito: son el mismo gesto lateral. */
#morePanel, #subnavPanel{
  position:fixed; z-index:60; display:none; flex-direction:column; gap:2px;
  min-width:196px; padding:7px;
  background:var(--surface); border:1px solid var(--line); border-radius:18px;
  box-shadow:0 16px 40px rgba(18,40,58,.20);
}
#morePanel.open, #subnavPanel.open{display:flex;}
.more-item{
  display:flex; align-items:center; gap:10px; width:100%;
  border:none; background:none; border-radius:11px; padding:9px 11px;
  font-family:var(--font-body); font-size:12px; font-weight:500;
  color:var(--ink); text-align:left; cursor:pointer; transition:background .12s ease;
}
.more-item:hover{background:var(--bg);}
.more-item .icon{width:16px; height:16px; flex-shrink:0; color:var(--ink-soft);}
/* la pestaña en la que ya estás, marcada como lo está el "···" cuando su vista está activa.
   La tinta sale de --on-rose-ink, que applyTheme() calcula por luminancia del --rose de cada tema
   (ver la nota allí). El respaldo es var(--ink) para el instante anterior a que corra el JS. */
.more-item.on{background:var(--rose); color:var(--on-rose-ink, var(--ink));}
.more-item.on .icon{color:inherit;}
/* con el submenú abierto el tooltip del icono caería justo debajo del panel */
#sidebar .navbtn.subnav-on::after{opacity:0;}
/* cuando la vista actual es uno de los secundarios, el "···" queda marcado */
/* mismo relleno que el ítem marcado, así que misma tinta: su icono heredaba --ink-soft y quedaba
   con el mismo problema de contraste sobre el acento */
.dock-more.active{background:var(--rose); color:var(--on-rose-ink, var(--ink));}
.dock-more.active .icon{color:inherit;}
/* La marca vive en el header flotante. Las reglas colgaban de #sidebar; al
   moverla habrían dejado de aplicar, así que se re-scopean a .brand. */
.brand{
  font-family:var(--font-display); font-weight:400;
  letter-spacing:.2px; color:var(--ink); min-width:0; flex-shrink:0;
}
.brand .brand-name{display:block; white-space:nowrap; font-size:1.125rem; font-weight:600; line-height:1.15;}
.brand .sub{display:block; font-family:var(--font-body); font-size:0.5625rem; font-weight:700;
  letter-spacing:0.1em; text-transform:uppercase; color:var(--ink-soft); white-space:nowrap; margin-top:1px;}
.navbtn{
  display:flex; align-items:center; gap:11px;
  padding:9px 12px; border-radius:10px; border:none;
  background:none; font-weight:500; font-size:0.84375rem; color:var(--ink-soft);
  text-align:left; transition:.15s;
}
.navbtn:hover{background:var(--bg); color:var(--ink);}
.navbtn.active{background:var(--ink); color:var(--surface);}
/* El contenido llena TODO el ancho de #main (sin centrar ni cap) para que el borde
   derecho de las cards coincida SIEMPRE con el del search/actions del topbar. */
#main{flex:1; min-width:0; padding:0 0 80px; display:flex; flex-direction:column; align-items:stretch;}
#main-inner{width:100%;}
/* Topbar: cabecera aireada a lo ancho, arriba de todo. Izquierda: marca + saludo.
   Derecha: búsqueda + acciones circulares. Sin tarjeta ni borde. */
#topbar{width:100%; margin:0 0 30px;
  display:flex; flex-wrap:wrap; column-gap:20px; row-gap:0; align-items:center; justify-content:space-between;
  background:none; border:none; border-radius:0; box-shadow:none; padding:2px 0 0;}
/* FILA 1 — franja de marca discreta, solo en el dashboard. flex-basis:100% la fuerza
   a su propia línea; el resto (lead + actions) baja a la FILA 2 con space-between. */
/* El wordmark vive SIEMPRE en la FILA 1 arriba, en TODAS las vistas — no se mueve al cambiar de vista */
.topbar-brandrow{display:flex; flex-direction:column; flex-basis:100%; width:100%; margin-bottom:28px;
  align-items:center;}   /* PROVISIONAL 25-08: centrado para comparar (ver bloque de abajo) */
/* ============ PROVISIONAL — QUITAR CUANDO SE DECIDA DONDE VA EL LOGOTIPO ============
   El bloque de marca (logotipo + lema) son dos cajas de ancho fijo (--brandmark-w) dentro de
   una columna flex, asi que quien decide si van a la izquierda o al centro es align-items.
   Para verlo a la IZQUIERDA sin tocar el fichero, en la consola del navegador:
       document.documentElement.dataset.brand = 'left'    // y '' para volver al centro
   Al decidir: dejar align-items en el valor elegido y borrar este bloque entero.
   =================================================================================== */
:root[data-brand="left"]   .topbar-brandrow{align-items:flex-start;}
:root[data-brand="center"] .topbar-brandrow{align-items:center;}
:root[data-brand="right"]  .topbar-brandrow{align-items:flex-end;}
.topbar-lead > .topbar-brand{display:none;} /* el wordmark inline se oculta: ya vive arriba en todas las vistas */
.topbar-brandrow .topbar-brand-name{font-family:var(--font-display); font-weight:600; font-size:1.125rem;
  line-height:1.1; letter-spacing:.2px; color:color-mix(in srgb, var(--ink) 70%, transparent);}
/* ===== El logotipo de Laura, en la franja de marca =====
   ⚑ UN SOLO ARCHIVO PARA QUINCE TEMAS. El logotipo es monocromo con fondo transparente, asi que
   en vez de pintarlo como imagen se usa de MASCARA y el color lo pone la app: background-color con
   la misma tinta que tenia el texto. Asi sigue a cada tema — claro, oscuro, el que sea— sin tener
   que mantener quince PNG ni elegir uno equivocado en el tema numero dieciseis.
   ⚑ La palabra sigue en el HTML y solo se vuelve transparente: un lector de pantalla y un buscador
   siguen leyendo "Storyfeel", que es justo lo que un logotipo en <img> con alt vacio se lleva por
   delante.
   ⚑ LA MASCARA ES VECTORIAL. Con el PNG habia que elegir entre un salto de escala de 9.3x (borde
   sucio) o mantener un tamanio intermedio, y encima a 26px sin pantalla retina los trazos finos de
   la serif caian por debajo del pixel: por eso se veia dentada. Un SVG se rasteriza al tamanio
   exacto de cada pantalla, asi que ese problema desaparece en vez de mitigarse.
   ⚑ brand/wordmark.svg es el original de Laura con el viewBox recortado a la TINTA — ni un path
   tocado. Sin recortar traia 105 unidades de aire a la izquierda y el dibujo ocupaba el 39% del
   alto del lienzo, que es lo que obligaba antes al mask-size del 126.2%. Recortado, "contain"
   hace lo suyo y no hay numero que recalcular al cambiar el tamanio. */
@supports ((-webkit-mask-image: url("")) or (mask-image: url(""))){
  .topbar-brandrow .topbar-brand-name{
    display:block; width:var(--brandmark-w); height:calc(var(--brandmark-w) / 5.19); color:transparent; user-select:none;
    background-color:color-mix(in srgb, var(--ink) 70%, transparent);
    -webkit-mask:url("brand/wordmark.svg") center/contain no-repeat;
            mask:url("brand/wordmark.svg") center/contain no-repeat;
  }
}
@media(max-width:900px){ :root{--brandmark-w:132px;} }   /* 120 + 10%; el alto ya sale del ancho */
/* ⚑⚑ LA TINTA ES --ink-soft, NO --ink CON ALFA. El lema era --ink al 40% y no se leia: medido
   en los quince temas daba de 1.99:1 (Desert) a 3.38:1, o sea que NINGUNO llegaba al 4.5:1 que pide
   la norma para texto pequenio, y a 8px se nota mas de lo que dice el numero. Y no habia alfa que
   lo salvara: la misma transparencia sobre quince fondos distintos da quince contrastes distintos,
   y hasta el 70% seguia cayendo en Desert (3.76) y Seashore (3.95). --ink-soft es un color por
   tema, ya ajustado, y el peor de los quince es 4.54:1. Sigue por debajo del logotipo (5.93:1 en
   el tema por defecto), asi que la jerarquia aguanta: se lee, pero no compite.
   ⚑ El TAMANIO cuelga del ancho del logotipo en vez de ser un numero suelto. A 148.5px sale 9px y
   sobran 17.7px para que los reparta el justify; a 132px (moviles) sale 8px y sobran 15.7. Con 9px
   fijos en los dos, en movil sobraba 1.2px: el estirado desaparecia y bastaba que Mulish no
   cargase para que el lema se saliera del bloque.
   ⚑ El lema se ESTIRA hasta el ancho del logotipo. text-align-last:justify es lo que reparte una
   linea suelta — justify solo no hace nada en la ultima linea de un bloque, y esta linea es la
   ultima y la unica. Asi el bloque de marca queda cuadrado por los dos lados en vez de con el
   lema flotando corto debajo.
   ⚑ El ancho se hereda de una variable comun con el logotipo: son el mismo bloque, y dejar dos
   numeros sueltos es garantizar que un dia dejen de coincidir. */
.topbar-brandrow .topbar-brand-slogan{display:block; width:var(--brandmark-w); text-align:justify; text-align-last:justify;
  font-family:var(--font-body); font-size:calc(var(--brandmark-w) / 16.5); font-weight:700;
  letter-spacing:.1em; text-transform:uppercase; margin-top:3px; white-space:nowrap;
  color:var(--ink-soft);}
.topbar-lead{display:flex; align-items:center; gap:16px; min-width:0;}
/* marca (izq, encima del dock): wordmark + slogan, apartada del saludo con un divisor */
.topbar-brand{display:flex; flex-direction:column; justify-content:center; flex-shrink:0;
  padding-right:18px; border-right:1px solid var(--line);}
.topbar-brand-name{font-family:var(--font-display); font-weight:600; font-size:1.0625rem; line-height:1.1; color:var(--ink); letter-spacing:.2px;}
.topbar-brand-slogan{font-family:var(--font-body); font-size:0.5625rem; font-weight:600; letter-spacing:.09em; text-transform:uppercase; color:var(--ink-soft); margin-top:2px; white-space:nowrap;}
/* La estrella que iba pegada al título se ha quitado (25-08). Al ser un item flex, quitarla se
   lleva también su hueco de 16px, así que el título y su subtítulo empiezan exactamente donde
   empezaba ella — que es lo que se pidio. */
.topbar-heading{min-width:0;}
.topbar-title{font-family:var(--font-display); font-weight:600; font-size:1.75rem; line-height:1.12; letter-spacing:-.01em;
  color:var(--ink); margin:0; text-wrap:balance;}
.topbar-sub{font-family:var(--font-body); font-size:0.9375rem; color:var(--ink-soft); margin:3px 0 0; line-height:1.4;}
.topbar-actions{display:flex; align-items:center; gap:12px; flex-shrink:0;}
.topsearch{flex:0 0 clamp(220px, 26vw, 340px); min-width:0; display:flex; align-items:center; gap:8px;
  background:var(--surface); border:none; border-radius:999px; padding:6px 6px 6px 20px;
  box-shadow:0 4px 14px rgba(18,40,58,.06); transition:box-shadow .15s ease;}
.topsearch:focus-within{box-shadow:0 4px 18px rgba(18,40,58,.13);}
.topsearch input:not([type=checkbox]):not([type=radio]){flex:1; min-width:0; border:none; background:transparent; font-size:0.875rem; color:var(--ink); outline:none;}
.topsearch-btn{display:flex; align-items:center; justify-content:center; width:38px; height:38px; border-radius:50%;
  background:var(--ink); color:var(--surface); flex-shrink:0;}
.topsearch-btn .icon{width:17px; height:17px;}
.topbar-icon-btn{display:flex; align-items:center; justify-content:center; width:44px; height:44px; border-radius:50%;
  border:none; background:var(--surface); color:var(--ink); flex-shrink:0; cursor:pointer;
  box-shadow:0 4px 14px rgba(18,40,58,.06); transition:background .15s ease, color .15s ease;}
.topbar-icon-btn:hover{background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep));}
.topbar-icon-btn .icon{width:19px; height:19px;}
.theme-wrap{position:relative;}
.theme-menu{position:absolute; right:0; top:calc(100% + 8px); width:236px; background:var(--surface); border:1px solid var(--line); border-radius:14px; box-shadow:0 16px 44px rgba(28,26,23,.22); padding:8px; display:none; z-index:60;}
.theme-menu.open{display:block;}
/* An invisible bridge across the 8px gap between the button and the menu. The menu is a DOM child
   of .theme-wrap / .lang-wrap but is positioned OUTSIDE the wrapper's box, so that gap belongs to
   no element and a pointer crossing it fires mouseleave. The JS tolerates that with a 240ms delay;
   this removes the cause rather than only surviving it, which matters for a slow or shaky hand.
   Scoped to .open so it can never intercept a click while the menu is closed. */
.theme-menu.open::before{content:""; position:absolute; left:0; right:0; top:-8px; height:8px;}
.tm-head{font-size:0.65625rem; text-transform:uppercase; letter-spacing:1px; color:var(--ink-soft); padding:6px 8px 8px;}
.theme-opt{display:flex; align-items:center; gap:10px; width:100%; border:none; background:none; border-radius:9px; padding:6px 8px; text-align:left; font-size:0.8125rem; color:var(--ink); transition:.12s;}
.theme-opt:hover{background:var(--bg);}
.theme-opt.active{background:var(--bg); font-weight:600;}
.theme-swatch{display:flex; border-radius:6px; overflow:hidden; border:1px solid var(--line-strong); flex-shrink:0;}
.theme-swatch span{width:15px; height:15px; display:block;}
/* ===== theme groups: Mood and Nature collapse so eleven themes are not a wall =====
   The row is a sibling of the options rather than a wrapper around them, so an open group's items
   keep the exact .theme-opt layout — indenting or nesting them would make the active tick sit at a
   different offset depending on which group you were in. */
.theme-group{display:flex; align-items:center; gap:9px; width:100%; border:none; background:none;
  border-radius:9px; padding:9px 8px; text-align:left; font-size:0.8125rem; font-weight:600;
  color:var(--ink-soft); cursor:pointer; transition:.12s; margin-top:2px;}
.theme-group:hover{background:var(--bg); color:var(--ink);}
.theme-group .tg-label{flex:1;}
.theme-group .icon{width:15px; height:15px; flex-shrink:0; transition:transform .15s ease;}
.theme-group.open .icon{transform:rotate(90deg);}
.theme-group.open{color:var(--ink);}
/* a hair of inset so an open group reads as belonging to its header, without moving the swatches */
.theme-group-items{padding-left:6px; border-left:2px solid var(--line); margin:2px 0 4px 8px;}
/* the lateral panel, built to match #subnavPanel — same surface, same radius, same shadow, because
   it is the same idea and a reader should not have to notice it is a different component */
#themeFlyPanel{
  position:fixed; z-index:70; display:none; flex-direction:column; gap:2px;
  min-width:206px; padding:7px;
  background:var(--surface); border:1px solid var(--line); border-radius:18px;
  box-shadow:0 16px 40px rgba(18,40,58,.20);
}
#themeFlyPanel.open{display:flex;}
.theme-group.fly-on{background:var(--bg); color:var(--ink);}
.theme-group.fly-on .icon{transform:rotate(90deg);}
/* z-index 70 puts it over the menu (60): the panel overlaps the menu's left edge by design, which
   is what makes the two read as one surface rather than two floating boxes. */
.menu-btn{display:none; align-items:center; justify-content:center; width:38px; height:38px; border:1px solid var(--line-strong); background:var(--surface); border-radius:10px; color:var(--ink); flex-shrink:0; cursor:pointer;}
/* El <select> de idioma vivía en el sidebar; en un dock de 60px no cabe, así que
   el botón de idioma del header (antes solo móvil) pasa a estar siempre visible. */
#langBtn{display:flex;}
.lang-wrap{position:relative;}
.lang-menu{width:180px;}
.menu-btn:hover{border-color:var(--ink);}
.profile-btn{display:flex; align-items:center; gap:10px; width:100%; border:none; background:none; border-radius:10px; padding:8px 6px; margin-bottom:12px; border-bottom:1px solid var(--line); padding-bottom:16px; color:var(--ink); font-size:0.8125rem; font-weight:500; text-align:left; cursor:pointer; transition:.15s;}
.profile-btn:hover{opacity:.7;}
.profile-btn .avatar{display:flex; align-items:center; justify-content:center; width:28px; height:28px; border-radius:50%; background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep)); border:1.5px solid var(--rose-deep); font-weight:700; font-size:0.75rem; flex-shrink:0;}
.profile-btn .avatar .icon{width:16px; height:16px;}
.profile-name{overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.lang-select{width:100%; box-sizing:border-box; min-height:38px; border:1px solid var(--line-strong); border-radius:9px; padding:9px 12px; line-height:1.35; font-size:0.8125rem; background:var(--surface); color:var(--ink); cursor:pointer;}
.lang-select:focus{outline:none; border-color:var(--ink);}
#drawerOverlay{position:fixed; inset:0; background:rgba(20,18,15,.42); z-index:150; opacity:0; pointer-events:none; transition:opacity .25s;}
#app.drawer-open #drawerOverlay{opacity:1; pointer-events:auto;}

/* ===== page header (replaces cloud banner) ===== */
.banner{margin-bottom:40px; padding-bottom:22px; border-bottom:1px solid var(--line);}
.banner h1{font-size:clamp(1.75rem, 3vw, 2.375rem); font-weight:600; letter-spacing:.2px; color:var(--ink);}
.banner .sub{color:var(--ink-soft); font-size:0.875rem; margin-top:8px; max-width:560px; line-height:1.5;}

/* ===== generic ===== */
.card{
  background:var(--card-glass); border:1px solid var(--line); border-radius:var(--radius);
  box-shadow:var(--card-shadow); padding:18px;
}
.btn{
  border:1px solid var(--line-strong); background:transparent; border-radius:999px;
  padding:9px 18px; font-weight:500; font-size:0.8125rem; color:var(--ink);
  transition:.15s; display:inline-flex; align-items:center; gap:7px;
}
.btn:hover{border-color:var(--ink); background:var(--bg);}
.btn.primary{background:var(--ink); color:var(--surface); border-color:var(--ink);}
.btn.primary:hover{filter:brightness(.92);}
.btn.ghost{background:transparent; border-color:transparent; padding:9px 12px;}
.btn.ghost:hover{background:var(--bg); border-color:transparent;}
.btn.small{padding:6px 12px; font-size:0.75rem;}
.btn.danger{background:var(--danger); color:#fff; border-color:var(--danger);}
.btn.danger:hover{background:var(--danger); opacity:.9; border-color:var(--danger);}
.btn.danger:disabled{opacity:.45; cursor:default;}
/* VACIAR: contorno rojo apagado que se llena al pasar por encima. Es el aspecto que ya tenia
   "Borrar todos los datos" en Ajustes; ahora que hay dos sitios que vacian cosas, vive en una
   clase en vez de en un #id. Contorno y no relleno: un boton rojo macizo grita en una barra de
   cabecera, y el peso de la advertencia lo lleva el dialogo de confirmacion, que ya sale en rojo.
   Va DESPUES de .btn.ghost a proposito — misma especificidad, decide el orden. */
.btn.danger-outline{background:transparent; color:var(--danger-ink); border:1px solid var(--danger-ink);}
.btn.danger-outline:hover{background:var(--danger); color:#fff; border-color:var(--danger);}
.pill{display:inline-block; padding:3px 11px; border-radius:20px; font-size:0.6875rem; font-weight:600; letter-spacing:.3px; border:1px solid var(--line-strong); background:var(--bg); color:var(--ink-soft);}
.pill.read{background:var(--ink); color:var(--surface); border-color:var(--ink);}
.pill.tbr{background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep)); border-color:transparent;}
.pill.reading{background:var(--bg); color:var(--ink); border-color:var(--line-strong);}
.pill.dnf{background:transparent; color:var(--ink-faint); border-style:dashed;}
.toolbar{display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-bottom:24px;}
.toolbar input[type=text], .toolbar select{
  border:1px solid var(--line-strong); border-radius:999px; padding:8px 14px; font-size:0.8125rem; background:var(--surface);
}
.toolbar input[type=text]{min-width:200px;}
.empty{
  border:1px dashed var(--line-strong); border-radius:var(--radius); padding:48px 20px; text-align:center;
  color:var(--ink-soft); font-size:0.84375rem;
}
/* estado vacío elegante (biblioteca / frases sin datos): ícono + título + subtítulo + CTA */
.empty-state{display:flex; flex-direction:column; align-items:center; text-align:center; gap:9px; padding:64px 24px;}
.empty-state-icon{color:var(--rose-deep); opacity:.5;}
.empty-state-icon .icon{width:46px; height:46px;}
.empty-state-title{font-family:var(--font-display); font-weight:600; font-size:1.25rem; color:var(--ink); margin:2px 0 0;}
.empty-state-sub{font-size:0.875rem; color:var(--ink-soft); margin:0 0 10px; max-width:320px; line-height:1.45;}
.stars-row{display:inline-flex; gap:2px; font-size:0.9375rem; line-height:1;}
.star-wrap{position:relative; display:inline-block; width:1em; height:1em;}
.stars-row.editable .star-wrap{cursor:pointer;}
/* stars that can't be set yet: still clickable, so the tap can explain itself */
.stars-row.locked{cursor:help;}
/* "you already had an account" — shown once, right after a Create-account press lands in an
   existing Google account. Centred and led by a tick, because it's good news, not a failure. */
/* .modal sets max-width:560px further down the file, so this needs both classes to win */
/* confirmModal() — same shape as the welcome-back one, two buttons, danger tone by default */
.modal.cf-modal{max-width:430px; text-align:center;}
.cf-head{display:flex; flex-direction:column; align-items:center; gap:12px;}
.cf-head h3{margin:0;}
.cf-icon{display:inline-flex; align-items:center; justify-content:center; width:56px; height:56px;
  border-radius:50%; background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep));}
.cf-icon.danger{background:rgba(185,74,74,.13); color:var(--danger-ink);}
.cf-icon .icon{width:28px; height:28px; stroke-width:2.2;}
.cf-body{margin:14px 0 0; font-size:0.9375rem; line-height:1.6; color:var(--ink-soft);}
/* La nota de quien puso la bandera —casi siempre una hora— va debajo de la explicacion y se
   distingue de ella: es lo unico de este modal que cambia de un mantenimiento a otro. */
.cf-note{margin:12px 0 0; font-size:0.9375rem; font-weight:600; color:var(--ink);}
.cf-modal .modal-actions{justify-content:center; margin-top:22px;}

.modal.wb-modal{max-width:430px; text-align:center;}
.wb-head{display:flex; flex-direction:column; align-items:center; gap:12px; margin-bottom:6px;}
.wb-head h3{margin:0;}
.wb-icon{display:inline-flex; align-items:center; justify-content:center; width:56px; height:56px;
  border-radius:50%; background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep));}
.wb-icon .icon{width:28px; height:28px; stroke-width:2.4;}
.wb-body{margin:6px 0 0; font-size:0.9375rem; line-height:1.6; color:var(--ink-soft);}
.wb-mail{margin:12px 0 0; padding:8px 12px; border-radius:9px; background:var(--bg);
  border:1px solid var(--line); font-size:0.8125rem; font-weight:600; color:var(--ink);
  overflow-wrap:anywhere;}
.wb-modal .modal-actions{justify-content:center; margin-top:20px;}
/* "this one's already on the shelf" — sits between the cover and the title, so it's read before
   any of the form is filled in rather than after */
/* forma y color: .note-box (mas abajo), que es de donde lo saca ahora */
/* Los dos lados de un conflicto al importar un mapa, uno al lado del otro: sin verlos juntos no
   hay forma de elegir. En pantalla estrecha se apilan. */
.dup-cmp{display:grid; grid-template-columns:1fr 1fr; gap:10px; margin:14px 0 4px;}
@media(max-width:520px){ .dup-cmp{grid-template-columns:1fr;} }
.dup-side{border:1px solid var(--line-strong); border-radius:10px; padding:11px 13px; background:var(--bg); min-width:0;}
.dup-side-h{font-size:0.6875rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em; color:var(--ink-soft); margin-bottom:5px;}
.dup-side-t{font-family:var(--font-display); font-weight:600; font-size:0.9375rem; color:var(--ink); line-height:1.25;
  overflow-wrap:anywhere; margin-bottom:4px;}
.dup-side-m{font-size:0.78125rem; color:var(--ink-soft); line-height:1.45;}
.dup-all{margin:10px 0 0;}
/* the rest of the form while that warning stands: visibly out of play, and unreachable by tab
   or pointer (the controls inside are disabled too, so this is presentation, not the guard) */
#f_rest{transition:opacity .18s ease;}
#f_rest.dimmed{opacity:.4; pointer-events:none;}
/* El aviso de "esto aun no se puede puntuar": mismo recuadro que el resto (ver .note-box), pero
   este SE DESPLIEGA al tocar las estrellas. Por eso NO entra en la regla comun: el padding, el
   borde y el margen tienen que animarse junto a la altura — con ellos fijos, plegado dejaria una
   franja de fondo rosa de 20px bajo las estrellas. Y sigue en display:block, no flex: es un solo
   texto y lo que se anima es su caja. */
.rate-lock-tip{display:block; margin:0; padding:0 11px; max-height:0; opacity:0; overflow:hidden;
  border:0 solid var(--line-strong); border-radius:9px; background:var(--rose-tint); color:var(--ink);
  font-size:0.8125rem; line-height:1.45;
  transition:opacity .18s ease, max-height .18s ease, padding .18s ease, border-width .18s ease, margin .18s ease;}
.rate-lock-tip.show{opacity:1; max-height:8em; margin:8px 0; padding:9px 11px; border-width:1px;}
.star-bg{display:block;}
.star-fg{position:absolute; left:0; top:0; overflow:hidden; white-space:nowrap; pointer-events:none;}
.star-svg{width:1em; height:1em; display:block;}
/* etiqueta del Rating Guide al pasar el cursor (mismo patrón visual que .flame[data-tip]).
   El padding negativo agranda el área de hover sin mover el layout. */
.stars-row[data-tip]{position:relative; padding:5px 3px; margin:-5px -3px;}
.stars-row[data-tip]::after{content:attr(data-tip); position:absolute; bottom:calc(100% + 3px); left:50%; transform:translateX(-50%); width:max-content; max-width:190px; white-space:normal; text-align:center; background:var(--ink); color:var(--surface); font-size:0.6875rem; font-weight:600; line-height:1.3; padding:6px 9px; border-radius:7px; box-shadow:0 6px 18px rgba(0,0,0,.2); opacity:0; visibility:hidden; transition:opacity .12s; z-index:60; pointer-events:none;}
.stars-row[data-tip]::before{content:""; position:absolute; bottom:calc(100% - 2px); left:50%; transform:translateX(-50%); border:5px solid transparent; border-top-color:var(--ink); opacity:0; visibility:hidden; transition:opacity .12s; z-index:60; pointer-events:none;}
.stars-row[data-tip]:hover::after, .stars-row[data-tip]:hover::before{opacity:1; visibility:visible;}
/* donde estorbaría: el calendario recorta (overflow:hidden) y en Ajustes la etiqueta ya se lee al lado */
.mc-stars .stars-row[data-tip], .rating-guide-item .stars-row[data-tip]{padding:0; margin:0;}
.mc-stars .stars-row[data-tip]::after, .mc-stars .stars-row[data-tip]::before,
.rating-guide-item .stars-row[data-tip]::after, .rating-guide-item .stars-row[data-tip]::before{display:none;}
/* ===== scrollbars =====
   El truco es el borde transparente + background-clip:padding-box: el ancho REAL de la barra
   sigue siendo el declarado (el sitio que reserva el navegador), pero solo se pinta el hueco
   interior. Así la línea sale fina y, con el borde derecho más grueso, se despega del borde de
   la ventana en vez de pegarse a él. */
/* Paneles, tarjetas, desplegables y modales: TODOS la misma linea, y la misma que la de la
   pagina. Antes esto pintaba 4px de --ink-faint a opacidad plena y sin reaccion al apuntarlo, que
   al lado del carril de la pagina parecia de otra app.
     · 2px en reposo, 4px al apuntar
     · --ink-faint al 45% en reposo, entero al apuntar
     · engorda hacia dentro: el canto de fuera no se mueve y la linea no salta bajo el raton
   Los 10px que reserva el navegador siguen siendo la zona de agarre — el mismo papel que el
   ::before invisible del pulgar de la pagina — aunque solo se pinten 2. */
::-webkit-scrollbar{width:10px; height:10px;}
::-webkit-scrollbar-track{background:transparent;}
::-webkit-scrollbar-thumb{background:color-mix(in srgb, var(--ink-faint) 45%, transparent);
  background-clip:padding-box; border-radius:999px; border:4px solid transparent; min-height:36px;}
::-webkit-scrollbar-thumb:hover{background:var(--ink-faint); background-clip:padding-box;}
/* Vertical: adelgaza el borde IZQUIERDO, asi crece hacia la izquierda y el canto derecho no se
   mueve. Horizontal: los bordes de los lados son el LARGO, no el grosor, asi que ahi el que
   adelgaza es el de ARRIBA. */
::-webkit-scrollbar-thumb:vertical:hover{border-left-width:2px;}
::-webkit-scrollbar-thumb:horizontal:hover{border-top-width:2px;}
/* La barra de la PÁGINA no es nativa: una nativa no se puede meter hacia dentro, porque su ancho
   ES el hueco que el navegador reserva — despegarla un 10% costaría un 10% de página. Se oculta
   la nativa y se dibuja una flotante (#pageScroll, la monta initPageScrollbar en app.js): al no
   ocupar sitio en el layout puede colocarse donde queramos. */
html{scrollbar-width:none;}
html::-webkit-scrollbar{width:0; height:0;}
/* Separación entre la línea y el borde derecho del CONTENIDO, por fuera de él: así la línea cae
   en el margen y nunca se le monta encima a una tarjeta. Se referencia al contenido y no a la
   ventana porque #app está centrado y limitado a 1358px, así que el borde de la ventana no dice
   nada sobre dónde acaba lo que se lee. La posición horizontal la calcula initPageScrollbar
   midiendo #main; aquí solo vive la distancia, para poder tocarla sin abrir el JS. */
:root{--page-scroll-gap:30px;}
#pageScroll{
  /* top/bottom/left son valores de arranque: initPageScrollbar los mide y los reescribe para que
     el carril empiece donde empieza el contenido y termine donde termina. */
  position:fixed; top:16px; bottom:16px; left:0; width:2px;
  z-index:1900;                   /* por debajo del velo del modal (2100): allí no pinta nada */
  pointer-events:none;            /* flota sobre el contenido; solo el pulgar es agarrable */
  opacity:0; transition:opacity .2s ease;
}
#pageScroll.on{opacity:1;}
/* Alto FIJO, no proporcional al largo de la página: aquí la línea es una marca de posición, no
   una medida de cuánto queda. El JS solo la desplaza; el tamaño se toca aquí. */
#pageScrollThumb{
  position:absolute; left:0; top:0; width:100%; height:112px; border-radius:999px;
  /* El color es SIEMPRE --ink-faint (el tono más suave de la rampa de tinta, y por tanto propio de
     cada tema); lo que cambia en reposo es la opacidad. Bajarla en vez de saltar a otra variable
     deja el reposo como un número suelto que se puede afinar sin tocar el hover. */
  background:var(--ink-faint); opacity:.45; pointer-events:auto; cursor:grab;
  transition:width .15s ease, left .15s ease, opacity .15s ease;
}
/* Engorda hacia la izquierda: el canto derecho no se mueve, así la línea no salta al apuntarla.
   El :hover va en el pulgar, no en el carril: el carril tiene pointer-events:none y nunca lo
   recibiría. Y a 2px hace falta algo más ancho que la propia línea para poder agarrarla. */
#pageScrollThumb::before{content:""; position:absolute; inset:-2px -6px;}   /* zona de agarre invisible */
#pageScrollThumb:hover, #pageScrollThumb:active{width:4px; left:-2px; opacity:1;}
#pageScrollThumb:active{cursor:grabbing;}

/* ===== ajustes por superficie =====
   La APARIENCIA ya es comun (arriba). Aqui solo lo que depende de la forma de cada caja, y va en
   reglas con nombre y apellidos en vez de en variables heredadas: un ::-webkit-scrollbar-* se
   cuelga de UN elemento, asi que lo de una caja no se derrama en las de dentro.
   ⚑ Se probo con @property/inherits:false y NO sirve: un pseudo-elemento con una propiedad no
   heredada toma el valor INICIAL, no el del elemento que lo hospeda, asi que el ajuste no llegaba
   ni siquiera a la caja que lo declaraba. Las variables normales si llegan — --modal-radius y
   --modal-scroll-shift lo hacen— pero heredan, y por eso solo se leen en reglas que apuntan a la
   caja concreta.

   (a) ESQUINAS. Solo las cajas que son redondas ELLAS MISMAS. El carril recorre la caja entera, y
   arriba del todo el borde ya se esta curvando, asi que una linea recta se sale del arco. Se mete
   por arriba y por abajo lo que mide el radio: justo donde empieza el lado recto.
   Las listas de las tarjetas (.dash-rel-list, .quote-text, .rev-list, .qpick-list…) NO entran:
   su caja es cuadrada y lo redondo es la tarjeta de fuera, que ademas las separa con su padding,
   asi que no hay esquina que estorbar y meterles el carril seria recortarlo sin motivo. */
.modal::-webkit-scrollbar-track:vertical{margin:var(--modal-radius) 0;}
.modal::-webkit-scrollbar-track:horizontal{margin:0 var(--modal-radius);}
/* Los desplegables y paneles con radio propio de 12px. */
.ff-dd-panel::-webkit-scrollbar-track:vertical,
.combo-panel::-webkit-scrollbar-track:vertical,
.gb-results::-webkit-scrollbar-track:vertical,
.cal-results::-webkit-scrollbar-track:vertical,
.imp-table::-webkit-scrollbar-track:vertical,
.ms-panel::-webkit-scrollbar-track:vertical,
.smap-add-menu::-webkit-scrollbar-track:vertical,
.az-menu::-webkit-scrollbar-track:vertical{margin:12px 0;}

/* (c) LAS LISTAS DE LAS TARJETAS, al reves: la linea se separa de las PORTADAS, no del canto.
   Aqui el problema no es la esquina sino el vecino — la linea caia pegada al lomo de la portada.
   Y a diferencia del modal esto NO necesita ensanchar la barra: la pintura vive en el centro de
   los 10px, asi que hay 4px de margen a la derecha donde correrla. Se reparten los bordes del
   pulgar: se engorda el izquierdo y se adelgaza el derecho lo mismo. La barra sigue reservando
   sus 10px, que es justo lo que no puede cambiar — el ancho util de estas listas alimenta el
   calculo que hace caber tres portadas.
   Al tope de 4px: la pintura queda a ras del canto de su caja, que es lo mas lejos que puede
   estar de las portadas sin adelgazar la linea ni meterle padding a la lista.
   ⚑ El :hover generico adelgaza el borde IZQUIERDO hasta 2px para engordar hacia dentro. Con el
   izquierdo ya en 7px eso daria un pulgar de 7px de gordo, asi que aqui se recalcula: crece los
   mismos 2px y el canto derecho sigue sin moverse. */
.now-reading-list, .dash-rel-list, .dash-bar-list, .quote-text, .ns-list{--card-scroll-shift:4px;}
.now-reading-list::-webkit-scrollbar-thumb:vertical,
.dash-rel-list::-webkit-scrollbar-thumb:vertical,
.dash-bar-list::-webkit-scrollbar-thumb:vertical,
.quote-text::-webkit-scrollbar-thumb:vertical{
  border-left-width:calc(4px + var(--card-scroll-shift));
  border-right-width:calc(4px - var(--card-scroll-shift));
}
.now-reading-list::-webkit-scrollbar-thumb:vertical:hover,
.dash-rel-list::-webkit-scrollbar-thumb:vertical:hover,
.dash-bar-list::-webkit-scrollbar-thumb:vertical:hover,
.quote-text::-webkit-scrollbar-thumb:vertical:hover{border-left-width:calc(2px + var(--card-scroll-shift));}

/* (b) SEPARACION DEL CANTO. Solo el modal.
   No se puede mover dentro de los 10px: la pintura vive en el centro de esa caja (4px de borde
   transparente a cada lado de 2px de color), asi que correrla pediria un borde izquierdo negativo.
   Lo que se mueve es la CAJA: la barra se ensancha lo que valga el desplazamiento y el borde
   DERECHO del pulgar se lleva ese ancho de mas. Cuesta ese hueco reservado, que en el modal sale
   de su padding de 28px y no del texto.
   ⚑ Los desplegables NO lo llevan: un menu de 190px no tiene 8px que regalar, y las listas de
   las tarjetas tampoco, porque su ancho util alimenta el calculo que hace caber tres portadas. */
.modal::-webkit-scrollbar:vertical{width:calc(10px + var(--modal-scroll-shift));}
.modal::-webkit-scrollbar-thumb:vertical{border-right-width:calc(4px + var(--modal-scroll-shift));}

/* Firefox no tiene los pseudo-elementos ni estado :hover para la barra, asi que se le dan las dos
   propiedades estandar — pero SOLO a el.
   ⚑ EN CHROME ESTA LINEA MATA TODO LO DE ARRIBA. Si scrollbar-width vale algo distinto de auto,
   Chrome se olvida de las reglas ::-webkit-scrollbar y pinta la suya: el hueco reservado dentro de
   un modal se fue de 10px a 0 y el dibujo de 2px/4px dejo de existir. La compuerta es el propio
   selector: Firefox no conoce ::-webkit-scrollbar, asi que solo el entra aqui.
   Sobre body y no sobre html, para no tocar la barra de la PAGINA, que esta oculta a proposito. */
@supports not selector(::-webkit-scrollbar){
  body, body *{
    scrollbar-width:thin;
    scrollbar-color:color-mix(in srgb, var(--ink-faint) 45%, transparent) transparent;
  }
}

/* ===== la puerta de actualizacion =====
   Tapa TODO y no se puede cerrar: no hay boton de descartar ni se cierra con Escape ni al pulsar
   fuera. Es a proposito — la razon de que exista es que dos clientes con contratos de datos
   distintos no escriban a la vez sobre las mismas filas, y un aviso que se puede posponer no
   sirve para eso.
   ⚑ z-index 10000: por encima de todo lo que hay. El techo anterior era la notificacion
   flotante (9999), luego el fantasma de los mapas (3000) y el fondo de los modales (2100).
   ⚑ El fondo lleva un color solido ADEMAS del desenfoque. backdrop-filter no esta en todas
   partes, y donde no esta un fondo puramente transparente dejaria la aplicacion perfectamente
   usable por debajo — justo lo que no se quiere. El color va con la tinta del tema, asi que
   funciona igual en los quince. */
.sf-update-gate{position:fixed; inset:0; z-index:10000; display:flex; align-items:center; justify-content:center;
  padding:24px; background:color-mix(in srgb, var(--ink) 46%, transparent);
  backdrop-filter:blur(7px); -webkit-backdrop-filter:blur(7px);}
.sf-update-card{background:var(--surface); border:1px solid var(--line); border-radius:var(--radius);
  box-shadow:0 18px 50px rgba(0,0,0,.28); padding:28px 26px; max-width:380px; width:100%; text-align:center;}
.sf-update-title{font-family:var(--font-display); font-weight:600; font-size:1.25rem; color:var(--ink); margin-bottom:10px;}
.sf-update-body{font-size:0.875rem; line-height:1.55; color:var(--ink-soft); margin:0 0 20px;}
.sf-update-card .btn{width:100%; justify-content:center;}

/* ===== modal ===== */
.modal-bg{position:fixed; inset:0; background:rgba(28,26,23,.35); display:none; align-items:center; justify-content:center; z-index:2100; padding:20px; backdrop-filter:blur(2px);}
.modal-bg.open{display:flex;}
.modal{--modal-radius:18px; --modal-scroll-shift:8px;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--modal-radius); max-width:560px; width:100%; max-height:88vh; overflow-y:auto; padding:28px; box-shadow:0 20px 60px rgba(28,26,23,.18);}
.modal h3{margin-bottom:18px; font-size:1.3125rem;}
/* monthly-quote picker (choose from saved quotes) */
.qpick-list{display:flex; flex-direction:column; gap:10px; max-height:52vh; overflow-y:auto; margin-bottom:6px;}
.qpick-item{border:1px solid var(--line); border-radius:12px; padding:13px 14px; background:var(--bg);}
.qpick-text{margin:0 0 10px; font-family:var(--font-quote); font-size:1.0625rem; line-height:1.4; color:var(--ink);}
.qpick-foot{display:flex; align-items:center; justify-content:space-between; gap:12px;}
.qpick-src{font-size:0.78125rem; color:var(--ink-soft); min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.qpick-foot .btn{flex-shrink:0;}
.field{margin-bottom:14px;}
.field-tabs{margin-bottom:22px;}
.field label{display:block; font-size:0.71875rem; font-weight:600; margin-bottom:5px; color:var(--ink-soft); letter-spacing:.3px; text-transform:uppercase;}
.field input, .field textarea, .field select{
  width:100%; border:1px solid var(--line-strong); border-radius:9px; padding:9px 11px; font-size:0.875rem; background:var(--surface);
}
.field input:focus, .field textarea:focus, .field select:focus{outline:none; border-color:var(--ink);}
.field label.check-row{display:flex; align-items:center; gap:9px; margin:0; text-transform:none; letter-spacing:0; font-size:0.84375rem; font-weight:500; color:var(--ink); cursor:pointer;}
.field label.check-row input[type=checkbox]{width:17px; height:17px; flex-shrink:0; margin:0; accent-color:var(--rose-deep); cursor:pointer;}
.field textarea{min-height:80px; resize:vertical;}
/* ===== combobox (.combo): un campo de texto con TODA la lista siempre a mano =====
   Panel y opciones copian a .ff-dd a proposito — un desplegable no puede tener dos idiomas en la
   misma app. Lo unico distinto es que aqui el ancho lo manda el campo (left/right:0), no el
   contenido: el panel cuelga de un input que ya ocupa media fila. */
.combo{position:relative;}
.field .combo-input{padding-right:34px;}   /* el hueco de la flecha, que va montada encima */
.combo-caret{position:absolute; top:0; right:0; height:100%; width:32px; display:flex; align-items:center;
  justify-content:center; padding:0; border:none; background:none; color:var(--ink-faint); cursor:pointer;}
.combo-caret .icon{width:13px; height:13px; transform:rotate(90deg); transition:.12s;}
.combo.open .combo-caret .icon{transform:rotate(-90deg);}
.combo-panel{position:absolute; top:calc(100% + 5px); left:0; right:0; z-index:30; max-height:220px; overflow-y:auto;
  background:var(--surface); border:1px solid var(--line); border-radius:12px; box-shadow:var(--card-shadow); padding:6px; display:none;}
.combo.open .combo-panel{display:block;}
.combo.up .combo-panel{top:auto; bottom:calc(100% + 5px);}
.combo-opt{display:block; width:100%; text-align:left; border:none; background:none; padding:7px 9px; border-radius:8px;
  font-size:0.8125rem; color:var(--ink); cursor:pointer; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
/* display:block gana a [hidden] por especificidad, asi que el filtrado tiene que decirlo aparte */
.combo-opt[hidden]{display:none;}
.combo-opt:hover{background:var(--bg);}
.combo-opt.on{background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep));}
.combo-clear{color:var(--ink-soft); font-style:italic;}
/* ===== AVISOS DENTRO DE UN MODAL (.note-box) =====
   Un solo idioma para todos: el recuadro que sale cuando algo pide una decision o advierte de una
   consecuencia. Cada seccion traia el suyo — cuatro paddings, tres radios, tres tamanios de letra
   y tres colores de texto para la misma clase de mensaje.
   Los cuatro que hay hoy: la saga que ya reclama el libro (.ord-note), el libro duplicado
   (.dup-warn, x3), la portada que se pedira a un tercero (.cover-note.warn) y la nota de portadas
   de la importacion (.imp-note). Uno nuevo solo tiene que ponerse .note-box.
   ⚑ NADA de justify-content:space-between aqui: .imp-note es un icono y su texto, y separarlos a
   los cantos los deja como dos cosas sueltas. El boton, si lo hay, se va solo a la derecha. */
.note-box, .ord-note, .dup-warn, .imp-note, .cover-note.warn{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  margin:8px 0; padding:9px 11px; border:1px solid var(--line-strong); border-radius:9px;
  background:var(--rose-tint); color:var(--ink); font-size:0.8125rem; line-height:1.45;
}
/* ⚑ display:flex gana a [hidden], asi que cada uno necesita decirlo aparte. .cover-note.warn NO es
   opcional: al vaciar el campo de la URL se pone hidden pero NO se le quita la clase, asi que sin
   esto se quedaria un recuadro vacio en pantalla. */
.note-box[hidden], .ord-note[hidden], .dup-warn[hidden], .imp-note[hidden],
.cover-note.warn[hidden]{display:none;}
.note-box .btn, .ord-note .btn{flex-shrink:0; margin-left:auto;}
/* ⚑ NINGUNA excepcion de color: los avisos de modal se leen todos igual, decision del usuario
   (23-08). El de libro duplicado llevaba tinta y borde de peligro; lo que lo distingue no es el
   rojo sino lo que HACE — apaga el resto del formulario y quita Guardar. El de la portada nunca
   fue rojo a proposito, y ahora coinciden. */
/* Recibe el foco por programa (tabindex="-1"), asi que el anillo es justo lo que se quiere ver:
   es la senial de "esto es lo que acaba de pasar". :focus y no :focus-visible — un foco puesto a
   mano no siempre cuenta como "visible" para el navegador. */
.note-box:focus, .ord-note:focus{outline:2px solid var(--rose-deep); outline-offset:2px;}
.row2{display:grid; grid-template-columns:1fr 1fr; gap:10px;}
/* "P." is a two-character prefix, not a heading — it belongs beside its field */
.field.field-inline{display:flex; align-items:center; gap:9px;}
.field.field-inline > label{margin:0; flex-shrink:0;}
.field.field-inline > input{flex:1; min-width:0;}
/* A checkbox sharing a row with an input has no label above it, so it would sit up on
   the input's LABEL line. Drop it onto the input's own band: 38px = the input's box
   (9px padding x2 + 1px border x2 + an 18px line). Matches Add Quote and Add Highlight. */
.row2 .field:has(> label.check-row){display:flex; align-items:flex-end;}
.row2 .field:has(> label.check-row) > label.check-row{min-height:38px; width:100%;}
.mood-row{display:flex; flex-wrap:wrap; gap:6px;}
.mood{font-size:1.25rem; line-height:1; padding:6px; border:1px solid var(--line-strong); border-radius:10px; background:var(--surface); cursor:pointer; filter:grayscale(1) opacity(.55); transition:.12s;}
.mood:hover{filter:grayscale(.3);}
.mood.on{filter:none; border-color:var(--ink); background:var(--bg);}
.mood.mood-more{filter:none; opacity:1; color:var(--ink-soft); display:inline-flex; align-items:center; justify-content:center;}
.mood.mood-more:hover{filter:none; color:var(--ink); border-color:var(--ink-soft);}
.mood.mood-more svg{width:17px; height:17px;}
.mood-panel{display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; padding:10px; border:1px solid var(--line); border-radius:12px; background:var(--bg);}
.mood-panel[hidden]{display:none;}
/* colour-tab picker: swatch chips sourced from the Settings legend */
.tab-chip{width:34px; height:34px; flex-shrink:0; border:1px solid var(--line-strong); border-radius:9px; cursor:pointer; padding:0;
  background:var(--c); transition:.12s; position:relative;}
.tab-chip:hover{transform:scale(1.06);}
.tab-chip.on{border-color:var(--ink); box-shadow:0 0 0 2px var(--surface), 0 0 0 4px var(--ink);}
/* chips inside the collapsed quick rows aren't in a flex container, so space them directly */
#hl_tabs_quick .tab-chip, #hl_feels_quick .mood{margin:0 5px 5px 0;}
.tab-empty{font-size:0.78125rem; color:var(--ink-soft); padding:2px 4px;}
.modal-actions{display:flex; justify-content:flex-end; gap:8px; margin-top:18px;}

/* romance hero block — the differentiator, given top visual weight */
.romance-card{background:linear-gradient(155deg, var(--rose-tint) 0%, var(--surface) 78%); border:1px solid var(--rose); border-radius:16px; padding:22px; margin:22px 0; box-shadow:var(--card-shadow);}
.rc-head{display:flex; align-items:center; gap:9px;}
.rc-head .icon{width:20px; height:20px; color:var(--rose-deep);}
.rc-title{font-family:var(--font-display); font-size:1.25rem; color:var(--ink); letter-spacing:.2px;}
.rc-sub{font-size:0.71875rem; color:var(--ink-soft); margin:3px 0 18px 0;}
.rc-block{margin-bottom:20px;}
.rc-block:last-child{margin-bottom:0;}
.rc-label{font-size:0.6875rem; font-weight:700; margin-bottom:9px; color:var(--rose-deep); letter-spacing:.6px; text-transform:uppercase;}

/* spice flames */
.spice-row{display:flex; align-items:center; gap:5px;}
.flame{position:relative; font-size:1.5625rem; line-height:1; cursor:pointer; filter:grayscale(1) opacity(.38); transition:transform .12s, filter .12s; user-select:none;}
.flame:hover{transform:scale(1.14);}
.flame.on{filter:none;}
/* tooltip de significado por nivel (hover en desktop, .tip-open en tap largo mobile) */
.flame[data-tip]::after{content:attr(data-tip); position:absolute; bottom:calc(100% + 9px); left:50%; transform:translateX(-50%); width:max-content; max-width:190px; white-space:normal; text-align:center; background:var(--ink); color:var(--surface); font-size:0.6875rem; font-weight:600; line-height:1.3; padding:6px 9px; border-radius:7px; box-shadow:0 6px 18px rgba(0,0,0,.2); opacity:0; visibility:hidden; transition:opacity .12s; z-index:60; pointer-events:none;}
.flame[data-tip]::before{content:""; position:absolute; bottom:calc(100% + 4px); left:50%; transform:translateX(-50%); border:5px solid transparent; border-top-color:var(--ink); opacity:0; visibility:hidden; transition:opacity .12s; z-index:60; pointer-events:none;}
.flame[data-tip]:hover::after, .flame[data-tip]:hover::before, .flame.tip-open::after, .flame.tip-open::before{opacity:1; visibility:visible;}
.spice-caption{margin-left:12px; font-size:0.8125rem; color:var(--rose-deep); font-weight:600;}

/* compact spice indicator on library cards & table rows */
.spice-mini{font-size:0.6875rem; line-height:1; letter-spacing:-2px; white-space:nowrap;}
.rating-cell{display:flex; align-items:center; gap:9px;}

/* toggleable text chips (tropes & content warnings) */
.chip-wrap{display:flex; flex-wrap:wrap; gap:7px;}

/* Google Books search in the add/edit form */
.gb-field{position:relative;}
.gb-search{display:flex; align-items:center; gap:9px; border:1px solid var(--line-strong); border-radius:12px; padding:9px 14px; background:var(--surface);}
.gb-search:focus-within{border-color:var(--rose-deep);}
/* Vaciar la búsqueda: hijo flex del propio buscador (ambos wrappers ya lo son), así no se
   superpone al texto ni pelea con la lupa que el buscador del topbar lleva a su derecha.
   Solo aparece cuando hay algo que borrar: un aspa permanente pide que la pulses. */
.search-clear{display:none; align-items:center; justify-content:center; flex-shrink:0;
  width:20px; height:20px; padding:0; border:none; background:none; border-radius:50%;
  color:var(--ink-faint); cursor:pointer; transition:color .12s ease, background .12s ease;}
.has-value > .search-clear{display:flex;}
.search-clear:hover{color:var(--ink); background:var(--bg);}
.search-clear .icon{width:13px; height:13px;}
.gb-search .icon{width:17px; height:17px; color:var(--ink-faint); flex-shrink:0;}
.gb-search input:not([type=checkbox]):not([type=radio]){flex:1; min-width:0; border:none; background:transparent; font-size:0.84375rem; color:var(--ink); outline:none;}
.jr-search{margin:0 0 18px;}
.feels-search{margin:0 0 14px;}
/* Feels filters: two dropdowns under the search — every configured colour, the full feelings list */
.feels-filters{display:flex; align-items:center; flex-wrap:wrap; gap:8px; margin:0 0 18px;}
.ff-icon{display:flex; color:var(--ink-faint); flex-shrink:0;}
.ff-icon .icon{width:15px; height:15px;}
.ff-dd{position:relative;}
.ff-dd-btn{display:inline-flex; align-items:center; gap:7px; max-width:220px; border:1px solid var(--line-strong);
  border-radius:999px; background:var(--surface); padding:8px 14px; font-size:0.8125rem; color:var(--ink); cursor:pointer; transition:.12s;}
.ff-dd-btn:hover{border-color:var(--ink-soft);}
.ff-dd-btn.on{border-color:var(--rose-deep);}
.ff-dd-btn .icon{width:13px; height:13px; color:var(--ink-faint); flex-shrink:0; transform:rotate(90deg); transition:.12s;}
.ff-dd.open .ff-dd-btn .icon{transform:rotate(-90deg);}
.ff-dd-txt{white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.ff-dd-txt.muted{color:var(--ink);}   /* filter name reads dark, like the Stories toolbar selects */
.ff-sw{width:14px; height:14px; border-radius:4px; flex-shrink:0; border:1px solid var(--line-strong);}
.ff-em{font-size:1.0625rem; line-height:1; flex-shrink:0;}
.ff-dd-panel{position:absolute; top:calc(100% + 5px); left:0; z-index:30; min-width:190px; max-height:280px; overflow-y:auto;
  background:var(--surface); border:1px solid var(--line); border-radius:12px; box-shadow:var(--card-shadow); padding:6px; display:none;}
.ff-dd.open .ff-dd-panel{display:block;}
.ff-dd-opt{display:flex; align-items:center; gap:9px; width:100%; text-align:left; border:none; background:none;
  padding:7px 9px; border-radius:8px; font-size:0.8125rem; color:var(--ink); cursor:pointer;}
.ff-dd-opt:hover{background:var(--bg);}
.ff-dd-opt.on{background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep));}
/* Story Maps export menu: a .btn trigger wearing the .ff-dd panel. The header row is right-aligned,
   so the panel hangs from the button's right edge instead of its left. */
.smap-export-dd .ff-dd-panel{left:auto; right:0;}
.smap-export-dd .ff-dd-opt .ff-icon{display:inline-flex; color:var(--ink-faint);}
.smap-export-dd .ff-dd-opt .icon{width:15px; height:15px;}
/* rating filter dropdown: a row of half-star pickable stars above the "Any rating" clear option */
.ff-rating-panel{min-width:auto;}
.ff-rating-pick{padding:9px 11px 7px;}
.ff-rating-pick .stars-row{font-size:1.5rem;}
.ff-dd-btn .stars-row{font-size:1rem;}
.ff-clear{border:none; background:none; color:var(--rose-deep); font-size:0.75rem; font-weight:600; cursor:pointer; padding:2px 6px;}
.ff-clear:hover{text-decoration:underline;}
/* mood (tone) swatch — same gradients as the card tones */
.ff-tone-sw{width:14px; height:14px; border-radius:4px; flex-shrink:0; border:1px solid var(--line-strong);}
.ff-tone-sw.tone-daylight{background:var(--tone-daylight);}
.ff-tone-sw.tone-dusk{background:var(--tone-dusk);}
.ff-tone-sw.tone-lamplight{background:var(--tone-lamplight);}
/* combined day/month/year date filter — three selects stacked in one dropdown */
.ff-date-panel{min-width:212px; padding:10px 12px;}
.ff-date-row{display:flex; align-items:center; justify-content:space-between; gap:12px; padding:5px 2px; margin:0; cursor:pointer;}
.ff-date-row > span{font-size:0.71875rem; font-weight:600; color:var(--ink-soft); text-transform:uppercase; letter-spacing:.3px;}
.ff-date-row select{flex:0 0 auto; min-width:98px; border:1px solid var(--line-strong); border-radius:8px; padding:5px 8px;
  font-size:0.8125rem; background:var(--surface); color:var(--ink); cursor:pointer;}
.ff-date-row select:focus{outline:none; border-color:var(--rose-deep);}
/* Feels pagination bar: count · page nav · per-page select */
.feels-pager{display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:14px; margin-top:26px;}
.fp-count{font-size:0.8125rem; color:var(--ink-soft);}
.fp-nav{display:flex; align-items:center; gap:5px; flex-wrap:wrap;}
.fp-btn{min-width:34px; height:34px; display:inline-flex; align-items:center; justify-content:center; padding:0 9px;
  border:1px solid var(--line-strong); border-radius:999px; background:var(--surface); color:var(--ink);
  font-size:0.8125rem; cursor:pointer; transition:.12s;}
.fp-btn:hover:not(:disabled){border-color:var(--ink-soft);}
.fp-btn:disabled{opacity:.4; cursor:default;}
.fp-btn.on{background:var(--rose-tint); border-color:var(--rose-deep-on-tint, var(--rose-deep)); color:var(--rose-deep-on-tint, var(--rose-deep)); font-weight:700;}
.fp-btn .icon{width:15px; height:15px;}
.fp-ellip{padding:0 3px; color:var(--ink-soft);}
.fp-per{display:inline-flex; align-items:center; gap:8px; font-size:0.8125rem; color:var(--ink-soft);}
.fp-per select{border:1px solid var(--line-strong); border-radius:999px; padding:6px 12px; font-size:0.8125rem;
  background:var(--surface); color:var(--ink); cursor:pointer;}
.fp-per select:focus{outline:none; border-color:var(--rose-deep);}
/* print modal: pick which set of cards to print */
.print-opts{display:flex; flex-direction:column; gap:8px; margin-bottom:4px;}
.print-opt{display:flex; align-items:center; gap:10px; padding:11px 14px; border:1px solid var(--line-strong); border-radius:12px; cursor:pointer; transition:.12s;}
.print-opt:hover{border-color:var(--ink-soft);}
.print-opt input{margin:0; accent-color:var(--rose-deep);}
.print-opt-label{flex:1; font-size:0.875rem; color:var(--ink);}
.print-opt-count{font-size:0.8125rem; color:var(--ink-soft); font-variant-numeric:tabular-nums;}
.print-opt.disabled{opacity:.45; cursor:default;}
.print-opt:has(input:checked){border-color:var(--rose-deep-on-tint, var(--rose-deep)); background:var(--rose-tint);}

/* ===== Diary tab: an A5 lined page + a mini calendar ===== */
.diary-wrap{display:flex; flex-direction:column; gap:14px;}
.diary-print-row{display:flex; justify-content:flex-end; width:100%;}
.diary-cols{display:flex; gap:28px; align-items:flex-start; flex-wrap:wrap;}   /* toolbar + calendar share the top line */
/* The diary is ALWAYS laid out at 570px (page + toolbar) so the text never re-wraps — on narrow screens
   the whole block is uniformly scaled down (transform) instead of reflowing. That keeps the on-screen
   layout identical to the print output and keeps stickers anchored to their spot in the text. JS
   (fitDiary) sets .diary-main-fit's width/height to the scaled footprint and .diary-main's transform. */
.diary-main-fit{flex:0 0 auto; overflow:hidden;}
.diary-main{--diary-w:570px; transform-origin:top left;}   /* page + toolbar share this width so they line up */
.diary-side{flex:0 0 auto; display:flex; flex-direction:column; gap:14px; align-items:flex-start;}
.diary-side-row{display:flex; gap:14px; align-items:stretch;}
/* sticker tray — a little palette of decorations to drag onto the page */
.diary-tray{width:260px; max-width:100%; box-sizing:border-box; border:1px solid var(--line); border-radius:14px; background:var(--surface); padding:11px 12px 12px; box-shadow:var(--card-shadow);}
.dtray-head{font-size:0.6875rem; font-weight:700; text-transform:uppercase; letter-spacing:.4px; color:var(--ink-soft); margin:0 2px 9px;}
.dtray-grid{display:flex; flex-wrap:wrap; gap:8px;}
.dtray-item{width:52px; height:52px; padding:6px; box-sizing:border-box; border:1px solid var(--line); border-radius:12px; background:#fff; cursor:grab; transition:.12s;}
.dtray-item:hover{border-color:var(--rose-deep); transform:translateY(-1px); box-shadow:0 3px 8px rgba(0,0,0,.1);}
.dtray-item:active{cursor:grabbing;}
.dtray-item svg, .dtray-item img{display:block; width:100%; height:100%; object-fit:contain; pointer-events:none;}
.dtray-hint{font-size:0.75rem; color:var(--ink-soft); margin:9px 2px 0; line-height:1.4;}
.diary-toolbar{display:flex; align-items:center; flex-wrap:wrap; gap:6px; padding:7px 9px; width:var(--diary-w); box-sizing:border-box; scroll-margin-top:14px;
  border:1px solid var(--line-strong); border-radius:12px 12px 0 0; border-bottom:none; background:var(--surface);}
.diary-font{border:1px solid var(--line-strong); border-radius:8px; padding:5px 9px; font-size:0.8125rem; background:var(--surface); color:var(--ink); cursor:pointer; max-width:170px;}
.diary-font option{background:var(--surface); color:var(--ink);}
.diary-color{width:30px; height:30px; flex-shrink:0; padding:0; border:1px solid var(--line-strong); border-radius:8px; background:none; cursor:pointer;}
.diary-color::-webkit-color-swatch-wrapper{padding:2px;}
.diary-color::-webkit-color-swatch{border:none; border-radius:5px;}
.diary-color::-moz-color-swatch{border:none; border-radius:5px;}
/* the page itself — A5 (5.8×8.3in), cream offset paper, classic ruled lines.
   background-origin:content-box tiles the lines from the text's content box, and the line
   sits at the text baseline (~26px in the 32px line box) so writing rests ON the rule. */
.diary-page{position:relative; width:var(--diary-w); height:calc(var(--diary-w) * 1.431); box-sizing:border-box; padding:16px 34px 26px 58px; overflow-y:auto;
  border:1px solid var(--line-strong); border-radius:0 0 6px 6px;
  color:#3b3630; font-size:20px; line-height:32px;
  background-image:
    repeating-linear-gradient(to bottom, transparent 0 25px, #aab6c6 25px 26px, transparent 26px 32px),
    linear-gradient(to right, transparent 0 46px, #cf9aa0 46px 48px, transparent 48px);
  background-color:#E3DAC9; background-origin:content-box, padding-box; background-repeat:repeat, no-repeat;
  background-attachment:local, local; box-shadow:0 12px 30px rgba(0,0,0,.12);
  -webkit-backdrop-filter:blur(9px); backdrop-filter:blur(9px);}   /* frosted glass when the page colour is translucent */
/* background colour + transparency control (in the toolbar) */
.diary-bg{display:inline-flex; align-items:center; gap:6px;}
.diary-bg-alpha{width:64px; height:6px; -webkit-appearance:none; appearance:none; background:linear-gradient(to right, transparent, var(--ink-soft)); border-radius:999px; cursor:pointer; outline:none;}
.diary-bg-alpha::-webkit-slider-thumb{-webkit-appearance:none; width:14px; height:14px; border-radius:50%; background:#fff; border:1px solid var(--line-strong); box-shadow:0 1px 2px rgba(0,0,0,.25); cursor:pointer;}
.diary-bg-alpha::-moz-range-thumb{width:14px; height:14px; border-radius:50%; background:#fff; border:1px solid var(--line-strong); cursor:pointer;}
.diary-date{position:relative; z-index:1; height:32px; line-height:32px; text-align:right; color:#8a7e6c; text-transform:capitalize; white-space:nowrap; overflow:hidden;}
/* first page: writing starts on the third line — date is line 1, line 2 blank (32px margin).
   continuation pages carry no date, so writing starts on line 1 → 24 lines instead of 22. */
.diary-write{position:relative; z-index:1; margin-top:32px; min-height:700px; outline:none; line-height:inherit;}
.diary-page.cont .diary-write{margin-top:0;}
/* decorative sticker layer — sits behind the text (z-index 0); sticker-mode lifts it above so stickers can be handled */
.diary-stickers{position:absolute; inset:0; z-index:0; pointer-events:none; overflow:hidden; border-radius:inherit;}
.dsticker{position:absolute; pointer-events:none;}
.dsticker svg, .dsticker img{display:block; width:100%; height:auto; filter:drop-shadow(0 0 1.5px #fff) drop-shadow(0 0 1.5px #fff) drop-shadow(0 1px 2px rgba(0,0,0,.18));}
.dsticker .ds-del, .dsticker .ds-handle{display:none;}
.diary-page.stickers-edit .diary-stickers{z-index:5; pointer-events:auto;}
.diary-page.stickers-edit .diary-write{pointer-events:none;}
.diary-page.stickers-edit .dsticker{pointer-events:auto; cursor:move; touch-action:none;}
.dsticker.sel{outline:2px dashed var(--rose-deep); outline-offset:5px; border-radius:6px;}
.diary-page.stickers-edit .dsticker.sel .ds-del{display:flex; align-items:center; justify-content:center; position:absolute; top:-11px; right:-11px;
  width:20px; height:20px; border-radius:50%; background:#fff; border:1px solid var(--line-strong); color:var(--ink); font-size:15px; line-height:1; cursor:pointer; padding:0; box-shadow:0 1px 3px rgba(0,0,0,.2);}
.diary-page.stickers-edit .dsticker.sel .ds-handle{display:block; position:absolute; right:-9px; bottom:-9px;
  width:17px; height:17px; border-radius:50%; background:var(--rose-deep); border:2px solid #fff; cursor:nwse-resize; box-shadow:0 1px 3px rgba(0,0,0,.2);}
.dstick-toggle.on{background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep)); border-color:var(--rose-deep-on-tint, var(--rose-deep));}
.dclear-btn{color:var(--ink-soft);}
.dclear-btn:hover{background:#fdecec; color:#c0392b; border-color:#e6b0aa;}
.diary-write:empty::before{content:attr(data-ph); color:#8f8574; font-style:italic;}
.diary-write :is(h1,h2,h3,p){margin:0; line-height:inherit;}
/* ⚑ Misma reparticion que `.feels-pager`: el recuento a la izquierda y la botonera a la
   derecha. El diario no lleva el selector de "por pagina" —sus paginas son las de UNA entrada,
   no un troceado que se pueda elegir— asi que son dos piezas en vez de tres, pero se colocan
   igual. `flex-wrap` para que en el movil la botonera baje entera en lugar de estrujarse. */
.diary-pager{display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap;
  gap:10px; margin-top:12px; width:var(--diary-w); box-sizing:border-box;}
.dp-count{font-size:0.8125rem; color:var(--ink-soft); font-variant-numeric:tabular-nums;}
.diary-pager .fp-count{font-variant-numeric:tabular-nums;}
.diary-pager .btn.small{margin-left:auto;}
/* mini calendar */
.diary-cal{width:260px; max-width:100%; border:1px solid var(--line); border-radius:14px; background:var(--surface); padding:12px 12px 13px; box-shadow:var(--card-shadow);}
.dc-head{display:flex; align-items:center; justify-content:space-between; margin-bottom:7px;}
.dc-month{font-weight:600; font-size:1rem; color:var(--ink); text-transform:capitalize;}
.dc-nav{width:30px; height:30px; display:inline-flex; align-items:center; justify-content:center; border:none; background:none; color:var(--ink-soft); border-radius:8px; cursor:pointer;}
.dc-nav:hover{background:var(--bg); color:var(--ink);}
.dc-nav .icon{width:16px; height:16px;}
.dc-dows{display:grid; grid-template-columns:repeat(7,1fr); gap:2px; margin-bottom:4px;}
.dc-dows span{text-align:center; font-size:0.6875rem; font-weight:600; color:var(--ink-faint); text-transform:uppercase; letter-spacing:.2px;}
.dc-grid{display:grid; grid-template-columns:repeat(7,1fr); gap:2px;}
.dc-day{position:relative; aspect-ratio:1; display:flex; align-items:center; justify-content:center; border:none; background:none;
  border-radius:8px; font-size:0.9375rem; color:var(--ink); cursor:pointer;}
.dc-day:hover{background:var(--bg);}
.dc-day.today{font-weight:700; box-shadow:inset 0 0 0 1px var(--line-strong);}
.dc-day.sel{background:var(--rose-deep); color:var(--on-rose-deep-ink);}
.dc-dot{position:absolute; bottom:4px; left:50%; transform:translateX(-50%); width:4.5px; height:4.5px; border-radius:50%; background:var(--rose-deep);}
.dc-day.sel .dc-dot{background:#fff;}
.dc-empty{aspect-ratio:1;}
/* diary search results (to the right of the calendar) */
.diary-results{flex:0 0 260px; max-width:100%; position:relative; overflow:hidden;
  border:1px solid var(--line); border-radius:14px; background:var(--surface); box-shadow:var(--card-shadow);}
.diary-results-inner{position:absolute; inset:0; overflow-y:auto; padding:10px 10px 8px;}
.dr-head{display:flex; justify-content:space-between; align-items:center; gap:8px; font-size:0.6875rem; font-weight:700;
  text-transform:uppercase; letter-spacing:.4px; color:var(--ink-soft); margin:2px 2px 8px;}
.dr-count{background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep)); border-radius:999px; padding:1px 8px; font-size:0.6875rem; font-variant-numeric:tabular-nums;}
.dr-item{display:block; width:100%; text-align:left; border:none; background:none; border-radius:9px; padding:8px 9px; cursor:pointer; transition:.12s;}
.dr-item:hover{background:var(--bg);}
.dr-item.on{background:var(--rose-tint);}
.dr-date{display:block; font-size:0.8125rem; font-weight:600; color:var(--ink); text-transform:capitalize; margin-bottom:2px;}
.dr-snip{display:block; font-size:0.75rem; color:var(--ink-soft); line-height:1.4;}
.dr-snip mark{background:#fff3a8; color:inherit; border-radius:3px; padding:0 1px;}
.dr-empty{font-size:0.8125rem; color:var(--ink-soft); padding:8px 4px;}
@media (max-width:720px){ .diary-side-row{flex-wrap:wrap;} }   /* diary width stays 570px; fitDiary scales it */

/* the print sheet lives off-screen until the browser prints it.
   the card must hold its 300x420 design size here so fitHighlightCards() (which runs in
   screen context) measures the real box and shrinks long moments the same as on-screen. */
#print-root{position:absolute; left:-99999px; top:0;}
#print-root .hl-card{ width:300px !important; height:420px !important; }
@media print {
  @page { margin: 0.14in; }
  html, body { background:#fff !important; }
  body.printing-cards > *:not(#print-root){ display:none !important; }
  #print-root{ position:static !important; left:auto !important; top:auto !important; }
  #print-root, #print-root *{ -webkit-print-color-adjust:exact !important; print-color-adjust:exact !important; }
  /* one sheet per page; the small top/left strip holds the cut marks (padding keeps them off the cards) */
  .print-page{ position:relative; padding:0.16in 0 0 0.16in; page-break-after:always; }
  .print-page:last-child{ page-break-after:auto; }
  .print-grid{ position:relative; display:flex; flex-wrap:wrap; align-content:flex-start; gap:0; }
  /* tile = card trim (2.5x3.5in) + 1/8in bleed on every side = 2.75x3.75in; the bleed is the tone colour */
  .print-tile{ position:relative; width:2.75in; height:3.75in; box-sizing:border-box; break-inside:avoid; page-break-inside:avoid; }
  .print-tile.tone-daylight{ background:var(--tone-daylight); }
  .print-tile.tone-dusk{ background:var(--tone-dusk); }
  .print-tile.tone-lamplight{ background:var(--tone-lamplight); }
  /* card content fills the centred 2.5x3.5in trim; its own background is transparent so the tile bleeds through */
  .print-tile .hl-card{ position:absolute; top:0.125in; left:0.125in; transform:scale(0.8); transform-origin:top left;
     background:transparent !important; border-radius:0 !important; box-shadow:none !important; }
  .print-tile .jrq-star{ visibility:hidden !important; }   /* the star is the one thing that never prints */
  /* cut marks: short ticks in the top strip (vertical cuts) and left strip (horizontal cuts) only */
  .cut-v{ position:absolute; top:-0.13in; height:0.09in; border-left:0.5pt solid #555; }
  .cut-h{ position:absolute; left:-0.13in; width:0.09in; border-top:0.5pt solid #555; }
  /* Diary print — render each page as a PIXEL-EXACT replica of the on-screen page: same 570px width,
     padding, 20px font, 32px line box and ruled background. That way the text wraps & paginates exactly
     as the user sees it, and the fractional sticker positions land in the same spot. CSS px print at
     1/96in, so 570px = 5.94in wide. Rules are a single line-box-tall gradient tiled seamlessly. */
  .diary-printing .diary-print-page{ position:relative; width:570px; height:calc(570px * 1.431); box-sizing:border-box;
    padding:16px 34px 26px 58px; overflow:hidden; break-after:page; page-break-after:always;
    color:#3b3630; font-size:20px; line-height:32px; -webkit-print-color-adjust:exact; print-color-adjust:exact;
    background-image:
      repeating-linear-gradient(to bottom, transparent 0 24px, #c7d2e2 24px 26px, transparent 26px 32px),
      linear-gradient(to right, transparent 0 46px, #eebfc6 46px 48px, transparent 48px);
    background-repeat:no-repeat, no-repeat;
    background-position:0 16px, 0 0;
    background-origin:border-box, border-box; }
  .diary-printing .diary-print-page:last-child{ break-after:auto; page-break-after:auto; }
  /* date: right-aligned, one line, inherits the entry's handwriting font — exactly like on screen */
  .diary-printing .dpp-date{ height:32px; line-height:32px; text-align:right; color:#8a7e6c; text-transform:capitalize;
    white-space:nowrap; overflow:hidden; position:relative; z-index:1; }
  .diary-printing .dpp-body{ margin-top:32px; position:relative; z-index:1; }   /* blank line 2, writing starts line 3 */
  .diary-printing .diary-print-page.cont .dpp-body{ margin-top:0; }             /* continuation pages: writing on line 1 */
  /* stickers print behind the writing, same fractional positions as on screen */
  .diary-printing .dpp-stickers{ position:absolute; inset:0; z-index:0; }
  .diary-printing .dpp-sticker{ position:absolute; }
  .diary-printing .dpp-sticker svg, .diary-printing .dpp-sticker img{ display:block; width:100%; height:auto; -webkit-print-color-adjust:exact; print-color-adjust:exact; }
  /* trim marks: two small VERTICAL ticks at the top and two HORIZONTAL ticks at the left, 1/8in (12px)
     inside the edges (like the card cut marks) */
  .diary-printing .dpp-cut{ position:absolute; z-index:3; pointer-events:none; }
  .diary-printing .dpp-cut.t-l{ top:0; left:12px; height:16px; border-left:0.5pt solid #555; }   /* top, vertical, 1/8in from left */
  .diary-printing .dpp-cut.t-r{ top:0; right:12px; height:16px; border-left:0.5pt solid #555; }   /* top, vertical, 1/8in from right */
  .diary-printing .dpp-cut.l-t{ left:0; top:12px; width:16px; border-top:0.5pt solid #555; }      /* left, horizontal, 1/8in from top */
  .diary-printing .dpp-cut.l-b{ left:0; bottom:12px; width:16px; border-top:0.5pt solid #555; }   /* left, horizontal, 1/8in from bottom */
}
/* diary print modal: include-toggles */
.print-check{display:flex; align-items:center; gap:9px; margin-top:10px; font-size:0.875rem; color:var(--ink); cursor:pointer;}
.print-check input{width:16px; height:16px; accent-color:var(--rose-deep); cursor:pointer;}
/* delete-chip menu: pick Text / Stickers before clearing the entry */
.diary-clear-menu{position:absolute; z-index:1200; min-width:180px; background:var(--surface); border:1px solid var(--line-strong); border-radius:12px; box-shadow:0 10px 28px rgba(0,0,0,.18); padding:11px;}
.dcm-head{font-size:0.6875rem; font-weight:700; text-transform:uppercase; letter-spacing:.4px; color:var(--ink-soft); margin:0 2px 3px;}
.dcm-warn{font-size:0.75rem; color:var(--rose-deep); margin:0 2px 9px; opacity:.85;}
.dcm-opt{display:flex; align-items:center; gap:9px; padding:5px 2px; font-size:0.875rem; color:var(--ink); cursor:pointer;}
.dcm-opt input{width:16px; height:16px; accent-color:var(--rose-deep); cursor:pointer;}
.dcm-opt input:disabled{opacity:.4;} .dcm-opt input:disabled + span{opacity:.4;}
.diary-clear-menu .btn.danger{width:100%; justify-content:center; margin-top:10px;}

/* ---- Feels: the highlight card (mockup tones: daylight / dusk / lamplight) ----
   A fixed card of a fixed width — a printed object, not a box that grows with its text.
   5:7 is a real trading card, 2.5 x 3.5in (the mockup's 4:5 was a sketch, not a spec).
   Three divisions: meta / body / book. The body centres the moment and its thought;
   fitHighlightCards() scales the moment to whatever room is left. */
/* 4 across on a normal desktop: flexible tracks (1fr max, not a fixed 300px — a fixed
   max makes auto-fill size to 300 and only 3 fit in the ~1159px content column).
   Tracks shrink to ~281px to fit 4, and collapse to fewer on narrower screens. */
.feels-cards{display:grid; grid-template-columns:repeat(auto-fill, minmax(min(260px,100%),1fr));
  justify-content:start; gap:24px 12px;}
.hl-item{display:flex; flex-direction:column; gap:8px; position:relative;}
.hl-card{aspect-ratio:5/7; display:grid; grid-template-rows:auto 1fr auto; padding:20px; overflow:hidden; position:relative;}
/* the reader's colour tab — a thin bar flush to the top-right edge */
.hl-tab{position:absolute; top:56px; right:0; width:5px; height:42px; border-radius:3px 0 0 3px; box-shadow:-1px 0 3px rgba(0,0,0,.12);}
/* copy/edit/delete are chrome, not part of the printed object.
   Touch: they sit in a row beneath the card. Mouse: they float in front of the
   card on hover — same frosted-glass overlay + circular buttons as the Stories covers. */
.hl-acts{display:flex; justify-content:flex-end; gap:6px;}
@media (hover:hover) and (pointer:fine){
  .hl-acts{position:absolute; inset:0; z-index:3; justify-content:center; align-items:center; gap:12px;
    background:rgba(15,20,28,.22); backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
    border-radius:var(--radius); opacity:0; pointer-events:none; transition:opacity .18s ease;}
  .hl-item:hover .hl-acts{opacity:1;}
  .hl-acts .jr-act{width:42px; height:42px; border-radius:12px; border:1px solid var(--line);
    background:var(--surface); color:var(--ink); box-shadow:0 2px 7px rgba(28,26,23,.22);
    pointer-events:auto; opacity:1; transition:transform .12s, background .12s, border-color .12s, color .12s;}
  .hl-acts .jr-act:hover{transform:scale(1.1); background:var(--bg); border-color:var(--ink);}
  .hl-acts .jr-act[data-del-highlight]:hover{background:var(--danger); color:#fff; border-color:var(--danger);}
  .hl-acts .jr-act .icon{width:18px; height:18px;}
  .hl-card .jrq-star{position:relative; z-index:4;}   /* keep the favourite toggle visible + usable above the hover overlay */
}
.hlc-top{display:flex; align-items:center; gap:10px;}
.hlc-tone{font-size:0.625rem; font-weight:800; letter-spacing:.12em; text-transform:uppercase; color:var(--rose-deep);}
.hlc-date{margin-left:auto; font-size:0.625rem; font-weight:700; letter-spacing:.06em; color:var(--ink-soft); white-space:nowrap;}
.hlc-body{display:flex; flex-direction:column; justify-content:center; gap:12px; min-height:0; padding:14px 0; overflow:hidden;}
.hlc-moment{flex:1 1 auto; min-height:0; margin:0; display:flex; align-items:center; overflow:hidden;}
/* El momento es la cita importada del libro: palabras del autor, no de Laura.
   Va en Source Serif 4 redonda, como el resto de citas. */
.hlc-moment > span{display:block; width:100%; font-family:var(--font-quote);
  line-height:1.32; color:var(--ink); text-wrap:balance;}
.hl-rule{flex:0 0 auto; width:34px; height:1px; background:var(--line-strong); margin:0;}   /* moment ↔ thought */
/* the thought holds a steady reading size and clamps, so the moment's zone is predictable */
/* El pensamiento SÍ es de Laura: va manuscrito, sin cursiva (Caveat no tiene
   itálica real y el navegador la falsearía inclinando el trazo). Sube de 13px a
   17px porque el ojo medio de Caveat es ~0.7× el de Mulish; como además es mucho
   más estrecha, las 3 líneas del clamp siguen cabiendo en la tarjeta. */
.hl-note{flex:0 0 auto; font-family:var(--font-hand); font-weight:var(--hand-weight); font-size:1.0625rem;
  color:var(--ink-soft); font-style:normal; line-height:1.45; margin:0;
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; overflow:hidden;}
/* the book takes the full width; the actions get their own row beneath it, so nothing
   competes with the title for space */
.hl-card .jr-foot{display:flex; flex-direction:column; align-items:stretch; gap:8px;}
.hl-card .jr-foot-acts{justify-content:flex-end; margin-right:-6px;}   /* optical: pull the icons flush to the edge */
/* the book's foot reads over three rows: title / author / page + mark (right-aligned).
   The mark is pushed right by margin, not space-between, so it holds the edge on
   cards with no page number. */
.hlc-bylinerow{display:flex; align-items:baseline; gap:10px; margin-top:2px;}
.hlc-page{text-transform:uppercase; letter-spacing:.05em; font-size:0.6875rem; font-weight:600; color:var(--ink-soft);}
/* the feeling closes the body on its own line, under the thought, right-aligned */
.hl-feel{align-self:flex-end; flex:0 0 auto; font-size:1.25rem; line-height:1;}
/* the maker's mark — always English, it's the brand rather than copy, so it isn't localized */
.hlc-mark{margin-left:auto; flex-shrink:0; white-space:nowrap; font-size:0.5625rem; font-weight:700;
  letter-spacing:.11em; text-transform:uppercase; color:var(--ink-faint);}
.hlc-mark b{font-family:var(--font-display); font-size:0.6875rem; font-weight:700; letter-spacing:.02em;}
.hl-card .jr-foot-title{font-family:var(--font-display); font-style:italic; font-weight:400; font-size:1.0625rem;
  white-space:normal; line-height:1.25;
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden;}   /* keep the foot a fixed height */
.hl-card .jr-foot-author{text-transform:uppercase; letter-spacing:.05em; font-size:0.6875rem; font-weight:600;
  white-space:normal; line-height:1.35; margin-top:3px; min-width:0;}
/* Each tone paints the card and re-binds the tokens the shared card rules read.
   Gradients are the mockup's own. Every tone states its full token set: a tone
   is a fixed surface, so it must not inherit ink from the app theme (a daylight
   card under the dark theme would otherwise draw cream text on cream). */
.tone-daylight .tone-swatch, .hl-card.tone-daylight{background:var(--tone-daylight);}
.tone-dusk .tone-swatch, .hl-card.tone-dusk{background:var(--tone-dusk);}
.tone-lamplight .tone-swatch, .hl-card.tone-lamplight{background:var(--tone-lamplight);}
/* El acento (--rose-deep) lo consumen .hlc-tone (la etiqueta eyebrow) y .jrq-star.on. */
.hl-card.tone-daylight{
  --ink:#2C1E2C; --ink-soft:#6E5A6A; --ink-faint:#9A8794;
  --line:#ECDCD8; --line-strong:#DFC9C4; --bg:#FBF3F1;
  --rose-deep:var(--tone-daylight-accent);
}
.hl-card.tone-lamplight{
  --line:rgba(255,255,255,.24); --line-strong:rgba(255,255,255,.42);
  --bg:rgba(255,255,255,.14); border-color:transparent;
  --ink:#FBEFF2; --ink-soft:rgba(241,228,233,.74); --ink-faint:rgba(241,228,233,.5);
  --rose-deep:var(--tone-lamplight-accent); --hand-weight:600;
}
/* dusk ya no puede asumir texto blanco: en 5 de 6 temas su fondo es claro y el texto
   oscuro, así que bordes y matices se derivan del propio texto manteniendo las mismas
   proporciones de opacidad que tenían con el blanco. */
.hl-card.tone-dusk{
  --ink:var(--tone-dusk-ink);
  --ink-soft:color-mix(in srgb, var(--tone-dusk-ink) 80%, transparent);
  --ink-faint:color-mix(in srgb, var(--tone-dusk-ink) 56%, transparent);
  --line:color-mix(in srgb, var(--tone-dusk-ink) 24%, transparent);
  --line-strong:color-mix(in srgb, var(--tone-dusk-ink) 42%, transparent);
  --bg:color-mix(in srgb, var(--tone-dusk-ink) 14%, transparent);
  --rose-deep:var(--tone-dusk-accent); --hand-weight:600; border-color:transparent;
}
/* tone picker (Add / Edit Highlight) */
.tone-row{display:flex; gap:8px;}
.tone{flex:1 1 0; min-width:0; border:1px solid var(--line); border-radius:12px; padding:0; background:none; cursor:pointer; overflow:hidden; transition:.12s;}
.tone .tone-swatch{display:block; height:32px;}
.tone .tone-name{display:block; font-size:0.625rem; font-weight:700; letter-spacing:.05em; text-transform:uppercase;
  color:var(--ink-soft); padding:7px 6px; line-height:1.3;}
.tone:hover{border-color:var(--ink-soft);}
.tone.on{border-color:var(--rose-deep-on-tint, var(--rose-deep)); box-shadow:0 0 0 2px var(--rose-tint);}
.tone.on .tone-name{color:var(--ink);}
.gb-results{margin-top:8px; border:1px solid var(--line); border-radius:12px; background:var(--surface); box-shadow:var(--card-shadow); max-height:300px; overflow-y:auto; overflow-x:hidden;}
.gb-results[hidden]{display:none;}
.gb-status{padding:14px 16px; font-size:0.78125rem; color:var(--ink-soft);}
.gb-item{display:flex; gap:12px; align-items:flex-start; width:100%; text-align:left; border:none; background:none; padding:10px 12px; border-bottom:1px solid var(--line); cursor:pointer; transition:.12s;}
.gb-item:last-child{border-bottom:none;}
.gb-item:hover{background:var(--bg);}
.gb-item.sel{background:var(--rose-tint);}
.cal-results{margin-top:8px; border:1px solid var(--line); border-radius:12px; max-height:220px; overflow-y:auto;}
/* Cuando dentro solo hay un aviso, el carril se aparta: el .note-box ya trae borde y margen, y
   dos bordes concentricos se leen como una caja rota. */
.cal-results.bare{border:none; margin-top:0; overflow:visible;}
.gb-thumb{width:40px; min-width:40px; height:60px; border-radius:5px; background-size:cover; background-position:center; background-color:var(--bg); border:1px solid var(--line); display:flex; align-items:center; justify-content:center; font-size:0.5rem; color:var(--ink-faint);}
.gb-info{flex:1; min-width:0;}
.gb-it-title{font-size:0.8125rem; font-weight:600; color:var(--ink); line-height:1.25;}
.gb-it-author{font-size:0.71875rem; color:var(--ink-soft); margin-top:2px;}
.gb-it-meta{font-size:0.6875rem; color:var(--ink-faint); margin-top:3px;}

/* import-from-Goodreads modal */
.imp-drop{display:flex; align-items:center; justify-content:center; gap:9px; border:1.5px dashed var(--line-strong); border-radius:12px; padding:18px; font-size:0.8125rem; color:var(--ink-soft); cursor:pointer; transition:.15s; text-transform:none; letter-spacing:0; font-weight:500;}
.imp-drop:hover{border-color:var(--rose-deep); color:var(--ink); background:var(--bg);}
.imp-drop .icon{width:17px; height:17px;}
.imp-drop.over{border-color:var(--rose-deep-on-tint, var(--rose-deep)); background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep));}   /* dragging a file over it */
/* Feels import: a stacked drop card — Drag & Drop / types / (space) / — or — / (space) / browse.
   The selector must out-specify `.field label{display:block}`, or the card falls back to inline
   flow and the rows collapse. */
.field label.hl-dropzone{display:flex; flex-direction:column; align-items:center; gap:8px; text-align:center;
  border:1.5px dashed var(--line-strong); border-radius:16px; padding:26px 20px; cursor:pointer; transition:.15s;}
.hl-dropzone:hover{border-color:var(--rose-deep); background:var(--bg);}
.hl-dropzone.over{border-color:var(--rose-deep-on-tint, var(--rose-deep)); background:var(--rose-tint);}
.dz-title{font-family:var(--font-display); font-size:1.375rem; color:var(--ink); line-height:1;
  text-transform:none; letter-spacing:0;}   /* the label's uppercase inherits in — keep the title title-case */
.dz-types{font-size:0.6875rem; font-weight:700; letter-spacing:.09em; text-transform:uppercase; color:var(--ink-soft);}
.dz-or{display:flex; align-items:center; gap:9px; font-size:0.5625rem; font-weight:700; letter-spacing:.14em;
  text-transform:uppercase; color:var(--ink-faint); margin:18px 0;}   /* the blank rows above and below */
.dz-or::before, .dz-or::after{content:"—"; color:var(--line-strong);}
.dz-browse{border:1px solid var(--line-strong); border-radius:999px; padding:8px 20px; font-size:0.6875rem;
  font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--ink); background:var(--surface); transition:.12s;}
.hl-dropzone:hover .dz-browse{border-color:var(--ink);}
.hl-dropzone.over .dz-browse{border-color:var(--rose-deep); color:var(--rose-deep);}
/* "1 / 5" beside the title while walking the imported moments */
.hlw-count{margin-left:10px; font-size:0.6875rem; font-weight:700; letter-spacing:.08em; color:var(--ink-faint);}
.imp-hint{margin:8px 2px 0; font-size:0.6875rem; line-height:1.45; color:var(--ink-faint); text-align:center;}
.imp-or{display:flex; align-items:center; gap:10px; margin:12px 0; font-size:0.6875rem; color:var(--ink-faint);
  text-transform:uppercase; letter-spacing:.08em;}
.imp-or::before, .imp-or::after{content:""; flex:1; height:1px; background:var(--line);}
.imp-summary{font-size:0.8125rem; font-weight:600; color:var(--ink); margin:18px 0 10px;}
/* .imp-note: forma y color en .note-box; aqui solo su icono */
.imp-note .icon{width:14px; height:14px; flex-shrink:0;}
.imp-skip{display:flex; align-items:center; gap:9px; font-size:0.78125rem; color:var(--ink-soft); margin-bottom:12px; cursor:pointer;}
.imp-skip input{width:16px; height:16px; accent-color:var(--rose-deep); cursor:pointer;}
.imp-table{max-height:300px; overflow-y:auto; border:1px solid var(--line); border-radius:12px;}
.imp-table table{margin:0;}
.imp-table th{position:sticky; top:0; background:var(--surface); z-index:1;}
.imp-table .stars-row{font-size:0.75rem;}
tr.imp-dup{opacity:.5;}
.imp-badge{display:inline-block; font-size:0.625rem; font-weight:600; padding:2px 8px; border-radius:12px; white-space:nowrap;}
.imp-badge.new{background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep));}
.imp-badge.dup{background:var(--bg); color:var(--ink-faint); border:1px solid var(--line);}

/* multi-select dropdown (tropes / content warnings) */
.ms{position:relative;}
.ms-control{display:flex; flex-wrap:wrap; gap:6px; align-items:center; min-height:42px; border:1px solid var(--line-strong); border-radius:12px; padding:8px 34px 8px 12px; background:var(--surface); cursor:pointer; position:relative;}
.ms-control:hover{border-color:var(--ink);}
.ms-placeholder{color:var(--ink-faint); font-size:0.78125rem;}
.ms-caret{position:absolute; right:13px; top:50%; transform:translateY(-50%); font-size:0.6875rem; color:var(--ink-soft); pointer-events:none;}
.ms-chip{display:inline-flex; align-items:center; gap:6px; background:var(--rose-deep); color:var(--on-rose-deep-ink); border-radius:16px; padding:3px 5px 3px 10px; font-size:0.71875rem; font-weight:600; line-height:1.4;}
.ms-chip.cw{background:#9a4a3b;}
.ms-chip button{background:none; border:none; color:inherit; font-size:0.875rem; line-height:1; cursor:pointer; opacity:.8; padding:0 1px;}
.ms-chip button:hover{opacity:1;}
.ms-panel{position:absolute; left:0; right:0; top:calc(100% + 6px); z-index:30; background:var(--surface); border:1px solid var(--line); border-radius:12px; box-shadow:0 16px 40px rgba(28,26,23,.20); padding:6px; max-height:230px; overflow-y:auto;}
.ms-panel[hidden]{display:none;}
.ms-opt{display:flex; align-items:center; gap:10px; padding:8px 10px; border-radius:8px; font-size:0.78125rem; color:var(--ink); cursor:pointer;}
.ms-opt:hover{background:var(--bg);}
.ms-opt.sel{font-weight:600;}
.ms-check{width:17px; height:17px; border-radius:5px; border:1.5px solid var(--line-strong); flex-shrink:0; display:flex; align-items:center; justify-content:center; font-size:0.6875rem; color:#fff;}
.ms-opt.sel .ms-check{background:var(--rose-deep); border-color:var(--rose-deep); color:var(--on-rose-deep-ink);}
.ms.cw .ms-opt.sel .ms-check{background:#9a4a3b; border-color:#9a4a3b;}
.ms-add{padding:6px 4px 2px;}
.ms-add input{width:100%; border:1px solid var(--line-strong); border-radius:9px; padding:8px 11px; font-size:0.75rem; background:var(--surface); color:var(--ink);}
.ms-add input:focus{outline:none; border-color:var(--rose-deep);}

/* ===== library grid/table ===== */
.book-grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(130px,1fr)); gap:18px;}
.book-grid > .note-box{grid-column:1/-1;}   /* el aviso ocupa la fila entera, no una celda */
.book-card{cursor:pointer; text-align:left;}
.cover{
  width:100%; aspect-ratio:2/3; border:1px solid var(--line); border-radius:8px; background:var(--bg);
  background-size:cover; background-position:center; position:relative;
  display:flex; align-items:center; justify-content:center; font-size:0.6875rem; color:var(--ink-faint); padding:6px; text-align:center; overflow:hidden;
}
.avail-badge{position:absolute; top:9px; left:9px; max-width:62%; background:var(--rose-deep); color:var(--on-rose-deep-ink); font-size:0.59375rem; font-weight:700; letter-spacing:.4px; text-transform:uppercase; padding:4px 8px; border-radius:7px; box-shadow:0 2px 7px rgba(28,26,23,.28); z-index:1; white-space:nowrap;}

/* ===== settings ===== */
.set-actions{display:flex; gap:10px; flex-wrap:wrap; margin:16px 0 4px;}
.set-danger{display:flex; gap:16px; align-items:center; justify-content:space-between; flex-wrap:wrap; margin-top:22px; padding-top:18px; border-top:1px solid var(--line);}
.set-danger-text{font-size:0.8125rem; color:var(--ink-soft); line-height:1.5; max-width:520px;}
.set-danger-text strong{color:var(--danger-ink); display:block; margin-bottom:2px;}
/* (el aspecto vive ahora en .btn.danger-outline) */
.faq-item{border-bottom:1px solid var(--line); padding:4px 0;}
.faq-item:last-child{border-bottom:none;}
.faq-item summary{cursor:pointer; font-weight:600; font-size:0.875rem; color:var(--ink); padding:12px 4px; list-style:none; display:flex; align-items:center; justify-content:space-between;}
.faq-item summary::-webkit-details-marker{display:none;}
.faq-item summary::after{content:"+"; font-size:1.125rem; color:var(--ink-soft); font-weight:400;}
.faq-item[open] summary::after{content:"–";}
.faq-item p{margin:0 4px 14px; font-size:0.84375rem; color:var(--ink-soft); line-height:1.6;}
.faq-list{margin:0 4px 14px; padding-left:20px; font-size:0.84375rem; color:var(--ink-soft); line-height:1.6;}
.faq-list li{margin:2px 0;}
/* FIX 4: match the Settings button to the language selector (full width + same height).
   The active state only recolors it (see .navbtn.active) — size stays the same. */
.settings-btn{margin-bottom:10px; width:100%; box-sizing:border-box; min-height:38px; padding:9px 12px; line-height:1.35;}

/* ===== series ===== */
.series-list{display:grid; grid-template-columns:repeat(auto-fill,minmax(340px,1fr)); gap:18px;}
.col-list{display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:18px; align-items:start;}
.series-name{font-family:var(--font-display); font-size:1.375rem; font-weight:600; color:var(--ink); line-height:1.12;}
.series-meta{font-size:0.8125rem; color:var(--ink-soft); margin-top:3px;}
.series-card{display:flex; flex-direction:column; padding:24px;}
/* Tu nota sobre la serie: manuscrita. Sube de 0.84rem a 1.125rem para compensar
   el ojo medio pequeño de Caveat, y a --ink porque el gris suave más un trazo
   fino ya no se leía cómodo. */
.series-comment{margin-top:12px; margin-bottom:18px; font-family:var(--font-hand); font-size:1.125rem; font-weight:var(--hand-weight); color:var(--ink); line-height:1.5;}
.ss-head{display:flex; align-items:flex-start; justify-content:space-between; gap:14px; margin-bottom:14px;}
.ss-titlewrap{min-width:0;}
.ss-headright{display:flex; align-items:center; gap:8px; flex-shrink:0;}
.ss-prog-badge{background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep)); font-size:0.8125rem; font-weight:700; border-radius:999px; padding:5px 13px; white-space:nowrap;}
/* Editar la serie. Antes era un ">" suelto: un chevron dice "hay algo mas alla", no "esto se
   toca para cambiarlo", y menos pegado a un contador. Lapiz — el mismo icono y el mismo aspecto
   que .jr-act, que ya es como se edita en el resto de la app — y con contorno, para que se lea
   como un boton y no como un adorno del "1/7" de al lado. */
.ss-edit{display:flex; align-items:center; justify-content:center; width:30px; height:30px; flex-shrink:0;
  border:1px solid var(--line-strong); background:var(--surface); border-radius:8px;
  color:var(--ink-soft); cursor:pointer; transition:.12s;}
.ss-edit:hover{color:var(--ink); border-color:var(--ink);}
.ss-edit .icon{width:16px; height:16px;}
.ss-bar{height:9px; border-radius:999px; background:var(--rose-tint); overflow:hidden; margin-bottom:12px;}
.ss-bar span{display:block; height:100%; border-radius:999px; background:var(--rose-deep); transition:width .4s ease, background-color .4s ease;}
.ss-toread{display:inline-block; background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep)); font-size:0.8125rem; font-weight:600; border-radius:999px; padding:6px 14px; margin-bottom:14px;}
.ss-now{display:flex; align-items:flex-start; gap:8px; font-size:0.875rem; color:var(--ink-soft); line-height:1.4; margin-bottom:14px;}
.ss-now .icon{width:18px; height:18px; flex-shrink:0; color:var(--rose-deep); margin-top:1px;}
.ss-now strong{color:var(--ink); font-weight:600;}
.ss-grid{display:flex; flex-wrap:wrap; align-content:flex-start; gap:11px; margin-bottom:18px;}
.ss-slot{flex:0 0 calc((100% - 44px) / 5); align-self:flex-start; box-sizing:border-box; aspect-ratio:2 / 3; position:relative; border-radius:9px; background:var(--bg); background-size:cover; background-position:center; border:1px solid var(--line);}
.ss-slot.ss-empty{border:2px dashed var(--line-strong); background:var(--bg);}
.ss-slot-title{position:absolute; inset:0; display:flex; align-items:center; justify-content:center; text-align:center; padding:6px; font-size:0.5625rem; color:var(--ink-faint); overflow:hidden;}
.ss-badge{position:absolute; top:-6px; left:-6px; z-index:1; background:var(--ink); color:var(--surface); font-size:0.6875rem; font-weight:700; border-radius:6px; min-width:20px; height:20px; padding:0 5px; display:flex; align-items:center; justify-content:center; box-shadow:0 1px 3px rgba(0,0,0,.25);}
/* Numero sin poner: el badge sigue ahi — el hueco diria menos que el interrogante— pero apagado,
   para que se lea como "falta esto" y no como un numero mas. */
.ss-badge.unset{background:var(--ink-faint); color:var(--surface); font-weight:600;}
/* El campo del numero dentro del menu de la casilla. */
.ss-menu-num{display:flex; align-items:center; gap:8px; padding:8px 10px 9px; border-bottom:1px solid var(--line);}
.ss-menu-num label{font-size:0.71875rem; color:var(--ink-soft); white-space:nowrap;}
.ss-menu-num input{width:64px; margin-left:auto; border:1px solid var(--line-strong); border-radius:8px;
  padding:5px 8px; font-family:inherit; font-size:0.8125rem; background:var(--surface); color:var(--ink);}
.ss-menu-num input:focus{outline:2px solid var(--rose); outline-offset:1px;}
.ss-check{position:absolute; bottom:5px; right:5px; z-index:1; background:var(--success); color:#fff; border-radius:999px; width:22px; height:22px; display:flex; align-items:center; justify-content:center; box-shadow:0 1px 4px rgba(0,0,0,.3); border:2px solid var(--surface);}
.ss-check .icon{width:13px; height:13px; stroke-width:3;}
/* highlight del siguiente libro a leer en la serie */
.ss-slot.ss-next{border:2px solid var(--rose-deep); box-shadow:0 0 0 3px var(--rose-tint);}
.ss-next-badge{position:absolute; top:-9px; left:50%; transform:translateX(-50%); z-index:2; background:var(--rose-deep); color:var(--on-rose-deep-ink); font-size:0.56rem; font-weight:700; letter-spacing:.02em; padding:2px 7px; border-radius:999px; white-space:nowrap; box-shadow:0 1px 4px rgba(0,0,0,.25);}
.ss-num{position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:0.9375rem; font-weight:700; color:var(--ink-soft);}
.ss-slot.filled[data-ss-book]{cursor:pointer;}
.ss-slot.filled[data-ss-book]:hover{box-shadow:0 0 0 2px var(--rose);}
.ss-slot.filled[data-ss-book].ss-next:hover{box-shadow:0 0 0 3px var(--rose-tint);}
/* mini-menú flotante al hacer clic en un libro de la serie (mismo lenguaje visual que .cover-menu) */
.ss-menu{position:fixed; z-index:60; background:var(--surface); border:1px solid var(--line); border-radius:10px; box-shadow:0 6px 20px rgba(28,26,23,.25); overflow:hidden; min-width:150px; display:flex; flex-direction:column;}
.ss-menu-item{background:none; border:none; text-align:left; padding:10px 14px; font-size:0.9rem; color:var(--ink); cursor:pointer; width:100%; font-family:inherit;}
.ss-menu-item:hover{background:var(--bg);}
.ss-menu-item.danger{color:var(--danger); border-top:1px solid var(--line);}
.ss-rating{margin-top:14px; margin-bottom:18px;}
/* Borde CONTINUO de 1px, como el resto de la app. El de puntos es el idioma de los HUECOS de la
   rejilla (.ss-slot.ss-empty: un puesto que aun no tiene libro), y un boton no es un hueco: al
   llevar el mismo trazo parecia una casilla vacia mas. El relleno ya lo delimita, asi que el borde
   puede ser fino. */
.ss-addnext{margin-top:auto; width:100%; display:flex; align-items:center; justify-content:center; gap:9px; padding:14px; border:1px solid var(--rose-deep); background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep)); border-radius:12px; font-size:0.90625rem; font-weight:600; cursor:pointer; transition:.15s; font-family:inherit;}
.ss-addnext:hover{background:var(--rose); color:var(--on-rose-ink, var(--ink)); border-color:var(--rose);}
.ss-addnext .icon{width:18px; height:18px;}
/* ===== collections ===== */
.col-card{padding:0; overflow:hidden;}
.col-banner{display:flex; height:172px; background:var(--bg); cursor:pointer; position:relative;}
.col-chev{position:absolute; bottom:10px; right:10px; width:30px; height:30px; border-radius:999px; background:rgba(28,26,23,.55); color:#fff; display:flex; align-items:center; justify-content:center; transition:transform .2s; pointer-events:none;}
.col-chev .icon{width:18px; height:18px; transform:rotate(90deg); transition:transform .2s;}
.col-card.open .col-chev .icon{transform:rotate(-90deg);}
.col-count-head{font-size:0.8125rem; font-weight:600; color:var(--ink-soft); padding:12px 18px 0; border-top:1px solid var(--line);}
.col-count-head + .col-expand{border-top:none; padding-top:10px;}
.col-desc{font-size:0.75rem; color:var(--ink-soft); margin-top:2px; line-height:1.35;}
.col-expand{display:grid; grid-template-columns:repeat(auto-fill,minmax(86px,1fr)); gap:16px; padding:4px 18px 20px; border-top:1px solid var(--line);}
.col-exp-book{cursor:pointer;}
.col-exp-book .cover{width:100%; aspect-ratio:2/3; font-size:0.5625rem; transition:.15s;}
.col-exp-book:hover .cover{transform:translateY(-3px); box-shadow:0 6px 16px rgba(28,26,23,.18);}
.col-exp-title{font-size:0.71875rem; font-weight:600; color:var(--ink); margin-top:7px; line-height:1.25; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;}
.col-exp-author{font-size:0.65625rem; color:var(--ink-soft); margin-top:2px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.col-banner-cover{flex:1; min-width:0; background-size:cover; background-position:center; background-color:var(--line); border-right:1px solid rgba(255,255,255,.35); display:flex; align-items:center; justify-content:center; overflow:hidden;}
.col-banner-cover:last-child{border-right:none;}
.col-banner-cover span{font-size:0.5625rem; color:var(--ink-soft); text-align:center; padding:4px; line-height:1.2;}
.col-banner-empty{flex:1; display:flex; align-items:center; justify-content:center; color:var(--ink-faint); font-size:0.8125rem;}
.col-foot{display:flex; align-items:center; justify-content:space-between; gap:12px; padding:16px 18px;}
.col-foot-text{min-width:0;}
.col-foot-acts{display:flex; gap:8px; flex-shrink:0;}

/* ordered/edit lists in modals */
.ord-list{display:flex; flex-direction:column; gap:7px; margin-bottom:10px;}
.ord-item{display:flex; align-items:center; gap:10px; border:1px solid var(--line); border-radius:9px; padding:6px 9px;}
/* El numero de la lista es EDITABLE: es el mismo dato que la ficha del libro y que el badge de
   la rejilla, asi que se escribe donde se lee. Mas ancho que los 16px de antes porque ahora hay
   que poder meter el cursor y un numero de dos cifras. */
/* El numero de la lista NO se escribe: sale del puesto. Es una etiqueta, no un campo — se
   reordena con las flechas y los numeros se recolocan solos. Va en una capsula para que se lea
   como un dato del libro y no como texto suelto. */
.ord-item .ord-num{font-family:var(--font-display); font-weight:600; color:var(--ink-soft);
  flex:0 0 34px; text-align:center; font-size:0.8125rem; background:var(--bg);
  border-radius:7px; padding:4px 0; user-select:none;}
.ord-title{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:0.8125rem;}
.ord-btn{width:26px; height:26px; border:1px solid var(--line-strong); background:var(--surface); border-radius:7px; color:var(--ink-soft); cursor:pointer; flex-shrink:0;}
.ord-btn:hover:not(:disabled){border-color:var(--ink); color:var(--ink);}
.ord-btn:disabled{opacity:.3; cursor:default;}
/* En el telefono la fila se queda sin sitio para el TITULO, que es lo unico que dice de que libro
   hablamos: con el campo del numero (44px), la miniatura (30), tres botones (78) y cinco huecos
   (50) sobre 275px de fila, al titulo le quedaban 53px — cinco letras y puntos suspensivos.
   La miniatura es lo prescindible: bonita, pero el titulo ya identifica el libro y la portada se
   ve en la rejilla de la serie a un dedo de distancia. Fuera ella y huecos mas estrechos, el
   titulo pasa de 53 a ~100px. */
@media(max-width:600px){
  .ord-item{gap:7px;}
  .ord-item .mini-cover{display:none;}
}
.ord-rm:hover{background:var(--danger); color:#fff; border-color:var(--danger);}

/* ===== journal: quotes & reviews ===== */
.jr-sec-head{display:flex; align-items:center; justify-content:flex-end; gap:12px; margin-top:6px; margin-bottom:20px;}
.jrq-star{border:none; background:none; cursor:pointer; color:var(--line-strong); padding:2px; transition:.12s; line-height:0;}
.jrq-star .star-svg{width:24px; height:24px;}
.jrq-star.on{color:var(--rose-deep);}
.jrq-star:hover{transform:scale(1.12);}
.jr-divider{height:1px; background:var(--line); margin:18px 0 14px;}
.jr-foot{display:flex; align-items:center; gap:13px;}
.jr-foot .mini-cover{width:42px; height:62px;}
.jr-foot-text{flex:1; min-width:0;}
.jr-foot-title{font-weight:700; font-size:0.96875rem; color:var(--ink); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.jr-foot-author{font-size:0.84375rem; color:var(--ink-soft); margin-top:2px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.jr-foot-acts{display:flex; gap:6px; flex-shrink:0;}
.jr-act{width:34px; height:34px; border:none; background:none; border-radius:8px; color:var(--ink-soft); cursor:pointer; display:flex; align-items:center; justify-content:center; transition:.12s;}
.jr-act:hover{background:var(--bg); color:var(--ink);}
.jr-act.copied{color:var(--success-ink);}
.jr-act.busy{opacity:.45; cursor:progress;}
.sf-toast{position:fixed; left:50%; bottom:28px; transform:translateX(-50%) translateY(12px); z-index:9999;
  background:var(--ink); color:var(--surface); padding:10px 18px; border-radius:999px;
  font-size:0.85rem; font-weight:600; letter-spacing:.2px; text-align:center; max-width:88vw;
  box-shadow:0 8px 28px rgba(0,0,0,.24); opacity:0; pointer-events:none;
  transition:opacity .22s ease, transform .22s ease;}
.sf-toast.show{opacity:1; transform:translateX(-50%) translateY(0);}
@media (prefers-reduced-motion: reduce){ .sf-toast{transition:opacity .18s ease;} }
.jr-act .icon{width:18px; height:18px;}
.jr-selbook{margin:8px 0;}

/* rich-text editor */
.rt-toolbar{display:flex; align-items:center; gap:6px; flex-wrap:wrap; margin-bottom:8px;}
.rt-btn{min-width:34px; height:32px; padding:0 9px; border:1px solid var(--line-strong); background:var(--surface); border-radius:8px; color:var(--ink); cursor:pointer; font-size:0.8125rem;}
.rt-btn:hover{border-color:var(--ink);}
.rt-sep{width:1px; height:22px; background:var(--line); margin:0 3px;}
.rt-hl{width:26px; height:26px; border:1px solid var(--line-strong); border-radius:7px; cursor:pointer;}
.rt-hl:hover{transform:scale(1.08);}
.rt-nohl{font-size:0.6875rem; color:var(--ink-soft);}
/* "no highlight" swatch: white chip crossed by a diagonal line, no text */
.rt-hl-none{background:
  linear-gradient(to top right, transparent calc(50% - 1.2px), #cf5b63 calc(50% - 1.2px), #cf5b63 calc(50% + 1.2px), transparent calc(50% + 1.2px)),
  #fff;}
.rt-editor{border:1px solid var(--line-strong); border-radius:12px; padding:12px 14px; min-height:140px; font-size:0.875rem; line-height:1.65; color:var(--ink); outline:none; background:var(--surface);}
.rt-editor:focus{border-color:var(--ink);}
.cover-act{width:27px; height:27px; border-radius:8px; border:1px solid var(--line); background:var(--surface); color:var(--ink); display:flex; align-items:center; justify-content:center; box-shadow:0 2px 7px rgba(28,26,23,.22); cursor:pointer; opacity:.92; transition:transform .12s, background .12s, opacity .12s;}
.book-card:hover .cover-act, .release-card:hover .cover-act{opacity:1;}
.cover-act .icon{width:14px; height:14px; stroke-width:1.8;}
.cover-act:hover{transform:scale(1.1); background:var(--bg); border-color:var(--ink);}
.cover-act.danger:hover{background:var(--danger); color:#fff;}
.cover-act.active{background:var(--rose); color:#fff; border-color:var(--rose); opacity:1;}
.cover-act.active:hover{background:var(--rose-deep); border-color:var(--rose-deep); color:var(--on-rose-deep-ink);}
/* Stories (desktop): overlay frost centrado con editar/eliminar al hacer hover */
.cover-hover{position:absolute; inset:0; z-index:2; display:flex; align-items:center; justify-content:center; gap:12px;
  background:rgba(15,20,28,.22); backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
  border-radius:inherit; opacity:0; pointer-events:none; transition:opacity .18s ease;}
.book-card:hover .cover-hover, .release-card:hover .cover-hover{opacity:1;}
.cover-hover .cover-act{width:42px; height:42px; pointer-events:auto; opacity:1;}
.cover-hover .cover-act .icon{width:18px; height:18px;}
/* TBR: 4 acciones en 2×2, más pequeñas y con aire respecto al borde de la portada */
.cover-hover-4{display:grid; grid-template-columns:repeat(2, auto); gap:9px; place-content:center; padding:16px;}
.cover-hover-4 .cover-act{width:33px; height:33px;}
.cover-hover-4 .cover-act .icon{width:15px; height:15px;}
/* Settings: separación entre las cards */
.settings-view{display:flex; flex-direction:column; gap:24px;}
/* lista de Stories: editar + basura juntos */
.lib-row-actions{display:inline-flex; gap:6px; align-items:center;}
.lib-del{color:var(--danger); padding-left:9px; padding-right:9px;}
.lib-del .icon{width:15px; height:15px;}
.lib-del:hover{background:var(--danger); color:#fff; border-color:var(--danger);}
/* FIX 3: campo de portada colapsable (URL + examinar ocultos cuando ya hay portada) */
.cover-field-row{display:flex; gap:12px; align-items:flex-start; margin-top:8px;}
.cover-controls{display:flex; flex-direction:column; gap:8px; flex:1; min-width:0;}
.cover-controls[hidden]{display:none;}
.cover-change-link{background:none; border:none; padding:0; color:var(--rose-deep); font-size:0.84rem; font-weight:600; cursor:pointer; align-self:center;}
.cover-change-link:hover{text-decoration:underline;}
.cover-change-link[hidden]{display:none;}
/* FIX 2: botón "⋮" + mini menú Editar/Eliminar (solo móvil) */
.cover-menu-btn{display:none; position:absolute; top:7px; right:7px; z-index:3; width:27px; height:27px; border-radius:8px; border:1px solid var(--line); background:var(--surface); color:var(--ink); align-items:center; justify-content:center; font-size:1.1rem; line-height:1; box-shadow:0 2px 7px rgba(28,26,23,.22); cursor:pointer;}
.cover-menu{position:absolute; top:38px; right:7px; z-index:4; background:var(--surface); border:1px solid var(--line); border-radius:10px; box-shadow:0 6px 20px rgba(28,26,23,.25); overflow:hidden; min-width:120px; flex-direction:column;}
.cover-menu[hidden]{display:none;}
.cover-menu-item{background:none; border:none; text-align:left; padding:10px 14px; font-size:0.9rem; color:var(--ink); cursor:pointer; width:100%;}
.cover-menu-item:hover{background:var(--bg);}
.cover-menu-item.danger{color:var(--danger); border-top:1px solid var(--line);}
/* La fila de "ya salio" del modal de un lanzamiento: separada del par Cancelar/Guardar por una
   linea, porque no es una variante de guardar sino la salida del titulo hacia la biblioteca. */
.rel-outnow{display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  margin:16px 0 4px; padding-top:14px; border-top:1px solid var(--line);}
.rel-outnow-lbl{font-size:0.71875rem; font-weight:700; letter-spacing:.4px; text-transform:uppercase;
  color:var(--rose-deep); margin-right:auto;}
@media (max-width:768px){
  .cover-card .cover-hover{display:none;}   /* en móvil, las dos acciones viven en el menú "⋮" */
  .cover-card .cover-menu-btn{display:flex;}
  .cover-menu:not([hidden]){display:flex;}
}
/* ============================================================
   STORY MAPS — list cards + node-graph canvas editor
============================================================ */
.smap-grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(min(240px,100%),1fr)); gap:16px; margin-top:4px;}
.smap-card{position:relative; background:var(--card-glass); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--card-shadow); padding:16px 16px 14px; cursor:pointer; transition:transform .12s, box-shadow .12s, border-color .12s;}
.smap-card:hover{transform:translateY(-2px); border-color:var(--rose); box-shadow:0 10px 26px rgba(28,26,23,.12);}
.smap-card-top{display:flex; align-items:center; gap:8px; color:var(--rose-deep);}
.smap-card-icon .icon{width:18px; height:18px;}
.smap-card-type{font-size:0.72rem; font-weight:700; letter-spacing:.05em; text-transform:uppercase;}
/* share · edit · delete are chrome, not part of the card's content.
   Touch: a plain row in the card's top-right. Mouse: they float in front of the card on hover
   behind a frosted-glass overlay — the same treatment as the Stories covers (.cover-hover) and
   the Feels cards (.hl-acts), so all three surfaces behave identically. */
.smap-card-acts{display:flex; gap:2px; margin-left:auto;}
.smap-card-acts .jr-act{width:28px; height:28px; color:var(--ink-faint);}
.smap-card-acts .jr-act:hover{color:var(--ink); background:var(--bg);}
.smap-card-acts .jr-act[data-del-map]:hover{color:var(--danger);}
@media (hover:hover) and (pointer:fine){
  .smap-card-acts{position:absolute; inset:0; z-index:3; margin:0; justify-content:center; align-items:center; gap:12px;
    background:rgba(15,20,28,.22); backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
    border-radius:var(--radius); opacity:0; pointer-events:none; transition:opacity .18s ease;}
  .smap-card:hover .smap-card-acts, .smap-card:focus-within .smap-card-acts{opacity:1;}
  .smap-card-acts .jr-act{width:42px; height:42px; border-radius:12px; border:1px solid var(--line);
    background:var(--surface); color:var(--ink); box-shadow:0 2px 7px rgba(28,26,23,.22);
    pointer-events:auto; opacity:1; transition:transform .12s, background .12s, border-color .12s, color .12s;}
  .smap-card-acts .jr-act:hover{transform:scale(1.1); background:var(--bg); border-color:var(--ink);}
  .smap-card-acts .jr-act[data-del-map]:hover{background:var(--danger); color:#fff; border-color:var(--danger);}
  .smap-card-acts .jr-act .icon{width:18px; height:18px;}
}
.smap-card-title{font-family:var(--font-display); font-size:1.15rem; margin:8px 0 4px; color:var(--ink); word-break:break-word;}
.smap-card-meta{font-size:0.78rem; color:var(--ink-soft);}
/* search results: WHICH nodes matched, so a hit is an answer and not just a filtered card */
.smap-card-hits{display:flex; flex-wrap:wrap; align-items:center; gap:5px; margin-top:8px;}
.smap-hits-label{font-size:0.6875rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--ink-faint);}
.smap-hit{max-width:100%; padding:2px 7px; border-radius:999px; background:var(--bg); border:1px solid var(--line);
  font-size:0.72rem; color:var(--ink-soft); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.smap-hit.more{background:transparent; border-color:transparent; color:var(--ink-faint);}
.smap-hit mark{background:#fff3a8; color:inherit; border-radius:3px; padding:0 1px;}

/* editor shell */
.smap-editor{display:flex; flex-direction:column; gap:10px;}
.smap-toolbar{display:flex; align-items:center; gap:12px; flex-wrap:wrap;}
/* floating canvas controls, top-right, in reading order: help · add · fit.
   They live inside .smap-canvas but OUTSIDE .smap-world, so pan/zoom doesn't move them.
   .smap-fab is the shared look; the modifiers only set the horizontal slot (34px + 8px gap). */
.smap-fab{position:absolute; top:12px; z-index:20; width:34px; height:34px; padding:0;
  display:flex; align-items:center; justify-content:center; border:1px solid var(--line); border-radius:9px;
  background:var(--surface); color:var(--ink-soft); cursor:pointer; box-shadow:0 2px 8px rgba(28,26,23,.16); transition:.12s;}
.smap-fab:hover{color:var(--rose-deep); border-color:var(--rose); box-shadow:0 3px 12px rgba(28,26,23,.22);}
.smap-fab .icon{width:18px; height:18px;}
.smap-help-fab{right:96px;}
.smap-fit-fab{right:12px;}
/* the add "+" needs a positioned wrapper so its dropdown can anchor under it; the wrapper takes the
   slot and the button sits statically inside it (overrides .smap-fab's absolute — must come after) */
.smap-add-fab{position:static;}
.smap-add-fab[aria-expanded="true"]{color:var(--rose-deep-on-tint, var(--rose-deep)); border-color:var(--rose); background:var(--rose-tint);}
.smap-ed-type{display:inline-flex; align-items:center; gap:6px; font-size:0.82rem; font-weight:700; color:var(--rose-deep);}
.smap-ed-type .icon{width:16px; height:16px;}
.smap-title-input{flex:1 1 180px; min-width:120px; font-family:var(--font-display); font-size:1.1rem; padding:6px 10px; border:1px solid var(--line); border-radius:10px; background:var(--surface); color:var(--ink);}
.smap-title-input:focus{outline:none; border-color:var(--rose);}
.smap-chip-dot{width:10px; height:10px; border-radius:50%; background:var(--acc,var(--rose)); flex-shrink:0;}

/* action bar (align / distribute / connect) — ALWAYS visible; buttons enable with the selection */
.smap-align-bar{display:flex; align-items:center; gap:8px; flex-wrap:wrap; padding:6px 10px; border:1px solid var(--line); border-radius:12px; background:var(--rose-tint);}
.smap-align-bar[hidden]{display:none;}
.smap-align-count{font-size:0.78rem; font-weight:700; color:var(--rose-deep); white-space:nowrap;}
/* with <2 selected the counter becomes a quiet hint instead of a bold count */
.smap-align-count.muted{font-weight:500; color:var(--ink-faint);}
.smap-align-group{display:inline-flex; gap:3px;}
.smap-align-sep{width:1px; align-self:stretch; background:var(--line); margin:2px 2px;}
.smap-tool{display:inline-flex; align-items:center; justify-content:center; gap:6px; width:30px; height:30px; padding:0; border:1px solid transparent; border-radius:8px; background:var(--surface); color:var(--ink-soft); cursor:pointer; transition:.12s;}
.smap-tool svg{width:18px; height:18px;}
.smap-tool:hover:not(:disabled){color:var(--rose-deep); border-color:var(--rose);}
.smap-tool:disabled{opacity:.35; cursor:not-allowed;}
.smap-tool-wide{width:auto; padding:0 11px; font-size:0.78rem; font-weight:600; color:var(--ink);}
.smap-tool-wide:hover{color:var(--rose-deep);}
/* the one destructive tool in the bar: neutral at rest, red only once it can actually fire */
.smap-tool-danger:hover:not(:disabled){color:#fff; background:var(--danger); border-color:var(--danger);}

/* canvas help modal (the "?" fab) — two columns: gesture, then what it does */
.smap-help{display:flex; flex-direction:column; gap:14px; margin:4px 0 2px;}
.smap-help-sec h4{margin:0 0 5px; font-size:0.76rem; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:var(--rose-deep);}
.smap-help-row{display:grid; grid-template-columns:minmax(96px,34%) 1fr; gap:10px; align-items:baseline; padding:3px 0; border-top:1px solid var(--line);}
.smap-help-sec .smap-help-row:first-of-type{border-top:none;}
.smap-help-key{font-size:0.78rem; font-weight:700; color:var(--ink);}
.smap-help-what{font-size:0.82rem; color:var(--ink-soft); line-height:1.35;}
@media (max-width:520px){ .smap-help-row{grid-template-columns:1fr; gap:1px;} }

/* canvas + world (dot grid) */
/* pan/zoom canvas: no scrollbars — the world is translated+scaled via transform (middle-drag pans, wheel zooms).
   The infinite dot-grid lives on the CANVAS (fills the viewport) and is driven by smapApplyView (size/offset). */
.smap-canvas{position:relative; height:68vh; min-height:420px; overflow:hidden; border:1px solid var(--line); border-radius:14px; cursor:default;
  background-color:var(--bg);
  background-image:radial-gradient(circle, var(--line-strong) 1.3px, transparent 1.3px);
  background-size:26px 26px;}
.smap-canvas.panning{cursor:grabbing;}
/* Touch: the canvas owns its gestures — one finger pans, two pinch — so the browser must not also
   scroll the page or zoom the document from them, and a press must not raise the text callout.
   The Add menu is exempt: pressing a tile there is how you scroll the list. */
.smap-canvas{touch-action:none; -webkit-user-select:none; user-select:none; -webkit-touch-callout:none;}
.smap-add-menu{touch-action:pan-y;}
/* ...but editing a node's text needs the caret, the selection and the magnifier back */
.smap-node-text[contenteditable="true"]{touch-action:auto; -webkit-user-select:text; user-select:text; -webkit-touch-callout:default;}
/* the long-press has armed the rubber band — say so, since nothing else moved yet */
.smap-canvas.marquee{cursor:crosshair;}
.smap-canvas.marquee .smap-node{opacity:.82; transition:opacity .12s;}
.smap-world{position:absolute; top:0; left:0; transform-origin:0 0; background:transparent;}
.smap-edges{position:absolute; inset:0; pointer-events:none; overflow:visible;}
.smap-edge{fill:none; stroke:var(--line-strong); stroke-width:2.4; stroke-linecap:round;}
.smap-edge.selected{stroke:var(--rose); stroke-width:3.4;}   /* selected connector (arrows inherit via context-stroke) */
.smap-edge-temp{stroke:var(--rose); stroke-dasharray:6 6;}
.smap-edge-hit{fill:none; stroke:transparent; stroke-width:16; pointer-events:stroke; cursor:pointer;}
/* Discontinua: el patron va en el CSS y no en el atributo, asi que sigue al grosor del trazo y
   una linea seleccionada (mas gruesa) no se queda con los guiones de la fina. */
.smap-edge.dashed{stroke-dasharray:7 5;}
.smap-edge-sep{width:1px; align-self:stretch; margin:2px 3px; background:var(--line-strong);}
.smap-edge-dash svg, .smap-edge-swap svg{width:15px; height:15px; display:block;}
.smap-edge-label{fill:var(--ink-soft); font-family:var(--font-hand); font-size:15px; text-anchor:middle; paint-order:stroke; stroke:var(--bg); stroke-width:4px; pointer-events:none;}
/* "+" add-node button at a connector's midpoint — revealed with the endpoints on hover */
.smap-edge-add{opacity:0; pointer-events:none; cursor:pointer; transition:opacity .12s;}
.smap-edge-g:hover .smap-edge-add{opacity:1; pointer-events:all;}
.smap-edge-add circle{fill:var(--surface); stroke:var(--rose); stroke-width:1.6;}
.smap-edge-add path{stroke:var(--rose); stroke-width:1.9; stroke-linecap:round;}
.smap-edge-add:hover circle{fill:var(--rose-tint); stroke-width:2.2;}
/* endpoint grab handles: a top overlay (above nodes) so an endpoint is always grabbable to re-route */
/* top overlay sits ABOVE the nodes AND their cardinal handles (z-index:6) so an endpoint wins the
   hit-test; the container passes clicks through, only its hit paths + endpoint circles are live */
.smap-edge-handles{position:absolute; inset:0; pointer-events:none; overflow:visible; z-index:7;}
/* from/to endpoints AND the middle handle are hidden until the connector (its hit path) or one of
   them is hovered — so hovering a bezier reveals all three (from · middle · to) */
.smap-edge-end, .smap-edge-mid{fill:var(--surface); stroke:var(--rose); stroke-width:2; opacity:0; pointer-events:none; cursor:crosshair; transition:opacity .12s, stroke-width .12s;}
.smap-edge-g:hover .smap-edge-end, .smap-edge-g:hover .smap-edge-mid{opacity:1; pointer-events:all;}
.smap-edge-end{cursor:grab;}
.smap-edge-end:hover, .smap-edge-mid:hover{stroke-width:2.8; fill:var(--rose-tint);}
/* the middle handle reads as the descent point: a filled rose dot */
.smap-edge-mid{fill:var(--rose); stroke:var(--rose);}
.smap-edge-mid:hover{fill:var(--rose-deep);}

/* nodes — width fits the text (grows to content, caps at max, then wraps) */
.smap-node{position:absolute; width:max-content; min-width:72px; max-width:240px; box-sizing:border-box; background:var(--surface); border:1px solid var(--line-strong);
  border-left:3px solid var(--acc,var(--rose)); border-radius:12px; padding:10px 14px; box-shadow:0 3px 10px rgba(28,26,23,.12);
  cursor:grab; font-size:0.86rem; color:var(--ink); user-select:none;}
/* ⚑ SIN z-index. Antes el nodo arrastrado saltaba al frente mientras duraba el gesto: con el
   orden de apilado ya en manos del usuario, eso contradice lo que acaba de pedir — mandas una
   imagen al fondo, la mueves un poco y la ves saltar arriba. La sombra ya dice que se esta
   arrastrando; el sitio en la pila no se toca. */
.smap-node.dragging{cursor:grabbing; box-shadow:0 8px 22px rgba(28,26,23,.22);}
.smap-node.selected{box-shadow:0 0 0 2px var(--rose), 0 4px 12px rgba(28,26,23,.18);}
/* floating text-format menu while editing a node (font / colour / size / bold / italic) */
.smap-text-menu{position:absolute; z-index:20; transform:translateY(calc(-100% - 8px)); display:flex; align-items:center; gap:5px; padding:5px 7px; background:var(--surface); border:1px solid var(--line); border-radius:11px; box-shadow:0 8px 24px rgba(28,26,23,.22); white-space:nowrap;}
.smap-text-menu .smap-tm-font{max-width:120px; height:26px; border:1px solid var(--line); border-radius:7px; background:var(--surface); color:var(--ink); font-size:0.8rem; padding:0 4px;}
.smap-text-menu .smap-tm-color{width:26px; height:26px; padding:0; border:1px solid var(--line); border-radius:7px; background:var(--surface); cursor:pointer;}
.smap-text-menu .smap-tm-btn{min-width:28px; height:26px; padding:0 6px; border:1px solid var(--line); background:var(--surface); color:var(--ink); border-radius:7px; font-size:0.85rem; line-height:1; cursor:pointer; transition:.12s;}
.smap-text-menu .smap-tm-btn:hover{border-color:var(--rose); color:var(--rose-deep);}
.smap-text-menu .smap-tm-btn.on{background:var(--rose-tint); border-color:var(--rose); color:var(--rose-deep-on-tint, var(--rose-deep));}
/* junction = the connector's midpoint anchor (a marriage's descent point). It's a pure data anchor:
   fully invisible + non-interactive — the visible/grabbable proxy is the .smap-edge-mid handle in the
   edge overlay (revealed with from/to on hover). Kept 20px so child edges attach near the midpoint. */
.smap-node.smap-junction{width:20px; min-width:0; max-width:none; height:20px; min-height:0; padding:0; border:none; background:none; box-shadow:none; opacity:0; pointer-events:none;}
.smap-node-text{min-height:1.2em; line-height:1.35; word-break:break-word; white-space:pre-wrap; text-align:center;}
.smap-node-text[contenteditable]{outline:none; cursor:text;}
.smap-node-ph{color:var(--ink-faint); font-style:italic;}
.smap-node-del{position:absolute; top:3px; right:3px; width:20px; height:20px; border:none; background:none; color:var(--ink-faint); border-radius:6px; display:none; align-items:center; justify-content:center; cursor:pointer;}
.smap-node-del .icon{width:12px; height:12px;}
.smap-node:hover .smap-node-del{display:flex;}
.smap-node-del:hover{background:var(--danger); color:#fff;}
/* palette / kind accents — the four BASIC shapes each get their own colour so they stay
   tellable apart now that all four appear in every map type. Muted to sit inside the warm
   paper palette rather than compete with it; all four keep contrast on the dark themes too. */
.kind-character,.kind-person{--acc:var(--rose);}   /* .kind-person = pre-migration nodes */
.kind-place{--acc:#7a9464;}                        /* sage — a location */
.kind-event{--acc:#c98a2b;}                        /* amber — something that happens */
.kind-note{--acc:#5b9aa0;}                         /* teal — an aside */
/* structural shapes keep the neutral/rose accents; they read as scaffolding, not content */
.kind-title,.kind-hub,.kind-cloud,.kind-idea{--acc:var(--rose);}
.kind-group,.kind-hier{--acc:#8b6db0;}
.kind-bracket{--acc:#c98a2b;}

/* title box — a heading label, no connectors, centred; size/weight come from its text defaults */
.smap-node.kind-title{border-left:1px solid var(--line-strong); padding:12px 20px; min-width:120px;}
/* hub — a large circle with 12 radial connectors, text centred inside. Neutral GRAY border (not the
   rose accent, which reads blue/cyan in some themes) — the accent lives on the connectors instead. */
.smap-node.kind-hub{width:150px; height:150px; min-width:0; max-width:none; border:2.5px solid var(--ink-faint); border-radius:50%;
  display:flex; align-items:center; justify-content:center; padding:18px; text-align:center;}
.smap-node.kind-hub .smap-node-text{width:100%;}
/* cloud — same radial behaviour as the hub, drawn as a cumulus. The node box itself is bare (no border,
   no background); the silhouette is an inline SVG sized to the box, so the outline stays crisp at any zoom
   and follows the theme. Box ratio matches the 200x130 viewBox so the path isn't distorted. */
.smap-node.kind-cloud{width:186px; height:121px; min-width:0; max-width:none; border:none; background:transparent;
  border-radius:0; box-shadow:none; padding:30px 26px 24px; display:flex; align-items:center; justify-content:center; text-align:center;}
.smap-node.kind-cloud .smap-node-text{width:100%; position:relative; z-index:1;}
.smap-cloud-svg{position:absolute; inset:0; width:100%; height:100%; pointer-events:none; z-index:0;}
.smap-cloud-svg path{fill:var(--surface); stroke:var(--ink-faint); stroke-width:2.5; stroke-linejoin:round;}
.smap-node.kind-cloud.selected .smap-cloud-svg path{stroke:var(--rose); stroke-width:3;}
.smap-node.kind-cloud .smap-node-del{z-index:2;}

/* cardinal connectors — fade in on node hover */
.smap-handle{position:absolute; width:13px; height:13px; border-radius:50%; background:var(--surface); border:2px solid var(--rose); box-shadow:0 1px 3px rgba(28,26,23,.25); opacity:0; transition:opacity .12s, transform .12s; cursor:crosshair; z-index:6;}
.smap-node:hover .smap-handle{opacity:1;}
.smap-handle:hover{transform:scale(1.25);}
.smap-handle.h-t{top:-7px; left:50%; margin-left:-6.5px;}
.smap-handle.h-b{bottom:-7px; left:50%; margin-left:-6.5px;}
.smap-handle.h-l{left:-7px; top:50%; margin-top:-6.5px;}
.smap-handle.h-r{right:-7px; top:50%; margin-top:-6.5px;}
.smap-handle-radial{margin-left:-6.5px; margin-top:-6.5px;}   /* positioned via inline left/top % around the circle */

/* "+" that spawns a child on a hier/bracket node. Shown on node hover; it OVERLAPS the flow-facing edge
   (no dead gap between node and button) and carries a large invisible ::before halo that bridges back into
   the node — so moving the cursor node→"+" never leaves the hover region and the button can't vanish
   mid-travel. z-index:8 keeps it ABOVE the edge-handles overlay (z7) so it stays clickable even once a
   child's connector runs under it (otherwise the click lands on the bezier). */
.smap-child-add{position:absolute; width:22px; height:22px; padding:0; border:none; border-radius:50%; background:var(--rose); color:#fff;
  display:none; align-items:center; justify-content:center; cursor:pointer; box-shadow:0 0 0 3px var(--bg), 0 2px 6px rgba(28,26,23,.28); z-index:8;}
.smap-child-add::before{content:""; position:absolute; inset:-14px;}   /* transparent hover/hit halo bridging to the node */
.smap-child-add .icon{width:13px; height:13px;}
.smap-node:hover .smap-child-add{display:flex;}
.smap-child-add:hover{background:var(--rose-deep); color:var(--on-rose-deep-ink); transform:scale(1.1);}
/* overlap the node edge so there's no gap to cross; the flow-side cardinal handle hides (the "+" owns that spot) */
.smap-child-add.hier{bottom:-15px; left:50%; margin-left:-11px;}
.smap-child-add.bracket-r{right:-15px; top:50%; margin-top:-11px;}
.smap-child-add.bracket-l{left:-15px; top:50%; margin-top:-11px;}   /* bracket grows both ways: a left + and a right + */
.smap-node.kind-hier .smap-handle.h-b{display:none;}
.smap-node.kind-bracket .smap-handle.h-r, .smap-node.kind-bracket .smap-handle.h-l{display:none;}
/* ---- coarse pointers: SELECTION does the job :hover does on a desktop ----------------------
   The connector handles, the delete button and the "+" child buttons are all revealed by :hover,
   which a finger never produces — untouched, they'd simply be unreachable on a phone. Selecting
   (a tap) reveals the same set, and every target grows to something a fingertip can actually land
   on. Kept in one media query so the desktop look is untouched. */
@media (hover:none), (pointer:coarse){
  .smap-node.selected .smap-handle{opacity:1;}
  .smap-node.selected .smap-node-del{display:flex;}
  .smap-node.selected .smap-child-add{display:flex;}
  .smap-edge-g.selected .smap-edge-end, .smap-edge-g.selected .smap-edge-mid{opacity:1; pointer-events:all;}
  .smap-edge-g.selected .smap-edge-add{opacity:1; pointer-events:all;}
  /* the dots stay visually small; their hit areas do not */
  .smap-handle{width:16px; height:16px;}
  .smap-handle::before{content:""; position:absolute; inset:-12px;}
  .smap-handle.h-t{top:-8px; margin-left:-8px;}
  .smap-handle.h-b{bottom:-8px; margin-left:-8px;}
  .smap-handle.h-l{left:-8px; margin-top:-8px;}
  .smap-handle.h-r{right:-8px; margin-top:-8px;}
  .smap-handle-radial{margin-left:-8px; margin-top:-8px;}
  .smap-node-del{width:27px; height:27px;}
  .smap-node-del .icon{width:14px; height:14px;}
  .smap-child-add{width:27px; height:27px;}
  .smap-child-add .icon{width:15px; height:15px;}
  circle.smap-edge-end, circle.smap-edge-mid{r:9px;}
  .smap-edge-hit{stroke-width:30;}
  .smap-tool{width:38px; height:38px;}          /* toolbar buttons are fingertip targets too */
  .smap-tool-wide{width:auto; height:38px;}
}

/* drag ghost from the palette */
/* ===== nodo de imagen =====
   La foto manda: sin relleno ni borde de caja, para que se vea la imagen y no el marco. El ancho
   lo fija el nodo (n.imgW) y el alto sale de la proporcion, asi que redimensionar no deforma. */
/* Mientras se arrastra un fichero por encima: el lienzo entero dice que si, sin mover nada. */
.smap-canvas.dropping{outline:2px dashed var(--rose-deep); outline-offset:-6px; background-color:var(--rose-tint);}
/* ⚑ .smap-node trae max-width:240px y min-width:72px. Con ellos puestos el NODO dejaba de crecer a
   240 mientras el alto escrito en la imagen seguia subiendo: el object-fit recortaba y parecia que
   la imagen hacia zoom. Aqui el ancho lo manda el nodo y punto. */
/* ⚑ SIN tarjeta detras. Un nodo normal pinta fondo, borde y sombra; en uno de imagen eso se ve
   POR los huecos de un PNG con transparencia, que es justo lo que la transparencia no deberia
   hacer. Aqui la imagen ES el nodo: fondo transparente, sin borde y sin sombra, y lo que se ve
   por detras es el lienzo.
   Sin borde, ademas, el ancho pintado coincide EXACTO con n.imgW (antes se comia 1px+3px). */
.smap-node.kind-image{padding:0; overflow:visible; max-width:none; min-width:0;
  background:transparent; border:none; box-shadow:none;}
/* la marca de "elegido" vivia en el box-shadow, asi que hay que devolversela */
.smap-node.kind-image.selected{box-shadow:0 0 0 2px var(--rose);}
.smap-node.kind-image .smap-img{display:block; width:100%; height:auto; border-radius:10px; pointer-events:none;}
.smap-img-empty{display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px;
  width:100%; min-height:96px; border:2px dashed var(--line-strong); border-radius:10px; background:var(--bg);
  color:var(--ink-soft); font-size:0.75rem; font-family:inherit; cursor:pointer; padding:12px 8px; text-align:center;}
.smap-img-empty:hover{border-color:var(--rose-deep); color:var(--rose-deep-on-tint, var(--rose-deep));}
.smap-img-empty svg{width:18px; height:18px;}
.smap-img-cap{padding:6px 10px 8px; text-align:center;}
/* Tirador de la esquina: solo al apuntar el nodo, no es parte del dibujo. Cursor en diagonal para
   que se lea como lo que es antes de tocarlo. */
.smap-img-grip{position:absolute; width:14px; height:14px; display:none; z-index:4;
  border:1.5px solid var(--rose-deep); border-radius:4px; background:var(--surface);
  box-shadow:0 2px 6px rgba(28,26,23,.22);}
/* ⚑ NO al 50%: ahi vive el conector de ese lado (.smap-handle.h-r / .h-b), un circulo de 13px
   con z-index 6, asi que los dos tiradores caian debajo de el y no habia forma de agarrarlos.
   Van al 25% del lado — lejos del conector (50%) y lejos de la esquina (100%) — y por encima de
   el, para que en un nodo pequenio, donde aun se rocen, gane el tirador. Los otros tres lados
   siguen libres para tirar de un conector. */
.smap-img-grip.grip-xy{right:-7px; bottom:-7px; cursor:nwse-resize;}
.smap-img-grip.grip-x{right:-7px; top:25%; margin-top:-7px; cursor:ew-resize; z-index:7;}
.smap-img-grip.grip-y{bottom:-7px; left:25%; margin-left:-7px; cursor:ns-resize; z-index:7;}
.smap-node.kind-image:hover .smap-img-grip, .smap-node.kind-image.selected .smap-img-grip{display:block;}
.smap-ghost{position:fixed; z-index:3000; pointer-events:none; opacity:.85; cursor:grabbing; box-shadow:0 8px 22px rgba(28,26,23,.28);}

/* unified "Add" shape menu in the toolbar */
/* sits in the middle fab slot inside the canvas; anchors the dropdown below the "+" */
.smap-add-wrap{position:absolute; top:12px; right:54px; z-index:21;}
/* .smap-canvas is overflow:hidden (pan/zoom), so a tall menu would be clipped rather than
   overflow the canvas — cap it and let it scroll instead */
.smap-add-menu{position:absolute; top:calc(100% + 6px); right:0; z-index:30; width:216px; background:var(--surface); border:1px solid var(--line); border-radius:12px; box-shadow:0 10px 28px rgba(28,26,23,.22); padding:8px; max-height:min(380px,58vh); overflow-y:auto;}
.smap-add-menu[hidden]{display:none;}
.smap-add-group-label{font-size:.64rem; font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--ink-soft); margin:5px 4px 6px;}
.smap-add-grid{display:grid; grid-template-columns:1fr 1fr; gap:6px; margin-bottom:2px;}
.smap-add-tile{display:flex; align-items:center; gap:7px; padding:8px 9px; border:1px solid var(--line); border-radius:9px; background:var(--surface); color:var(--ink); cursor:grab; font-size:.82rem; text-align:left;}
.smap-add-tile:hover{border-color:var(--rose); background:var(--rose-tint);}
.smap-add-tile:active{cursor:grabbing;}
.smap-tile-ic{width:20px; height:20px; display:flex; align-items:center; justify-content:center; color:var(--acc,var(--rose)); flex-shrink:0;}
.smap-tile-ic svg{width:18px; height:18px;}
.smap-tile-ic .smap-chip-dot{width:11px; height:11px; border-radius:50%; background:var(--acc,var(--rose));}

/* rubber-band selection rectangle */
.smap-marquee{position:absolute; z-index:15; pointer-events:none; border:1px solid var(--rose); background:rgba(190,110,132,.13); border-radius:3px;}

/* edge label / delete / direction popover */
.smap-edge-editor{position:absolute; z-index:20; display:flex; flex-direction:column; gap:5px; background:var(--surface); border:1px solid var(--line); border-radius:10px; box-shadow:0 6px 20px rgba(28,26,23,.22); padding:5px;}
.smap-edge-dir{display:flex; gap:3px; justify-content:center;}
.smap-dir-btn{flex:1; min-width:30px; height:26px; border:1px solid var(--line); background:var(--surface); color:var(--ink-soft); border-radius:7px; font-size:0.95rem; line-height:1; cursor:pointer; transition:.12s;}
.smap-dir-btn:hover{border-color:var(--rose); color:var(--ink);}
.smap-dir-btn.on{background:var(--rose-tint); border-color:var(--rose); color:var(--rose-deep-on-tint, var(--rose-deep));}
.smap-edge-row{display:flex; align-items:center; gap:4px;}
.smap-edge-input{border:none; outline:none; background:none; color:var(--ink); font-size:0.82rem; width:120px; padding:2px 4px;}
.smap-edge-del{border:none; background:none; color:var(--ink-faint); cursor:pointer; display:flex; padding:2px;}
.smap-edge-del .icon{width:14px; height:14px;}
.smap-edge-del:hover{color:var(--danger);}

/* new-map modal type picker */
.smap-type-pick{display:grid; grid-template-columns:1fr; gap:8px;}
.smap-type-opt{display:grid; grid-template-columns:auto 1fr; grid-template-rows:auto auto; column-gap:10px; align-items:center; text-align:left; padding:10px 12px; border:1px solid var(--line); border-radius:12px; background:var(--surface); cursor:pointer; color:var(--ink);}
.smap-type-opt:hover{border-color:var(--rose);}
.smap-type-opt.sel{border-color:var(--rose); background:var(--rose-tint);}
.smap-type-ic{grid-row:1 / span 2; width:32px; height:32px; display:flex; align-items:center; justify-content:center; color:var(--rose-deep);}
.smap-type-ic .icon{width:22px; height:22px;}
.smap-type-name{font-weight:700; font-size:0.92rem;}
.smap-type-opt small{color:var(--ink-soft); font-size:0.76rem;}

.next-badge{position:absolute; bottom:7px; left:7px; z-index:2; background:var(--rose); color:#fff; font-size:.66rem; font-weight:700; padding:3px 9px; border-radius:999px; box-shadow:0 2px 7px rgba(28,26,23,.22); letter-spacing:.01em; white-space:nowrap;}
.book-card.is-next{outline:2px solid var(--rose); outline-offset:2px; border-radius:8px; background:var(--rose-tint);}
.tbr-section-head{font-size:.82rem; font-weight:700; color:var(--rose-deep); letter-spacing:.02em; margin:22px 2px 12px;}
.tbr-section-head:first-of-type{margin-top:6px;}
/* BOM: botón "✕" para quitar el libro de un mes */
.bom-remove{position:absolute; top:6px; right:6px; z-index:3; width:24px; height:24px; border-radius:50%; border:none; background:rgba(28,26,23,.62); color:#fff; font-size:0.82rem; line-height:1; display:flex; align-items:center; justify-content:center; cursor:pointer; box-shadow:0 2px 6px rgba(28,26,23,.25); opacity:0; transition:opacity .12s, background .12s;}
.month-slot .cover:hover .bom-remove, .bom-remove:focus-visible{opacity:1;}
.bom-remove:hover{background:var(--danger);}
@media (max-width:768px){ .bom-remove{opacity:1;} }   /* en táctil siempre visible */
/* error de validación en campos de formulario */
.field-error{color:var(--danger); font-size:0.78rem; margin-top:6px;}
.field-error[hidden]{display:none;}
.reread-badge{position:absolute; bottom:7px; left:7px; z-index:2; background:var(--surface); color:var(--ink); border:1px solid var(--line-strong); font-size:.62rem; font-weight:600; padding:2px 7px; border-radius:999px; box-shadow:0 2px 6px rgba(28,26,23,.18); letter-spacing:.01em; white-space:nowrap;}
.reread-check{display:flex; align-items:center; gap:9px; margin:14px 0 4px; cursor:pointer; font-size:.92rem; color:var(--ink);}
.reread-check input{width:17px; height:17px; accent-color:var(--rose); cursor:pointer; flex-shrink:0;}
.rg-info-wrap{position:relative; display:inline-flex; vertical-align:middle; margin-left:6px;}
.rg-info{width:16px; height:16px; border-radius:50%; border:1px solid var(--line-strong); background:transparent; color:var(--ink-soft); font-size:.72rem; font-weight:700; font-style:italic; font-family:Georgia,"Times New Roman",serif; line-height:1; display:flex; align-items:center; justify-content:center; cursor:pointer; padding:0; transition:color .12s, border-color .12s;}
.rg-info:hover{border-color:var(--rose); color:var(--rose);}
.rg-tip{position:absolute; top:calc(100% + 9px); left:-6px; z-index:60; width:214px; background:var(--surface); border:1px solid var(--line); border-radius:12px; box-shadow:0 8px 26px rgba(28,26,23,.20); padding:11px 13px; opacity:0; visibility:hidden; transform:translateY(-3px); transition:opacity .14s, transform .14s; pointer-events:none;}
.rg-info-wrap:hover .rg-tip, .rg-tip.tip-open{opacity:1; visibility:visible; transform:translateY(0);}
.rg-tip::before{content:""; position:absolute; bottom:100%; left:12px; border:6px solid transparent; border-bottom-color:var(--line);}
.rg-tip::after{content:""; position:absolute; bottom:100%; left:12px; margin-bottom:-1px; border:6px solid transparent; border-bottom-color:var(--surface);}
.rg-tip-title{font-size:.66rem; font-weight:700; color:var(--ink); margin-bottom:8px; letter-spacing:.05em; text-transform:uppercase;}
.rg-tip-row{display:flex; align-items:center; gap:8px; padding:2.5px 0;}
.rg-tip-stars{display:inline-flex; gap:1px; flex-shrink:0; width:74px;}
.rg-tip-stars .star-svg{width:12px; height:12px;}
.rg-tip-lbl{font-size:.82rem; color:var(--ink-soft); font-weight:600;}
.book-title{font-weight:600; font-size:0.78125rem; margin-top:8px; line-height:1.3; color:var(--ink);}
.book-author{font-size:0.6875rem; color:var(--ink-soft); margin-top:1px;}
.book-series-ref{font-size:0.6875rem; color:var(--rose-deep); margin-top:2px; font-weight:500;}
table.lib-table{width:100%; border-collapse:collapse; font-size:0.84375rem;}
table.lib-table th{text-align:left; border-bottom:1px solid var(--line-strong); padding:10px 6px; font-size:0.6875rem; letter-spacing:.3px; text-transform:uppercase; color:var(--ink-soft); font-weight:600;}
table.lib-table td{padding:10px 6px; border-bottom:1px solid var(--line); vertical-align:middle;}
.table-collection-sub{font-size:0.71875rem; color:var(--ink-soft); margin-top:2px;}
table.lib-table tr:hover{background:var(--bg);}
.mini-cover{width:30px; height:44px; border:1px solid var(--line); border-radius:4px; background-size:cover; background-position:center; background-color:var(--bg); flex-shrink:0;}
.title-cell{display:flex; gap:10px; align-items:center;}

.spine{
  width:var(--w,32px); height:var(--h,120px); margin:0 1px; flex-shrink:0;
  border-radius:5px 5px 2px 2px;
  display:flex; align-items:center; justify-content:center;
  writing-mode:vertical-rl; transform:rotate(180deg);
  font-size:0.62rem; font-weight:700; color:#fff; cursor:pointer; overflow:hidden;
  box-shadow:inset 1.5px 0 0 rgba(255,255,255,.14), inset -2px 0 5px rgba(0,0,0,.22), 1px 1px 2px rgba(0,0,0,.18);
  transition:box-shadow .15s, filter .15s;
}
.spine-title{padding:9px 0; max-height:100%; overflow:hidden; text-overflow:ellipsis; text-shadow:0 1px 3px rgba(0,0,0,.55), 0 0 2px rgba(0,0,0,.4);}
.spine:hover{box-shadow:inset 1.5px 0 0 rgba(255,255,255,.2), inset -2px 0 6px rgba(0,0,0,.25), 0 2px 7px rgba(0,0,0,.22); filter:brightness(1.07);}
.spine.spine-dark{color:#241c14;}
.spine.spine-dark .spine-title{text-shadow:0 1px 2px rgba(255,255,255,.6), 0 0 2px rgba(255,255,255,.5);}

/* ===== year history bar (bingo & A-Z) ===== */
.hist-bar{display:flex; align-items:center; gap:14px; margin:2px 0 22px; flex-wrap:wrap;}
.hist-nav{display:inline-flex; align-items:center; gap:12px;}
/* selector de año único: el año (display) ES el dropdown, entre las flechas */
.hist-year-pick{position:relative; display:inline-flex; align-items:center;}
.hist-year-select{appearance:none; -webkit-appearance:none; border:none; background:transparent; cursor:pointer;
  font-family:var(--font-display); font-weight:500; font-size:1.5rem; color:var(--ink);
  text-align:center; text-align-last:center; padding:2px 24px 2px 10px; min-width:88px;}
.hist-year-select:focus{outline:none;}
.hist-year-pick::after{content:""; position:absolute; right:7px; top:50%; width:7px; height:7px;
  border-right:2px solid var(--ink-soft); border-bottom:2px solid var(--ink-soft);
  transform:translateY(-68%) rotate(45deg); pointer-events:none;}
.hist-badge{display:inline-flex; align-items:center; gap:6px; padding:5px 12px; border-radius:20px; background:var(--bg); border:1px solid var(--line); font-size:0.78125rem; font-weight:600; color:var(--ink-soft); margin-left:auto;}
/* Games centres its year bar over the board it belongs to. The badge has to give up its auto
   left margin here, or it would shove itself to the far right and leave the arrows stranded. */
.hist-bar-center{justify-content:center;}
/* Barra con accion a la derecha: rejilla de tres columnas para que el selector quede centrado
   respecto al ANCHO COMPLETO y no respecto a lo que sobra despues del boton. */
.hist-bar-action{display:grid; grid-template-columns:1fr auto 1fr; align-items:center;}
.hist-bar-action .hist-nav{grid-column:2; justify-self:center;}
.hist-bar-action .hist-bar-end{grid-column:3; justify-self:end; display:flex; gap:10px;}
.hist-bar-center .hist-badge{margin-left:0;}
/* A-Z moves its counter to its own line, so the bar gives up the gap it would have kept below
   itself and the counter row carries it instead. */
.hist-bar-tight{margin-bottom:8px;}
.az-count-row{display:flex; justify-content:center; margin:0 0 22px;}
/* It wears the dark button look — .btn.primary is var(--ink) on var(--surface), so it repaints
   with the chosen template on its own — but it is a readout, not a control: no hover, no cursor,
   and nothing for a keyboard or a screen reader to land on. */
.az-count{cursor:default; pointer-events:none;}
@media(max-width:600px){ .hist-year-select{font-size:1.2rem;} .hist-badge{margin-left:0;} }
/* En el telefono el ano y el boton no caben en la misma linea: el boton baja, centrado bajo el
   selector. Se apila en vez de encogerse porque un boton con el texto partido en dos lineas se
   lee peor que uno entero una fila mas abajo. */
@media(max-width:600px){
  .hist-bar-action{grid-template-columns:1fr; row-gap:14px;}
  .hist-bar-action .hist-nav{grid-column:1; justify-self:center;}
  .hist-bar-action .hist-bar-end{grid-column:1; justify-self:center;}
}

/* ===== bingo ===== */
/* The board caps at 760px while #main has no cap at all, so on a wide screen the whole section
   is centred as one column — otherwise the progress track would run edge to edge above a grid
   sitting well inside it. */
.bingo-wrap{max-width:760px; margin-inline:auto;}
.bingo-grid{display:grid; grid-template-columns:repeat(5,1fr); gap:8px;}
.bingo-cell{
  aspect-ratio:1; border:1px solid var(--line-strong); border-radius:10px; padding:8px; font-size:0.6875rem; font-weight:500;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; text-align:center; cursor:pointer; background:var(--surface);
  color:var(--ink-soft); transition:.15s; overflow:hidden; min-width:0;
}
.bingo-cell:hover{border-color:var(--ink);}
/* FIX 2: text truncates with ellipsis inside the square cell, never spills over the border */
.bingo-cat{line-height:1.2; max-width:100%; overflow:hidden; overflow-wrap:break-word; display:-webkit-box; -webkit-line-clamp:4; -webkit-box-orient:vertical;}
.bingo-book{font-size:0.71875rem; font-weight:600; font-style:italic; opacity:.9; line-height:1.15; padding-top:3px; border-top:1px solid currentColor; max-width:100%; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
/* barra de progreso del bingo */
.bingo-progress{display:flex; align-items:center; gap:12px; margin:2px 0 16px;}
.bingo-progress-track{flex:1; height:10px; border-radius:999px; background:var(--rose-tint); overflow:hidden;}
.bingo-progress-fill{height:100%; border-radius:999px; background:var(--rose-deep); transition:width .4s ease;}
.bingo-progress-label{font-size:0.8125rem; font-weight:600; color:var(--ink-soft); white-space:nowrap; flex-shrink:0;}
/* FIX 2: smaller bingo text on mobile so it fits the square cells */
@media(max-width:768px){
  .bingo-cell{font-size:0.5rem; padding:4px; gap:2px;}
  .bingo-cat{-webkit-line-clamp:5;}
  .bingo-book{font-size:0.5625rem; padding-top:2px;}
}
.bingo-cell.filled{background:var(--rose); color:#fff; border-color:var(--rose); font-weight:600;}

/* ===== A-Z ===== */
.az-cols{display:grid; grid-template-columns:1fr 1fr; gap:0 30px;}
.az-row{display:flex; align-items:center; gap:12px; margin-bottom:8px;}
.az-letter{width:18px; font-family:var(--font-display); font-weight:500; color:var(--rose-deep); font-size:1rem; flex-shrink:0;}
.az-field{flex:1; min-width:0; position:relative;}
.az-input{border:none; border-bottom:1px solid var(--line-strong); background:transparent; padding:6px 2px; font-size:0.84375rem; width:100%; box-sizing:border-box;}
.az-input:focus{outline:none; border-bottom-color:var(--ink);}
.az-progress-wrap{display:grid; grid-template-columns:1fr 1fr; gap:14px 30px; margin:14px 0 2px;}
.az-prog-head{display:flex; align-items:baseline; justify-content:space-between; gap:8px; margin-bottom:5px;}
.az-prog-label{font-weight:600; font-size:0.8125rem; color:var(--ink-soft);}
.az-prog-count{font-size:0.75rem; color:var(--ink-faint); white-space:nowrap;}
.az-prog-track{height:7px; border-radius:999px; background:var(--rose-tint); overflow:hidden;}
.az-prog-fill{height:100%; border-radius:999px; background:var(--rose-deep); transition:width .3s ease;}
/* dropdown de sugerencias de la biblioteca (estilo coherente con el picker de tropes) */
.az-menu{position:absolute; left:0; top:calc(100% + 4px); z-index:40; min-width:190px; max-width:min(280px, 80vw); background:var(--surface); border:1px solid var(--line); border-radius:12px; box-shadow:0 16px 40px rgba(28,26,23,.20); padding:5px; max-height:232px; overflow-y:auto;}
.az-opt{display:flex; align-items:center; gap:9px; width:100%; text-align:left; border:none; background:none; padding:6px 8px; border-radius:8px; cursor:pointer; transition:background .12s;}
.az-opt:hover{background:var(--bg);}
.az-thumb{width:24px; min-width:24px; height:36px; border-radius:4px; background:var(--bg) center/cover no-repeat; border:1px solid var(--line); display:flex; align-items:center; justify-content:center; font-size:0.5rem; color:var(--ink-faint);}
.az-opt-text{min-width:0; display:flex; flex-direction:column; gap:1px;}
.az-opt-title{font-size:0.8rem; color:var(--ink); line-height:1.2; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.az-opt-sub{font-size:0.7rem; color:var(--ink-soft); line-height:1.2; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
/* modal de revisión de fechas de fin */
.rev-list{max-height:52vh; overflow-y:auto; margin:0 -2px;}
.rev-row{display:flex; align-items:center; gap:12px; padding:9px 2px; border-bottom:1px solid var(--line);}
.rev-row:last-child{border-bottom:none;}
.rev-info{flex:1; min-width:0;}
.rev-title{font-weight:600; font-size:0.875rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.rev-author{font-size:0.75rem; color:var(--ink-soft); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.rev-date{font-size:0.8125rem; color:var(--ink-soft); font-variant-numeric:tabular-nums; flex-shrink:0;}
.rev-clear{flex-shrink:0;}
/* encabezado de cada mitad del revisor: las dos listas resuelven problemas opuestos y sin un
   rótulo entre ellas se leen como una sola lista larga */
.rev-sec-head{font-weight:700; font-size:0.8125rem; text-transform:uppercase; letter-spacing:.06em;
  color:var(--ink-soft); margin:18px 0 6px;}
.rev-sec-head:first-of-type{margin-top:4px;}
/* una fecha que falta, con su campo delante: se arregla sin salir de la lista */
.rev-gap{flex-wrap:wrap;}
.rev-fix{display:flex; flex-direction:column; gap:3px; flex-shrink:0; font-size:0.6875rem; color:var(--ink-soft);}
.rev-fix input{border:1px solid var(--line-strong); border-radius:8px; padding:5px 8px; font-family:inherit;
  font-size:0.8125rem; background:var(--surface); color:var(--ink); min-height:32px;}
.rev-fix input:focus{outline:2px solid var(--rose-deep); outline-offset:1px;}
/* el contador del botón de Ajustes: sólo aparece cuando hay algo que arreglar */
.set-badge{display:inline-flex; align-items:center; justify-content:center; min-width:19px; height:19px;
  margin-left:7px; padding:0 5px; border-radius:999px; background:var(--rose); color:var(--on-rose-ink, var(--ink));
  font-size:0.6875rem; font-weight:700; line-height:1;}

/* ===== month grid (BOM) ===== */
.month-grid{display:grid; grid-template-columns:repeat(6,1fr); gap:18px;}
.month-slot{text-align:left;}
.month-slot .mname{font-family:var(--font-display); font-weight:500; font-size:1.0625rem; margin-bottom:9px; color:var(--ink); letter-spacing:.2px; padding-bottom:6px; border-bottom:1px solid var(--line);}
.month-slot .book-title{margin-top:8px;}
.month-slot .book-author{font-size:0.6875rem; color:var(--ink-soft); margin-top:1px; margin-bottom:5px;}
.bom-stars{display:flex; flex-wrap:nowrap; white-space:nowrap; margin-top:4px;}

/* highlight temporal del libro sugerido por "Pick for me" en TBR */
.book-card.tbr-picked{outline:3px solid var(--rose); outline-offset:3px; border-radius:8px; animation:tbrGlow 2s ease-in-out;}
@keyframes tbrGlow{0%{box-shadow:0 0 0 0 var(--rose);}60%{box-shadow:0 0 0 14px transparent;}100%{box-shadow:0 0 0 0 transparent;}}

/* ===== Book of the Year — bracket espejado ===== */
/* tamaños centralizados en variables; cada breakpoint solo los ajusta */
.boy-tree{--boy-cw:60px; --boy-champ:158px; --boy-gap:24px; --boy-mlbl:0.62rem;
  --boy-ch:calc(var(--boy-cw)*1.5); --boy-champ-h:calc(var(--boy-champ)*1.5);   /* alturas fijas derivadas del ancho */
  position:relative; display:flex; align-items:stretch; justify-content:safe center; gap:var(--boy-gap); overflow-x:auto; padding:16px 10px 26px; min-height:600px;}
.boy-links{position:absolute; inset:0; width:100%; height:100%; z-index:0; pointer-events:none; overflow:visible;}
.boy-col{position:relative; z-index:1; display:flex; flex-direction:column; justify-content:space-around; gap:10px; flex-shrink:0;}
.boy-col-months{justify-content:space-around;}
.boy-col-fin{justify-content:center;}
.boy-col-champ{justify-content:center;}
.boy-pair{display:flex; flex-direction:column; gap:calc(var(--boy-cw)*0.28);}
.boy-node{display:flex; align-items:center; justify-content:center;}
.boy-month-node{gap:8px;}
.boy-m-left{flex-direction:row-reverse;}   /* etiqueta al borde izquierdo, portada hacia el centro */
.boy-m-right{flex-direction:row;}          /* etiqueta al borde derecho */
.boy-month{font-size:var(--boy-mlbl); font-weight:700; letter-spacing:.06em; color:var(--rose-deep); width:2.2em; text-align:center; flex-shrink:0;}
.boy-slot{cursor:pointer; border-radius:8px; transition:transform .12s, box-shadow .12s;}
.boy-slot .boy-cover{position:relative; width:var(--boy-cw); height:var(--boy-ch); flex-shrink:0; box-sizing:border-box; border-radius:8px; background:var(--rose-tint) center/cover no-repeat; box-shadow:0 2px 7px rgba(28,26,23,.16); display:flex; align-items:flex-end; justify-content:center; font-size:0.54rem; line-height:1.1; color:var(--ink-soft); text-align:center; padding:4px; overflow:hidden;}
.boy-slot.boy-empty{cursor:default;}
.boy-slot.boy-empty .boy-cover{background:var(--rose-tint); border:1.5px dashed var(--rose); box-shadow:none;}
.boy-slot[data-boy-month].boy-empty{cursor:pointer;}
/* "+" solo en slots de MES vacíos (clicables para asignar libro); desaparece al tener portada */
.boy-slot[data-boy-month].boy-empty .boy-cover::after{content:"+"; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); color:var(--rose-deep); font-size:calc(var(--boy-cw)*0.42); font-weight:400; line-height:1;}
.boy-slot.choosable:hover, .boy-slot[data-boy-month]:hover{transform:translateY(-2px); box-shadow:0 5px 14px rgba(28,26,23,.22);}
.boy-slot.choosable .boy-cover{box-shadow:0 0 0 2px var(--rose), 0 2px 7px rgba(28,26,23,.18);}
/* banner descartable de migración del bracket */
.boy-migrated{display:flex; align-items:center; gap:12px; background:var(--rose-tint); border:1px solid var(--rose); border-radius:12px; padding:11px 14px; margin:2px 0 14px; font-size:0.84rem; color:var(--ink-soft); line-height:1.4;}
.boy-migrated span{flex:1;}
.boy-migrated button{flex-shrink:0; background:none; border:none; cursor:pointer; color:var(--ink-soft); display:flex; padding:3px; border-radius:6px;}
.boy-migrated button:hover{background:var(--rose); color:var(--on-rose-ink, var(--ink));}
.boy-migrated button .icon{width:15px; height:15px;}
.boy-col-semi{justify-content:space-around;}
/* campeón central: marco rosa limpio (sin festón) alrededor de la portada */
.boy-champ{display:flex; flex-direction:column; align-items:center; text-align:center; gap:8px;}
.boy-crown .icon{width:calc(var(--boy-champ)*0.26); height:calc(var(--boy-champ)*0.26); color:var(--rose-deep);}
.boy-champ-frame{position:relative; padding:11px; display:inline-block; box-sizing:border-box; border-radius:16px; background:var(--rose-tint); border:1.5px dashed var(--rose);}
.boy-champ-frame.choosable{cursor:pointer;}
.boy-champ-frame:not(.boy-empty){background:var(--rose); border:1.5px solid transparent; box-shadow:0 8px 26px rgba(28,26,23,.22);}
.boy-champ-cover{width:var(--boy-champ); height:var(--boy-champ-h); flex-shrink:0; box-sizing:border-box; border-radius:9px; background:var(--surface) center/cover no-repeat; box-shadow:0 2px 8px rgba(28,26,23,.25); display:flex; align-items:flex-end; justify-content:center; font-size:0.72rem; color:var(--ink-soft); text-align:center; padding:6px; overflow:hidden;}
.boy-champ-label{font-family:var(--font-display); font-size:1.15rem; color:var(--ink); line-height:1.2; max-width:190px; margin-top:3px;}
.boy-champ-author{font-size:0.78rem; color:var(--ink-soft); margin-top:-3px;}
.boy-champ-crownlbl{font-size:0.74rem; font-weight:700; letter-spacing:.05em; color:var(--rose-deep); text-transform:uppercase; margin-top:3px;}
/* pantallas grandes: bracket más grande */
@media (min-width:1200px){
  .boy-tree{--boy-cw:72px; --boy-champ:188px; --boy-gap:32px; --boy-mlbl:0.68rem;}
  /* dock: crece de forma fluida solo en pantallas grandes (reutiliza el corte 1200px).
     El piso del clamp mantiene 60px/34px hasta que el vw obliga a crecer (~1500px+),
     así que a 1280/1440 se ve igual que ahora y solo escala en monitores anchos. */
  #app{--sidebar-w:clamp(60px, 5vw, 76px); --dock-icon:clamp(34px, 2.83vw, 42px);}
}
/* tablet */
@media (max-width:960px){ .boy-tree{--boy-cw:52px; --boy-champ:140px; --boy-gap:18px;} }
/* móvil: bracket compacto con scroll horizontal */
@media (max-width:720px){ .boy-tree{--boy-cw:42px; --boy-champ:108px; --boy-gap:12px; --boy-mlbl:0.56rem; justify-content:flex-start; min-height:0; padding:10px 6px 20px;}
  .boy-champ-label{font-size:1rem;} }
.boy-chooser{display:flex; gap:16px; justify-content:center; margin:6px 0 4px;}
.boy-choice{display:flex; flex-direction:column; align-items:center; gap:7px; background:none; border:2px solid transparent; border-radius:12px; padding:8px; cursor:pointer; width:120px; transition:border-color .12s, transform .12s;}
.boy-choice:hover{transform:translateY(-2px);}
.boy-choice.sel{border-color:var(--rose);}
.boy-choice-cover{width:88px; aspect-ratio:2/3; border-radius:7px; background:var(--rose-tint) center/cover no-repeat; box-shadow:var(--card-shadow); display:flex; align-items:flex-end; justify-content:center; font-size:0.62rem; color:var(--ink-soft); text-align:center; padding:6px; overflow:hidden;}
.boy-choice-title{font-size:0.8rem; color:var(--ink); line-height:1.2; text-align:center;}

/* ===== calendar ===== */
.cal-tabs{display:flex; align-items:center; gap:12px; margin-bottom:16px; flex-wrap:wrap;}
/* Toggle (Feels, Quotes&Reviews, Calendario): pastilla redondeada con fondo azul
   del dock (--ink) y el seleccionado en blanco (--surface). */
.cal-tab-group{display:inline-flex; background:var(--ink); border:1px solid transparent; border-radius:999px; padding:3px;}
.cal-tab{border:none; background:none; padding:7px 20px; border-radius:999px; font-size:0.84375rem; font-weight:600; color:var(--surface); opacity:.6; transition:.15s; min-height:38px;}
.cal-tab.active{background:var(--surface); color:var(--ink); opacity:1; box-shadow:0 1px 3px rgba(0,0,0,.12);}
.cal-tab:hover:not(.active){color:var(--surface); opacity:.85;}

.mc-head{display:flex; align-items:center; justify-content:center; gap:18px; margin:6px 0 18px;}
.mc-cur{font-family:var(--font-display); font-weight:500; font-size:1.5rem; color:var(--ink); min-width:210px; text-align:center; text-transform:capitalize;}

/* focused month: grid of cover cells — capped width so cells don't balloon on wide screens */
.mc-dow{display:grid; grid-template-columns:repeat(7,1fr); gap:6px; margin:0 auto 6px; max-width:1000px;}
.mc-dow span{text-align:center; font-size:0.6875rem; font-weight:700; text-transform:uppercase; letter-spacing:1px; color:var(--ink-faint); padding:4px 0;}
.mc-dow span:last-child{color:var(--rose-deep);}
.mc-grid{display:grid; grid-template-columns:repeat(7,1fr); gap:6px; margin:0 auto; max-width:1000px;}
/* every day is the same rectangle, whether it has covers or is empty (desktop 3:4, phones 2:3) */
.mc-cell{display:flex; flex-direction:column; min-width:0; min-height:0; aspect-ratio:3/4; overflow:hidden; border-radius:10px; border:1px solid var(--line); background:var(--surface); padding:6px 5px 8px;}
.mc-cell.empty{border:none; background:none;}
.mc-cell .mc-day{font-size:0.72rem; font-weight:600; color:var(--ink-soft); line-height:1; padding:1px 3px 4px; flex-shrink:0;}
.mc-cell.filled{cursor:pointer;}
/* covers centred in the middle of the day container */
.mc-books{flex:1; min-width:0; min-height:0;}
.mc-books.one{display:flex; align-items:center; justify-content:center;}
.mc-books.one .mc-book{width:66%; max-width:96px;}
/* 2+ books: a 2-up grid — two covers on top, two below (wraps beyond four) */
.mc-books.cols2{display:grid; grid-template-columns:1fr 1fr; gap:2px; align-content:center; justify-items:center; width:100%; height:100%;}
.mc-books.cols2.two .mc-book{width:82%;}   /* 2 books in one row: bigger, closer */
.mc-books.cols2.many .mc-book,
.mc-books.cols2.many .mc-more{width:66%;}   /* 3–4 books in two rows: smaller so a finish-day rating pill fits under each cover */
.mc-book{min-width:0; display:flex; flex-direction:column; align-items:center; gap:3px; container-type:inline-size;}
.mc-thumb{width:100%; aspect-ratio:2/3; border-radius:5px; background-size:cover; background-position:center; box-shadow:0 1px 4px rgba(0,0,0,.22); display:flex; overflow:hidden;}
.mc-book.is-start .mc-thumb{box-shadow:0 1px 4px rgba(0,0,0,.22), inset 3px 0 0 var(--rose-deep);}
.mc-thumb.no-cover{align-items:center; justify-content:center;}
.mc-thumb-title{padding:5px; font-size:0.6rem; font-weight:700; line-height:1.2; text-align:center; color:#fff; text-shadow:0 1px 2px rgba(0,0,0,.5);}
/* "+N" tile, shaped like a book slot — clicking it opens the day's full list */
.mc-more{width:82%; aspect-ratio:2/3; border:1px dashed var(--line-strong); border-radius:5px; background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep)); font-weight:700; font-size:0.82rem; display:flex; align-items:center; justify-content:center; cursor:pointer; padding:0;}
.mc-more:hover{background:var(--rose); color:var(--on-rose-ink, #fff);}
/* stars sit just below the cover, inside the container; they scale to the cover width
   (container query) and clip to it so they can never leave the box */
.mc-stars{display:flex; justify-content:center; box-sizing:border-box; max-width:100%; overflow:hidden; padding:0.15em 0.3em; background:var(--bg); border:1px solid var(--line); border-radius:20px; font-size:clamp(6px, 16cqw, 13px);}
.mc-stars .stars-row{display:inline-flex; gap:0.08em; font-size:inherit;}
.mc-stars .star-wrap{width:1em; height:1em;}

/* year overview: 12 mini calendars, reading days tinted by the cover */
/* minmax(0,1fr), not 1fr: a tile carries an aspect-ratio, which turns any minimum HEIGHT it
   picks up into a minimum WIDTH, and plain 1fr columns refuse to shrink past it — the grid then
   overflows the content area and runs under the page rail. The old <table> shrank on its own. */
.cal-grid{display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:18px;}
.mini-cal{border:1px solid var(--line); border-radius:var(--radius); padding:14px; background:var(--surface);}
.mini-cal h4{display:inline-block; font-size:0.8125rem; font-weight:600; text-transform:uppercase; letter-spacing:1.5px; margin-bottom:10px; color:var(--ink-soft); cursor:pointer;}
.mini-cal h4:hover{color:var(--ink);}
/* day tiles hold covers, so they keep a 2:3 portrait — a table cell would ignore aspect-ratio */
.mcm-grid{display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); gap:2px;}
/* ⚑ El número del día va en su propia franja ARRIBA, no encima de la portada. Antes se pintaba
   centrado sobre la cubierta en blanco con sombra: legible sobre una portada oscura e invisible
   sobre una clara, y la portada la elige la editorial, no la lectora. Misma disposición que el mes
   ampliado — número arriba a la izquierda, la portada debajo — así las dos vistas se leen igual.
   El número se mantiene en TODAS las celdas, tengan portada o no, para que la fila de números
   quede a la misma altura; centrarlo sólo en las vacías las desalinearía. */
/* ⚑ La celda es más alta que 2:3 A PROPÓSITO, y ese es el único sitio donde se regula el tamaño
   de la portada. Con la cubierta ya fijada en 2:3, lo que la limita es la altura que le sobra a
   la celda tras la franja del número; en una celda 2:3 exacta salía a 35px de ancho dentro de 48
   y quedaba pequeña. Estirando la casilla a 1/1.62 la portada sube a ~39px y conserva unos 4-5px
   de aire a cada lado. Subir más este número la pega a los bordes: el aire lateral es lo que
   sobra, no algo que se pida aparte. */
.mini-cal .mcm{aspect-ratio:1/1.62; min-width:0; display:flex; flex-direction:column; font-size:0.7rem; color:var(--ink-soft); padding:0 2px 3px;}
.mini-cal .mcm .mcm-day{flex-shrink:0; font-size:0.6rem; line-height:1; padding:0 0 3px 1px;}
/* ⚑ La portada conserva la PROPORCIÓN 2:3 del libro, igual que en el mes ampliado, en lugar de
   estirarse hasta llenar la celda. Estirada dejaba de parecer un libro: recortaba la cubierta a
   una relación ~3:4 y, al ocupar todo el ancho, dos días seguidos se leían como una sola imagen.
   La altura la reparte el flex y `aspect-ratio` deriva el ancho, así que la cubierta se centra
   sola en horizontal con `align-self` y sobra aire a los lados sin tocar el tamaño de la celda.
   El margen inferior es el hueco con la fila de números de la semana siguiente, que iba pegada. */
.mini-cal .mcm .mcm-thumb{flex:1 1 auto; min-height:0; aspect-ratio:2/3; align-self:center; max-width:100%;
  margin-bottom:4px; border-radius:4px; background-size:cover; background-position:center; box-shadow:0 1px 3px rgba(0,0,0,.18);}
/* La fila de iniciales necesita separarse de la primera semana: pegada a los números parecía
   parte de ellos. El margen va aquí y no en el `gap` de la rejilla, que separa TODAS las filas. */
.mini-cal .mcm.dow{aspect-ratio:auto; min-height:14px; margin-bottom:7px; align-items:center; justify-content:center; font-weight:700; color:var(--ink-faint); font-size:0.6rem;}
.mini-cal .mcm.has-entry{cursor:pointer;}
.mini-cal .mcm.has-entry .mcm-day{color:var(--ink); font-weight:700;}
.cal-day-book{display:flex; gap:14px; align-items:flex-start; margin-bottom:14px;}
.cal-day-info{min-width:0;}
@media(max-width:900px){ .cal-grid{grid-template-columns:repeat(2,minmax(0,1fr));} }
@media(max-width:680px){
  .cal-grid{grid-template-columns:repeat(2,minmax(0,1fr));}
  .mc-cur{font-size:1.2rem; min-width:150px;}
  .mc-grid,.mc-dow{gap:3px;}
  /* phones: keep the taller 2:3 rectangle, just tighter spacing */
  .mc-cell{aspect-ratio:2/3; padding:3px 3px 5px;}
  .mc-cell .mc-day{font-size:0.62rem; padding:0 2px 3px;}
  .mc-books.cols2{gap:2px;}
  /* ⚑ El año en el móvil: la celda mide 17×25px, así que una franja de 12px se comía la mitad de
     la portada (bajaba a 14px de alto). Se encoge el número y se estira la celda lo justo para
     que la portada recupere su tamaño anterior: 17×23 frente a los 25 de antes, a cambio de unos
     40px más de alto por mes. Sin esto, quitar el solape resolvía la legibilidad del número
     rompiendo lo único que hace útil esta vista, que es reconocer la portada de un vistazo. */
  /* el aire alrededor de la portada se reduce a 1px: a 17px de ancho, 2px por lado se comerían
     casi una cuarta parte de la cubierta */
  .mini-cal .mcm{aspect-ratio:1/1.85; padding:0 1px 2px;}
  .mini-cal .mcm .mcm-day{font-size:0.5rem; padding:0 0 1px 1px;}
  .mini-cal .mcm .mcm-thumb{margin-bottom:2px;}
  .mini-cal .mcm.dow{margin-bottom:4px;}
}

/* ===== stats ===== */
/* daily reading streak card (Leí hoy + 30-day heatmap) */
/* La tarjeta se estira a la altura de la fila; el encabezado se queda arriba y TODO lo demas
   —selector, circulos y regla— se centra en el hueco que sobra, en vertical y en horizontal.
   Se centra el bloque entero, no cada pieza: centradas por separado, la regla y el selector
   buscarian cada uno su propio medio y no quedarian a plomo. */
.streak-card{margin-bottom:30px; padding:16px 20px; display:flex; flex-direction:column;}
.streak-body{flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center;
  /* El aire bajo el encabezado se pone AQUI, arriba y abajo por igual, en vez de como
     margin-bottom del encabezado: un margen exterior queda fuera de la caja que se centra y
     empujaba el bloque 22px hacia abajo, asi que el "centro" no era el centro. */
  padding:22px 0;}
.streak-body > *{width:100%;}
.streak-card .streak-hint{text-align:center;}
/* ===== Reading Streak header — grid fijo de 3 columnas ===== */
/* Una sola fila: titulo a la izquierda, la racha en el centro y el boton a la derecha. El selector
   de semana ya no vive aqui — bajo a su propia fila, encima de los circulos con los que se alinea. */
.streak-head{
  display:grid;
  grid-template-columns:1fr auto 1fr;              /* izq | centro | der */
  grid-template-areas:"title count button";
  align-items:center;
  column-gap:14px;
  margin-bottom:0;                                 /* el aire lo pone .streak-body (padding simetrico) */
}
.streak-head .section-title{ grid-area:title; justify-self:start; align-self:center; margin:0; }
/* La racha es la cifra que da nombre a la tarjeta, asi que se lee como tal y no como un pie */
.streak-count{
  grid-area:count; justify-self:center; align-self:center;
  display:inline-flex; align-items:baseline; gap:5px; line-height:1;
  color:var(--ink-faint); font-weight:600; font-size:1rem;
}
.streak-count.active{ color:var(--rose-deep); }
.streak-flame{ font-size:1.25rem; align-self:center; }
.streak-count-num{ font-size:1.6rem; font-weight:800; letter-spacing:-.2px; }
.streak-count-unit{ font-size:1rem; font-weight:600; }
.streak-count.celebrate{ animation:streakPop .5s ease; }
@keyframes streakPop{0%{transform:scale(.75); opacity:.3;} 55%{transform:scale(1.16);} 100%{transform:scale(1); opacity:1;}}
/* Columna derecha: botón "Read today" a la derecha (discreto, sin cambios visuales) */
.streak-head > .streak-btn{ grid-area:button; justify-self:end; align-self:center; }
.streak-btn{border:none; border-radius:999px; padding:7px 14px; font-weight:600; font-size:0.8125rem; font-family:inherit; background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep)); cursor:pointer; transition:.15s; white-space:nowrap;}
.streak-btn:hover{filter:brightness(.96);}
.streak-btn.done, .streak-btn:disabled{background:transparent; color:var(--ink-faint); cursor:default; filter:none; opacity:1; padding:7px 2px;}
/* ⚑ El apilado del encabezado en pantalla estrecha vive AHORA en el bloque de ≤900px, con el
   resto de los ajustes del panel. Aqui habia una copia que todavia colocaba un area "nav" que ya
   no existe en el encabezado: una regla que nombra un area inexistente no falla, simplemente no
   hace nada, asi que habria seguido pareciendo correcta. */
/* weekly streak: navigation header + circles with the weekday letter above and the date below */
/* nav centrada (grid-area en el header) + layout flex de la barra semanal */
/* Fila propia, centrada sobre los circulos */
.streak-nav{display:flex; align-items:center; justify-content:center; gap:14px; margin:0 auto 16px;}
.streak-nav-label{font-size:0.85rem; font-weight:600; color:var(--ink); min-width:118px; text-align:center;}
.streak-nav-btn{display:flex; align-items:center; justify-content:center; width:28px; height:28px; border:none; background:none; color:var(--ink-soft); cursor:pointer; border-radius:50%; transition:.12s;}
/* ===== Zonas de toque en pantalla estrecha =====
   Los controles del panel miden entre 28 y 31px de alto. Apple pide 44 y Android 48, y aqui el
   caso peor no es incomodo sino DESTRUCTIVO: los chevrones de semana miden 28x28 y viven a un
   palmo de los circulos de dia, que escriben en el registro de lectura — fallar el toque no
   navega mal, cambia un dato.
   ⚑ Se agranda la zona SIN tocar el dibujo: un ::after transparente que sobresale de la caja. Un
   padding haria crecer tambien el fondo y el borde. Y solo por debajo de 900px, porque con raton
   el pixel ya es exacto y ampliar zonas invisibles en escritorio solo crea solapes.
   ⚑ El crecimiento es ASIMETRICO en los chevrones: hacia arriba y a los lados si, hacia abajo
   solo 4px, porque debajo estan los circulos y una zona invisible que los tapara robaria
   exactamente el toque que mas importa. Comprobado con elementFromPoint sobre los siete. */
@media(max-width:900px){
  .streak-nav-btn, .streak-btn, .dash-goal-row .btn, .dash-rel-head .btn, .year-arrow{position:relative;}
  /* El paso de anio de Estadisticas: 38px, la misma clase de problema que los del panel, y se
     quedo fuera de la pasada anterior porque aquella se limito a selectores del panel. */
  .year-arrow::after{content:""; position:absolute; inset:-6px -8px;}
  .streak-nav-btn::after{content:""; position:absolute; top:-9px; right:-9px; bottom:-4px; left:-9px;}
  .streak-btn::after, .dash-goal-row .btn::after, .dash-rel-head .btn::after{
    content:""; position:absolute; top:-7px; right:-6px; bottom:-7px; left:-6px;}
  /* ⚑ LOS CIRCULOS DE DIA SE QUEDAN COMO ESTAN, 33px, y no es un olvido. Su columna mide 34px
     en un telefono: cualquier zona invisible que sobresalga se mete en la del vecino, y entonces
     un toque al borde marca OTRO dia. Un objetivo algo pequenio se falla y no pasa nada; un
     objetivo que roba el del lado escribe un dato equivocado sin avisar. Medido: con inset
     -6px -4px el circulo pasaba a 41px de ancho dentro de una columna de 34. */
}

.streak-nav-btn:hover{background:var(--rose-tint); color:var(--ink);}
.streak-nav-btn:disabled{opacity:.28; cursor:default;}
.streak-nav-btn svg{width:16px; height:16px;}
.streak-week{display:flex; justify-content:space-between; gap:10px; width:100%;}
.wk-day{flex:1 1 0; display:flex; flex-direction:column; align-items:center; gap:5px; min-width:0;}
.wk-lbl{font-size:0.6875rem; font-weight:700; letter-spacing:.5px; color:var(--ink-faint); text-transform:uppercase;}
.wk-date{font-size:0.75rem; font-weight:600; color:var(--ink-faint);}
.wk-date.today-date{color:var(--rose-deep); font-weight:700;}
.hm-cell{width:33px; max-width:100%; aspect-ratio:1/1; border-radius:50%; background:transparent; border:2px solid transparent; transition:transform .12s; display:flex; align-items:center; justify-content:center;}
.hm-ico{width:44%; max-width:20px; height:auto; display:block;}
/* completed: filled with the theme colour + white check */
/* la tinta la elige applyTheme por luminancia: con #fff fijo el check era invisible en los
   cinco temas oscuros, donde --rose-deep es el color CLARO de los enlaces */
.hm-cell.read{background:var(--rose-deep); color:var(--on-rose-deep-ink);}
/* past, no reading logged: empty dashed circle */
.hm-cell.missed{background:transparent; border:2px dashed var(--line-strong);}
/* future: empty dashed circle, fainter, not interactive */
.hm-cell.future{background:transparent; border:2px dashed var(--line); opacity:.55;}
/* today, not yet logged: white with themed border + pulsing dot */
.hm-cell.today{background:var(--surface); border:2px solid var(--rose-deep); color:var(--rose-deep);}
.hm-cell.today .hm-ico{animation:hmPulse 1.4s ease-in-out infinite;}
@keyframes hmPulse{0%,100%{opacity:1; transform:scale(1);} 50%{opacity:.35; transform:scale(.72);}}
/* today, already logged: like completed but with an extra highlighted ring */
.hm-cell.today-read{border-color:var(--ink); box-shadow:0 0 0 2px var(--rose-tint);}
.hm-cell.today-read .hm-ico{animation:none;}
.hm-cell:not(.future):hover{transform:translateY(-2px);}
/* Las casillas pasadas y la de hoy son <button>: hay que quitarles el relleno y la tipografía por
   defecto o el círculo deja de ser un círculo. El puntero y el foco visible son lo que anuncia que
   se pueden pulsar — antes eran divs y nada decía que marcaran nada. */
button.hm-cell{padding:0; font:inherit; color:inherit; cursor:pointer; -webkit-appearance:none; appearance:none;}
button.hm-cell:focus-visible{outline:2px solid var(--rose-deep); outline-offset:2px;}
/* trial banner (PASO C): discreet strip under the topbar, follows the active theme */
.stats-glance{display:flex; align-items:center; gap:24px 32px; padding:22px 28px; margin-bottom:30px; flex-wrap:wrap;}
.glance-hero{display:flex; flex-direction:column; min-width:110px;}
.glance-hero-num{font-family:var(--font-display); font-weight:500; font-size:3.875rem; line-height:.9; color:var(--ink);}
.glance-hero-lbl{font-size:0.78125rem; color:var(--ink-soft); margin-top:7px; max-width:130px; line-height:1.3;}
.glance-hero-sub{font-size:0.72rem; color:var(--rose-deep); margin-top:5px; max-width:130px; line-height:1.3; font-weight:600;}
.glance-rest{display:flex; margin-left:auto;}
.glance-item{display:flex; flex-direction:column; padding:2px 24px;}
.glance-item + .glance-item{border-left:1px solid var(--line);}
.gi-num{font-family:var(--font-display); font-weight:500; font-size:1.875rem; line-height:1; color:var(--ink);}
.gi-star{font-size:1rem; color:var(--rose-deep); margin-left:2px; vertical-align:1px;}
.gi-lbl{font-size:0.65625rem; color:var(--ink-soft); text-transform:uppercase; letter-spacing:1.2px; margin-top:6px;}
/* ⚑ Hueco distinto por eje. Cuando la leyenda cabe al lado, 34px la separan de la rosquilla; al
   envolver, ese mismo 34 se convierte en un agujero vertical que estira la tarjeta entera — y la
   tarjeta mas alta manda sobre la altura de TODA la fila. Vertical 18, horizontal 34. */
.fmt-card{display:flex; gap:18px 34px; align-items:center; flex-wrap:wrap;}
/* La rosquilla baja de 150 a 124: son 26px menos de fila sin tocar una sola letra de la leyenda,
   que es lo que hay que poder leer. El atributo width/height del SVG lo pisa esta regla. */
.fmt-card > svg{width:124px; height:124px;}
.fmt-legend{flex:1; min-width:180px; display:flex; flex-direction:column; gap:8px;}
.fmt-legend .legend-row{font-size:0.84375rem;}
/* two-column stats rows on desktop */
.stats-row{--stats-gap:20px; display:grid; grid-template-columns:1fr 1fr; gap:var(--stats-gap); align-items:stretch; margin-bottom:20px;}
/* cards top-level de stats (guía de rating, reto): separación acorde al resto */
/* Stats: cada columna ES la card (título dentro); el .card interno queda desnudo. */
.stats-col{min-width:0; display:flex; flex-direction:column;
  background:var(--card-glass); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--card-shadow); padding:18px;}
.stats-col > .card{background:none; border:none; box-shadow:none; padding:0; border-radius:0;}
.stats-col .section-title{margin-top:0;}
.stats-col > .card{margin-bottom:0; flex:1;}
.stats-year-head{display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:16px;}
.stats-year-head .section-title{margin:0;}
.year-stepper{display:flex; align-items:center; gap:5px;}
.year-cur{font-family:var(--font-display); font-weight:500; font-size:1.125rem; color:var(--ink); min-width:48px; text-align:center;}
.year-arrow{display:inline-flex; align-items:center; justify-content:center; width:28px; height:28px; border:1px solid var(--line-strong); background:transparent; border-radius:8px; color:var(--ink-soft); cursor:pointer; transition:.15s;}
.year-arrow:hover:not(:disabled){border-color:var(--ink); color:var(--ink);}
.year-arrow:disabled{opacity:.35; cursor:default;}
.year-arrow svg{width:15px; height:15px;}
/* ⚑ LAS DOS FILAS SE ALINEAN. La primera tarjeta de cada una mide lo mismo (40%), y en la de tres
   las dos siguientes ocupan JUNTAS, hueco incluido, lo que ocupa la única de la de dos (60%).
   Con calc y no con fr: los fr reparten el espacio LIBRE, o sea el ancho menos los huecos, y cada
   fila tiene un número distinto de huecos — 20px la de dos, 40px la de tres. La misma proporción
   en fr da por eso anchos distintos, y las columnas no cuadran entre filas. Restando a cada
   columna la parte del hueco que le toca, la cuenta sale exacta a cualquier ancho.
   El hueco es una variable (declarada arriba, en la regla base) para que las restas sigan
   cuadrando si se cambia: escrito dos veces, se separan. */
.stats-row.three{grid-template-columns:
  calc(40% - var(--stats-gap) * 0.4) calc(30% - var(--stats-gap) * 0.8) calc(30% - var(--stats-gap) * 0.8);}
/* ⚑ El grafico del anio es un SVG de viewBox fijo (720 de ancho): en una columna de un tercio se
   dibuja al 47%, y su texto de 11 unidades sale a 5,2px reales — dibujado, no legible. Las
   unidades del DIBUJO crecen cuando la tarjeta se estrecha, asi que lo que se mantiene es el
   tamanio en pantalla. Container query y no media query porque lo que decide es el ancho de la
   tarjeta, no el de la ventana: la misma tarjeta mide distinto segun en que fila este. */
/* La tarjeta se estira a la altura de la fila, y el grafico no la llena: centrado en vertical en
   vez de colgando de arriba con el hueco debajo. */
/* ⚑ `column`, y no es un detalle. La tarjeta era una fila flex para centrar el SVG dentro, y al
   añadirle debajo las cifras del año se pusieron AL LADO: el gráfico perdía 95px de ancho para
   dejarles sitio, y las cifras quedaban colgando a la derecha en vez de al pie. En columna el SVG
   recupera el ancho entero y sigue centrado — en columna `align-items` centra en horizontal y
   `justify-content` en vertical, que es justo lo que ya hacía. */
.stats-year-card{container-type:inline-size; display:flex; flex-direction:column;
  align-items:center; justify-content:center;}
.stats-year-card > svg{width:100%;}
@container (max-width:520px){ .stats-year-card svg text{font-size:19px;} }
@container (max-width:400px){ .stats-year-card svg text{font-size:24px;} }
/* Al estrecharse, la fila de tres NO se rompe en 2+1 (que dejaba una tarjeta suelta y perdía la
   alineación con la fila de abajo): pasa a las mismas dos columnas, con el gráfico ocupando la de
   40% a lo alto y las dos rosquillas apiladas en la de 60%. Las columnas siguen cuadrando entre
   filas y ninguna tarjeta se queda coja. */
@media(max-width:1180px){
  .stats-row.three{grid-template-columns:
    calc(40% - var(--stats-gap) * 0.4) calc(60% - var(--stats-gap) * 0.6);}
  .stats-row.three > :first-child{grid-row:span 2;}
}
/* Longest book read no necesita el mismo ancho que Puntuaciones dadas: una lista de cinco titulos
   frente a una rosquilla con diez barras al lado. 2fr/3fr y no 40%/60% porque los porcentajes se
   reparten el ancho ENTERO y el hueco de 20px se suma aparte, asi que la fila se saldria. */
.stats-row.split{grid-template-columns:
  calc(40% - var(--stats-gap) * 0.4) calc(60% - var(--stats-gap) * 0.6);}
@media(max-width:820px){ .stats-row, .stats-row.three, .stats-row.split{grid-template-columns:1fr;} }

.longest-list{display:flex; flex-direction:column;}
.longest-card{display:flex; gap:16px; align-items:center; cursor:pointer; transition:.15s;}
.longest-card:hover{opacity:.85;}
.longest-card.has-more{padding-bottom:14px; border-bottom:1px solid var(--line); margin-bottom:4px;}
.longest-info{min-width:0;}
.longest-pages{font-family:var(--font-display); font-weight:500; font-size:1.875rem; color:var(--ink); line-height:1;}
.longest-pages span{font-size:0.8125rem; color:var(--ink-soft); font-family:var(--font-body);}
.longest-info .book-title{margin-top:6px;}
.longest-row{display:flex; align-items:center; gap:13px; padding:10px 0; cursor:pointer; border-top:1px solid var(--line); transition:.12s;}
.longest-row:first-of-type{border-top:none;}
.longest-row:hover{opacity:.78;}
.longest-rank{font-family:var(--font-display); font-size:1.1875rem; color:var(--ink-faint); width:18px; text-align:center; flex-shrink:0;}
.longest-row-info{flex:1; min-width:0;}
.longest-row-info .book-title{line-height:1.25;}
.longest-row-pages{font-size:0.78125rem; font-weight:600; color:var(--ink-soft); white-space:nowrap;}
.rating-guide-item{display:flex; gap:12px; align-items:center; margin-bottom:14px;}
.rating-guide-item .stars-row{font-size:1.1875rem; flex-shrink:0;}
.rg-texts{flex:1; min-width:0; display:flex; flex-direction:column; gap:5px;}
.rg-title{font-weight:600; font-size:0.84375rem; border:1px solid transparent; border-radius:7px; padding:5px 8px; background:var(--bg); color:var(--ink);}
.rg-desc{font-size:0.78125rem; border:1px solid transparent; border-radius:7px; padding:5px 8px; background:var(--bg); color:var(--ink-soft);}
.rg-title:focus, .rg-desc:focus{outline:none; border-color:var(--ink); background:var(--surface);}
.rg-color{width:34px; height:34px; flex-shrink:0; border:1px solid var(--line-strong); border-radius:8px; padding:2px; background:var(--surface); cursor:pointer;}
.rg-color::-webkit-color-swatch{border:none; border-radius:5px;}
.rg-color::-webkit-color-swatch-wrapper{padding:0;}
/* Settings → colour tabs: 12 rows of swatch + meaning (two columns on wide screens) */
.color-legend{display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:10px 18px;}
@media (max-width:560px){ .color-legend{grid-template-columns:1fr;} }
.cl-item{display:flex; gap:10px; align-items:center;}
.cl-color{width:34px; height:34px; flex-shrink:0; border:1px solid var(--line-strong); border-radius:8px; padding:2px; background:var(--surface); cursor:pointer;}
.cl-color::-webkit-color-swatch{border:none; border-radius:5px;}
.cl-color::-webkit-color-swatch-wrapper{padding:0;}
.cl-label{flex:1; min-width:0; font-size:0.84375rem; border:1px solid var(--line-strong); border-radius:7px; padding:6px 9px; background:var(--bg); color:var(--ink);}
.cl-label:focus{outline:none; border-color:var(--ink); background:var(--surface);}
.section-title{font-family:var(--font-display); font-weight:500; font-size:1.25rem; margin:36px 0 16px; color:var(--ink);}
.section-title:first-child{margin-top:0;}
.legend-row{display:flex; align-items:center; gap:8px; font-size:0.78125rem; margin-bottom:9px; color:var(--ink-soft);}
.legend-swatch{width:13px; height:13px; border-radius:3px; flex-shrink:0;}
.hint{font-size:0.8125rem; color:var(--ink-soft); margin-bottom:16px; line-height:1.5;}
/* ===== sidebar chrome: collapse + language ===== */
.side-top{display:flex; align-items:center; gap:8px; margin-bottom:16px; padding-bottom:16px; border-bottom:1px solid var(--line);}
.side-top .brand{margin-bottom:0; padding:0 4px; flex:1; min-width:0; overflow:hidden;}
.side-bottom{margin-top:auto; padding-top:16px;}

.lang-mini{display:none; align-items:center; justify-content:center; width:38px; height:38px; border-radius:50%; border:1px solid var(--line); background:var(--surface); color:var(--ink-soft); font-size:0.6875rem; font-weight:700; letter-spacing:.5px; cursor:pointer; transition:.15s;}
.lang-mini:hover{border-color:var(--ink); color:var(--ink);}

/* ===== collection / series pile (shelf view) ===== */
.spine.stacked{width:25px; height:var(--sh,118px); border-radius:3px 3px 1px 1px; margin-left:-10px; box-shadow:inset 1.5px 0 0 rgba(255,255,255,.13), -2px 0 5px rgba(0,0,0,.2);}
.spine.stacked:first-child{margin-left:0;}
.stack:hover .spine.stacked{filter:brightness(1.06);}
.stack-count{position:absolute; top:-9px; right:-3px; background:var(--ink); color:var(--surface); font-size:0.5rem; font-weight:700; border-radius:999px; padding:1px 6px; box-shadow:0 1px 3px rgba(0,0,0,.28);}

/* ===== library view switch (segmented) ===== */
.view-switch{display:inline-flex; gap:2px; border:1px solid var(--line-strong); border-radius:999px; padding:3px; background:var(--surface);}
.view-switch button{display:flex; align-items:center; justify-content:center; width:34px; height:30px; border:none; background:none; border-radius:999px; color:var(--ink-soft); transition:.15s;}
.view-switch button:hover{color:var(--ink); background:var(--bg);}
.view-switch button.active{background:var(--ink); color:var(--surface);}
/* compact variant sitting next to the "Add book" button (matches .btn.small height ~29px) */
.sec-view-switch{padding:1px;}
.sec-view-switch button{width:30px; height:25px;}

/* ===== covers view: deck/stack for series ===== */
.cover-stack{cursor:pointer;}
/* All covers are the SAME size. The deck's back covers sit BEHIND the
   full-size front and only peek up-right within the column gap, so the
   front == a single cover and nothing covers the neighbour. */
.cover-deck{position:relative;}
.cover-deck .cover{margin:0; width:100%; aspect-ratio:2/3;}
.cover-deck .deck-front{position:relative; z-index:2; box-shadow:var(--card-shadow);}
.cover-deck .deck-back{position:absolute; inset:0; box-shadow:0 4px 10px rgba(28,26,23,.12);}
.cover-deck .deck-1{transform:translate(6px,-4px);}
.cover-deck .deck-2{transform:translate(12px,-8px);}
.cover-stack:hover .deck-1{transform:translate(7px,-4px);}
.cover-stack:hover .deck-2{transform:translate(13px,-9px);}
.deck-count{position:absolute; top:-7px; right:6px; z-index:3; background:var(--ink); color:var(--surface); font-size:0.625rem; font-weight:700; border-radius:999px; padding:1px 6px; box-shadow:0 1px 3px rgba(0,0,0,.25);}
.book-grid.covers, .book-grid.shelf{column-gap:34px;}
/* rich info under each cover (covers view) */
.cover-card .cover-meta{margin-top:9px; display:flex; flex-direction:column; gap:5px;}
.cover-meta-row{display:flex; align-items:center; justify-content:space-between; gap:6px; min-height:16px;}
.cmr-left{display:flex; align-items:center; gap:7px;}
.cover-meta-row .stars-row{font-size:0.8125rem;}
.fmt-icon{display:inline-flex; color:var(--ink-soft);}
.fmt-icon .icon{width:16px; height:16px;}
.cover-card .pill{align-self:flex-start;}
.cover-card .cover-meta .book-title{margin-top:0;}
.cover-card .cover-meta .book-author{margin-top:0;}
.series-expanded{grid-column:1/-1; border:1px dashed var(--rose); background:var(--rose-tint); border-radius:var(--radius); padding:14px 16px 16px; margin:2px 0;}
.series-expanded-head{display:flex; align-items:center; justify-content:space-between; margin-bottom:12px;}
.series-expanded-title{font-family:var(--font-display); font-size:1.125rem; color:var(--ink);}
.series-expanded-title .series-count{font-family:var(--font-body); font-size:0.75rem; font-weight:700; color:var(--rose-deep); margin-left:4px;}

/* ===== releases (most anticipated) ===== */
.release-grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:20px;}
.release-card{cursor:pointer; text-align:left;}
.release-card .cover{margin-bottom:9px;}
.release-meta{display:flex; flex-wrap:wrap; gap:6px; align-items:center; margin-top:7px;}
.release-date{font-size:0.6875rem; color:var(--ink-soft);}
.countdown{font-size:0.625rem; font-weight:700; letter-spacing:.3px; padding:2px 9px; border-radius:999px; background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep)); border:1px solid transparent;}
.countdown.soon{background:var(--rose); color:#fff; border-color:transparent; box-shadow:0 2px 8px color-mix(in srgb, var(--rose) 45%, transparent);}
.countdown.out{background:var(--ink); color:var(--surface); border-color:var(--ink);}

/* ===== dashboard — composición en grid, cards sin borde, color dentro ===== */
.dash{display:flex; flex-direction:column; gap:18px;}
/* fila de 4 stat cards y filas mixtas [ancha ×2][1][1] */
/* minmax(0,1fr) y no 1fr: con cuatro cifras dentro, el minimo de contenido de la tarjeta de
   perfil empujaba su columna a 312px y le robaba el ancho a la de al lado (267 frente a 279).
   Con el minimo a cero las cuatro columnas quedan iguales y lo que cede es el contenido. */
.dash-stats{display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:18px; align-items:stretch;}
.dash-stats > *{margin:0;}
/* el calendario de la racha vive ahora en esta fila y pide dos columnas, como en la que estaba */
.dash-stats .streak-card{grid-column:span 2;}
/* la regla de reinicio, ahora bajo el calendario */
/* La regla de reinicio se lee una vez y decide si el cero de arriba se entiende o parece un fallo:
   a 0,6875rem era una nota al pie de una nota al pie. */
/* La pista de los circulos: mismo peso que la regla de reinicio — las dos son notas al pie — pero
   pegada a los circulos, que es de lo que habla. */
.streak-tap-hint{margin-top:14px; font-size:0.8125rem; font-weight:500; line-height:1.45;
  color:var(--ink-soft); text-align:center;}
/* El record y la regla son un par: la cifra primero, la explicación debajo y pegada a ella, no
   dos notas sueltas separadas por el mismo aire. Y el récord pesa más — es un dato, no una nota. */
/* Mismo color que el contador del encabezado, EN SUS DOS ESTADOS: gris cuando no hay racha viva,
   acento cuando la hay. Hablan del mismo número — uno el de ahora, otro el mejor que hubo — y dos
   colores distintos para lo mismo se leen como dos cosas distintas. */
.streak-record{margin-top:16px; font-size:0.875rem; font-weight:600; line-height:1.35;
  color:var(--ink-faint); text-align:center;}
.streak-record.active{color:var(--rose-deep);}
.streak-hint{margin-top:16px; font-size:0.8125rem; font-weight:500; line-height:1.45; color:var(--ink-soft);}
.streak-record + .streak-hint{margin-top:5px;}
.dash-row{display:grid; grid-template-columns:repeat(4,1fr); gap:18px; align-items:stretch;}
.dash-row > *{grid-column:span 1; margin:0;}
.dash-row .streak-card{grid-column:span 2;}
/* ⚑ Tres, no dos. Al subir la tarjeta de perfil a la primera fila, su fila se quedó en 2+1 de
   cuatro columnas y sobraba un hueco de 296px a la derecha. La columna suelta se la lleva
   "Leyendo ahora", que es la unica de esa fila cuyo contenido crece — el medidor es un aro de
   tamanio fijo y mas ancho solo le anade aire alrededor. En los breakpoints de abajo ambas
   vuelven a sus tramos, asi que esto solo aplica al ancho grande. */
/* ⚑ Las anchuras por fila, y el ORDEN importa: `.dash-row.two > *` y `.dash-row .dash-reading-card`
   tienen la misma especificidad, asi que gana la ultima escrita. Las de fila concreta van despues
   de las generales, o "Leyendo ahora" se llevaria tres columnas dentro de una fila de dos.
   Fila 2 (mitad y mitad) la resuelve `.two`. Fila 3: el grafico del anio se queda con tres y la
   media con una — es una cifra sola y mas ancho solo le anade aire. Fila 4: la cita se lleva dos,
   que es texto y agradece la medida; generos y autores, una cada uno, como antes. */
.dash-row .dash-reading-card{grid-column:span 3;}
.dash-row .quote-card{grid-column:span 2;}
.dash-row.two > *{grid-column:span 2;}
/* Tres iguales. ⚑ Y hay que repetir `.dash-reading-card`: la regla de arriba le da `span 3`, que
   en una rejilla de tres columnas se comeria la fila entera. Gana esta por especificidad (tres
   clases contra dos), pero se escribe explicita porque el dia que alguien reordene el fichero la
   especificidad es lo unico que lo sostiene. */
/* La marca de "proxima lectura" en la tarjeta del panel.
   ⚑ NO se reutiliza `.next-badge`: esa va en position:absolute (necesita un ancestro posicionado
   que aqui no existe) y pinta blanco sobre --rose, que es 2,3:1 en varios temas — el mismo fallo
   que este proyecto ya arreglo en las statcards. Esto es una etiqueta, no una pegatina: mismo
   tratamiento que `.ns-series`, sin posicionar y sin riesgo de contraste. */
.tbr-next-tag{font-size:0.6875rem; font-weight:700; letter-spacing:.02em; color:var(--rose-deep);
  margin-bottom:2px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}

.dash-row.trio{grid-template-columns:repeat(3,1fr);}
.dash-row.trio > *, .dash-row.trio .dash-reading-card{grid-column:span 1;}
/* ⚑ FILAS DE ALTO FIJO, y solo donde las tarjetas van una al lado de otra (≥1001px).
   Antes cada tarjeta medía lo que pedía su contenido y `align-items:stretch` estiraba a la más
   alta: pasar de tres lecturas en curso a cinco alargaba la fila 255px y el panel entero daba un
   salto. Las cifras salen de medir lo que ocupan hoy: "Leyendo ahora" con una a tres lecturas
   (305–311px) y la fila de favoritos con sus cuatro barras de género (184px). Lo que no quepa se
   desplaza DENTRO de la tarjeta; la tarjeta ya no crece.
   ⚑ Por debajo de 1001px cada tarjeta ocupa el ancho completo y NO se toca la altura: en el móvil
   una tarjeta corta con scroll dentro es peor que una tarjeta larga, y no hay vecina con la que
   igualarse. Ese 1001 es el único sitio donde vive el corte — fitDashQuote() lo lee de aquí
   (pregunta si la tarjeta es flex) en vez de repetir el número en el JS. */
@media(min-width:1001px){
  .dash-row.two > *, .dash-row.trio > *, .dash-row.favs > *{display:flex; flex-direction:column;}
  .dash-row.two > *, .dash-row.trio > *{height:312px;}
  .dash-row.favs > *{height:184px;}
  /* ⚑ `min-height:0` o no aparece el scroll: un hijo flex no baja de su contenido mínimo por
     defecto, así que la lista empujaría la tarjeta en vez de desplazarse dentro. */
  .dash-row.two .now-reading-list,
  .dash-row.two .dash-rel-list,
  .dash-row.trio .now-reading-list,
  .dash-row.trio .dash-rel-list,
  .dash-row.favs .dash-bar-list,
  .dash-row.favs .quote-text{flex:1; min-height:0; overflow-y:auto;}
}
/* ⚑ Y por debajo de 1001px, un TECHO en vez de una altura fija. La tarjeta sigue creciendo con
   su contenido — dos libros en curso siguen midiendo 308px — pero deja de crecer sin límite: once
   lanzamientos hacían una tarjeta de 919px, más de una pantalla de teléfono para una sola tarjeta.
   320px es una fila de portadas (231px) más media de la siguiente: el corte se VE, y un corte que
   se ve es lo que dice que hay más abajo. Aquí no hacen falta flex ni min-height:0 como arriba —
   un max-height sobre un bloque normal ya crea el contexto de desplazamiento. */
@media(max-width:1000px){
  .dash-row.two .now-reading-list,
  .dash-row.two .dash-rel-list,
  .dash-row.trio .now-reading-list,
  .dash-row.trio .dash-rel-list{max-height:320px; overflow-y:auto;}
}
/* dentro del dashboard las cards no llevan borde: fondo + sombra suave */
.dash .card{border:none; box-shadow:0 6px 20px rgba(18,40,58,.06);}
/* stat cards */
.statcard{background:var(--card-solid); border-radius:var(--radius); padding:20px; box-shadow:0 6px 20px rgba(18,40,58,.06);
  display:flex; flex-direction:column; gap:12px; min-height:118px;}
.statcard-top{display:flex; align-items:center; justify-content:space-between; gap:8px;}
.statcard-lbl{font-size:0.75rem; color:var(--ink-soft); font-weight:600; letter-spacing:.2px;}
.statcard-chip{font-size:1.05rem; line-height:1;}
.statcard-num{font-family:var(--font-display); font-weight:600; font-size:2rem; line-height:1; color:var(--ink); margin-top:auto;}
.statcard-sub{font-size:0.6875rem; color:var(--ink-faint); font-weight:600;}
/* la regla de reinicio de la racha: mas pequena y mas floja que el "Record", porque se lee una vez
   y luego solo esta ahi por si vuelve la duda */
.statcard-hint{font-size:0.625rem; font-weight:500; line-height:1.35; color:var(--ink-faint); margin-top:3px;}
/* ⚑ Tinta por luminancia, no #fff fijo. --rose es un acento de tono MEDIO: el blanco daba 2,32:1
   en Clasico (el tema por defecto) y fallaba en DOCE de los quince, con la etiqueta a 0,75rem, que
   es texto pequeno y pide 4,5:1. La onda y el subtitulo se derivan de la misma tinta para que no
   haya que acordarse de tres sitios. */
.statcard.accent{background:var(--rose); color:var(--on-rose-ink, #fff);}
.statcard.accent .statcard-lbl, .statcard.accent .statcard-num{color:var(--on-rose-ink, #fff);}
.statcard.accent .statcard-sub, .statcard.accent .statcard-hint{color:color-mix(in srgb, var(--on-rose-ink, #fff) 78%, transparent);}
.statcard.accent .statcard-wave{width:100%; height:30px; color:color-mix(in srgb, var(--on-rose-ink, #fff) 72%, transparent); margin-top:6px;}
/* medidor del reto */
.dash-gauge{display:flex; flex-direction:column; align-items:center; text-align:center;}
.dash-gauge .ring{width:158px; height:158px; margin:4px 0 6px;}
/* ⚑ El aire bajo el anillo se pone AQUI y no en el margen del .ring: la tarjeta es un flex en
   columna, y en un contenedor flex los margenes NO se funden — asi que estos 10px se SUMAN a los
   6px que el anillo ya lleva debajo. Ponerlos en el anillo tambien empujaria a cualquier otra
   cosa que se le cuelgue algun dia. */
/* ⚑ Dos lineas, no una: arriba el campo con su unidad, debajo el boton, los dos centrados.
   Se hace con un GRUPO dentro (.dash-goal-set) y no con flex-wrap: para que el boton saltase de
   linea el con wrap habria que darle flex-basis:100%, y entonces se estiraria de lado a lado de
   la tarjeta en vez de quedarse de su tamanio. Con el grupo, cada linea mide lo que mide. */
.dash-goal-row{display:flex; flex-direction:column; align-items:center; gap:10px; margin-top:10px;}
.dash-goal-set{display:flex; align-items:center; gap:8px;}
/* La unidad del reto: dice EN QUE se cuenta la meta. No se encoge ni se parte en dos lineas —
   es una palabra, y si la fila aprieta lo que cede es el campo, no la etiqueta. */
.goal-unit{font-size:0.8125rem; color:var(--ink-soft); white-space:nowrap; flex-shrink:0;}
.dash-goal-row input{width:66px; box-sizing:border-box; border:1px solid var(--line-strong); border-radius:8px; padding:6px 8px; font-size:0.8125rem; text-align:center;}
/* ⚑ Y sin las flechitas del <input type=number>. No es limpieza: Chrome las mete DENTRO de la
   caja al apuntarla, asi que con el texto centrado el numero daba un salto lateral al pasar el
   raton por encima. Se teclea igual, y las flechas de arriba/abajo del teclado siguen valiendo. */
.dash-goal-row input::-webkit-outer-spin-button,
.dash-goal-row input::-webkit-inner-spin-button{-webkit-appearance:none; margin:0;}
.dash-goal-row input{-moz-appearance:textfield; appearance:textfield;}
/* tarjeta de perfil */
.dash-profile{display:flex; flex-direction:column; align-items:center; text-align:center; gap:5px; padding:26px 18px; justify-content:center;}
/* ⚑⚑ INVERTIDA (26-08): abre el panel, asi que es la que dice de quien es esto. El fondo es
   --rose-deep, el MISMO color que la cifra de la racha y que los discos de los dias leidos de la
   tarjeta de al lado — o sea que no hay un color nuevo en el panel, hay uno que ya estaba usado
   ahi al lado, ahora en grande.
   ⚑ La tinta es --on-rose-deep-ink, que NO se escribe aqui: la calcula applyTheme() con
   inkOn(--rose-deep) y la deja en linea sobre :root. Sale negra en los cinco temas cuyo acento es
   claro (Dark Academia, Slow Burn, One More, Space Opera, Forest) y blanca en los otros diez. Un
   #fff fijo aqui seria blanco sobre amarillo en One More.
   ⚑⚑ Y NO HAY TINTA SECUNDARIA, que es lo que este color impone: sobre --rose-deep la tinta a
   plena opacidad ya solo da 4.92:1 en el peor tema (Slick, #2A6BDF), asi que CUALQUIER
   desvanecido cae por debajo del 4.5:1 — hasta el 90% se queda en 4.30. Las tres variables
   apuntan al mismo color a proposito y la jerarquia la hacen el tamanio y las versalitas. Si
   alguna vez se le pone aqui un --ink-soft translucido, se rompe en los diez temas de acento
   oscuro a la vez. */
.card.dash-profile{
  background:var(--rose-deep); border-color:transparent;
  --ink:var(--on-rose-deep-ink);
  --ink-soft:var(--on-rose-deep-ink);
  --ink-faint:var(--on-rose-deep-ink);
  --line:color-mix(in srgb, var(--on-rose-deep-ink) 26%, transparent);
  --line-strong:color-mix(in srgb, var(--on-rose-deep-ink) 45%, transparent);
}
.dash-profile-avatar{width:64px; height:64px; border-radius:50%; background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep));
  display:flex; align-items:center; justify-content:center; font-family:var(--font-display); font-weight:600; font-size:1.55rem; margin-bottom:6px;}
/* ⚑ El disco NO puede seguir siendo --rose-tint aqui: es un tinte del MISMO tono que el fondo de
   la tarjeta, asi que encima de ella casi desaparece. Pasa a --surface con la letra en
   --rose-deep, y ese par ya esta garantizado: --rose-deep tiene que leerse como texto de enlace
   sobre --surface, cosa que vigila tools/rose-deep-audit.mjs. Peor de los quince: 4.86:1, y de
   paso el disco se ve claro sobre el color en los quince. */
.dash-profile .dash-profile-avatar{background:var(--surface); color:var(--rose-deep);}
/* La foto llena el disco. ⚑ object-fit:cover Y el recorte cuadrado del optimizador: el recorte
   decide QUE pixeles se guardan y esto decide como se pintan. Sin lo segundo, una foto que no
   fuera perfectamente cuadrada por un pixel saldria estirada; sin lo primero, se guardarian
   pixeles que nunca se ven — y cada byte de esta foto viaja cifrado en cada guardado. */
.dash-profile-avatar{overflow:hidden;}
.dash-profile-avatar img{width:100%; height:100%; object-fit:cover; display:block;}

/* ===== el editor de la foto, en el modal de perfil =====
   ⚑ El disco de aqui NO es el de la tarjeta: alli el fondo es --rose-deep y aqui es la superficie
   del modal, asi que vuelve al par de siempre (--rose-tint con la tinta que calcula applyTheme).
   Reutilizar la clase de la tarjeta pintaria un disco claro sobre un fondo claro. */
.avatar-edit{display:flex; align-items:center; gap:14px;}
.avatar-prev{width:56px; height:56px; flex-shrink:0; border-radius:50%; overflow:hidden;
  background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep));
  display:flex; align-items:center; justify-content:center;
  font-family:var(--font-display); font-weight:600; font-size:1.35rem;}
.avatar-prev img{width:100%; height:100%; object-fit:cover; display:block;}
.avatar-edit-side{min-width:0; display:flex; flex-direction:column; gap:6px;}
.avatar-edit-btns{display:flex; gap:8px; flex-wrap:wrap;}
.avatar-msg{font-size:0.75rem; line-height:1.4; color:var(--ink-faint);}
/* El error usa la MISMA tinta que el resto de avisos del modal, que applyTheme calcula por tema:
   un rojo fijo aqui es ilegible en los cinco temas oscuros. */
.avatar-msg.bad{color:var(--rose-deep-on-tint, var(--rose-deep)); font-weight:600;}
.dash-profile-name{font-family:var(--font-display); font-weight:600; font-size:1.125rem; color:var(--ink);}
.dash-profile-role{font-size:0.8125rem; color:var(--ink-soft);}
/* Y las cifras envuelven: cuatro en una linea no caben en un cuarto de panel, asi que se ponen
   en dos por dos en vez de estirar la tarjeta. */
.dash-profile-stats{display:flex; flex-wrap:wrap; justify-content:center; gap:12px 22px; margin-top:14px;}
.dash-profile-stats > div{display:flex; flex-direction:column; gap:2px;}
.dp-num{font-family:var(--font-display); font-weight:600; font-size:1.2rem; color:var(--ink);}
.dp-lbl{font-size:0.625rem; color:var(--ink-faint); text-transform:uppercase; letter-spacing:.5px;}
/* responsive: en pantallas medianas el grid de 4 baja a 2, y las anchas ocupan todo */
@media(max-width:1000px){
  /* ⚑ A dos columnas el calendario de la racha pide las dos, asi que se va solo a su linea y deja
     al perfil y al ritmo cada uno con media linea vacia al lado. `dense` deja que el que viene
     detras rellene el hueco que quedo delante: perfil + ritmo arriba, calendario debajo. */
  .dash-stats{grid-template-columns:repeat(2,minmax(0,1fr)); grid-auto-flow:row dense;}
  .dash-row{grid-template-columns:repeat(2,1fr);}
  .dash-row .dash-reading-card, .dash-row .streak-card,
  .dash-row.pair > *, .dash-row.favs > *{grid-column:span 2;}
  /* ⚑ A dos columnas la fila de tres tiene que volver a DOS pistas, o el navegador conserva las
     tres y cada tarjeta se queda en un tercio de un panel ya estrecho. */
  .dash-row.trio{grid-template-columns:repeat(2,1fr);}
  .dash-row.trio > *, .dash-row.trio .dash-reading-card{grid-column:span 2;}
  /* ⚑ Y el medidor tambien, aqui. A dos columnas "Leyendo ahora" ocupa la linea entera y el
     medidor se quedaba solo en la siguiente con media linea vacia al lado: antes tenia la tarjeta
     de perfil de companiera, y esa se ha ido a la primera fila. */
  .dash-row .dash-gauge{grid-column:span 2;}
}
.dash-block{margin-bottom:18px;}
/* La tarjeta hospeda el velo, asi que necesita ser el marco de referencia: sin position el
   inset:0 del .cover-hover se mediria contra la pagina. overflow:hidden para que el cristal se
   corte por el radio de la tarjeta y no por sus esquinas cuadradas. */
.quote-card{cursor:pointer; position:relative; overflow:hidden;}
/* El velo de las portadas se enciende con :hover de .book-card/.release-card; aqui el que manda
   es la propia tarjeta de la cita. */
.quote-card:hover .quote-hover{opacity:1;}
.quote-text{font-family:var(--font-quote); font-size:1.3125rem; line-height:1.5; color:var(--ink); margin:0;}
.quote-src{margin-top:10px; font-size:0.78125rem; color:var(--ink-soft); font-weight:600;}
.quote-empty{display:flex; flex-direction:column; align-items:flex-start; gap:9px;}
.quote-empty-text{font-style:italic; color:var(--ink-faint); font-size:0.95rem;}
.quote-pick-link{background:none; border:none; padding:0; color:var(--rose-deep); font-weight:600; font-size:0.84rem; cursor:pointer;}
.quote-pick-link:hover{text-decoration:underline;}
/* estado vacío del dashboard para usuarias nuevas */
.dash-empty{display:flex; align-items:center; justify-content:center; min-height:60vh; padding:24px;}
.dash-empty-inner{text-align:center; max-width:480px; display:flex; flex-direction:column; align-items:center; gap:14px;}
.dash-empty-title{font-family:var(--font-display); font-size:2rem; font-weight:500; color:var(--ink); line-height:1.2;}
/* La ilustracion de la bienvenida: ancho fijo en pantalla (el archivo trae el doble, para retina) y
   nunca mas ancha que el hueco en un movil. width/height del <img> reservan el sitio antes de cargar. */
.dash-empty-img{width:min(320px, 80vw); height:auto; display:block;}
.dash-empty-inner .btn{margin-top:6px;}
/* Las dos entradas de una biblioteca vacia, lado a lado; en un movil estrecho, una debajo de otra.
   ⚑⚑ EL 480px DE .dash-empty-inner ESTA MEDIDO, no elegido a ojo. La fila entera (los dos botones mas
   el hueco de 10px) mide, con este CSS y con Mulish cargada de verdad: en 388, es 423, de 433, fr 441.
   Con el tope anterior de 420px solo cabia el ingles — por eso la duenia veia dos lineas en español,
   frances y aleman (2026-09-12) y una sola en ingles. La columna se encoge hasta su contenido, y su
   contenido mas ancho es justamente esta fila, asi que el tope de la columna ES el ancho disponible
   para los botones: 480 deja 39px de sobra sobre el peor caso. Si se traduce a algo mas largo, o se
   cambia la fuente o el padding del boton, se vuelve a medir — la cuenta es `boton + 10 + boton`.
   ⚑ Sin `width:max-content` a proposito: sobra. Con `wrap`, el max-content de esta fila ya es la suma
   de los dos botones, que es lo que hace que la columna pida 441px en frances y quepa en 480. */
.dash-empty-actions{display:flex; flex-wrap:wrap; justify-content:center; gap:10px;}
.ring{width:150px; height:150px;}
.dash-bar-row{display:flex; align-items:center; gap:10px; margin-bottom:10px; font-size:0.8125rem;}
.dash-bar-name{width:96px; flex-shrink:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--ink);}
.dash-bar-track{flex:1; height:8px; background:var(--bg); border-radius:999px; overflow:hidden;}
.dash-bar-fill{display:block; height:100%; background:var(--rose-deep); border-radius:999px;}
.dash-bar-val{width:22px; text-align:right; color:var(--ink-soft); font-weight:600;}

/* ===== Estadísticas: tarjetas nuevas ===== */
/* Las dos cifras del año, bajo el gráfico. Una debajo de otra y no en fila: son dos frases
   cortas, y en fila con un punto en medio se leían como una lista de la que el ojo no sabe dónde
   empieza cada dato. El AÑO no va aquí — lo dice el selector de la cabecera, a un palmo. */
.stats-year-note{display:flex; flex-direction:column; align-items:center; gap:3px;
  margin-top:12px; font-size:0.875rem; color:var(--ink-soft);}
.stats-year-note strong{font-weight:700; color:var(--ink);}
/* El delta va un escalon por debajo de las dos cifras: es contexto, no un dato mas. Y en el gris
   de siempre, sin verde ni rojo — este cuaderno no puntua a nadie por leer menos que el anio
   pasado. */
.syn-delta{margin-top:3px; font-size:0.8125rem; color:var(--ink-faint);}

/* Días por libro: una cifra grande y las dos puntas debajo. Las puntas son libros concretos, y
   por eso se pueden abrir — el dato interesa por el libro que hay detrás, no por el número. */
.days-card{display:flex; flex-direction:column;}
.days-hero{display:flex; align-items:baseline; gap:6px;}
.days-num{font-family:var(--font-display); font-weight:600; font-size:2.4rem; line-height:1; color:var(--ink);}
.days-unit{font-size:0.9375rem; font-weight:600; color:var(--ink-soft);}
.days-lbl{margin-top:6px; font-size:0.75rem; font-weight:600; letter-spacing:.2px;
  text-transform:uppercase; color:var(--ink-faint);}
.days-rows{margin-top:16px; display:flex; flex-direction:column; gap:8px;}
/* ⚑ Sin margen negativo. Sangrar la fila 6px a cada lado para que el fondo del hover llegue al
   borde la sacaba 12px fuera de la caja de contenido, y la tarjeta entera se volvia desplazable
   en horizontal — un scroll invisible pero real. El relleno solo, que va por dentro, no. */
.days-row{display:grid; grid-template-columns:auto 1fr auto; align-items:baseline; gap:10px;
  padding:4px 6px; border-radius:8px; cursor:pointer; transition:.12s;}
.days-row:hover{background:var(--bg);}
.days-row-lbl{font-size:0.75rem; font-weight:600; color:var(--ink-faint);}
.days-row-book{font-size:0.84375rem; color:var(--ink); min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.days-row-val{font-size:0.8125rem; font-weight:600; color:var(--ink-soft); white-space:nowrap;}

/* El largo medio cierra la lista de los más largos: misma tarjeta, misma unidad. */
.longest-avg{margin-top:14px; padding-top:12px; border-top:1px solid var(--line);
  font-size:0.8125rem; color:var(--ink-soft);}

/* Los chiles no se recortan a la mitad como haría un nombre largo. */
.spice-name{width:auto; flex-shrink:0; letter-spacing:1px;}
/* ⚑ .dash-bar-val mide 22px, que es lo que pide un contador de una cifra. Una nota media ("4,5★")
   son cuatro caracteres: no cabia y desbordaba la tarjeta por 8px. */
.bar-val-wide{width:auto; min-width:38px;}

/* ===== Panel: las tres tarjetas nuevas ===== */
/* Fila de dos de alto libre. `.two` no vale: esa lleva la altura fija de 312px de "Leyendo ahora"
   y aquí no hay nada que comparar con lo de al lado. */
.dash-row.pair > *{grid-column:span 2;}
/* ⚑ Y la fila de alto fijo también reparte a mitades. La cita ya pedía dos columnas por su cuenta
   (`.dash-row .quote-card`), pero su compañera no: se quedaba en una de cuatro y dejaba un hueco
   de 279px a la derecha. Cada tarjeta de una fila de DOS necesita su tramo, no sólo la que
   casualmente ya lo tenía. */
.dash-row.favs > *{grid-column:span 2;}

/* La identidad lectora, bajo el nombre: lo que queda de las dos tarjetas de barras que se han ido
   a Estadísticas. Es una etiqueta, no un dato — por eso va con la letra del rol y no de las cifras. */
.dash-profile-id{display:flex; flex-direction:column; gap:2px; margin-top:8px;
  font-size:0.75rem; color:var(--ink-faint); line-height:1.4;}

/* Cómo se ha sentido el mes. La tarjeta entera lleva al Feels: es un resumen, no un destino. */
.dash-feels{cursor:pointer;}
.feels-count{font-family:var(--font-display); font-size:1.375rem; font-weight:600; color:var(--ink);}
.feels-moods{display:flex; gap:14px; margin-top:12px;}
.feels-mood{display:inline-flex; align-items:center; gap:5px;}
.fm-emoji{font-size:1.5rem; line-height:1;}
.fm-n{font-size:0.8125rem; font-weight:700; color:var(--ink-soft);}
.feels-last{margin:14px 0 0; font-family:var(--font-quote); font-size:0.9375rem; line-height:1.5;
  color:var(--ink-soft);}

/* ⚑ TECHO, no altura fija. La fila "pair" no fija altura a sus tarjetas —la rejilla ya las
   iguala entre si— asi que aqui basta con impedir que crezca sin limite: con ocho sagas a medias
   la tarjeta se comia la pantalla, el mismo problema que tuvieron las otras listas del panel.
   DOS entradas y el resto al scroll. El numero sale de MEDIR, no de tantear: la primera fila mide
   108px y las siguientes 115 (llevan los 12px de la separacion), con 6px de hueco, asi que dos
   caben en 232. Se corta en 240 porque a esa altura asoman 3px de la tercera —su regla— y su
   portada sigue 9px por debajo del corte: la portada empieza tras 12px de relleno, asi que hay
   margen de sobra para que ni un pixel de ella se cuele. El corte se VE y dice que hay mas abajo,
   sin ensenar una tercera portada a medias.
   (Comprobado a mano: 236 no ensena nada de la tercera, 238 ya ensena la regla, y hasta 244 la
   portada sigue sin aparecer.)
   ⚑ Si algun dia una saga de nombre largo parte su titulo en dos lineas, esa fila crecera y se
   vera algo menos de la segunda. Es degradar, no romper. */
.ns-list{display:flex; flex-direction:column; max-height:240px; overflow-y:auto;}
/* Separador entre sagas: son cosas distintas, no una lista continua de libros. */
.ns-list .ns-body + .ns-body{border-top:1px solid var(--line); margin-top:6px; padding-top:12px;}

/* Lo siguiente de la saga y Hace un año comparten forma: portada pequeña + texto al lado. */
.ns-body, .ya-body{display:flex; gap:14px; align-items:flex-start; cursor:pointer; border-radius:10px;
  padding:6px; margin-top:2px; transition:.12s;}
.ns-body:hover, .ya-body:hover{background:var(--bg);}
.ns-cover{width:64px; flex-shrink:0; font-size:0.5625rem;}
.ya-cover{width:54px; flex-shrink:0; font-size:0.5rem;}
.ns-info, .ya-info{min-width:0; flex:1;}
.ns-series{font-size:0.75rem; font-weight:700; letter-spacing:.3px; text-transform:uppercase;
  color:var(--rose-deep); margin-bottom:3px;}
.ya-lead{font-size:0.75rem; font-weight:600; color:var(--ink-faint); margin-bottom:3px;}
.ns-progress{display:flex; align-items:center; gap:9px; margin-top:10px;}
.ns-track{flex:1; height:7px; background:var(--bg); border-radius:999px; overflow:hidden;}
.ns-fill{display:block; height:100%; background:var(--rose-deep); border-radius:999px;}
.ns-count{font-size:0.75rem; font-weight:600; color:var(--ink-soft); white-space:nowrap;}
/* El texto de un momento recordado. Va en la letra de las citas porque son palabras que la
   lectora eligio guardar, no un dato de la ficha — y se recorta a dos lineas para que la tarjeta
   siga midiendo lo mismo por largo que fuera el momento. */
.ya-moment{margin:2px 0 4px; font-family:var(--font-quote); font-size:0.9375rem; line-height:1.45;
  color:var(--ink); display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;}
.ya-mood{margin-right:6px;}
/* Una cita del diario puede traer una palabra larguisima o una URL pegada; sin esto desbordaria
   la tarjeta de alto fijo del panel. */
.ya-moment{overflow-wrap:anywhere;}

/* ===== Seccion Recuerdos ===== */
/* Los recuerdos se agrupan por cuanto hace, y el anio va FUERA de la tarjeta, como los titulos de
   seccion del resto de la app: dentro competiria con el "hace un anio" de cada linea y se leeria
   dos veces lo mismo. */
/* El paseo por fechas. Va arriba del todo, antes de los grupos: es el mando de la seccion, no un
   filtro escondido al final. */
.mem-date{display:flex; align-items:center; gap:10px; margin:0 0 22px; flex-wrap:wrap;}
.mem-date input[type="date"]{border:1px solid var(--line-strong); border-radius:9px; padding:7px 10px;
  font-size:0.875rem; font-family:inherit; background:var(--surface); color:var(--ink); min-height:38px;}
/* En el telefono el campo crece HASTA LOS 44px de verdad, porque un <input> no admite ::after y no
   puede llevar la zona invisible que agranda las flechas. Y va AQUI, pegado a la regla que pisa: en
   el bloque movil comun quedaria por encima de ella y perderia por orden de aparicion. */
@media(max-width:900px){ .mem-date input[type="date"]{min-height:44px;} }

.mem-group{margin-bottom:26px;}
.mem-year{font-family:var(--font-display); font-size:1.0625rem; font-weight:600; color:var(--rose-deep);
  margin:0 2px 10px;}
.mem-list{padding:10px 12px;}
/* Dentro de la lista cada recuerdo es una fila con su propio hueco; el separador es una linea, no
   un margen, para que se vea que pertenecen al mismo anio. */
.mem-list .ya-body + .ya-body{border-top:1px solid var(--line); margin-top:4px; padding-top:12px;}
/* En la seccion el momento puede respirar: la tarjeta del panel lo recorta a dos lineas porque la
   fila tiene alto fijo, y aqui no hay tal limite. */
.mem-list .ya-moment{-webkit-line-clamp:4;}
/* La fila del interruptor de correo: casilla arriba a la izquierda y el texto al lado, para que
   la explicación de dos líneas no empuje la casilla al centro vertical. */
.set-mail-row{display:flex; align-items:flex-start; gap:11px; cursor:pointer;}
.set-mail-row input{margin-top:3px; flex-shrink:0;}
.set-mail-row .hint{margin:0;}

/* La parte abandonada de cada barra: mismo color del nivel, mas clara y con un borde a la
   izquierda que la separa. No un color propio — el color CODIFICA la nota, y darle uno distinto
   diria que un 4 abandonado es otra nota, cuando es la misma opinion sobre un libro que no se
   acabo. Y no solo opacidad: en los temas oscuros bajar la opacidad la acerca al fondo en vez de
   separarla, asi que ademas lleva el filete. */
.rdist-fill-dnf{opacity:.45; border-left:2px solid var(--surface);}
.rdist-note{margin-top:12px; font-size:0.75rem; line-height:1.5; color:var(--ink-faint);}

/* El aviso bajo el campo de portada. Tres estados: comprobando, copia guardada, y el enlace que
   se quedara pidiendo la imagen a un tercero. El de aviso NO es rojo: no es un error, es una
   consecuencia que la lectora puede aceptar o cambiar subiendo el fichero. */
.cover-note{margin-top:8px; font-size:0.75rem; line-height:1.5; color:var(--ink-faint);}
.cover-note.ok{color:var(--ink-soft);}
/* .cover-note.warn: el recuadro lo pone .note-box, con el resto de avisos de modal */

/* stats: rating distribution bars */
.rdist-card{display:flex; flex-direction:column; justify-content:center;}
.rdist-row{display:flex; align-items:center; gap:12px; margin-bottom:13px;}
.rdist-row:last-child{margin-bottom:0;}
/* la fila lleva ahora la etiqueta de la guia junto a las estrellas: en media estrella es donde
   vive el «Casi ...» que escribio Laura, y sin el texto una fila de 3,5 y una de 4 se distinguen
   solo por medio icono */
.rdist-label{flex-shrink:0; display:flex; align-items:center; gap:8px; min-width:0;}
.rdist-tier{font-size:0.6875rem; color:var(--ink-soft); white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis; max-width:18ch;}
/* en pantalla estrecha la etiqueta cede sitio a la barra, que es lo que se compara */
.rdist-label .stars-row{transform:scale(.82); transform-origin:left center;}
/* ⚑ La barra no puede ser lo que ceda. En una tarjeta de ancho medio, las estrellas, la etiqueta y
   las dos cifras se llevaban el sitio y el carril se quedaba en 21px: una barra de 21px no compara
   nada, que es su unico trabajo. Con un minimo propio, lo que se acorta es la etiqueta. */
.rdist-track{flex:1 1 auto; min-width:70px; height:10px; background:var(--bg); border-radius:999px; overflow:hidden;}
.rdist-fill{display:block; height:100%; border-radius:999px; min-width:2px; transform-origin:left center; animation:rdistGrow .45s cubic-bezier(.22,1,.36,1) both;}
/* la fila lleva ahora las DOS cifras que antes estaban en dos tarjetas: el porcentaje manda y el
   recuento va debajo, mas flojo, porque se consulta y no se compara */
.rdist-val{flex-shrink:0; width:96px; text-align:right; font-size:0.8125rem; font-weight:700; color:var(--ink);
  display:flex; flex-direction:column; align-items:flex-end; line-height:1.25;}
.rdist-count{font-size:0.6875rem; font-weight:500; color:var(--ink-faint); white-space:nowrap;}
/* La cifra de abandonos, en su propio renglon bajo la de libros. Va algo mas apagada que la de
   arriba porque es un subconjunto de ella, no un dato paralelo. */
.rdist-count-dnf{opacity:.85;}
/* las barras hacen de leyenda dentro de .fmt-card: el hueco lo pone el margen de cada fila */
/* Y el minimo de la LISTA es el que decide si cabe al lado de la rosquilla o baja debajo: puesto
   demasiado bajo, entraba a la fuerza y aplastaba la barra en vez de envolver.
   ⚑ Acotado con min(...,100%): un minimo de 330px dentro de una columna de 303px en el movil no
   envuelve, se SALE — y arrastraba toda la seccion a desplazarse en horizontal. */
.rdist-list{gap:0; min-width:min(330px, 100%);}
/* En el movil los tres trozos no caben a la vez: la columna de cifras y el minimo de la barra
   ceden lo justo para que quepan la etiqueta Y la barra, en vez de que una expulse a la otra.
   ⚑ VA DESPUES de las reglas base: misma especificidad, así que colocado antes lo pisaba la
   regla general y la media query no hacía nada — se veía igual a 375px que a 1280. */
@media(max-width:560px){
  .rdist-tier{max-width:9ch;}
  /* ⚑ 84 y no 68: en la pantalla estrecha caben dos renglones, y el mas largo de los cuatro
     idiomas ("1 abandonados") no entra en 68px — con nowrap volveria a derramarse sobre la
     barra, que es justo lo que estas dos lineas vienen a arreglar. */
  .rdist-val{width:84px;}
  /* ⚑ Y la etiqueta tiene que poder encogerse. Con `flex-shrink:0` no cede, la barra ya no baja
     de sus 48px y la columna de cifras acababa 5px fuera de la fila: en las filas de media
     estrella el titulo es el mas largo ("Casi Obsesionada!"), y son justo esas las que se salian.
     Lo destapo ensanchar la columna de 68 a 84px para las dos lineas — con 68 cabia por poco.
     `min-width:0` es lo que permite que el `text-overflow:ellipsis` del titulo entre en juego:
     sin el, el minimo automatico de un item flex es su contenido y no hay nada que recortar. */
  .rdist-label{flex-shrink:1; min-width:0;}
  .rdist-track{min-width:48px;}
}
/* dashboard: upcoming releases panel */
/* ⚑ El mismo aire que deja un `.section-title` suelto (margin-bottom 16px). Dentro de esta
   cabecera el titulo lleva `margin:0` —tiene que estarlo, o no se alinea con el boton "Ver
   todos"— y al quitarselo se quitaba tambien el hueco de debajo: las tarjetas con cabecera
   pegaban las portadas al titulo y "Leyendo ahora", que no la lleva, respiraba 16px mas. Se
   devuelve el aire a la cabecera, que es quien puede darlo sin desalinear nada. */
.dash-rel-head{display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:16px;}
.dash-rel-list{display:flex; flex-direction:column; gap:4px;}
.dash-rel-item{display:flex; flex-direction:column; align-items:stretch; gap:8px; padding:9px 8px; border-radius:10px; cursor:pointer; transition:.12s;}
.dash-rel-item:hover{background:var(--bg);}
.dash-rel-main{display:flex; align-items:center; gap:13px;}
.dash-rel-item .mini-cover{width:38px; height:56px;}
.dash-rel-info{flex:1; min-width:0;}
/* quick actions for released ("Out now!") titles */
.dash-rel-actions{display:flex; gap:8px; flex-wrap:wrap; padding-left:51px;}
.dash-rel-btn{font-size:0.6875rem; font-weight:600; padding:5px 11px; border-radius:999px; border:1px solid var(--line-strong); background:var(--surface); color:var(--ink-soft); cursor:pointer; transition:.12s; font-family:inherit; white-space:nowrap;}
.dash-rel-btn:hover{border-color:var(--rose-deep); color:var(--rose-deep);}
.dash-rel-btn.primary{background:var(--rose-tint); border-color:transparent; color:var(--rose-deep-on-tint, var(--rose-deep));}
.dash-rel-btn.primary:hover{filter:brightness(.97);}
.dash-rel-title{font-weight:700; font-size:0.875rem; color:var(--ink); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.dash-rel-when{flex-shrink:0; text-align:right; display:flex; flex-direction:column; align-items:flex-end; gap:4px;}
.dash-months{display:flex; align-items:flex-end; gap:6px; height:170px;}
.dash-month{flex:1; display:flex; flex-direction:column; align-items:center; height:100%;}
.dash-month-track{flex:1; width:100%; display:flex; align-items:flex-end; justify-content:center; min-height:0;}
.dash-month-bar{width:100%; max-width:30px; background:var(--rose); border-radius:5px 5px 0 0; min-height:3px; transform-origin:bottom center; animation:barGrow .4s cubic-bezier(.22,1,.36,1) both;}
@keyframes rdistGrow{from{transform:scaleX(0);}to{transform:scaleX(1);}}
@keyframes barGrow{from{transform:scaleY(0);}to{transform:scaleY(1);}}
@media(prefers-reduced-motion:reduce){
  .rdist-fill, .dash-month-bar{animation:none;}
  *{animation-duration:.001ms !important; animation-iteration-count:1 !important; transition-duration:.001ms !important;}
}
.dash-month-lbl{font-size:0.625rem; color:var(--ink-soft); margin-top:7px; font-weight:600;}
.dash-month-val{font-size:0.625rem; color:var(--ink-faint); margin-top:2px; min-height:13px;}
/* currently reading: always horizontal mini-cards (cover on top, text below),
   no dividers, wrapping. Book #1 is large, the rest are medium. */
.now-reading-list{display:flex; flex-direction:row; flex-wrap:wrap; gap:24px; align-items:flex-start;}
.now-reading-item{display:flex; flex-direction:column; gap:11px; cursor:pointer; transition:.15s;}
.now-reading-item:hover{opacity:.85;}
.now-reading-cover{aspect-ratio:2/3; border-radius:9px; margin:0; box-shadow:var(--card-shadow); flex-shrink:0;}
.now-reading-info{min-width:0;}
.now-reading-info .book-author{margin-top:3px; color:var(--ink-soft);}
.now-reading-meta{font-size:0.71875rem; color:var(--ink-soft); margin-top:5px;}
.now-reading-reread{font-size:0.71875rem; color:var(--rose-deep); font-weight:600; margin-top:5px;}
.now-reading-item.large{width:160px;}
.now-reading-item.large .now-reading-cover{width:160px; font-size:0.75rem;}
.now-reading-item.large .book-title{font-size:1.125rem !important; font-family:var(--font-display); font-weight:500; line-height:1.15;}
.now-reading-item.large .book-author{font-size:0.8125rem;}
/* ⚑ TRES PORTADAS SIEMPRE. A 118px fijos solo entraban dos en una tarjeta de 377px (118×3 + dos
   huecos de 24 = 402 sobre 337 utiles), asi que la tercera se iba a una segunda linea que la
   altura fija recorta: la lectora veia dos libros y una barra de scroll.
   `min()` hace las dos cosas a la vez: en una tarjeta ancha manda el 118px de siempre y caben mas
   de tres; en una estrecha manda el reparto y caben exactamente tres. La proporcion no hay que
   sostenerla a mano — `.now-reading-cover` lleva `aspect-ratio:2/3` y el alto sigue al ancho.
   ⚑ Se restan 50 y no 48 (dos huecos de 24) por el redondeo subpixel: cuadrando al milimetro,
   337 disponibles contra 337 pedidos, basta medio pixel para que la tercera salte de linea. */
.now-reading-item.medium{width:min(118px, calc((100% - 50px) / 3));}
.now-reading-item.medium .now-reading-cover{width:100%; font-size:0.625rem;}
.now-reading-item.medium .book-title{font-size:0.84375rem !important; font-family:var(--font-body); font-weight:600; line-height:1.25;}
.now-reading-item.medium .book-author{font-size:0.71875rem;}
@media(max-width:560px){ .now-reading-item.large{width:132px;} .now-reading-item.large .now-reading-cover{width:132px;} }
/* single currently-reading book: bigger cover, info beside it, filling the card */
.now-reading-list.single{flex-direction:row;}
.now-reading-item.solo{flex-direction:row; align-items:center; gap:22px; width:100%;}
.now-reading-item.solo .now-reading-cover{width:150px;}
.now-reading-item.solo .now-reading-info{flex:1; min-width:0;}
.now-reading-item.solo .book-title{font-size:1.1875rem !important; font-family:var(--font-display); font-weight:500; line-height:1.15;}
.now-reading-item.solo .book-author{font-size:0.84375rem; margin-top:4px;}
.carousel{display:flex; gap:14px; overflow-x:auto; padding-bottom:8px;}
/* Las portadas reparten el ancho que hay en vez de medir 92px pase lo que pase: con diez y un
   panel de 1132px sobraban 86px a la derecha. `flex:1 1 0` les da a todas la misma parte, y los
   dos limites hacen el resto del trabajo:
   ⚑ `min-width` es lo que CONSERVA el desplazamiento lateral. Sin el, en un telefono diez
   portadas se repartirian 299px y quedarian a 17px cada una — ilegibles. Con el suelo, lo que no
   cabe se sale y el carrusel se desplaza, que es lo que ya hacia.
   ⚑ `max-width` es lo que evita el ridiculo contrario: con cuatro libros, repartir 1132px daria
   portadas de 272px de ancho y 408 de alto, y la tarjeta se comeria la pantalla. Por encima del
   tope se alinean a la izquierda y sobra sitio, que es lo correcto cuando hay pocos.
   La proporcion no hay que sostenerla: `aspect-ratio:2/3` ya estaba y el alto sigue al ancho. */
.carousel-item{flex:1 1 0; min-width:72px; max-width:120px; cursor:pointer;}
.carousel-item .cover{width:100%; aspect-ratio:2/3; font-size:0.625rem;}
.carousel-rating{margin-top:6px; display:flex; justify-content:center;}
.carousel-rating .stars-row{font-size:0.95rem;}

/* custom dropdown chevron with breathing room from the rounded edge */
.toolbar select, .field select, .lang-select{
  -webkit-appearance:none; -moz-appearance:none; appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%237A736A' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 14px center; background-size:14px;
  padding-right:40px;
}
:root[data-theme="dark-academia"] .toolbar select,
:root[data-theme="dark-academia"] .field select,
:root[data-theme="dark-academia"] .lang-select{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23A99F8A' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}

/* ===== responsive ===== */
@media(max-width:1150px){
  .month-grid{grid-template-columns:repeat(3,1fr);}
}
@media(max-width:900px){
  #app{flex-direction:column; max-width:none; padding:0;}
  /* sidebar becomes an off-canvas hamburger drawer */
  #menuBtn{display:flex;}
  #langBtn{display:flex;}
  /* el dock se deshace: vuelve a ser el drawer de siempre */
  #sidebar{position:fixed; top:0; left:0; height:100vh; width:272px; max-width:84vw; flex-direction:column; align-items:stretch; justify-content:flex-start; gap:2px; padding:24px 16px; overflow-y:auto; z-index:200; transform:translateX(-100%); transition:transform .25s ease; box-shadow:0 0 50px rgba(0,0,0,.22);
    background:var(--surface-glass); backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px); border-right:1px solid var(--line); border-radius:0;}
  #app.drawer-open #sidebar{transform:translateX(0);}
  /* piezas que solo existen para el dock */
  .dock-sep, .dock-spacer, .dock-more, .dock-signout{display:none;}
  /* en el drawer los 8 son botones normales, y las pestañas ya están dentro de cada sección */
  #morePanel, #subnavPanel{display:none !important;}
  #sidebar .side-top{display:flex;}
  #sidebar .navbtn.drawer-only{display:flex;}
  #sidebar .navbtn::after, .dock-more::after, #sidebar .profile-btn::after{content:none;}
  #sidebar .side-bottom{display:block; margin-top:auto; padding-top:16px;}
  /* #sidebar .navbtn (id+clase) gana a .navbtn a secas: hay que repetir el peso */
  #sidebar .navbtn{width:auto; height:auto; border-radius:10px; background:none; color:var(--ink); opacity:1;
    flex-direction:row; font-size:0.84375rem; gap:11px; padding:11px 12px; justify-content:flex-start; white-space:normal;}
  #sidebar .navbtn span{display:inline;}
  #sidebar .navbtn:hover{background:var(--bg);}
  #sidebar .navbtn.active{background:var(--ink); color:var(--surface);}
  #sidebar .profile-btn{width:100%; height:auto; padding:8px 6px; padding-bottom:16px; margin-bottom:12px; border-bottom:1px solid var(--line); justify-content:flex-start;}
  #sidebar .profile-btn .profile-name{display:inline;}
  #sidebar .profile-btn .avatar{width:28px; height:28px; background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep)); border-color:var(--rose-deep-on-tint, var(--rose-deep));}
  #sidebar .side-bottom .lang-select{display:block;}
  /* neutralize the desktop icon-rail collapse inside the drawer */
  /* topbar en móvil: en flujo (no fijo), envuelve. Saludo arriba; búsqueda + acciones debajo. */
  /* Alineado a las cards (18px laterales, igual que #main) + aire arriba para que el wordmark respire */
  #topbar{position:static; width:100%; z-index:auto; margin:0 auto 16px; padding:18px 18px 0; flex-wrap:wrap; row-gap:12px;
    background:none; border:none; border-radius:0; box-shadow:none;}
  /* la hamburguesa cae en el renglón de acciones, alineada con los botones circulares */
  .topbar-actions .menu-btn{width:44px; height:44px; border-radius:12px;}
  .topbar-sub{display:none;}
  .topbar-title{font-size:1.375rem;}
  .topbar-actions{width:100%; order:3; gap:10px;}
  .topsearch{flex:1; max-width:none;}
  #main{margin-left:0; padding:8px 18px 60px;}
  /* grids collapse */
  /* ⚑ El encabezado de la racha era lo que desbordaba el panel en el movil. En una sola linea
     pide 330px (titulo 68 + contador 42 + navegador de semana 202 + boton 114), y con el relleno
     de la tarjeta 370 — mas que los 339 de un telefono. Y como en esa fila todas las tarjetas
     comparten la MISMA columna, las tres se estiraban a 370 y arrastraban la seccion entera a
     desplazarse en horizontal: parecian tres fallos y era uno. Aqui se apila en tres lineas. */
  .streak-head{
    grid-template-columns:1fr auto;
    grid-template-areas:
      "title  button"
      "count  count";
    row-gap:12px;
  }
  .streak-head .section-title{align-self:center;}
  /* la cifra se queda centrada en su fila, como en pantalla ancha */
  .streak-count{justify-self:center; align-self:center;}
  .streak-head > .streak-btn{justify-self:end; align-self:center; width:auto;}
  .dash-stats{grid-template-columns:1fr 1fr;}
  /* ⚑ A media pantalla la tarjeta de perfil no cabe: sus tres cifras en fila miden 198px y con el
     relleno piden 234, mas de la mitad de un movil — el grid no la encogia por debajo de su
     contenido minimo y la fila entera se salia en horizontal. Ocupa las dos columnas. Y la de
     acento tambien, o los tres statcards restantes dejan media linea vacia al final. */
  /* En el movil cada tarjeta de esta fila ocupa el ancho entero: perfil, calendario y medidor son
     los tres altos, y media pantalla no le hace justicia a ninguno. */
  .dash-stats > *{grid-column:span 2;}
  .dash-row{grid-template-columns:1fr;}
  /* ⚑ TODOS los tramos vuelven a uno aqui, no solo estos tres. Con la rejilla en una columna, un
     `span 2` que se quede suelto no se recorta: crea una segunda columna implicita y la fila se
     sale de la pantalla. Cada regla de anchura de arriba necesita su contraparte en esta lista. */
  .dash-row .dash-reading-card, .dash-row .streak-card, .dash-row .quote-card,
  .dash-row.two > *, .dash-row.pair > *, .dash-row.favs > *{grid-column:span 1;}
  .dash-row.trio{grid-template-columns:1fr;}
  .dash-row.trio > *, .dash-row.trio .dash-reading-card{grid-column:span 1;}
  .cal-grid{grid-template-columns:repeat(2,minmax(0,1fr));}
  .month-grid{grid-template-columns:repeat(3,1fr);}
  .az-cols{grid-template-columns:1fr;}
  .az-progress-wrap{grid-template-columns:1fr; gap:12px;}
}
@media(max-width:600px){
  /* En pantalla estrecha el wordmark le comía el ancho al buscador (quedaba
     reducido a un par de caracteres). Manda la búsqueda. */
  #topbar .brand{display:none;}
  .banner{margin-bottom:24px; padding-bottom:18px;}
  .banner h1{font-size:1.625rem;}
  .stats-glance{gap:18px; padding:18px 18px;}
  .glance-hero-num{font-size:3rem;}
  /* ⚑ Rejilla de tres, no una fila que se estira. Con la cuarta cifra la fila aun cabia a duras
     penas; con la quinta (el ritmo, que llega del panel) pedia 341px en una tarjeta de 301 y la
     sacaba por la derecha. En dos lineas de tres caben las cinco sin encoger ninguna, y el filete
     separador salta al primero de cada linea, que si no colgaria del borde izquierdo. */
  .glance-rest{margin-left:0; width:100%; display:grid; grid-template-columns:repeat(3,1fr); row-gap:14px;}
  .glance-item{padding:2px 10px;}
  .glance-item:nth-child(3n+1){border-left:none; padding-left:0;}
  .section-title{font-size:1.125rem;}
  /* stack two-column form rows */
  .row2{grid-template-columns:1fr;}
  /* compact theme button to icon only */
  #themeBtn span{display:none;}
  /* touch targets ≥40px on mobile */
  .menu-btn{width:40px; height:40px;}
  #themeBtn{min-height:40px; min-width:40px; justify-content:center;}
  .year-arrow{width:38px; height:38px;}
  /* toolbars wrap nicely */
  .toolbar input[type=text]{min-width:0; flex:1 1 100%;}
  .bingo-grid{gap:5px;}
  .modal{padding:20px;}
  .cal-grid{grid-template-columns:repeat(2,minmax(0,1fr));}
  .month-grid{grid-template-columns:repeat(3,1fr);}
  .dash-months{height:140px;}
}

/* ===== auth / login screen — warm, literary, cozy (Storyfeel) ===== */
#authScreen{
  position:fixed; inset:0; z-index:2000; display:flex; align-items:center; justify-content:center;
  padding:24px; overflow-y:auto;
  background:var(--app-gradient);
  transition:background 0.6s ease;
}
#authScreen[hidden]{display:none;}
.auth-wrap{width:100%; max-width:432px; display:flex; flex-direction:column; align-items:center;}
/* brand mark above the card */
/* An <a> since v229 — the way back to the public site. Underline off, and the hover kept
   quiet: it is a way out, not something to draw the eye away from the form. */
.auth-mark{display:flex; align-items:center; gap:9px; margin-bottom:20px; color:var(--ink); text-decoration:none; transition:opacity .15s ease;}
.auth-mark:hover{opacity:.75;}
.auth-mark:focus-visible{outline:2px solid var(--rose-deep); outline-offset:4px; border-radius:6px;}
.auth-mark .am-name{font-family:var(--font-display); font-weight:500; font-size:1.375rem; letter-spacing:.3px;}
/* El logotipo, tambien aqui. Misma mascara y misma razon que en la app: un archivo, cualquier tema.
   La palabra se queda debajo, transparente, para el lector de pantalla y por si no hay mascaras. */
@supports ((-webkit-mask-image: url("")) or (mask-image: url(""))){
  .auth-mark .am-name, .plans-mark .pm-name{
    display:block; width:186px; height:36px; color:transparent; user-select:none;
    background-color:var(--ink);
    -webkit-mask:url("brand/wordmark.svg") center/contain no-repeat;
            mask:url("brand/wordmark.svg") center/contain no-repeat;
  }
}
@media(max-width:600px){ .auth-mark .am-name, .plans-mark .pm-name{width:158px; height:30px;} }
/* Banner shown only when the reader came from the pricing page with a plan chosen.
   Deliberately a banner and not a modal: a dialog on arrival has to be dismissed before
   they can reach the very fields it is telling them to fill in. It sits above the card,
   outside the form, because .auth-msg belongs to error reporting. */
.auth-intent{
  display:flex; align-items:center; gap:12px; width:100%;
  background:var(--rose-tint,#f7e9ec); border:1.5px solid var(--rose);
  border-left-width:4px; border-radius:14px; padding:13px 16px; margin-bottom:16px;
  box-shadow:0 6px 18px rgba(28,26,23,.06);
}
.auth-intent .ai-badge{display:flex; flex-shrink:0; color:var(--rose-deep);}
.auth-intent .ai-badge svg{width:22px; height:22px;}
.auth-intent .ai-body{display:flex; flex-direction:column; gap:2px; min-width:0;}
.auth-intent .ai-plan{font-family:var(--font-display); font-weight:700; font-size:1rem; color:var(--ink);}
/* ⚑ --ink, not --ink-soft. Measured on the rose tint, --ink-soft lands at 3.5–4.1:1 in
   romance, cozy and default — under the 4.5:1 body-text floor in PRODUCT.md. Hierarchy is
   carried by size and weight here instead of by fading the colour. */
.auth-intent .ai-per{font-weight:400; color:var(--ink); font-size:0.875rem;}
.auth-intent .ai-text{font-size:0.8125rem; line-height:1.45; color:var(--ink);}
/* language selector on the auth screen (same languages as the app sidebar) */
.auth-lang{position:relative; display:inline-flex; align-items:center; align-self:flex-end; margin-bottom:16px;}
.auth-lang > svg:first-child{position:absolute; left:12px; width:16px; height:16px; color:var(--ink-faint); pointer-events:none;}
.auth-lang .al-chev{position:absolute; right:11px; width:15px; height:15px; color:var(--ink-faint); pointer-events:none;}
.auth-lang select{
  appearance:none; -webkit-appearance:none; -moz-appearance:none;
  border:1.5px solid var(--line); background:var(--surface); border-radius:999px;
  padding:8px 32px 8px 34px; font-family:inherit; font-size:0.84375rem; font-weight:600;
  color:var(--ink); cursor:pointer; transition:.15s;
}
.auth-lang select:hover{border-color:var(--rose); background:var(--rose-tint);}
.auth-lang select:focus{outline:none; border-color:var(--rose-deep-on-tint, var(--rose-deep)); box-shadow:0 0 0 3px var(--rose-tint);}
.auth-card{
  width:100%; background:var(--surface); border:none; border-radius:26px;
  box-shadow:0 4px 14px rgba(28,26,23,.05), 0 24px 56px rgba(28,26,23,.11);
  padding:30px 30px 26px; display:flex; flex-direction:column;
}
/* segmented toggle (app pill style) */
.auth-tabs{display:flex; background:var(--rose-tint); border-radius:999px; padding:5px; gap:4px; margin-bottom:24px;}
.auth-tab{
  flex:1; border:none; background:none; border-radius:999px; padding:11px 10px; font-weight:600;
  font-size:0.875rem; color:var(--rose-deep); cursor:pointer; transition:.18s; font-family:inherit;
}
.auth-tab.active{background:var(--surface); color:var(--ink); box-shadow:0 1px 2px rgba(28,26,23,.07), 0 4px 10px rgba(28,26,23,.08);}
.auth-title{font-family:var(--font-display); font-weight:500; font-size:1.75rem; text-align:center; color:var(--ink); line-height:1.15;}
.auth-sub{text-align:center; color:var(--ink-soft); font-size:0.9375rem; line-height:1.5; margin:8px 0 24px;}
.auth-form{display:flex; flex-direction:column; gap:16px;}
.auth-field label{display:block; font-size:0.8125rem; font-weight:600; color:var(--ink-soft); margin-bottom:7px;}
.auth-input{position:relative; display:flex; align-items:center;}
.auth-input .ai-icon{position:absolute; left:14px; width:18px; height:18px; color:var(--ink-faint); pointer-events:none;}
.auth-input input{
  width:100%; border:1.5px solid var(--line); background:var(--surface); border-radius:14px;
  padding:13px 14px 13px 44px; font-size:0.9375rem; font-family:inherit; color:var(--ink); transition:.15s;
}
.auth-input.has-eye input{padding-right:46px;}
.auth-input input:focus{outline:none; border-color:var(--rose-deep-on-tint, var(--rose-deep)); box-shadow:0 0 0 3px var(--rose-tint);}
.auth-eye{
  position:absolute; right:8px; width:32px; height:32px; display:flex; align-items:center; justify-content:center;
  border:none; background:none; color:var(--ink-faint); cursor:pointer; border-radius:8px; transition:.15s;
}
.auth-eye:hover{color:var(--ink-soft);}
.auth-eye svg{width:18px; height:18px;}
/* El mismo ojo en cualquier otro campo de contrasenia (mountPasswordEyes en app.js). La envoltura
   solo le da sitio a la derecha del campo; el boton es .auth-eye tal cual.
   ⚑ `input[type]` y no `input`: gana a `.field input` venga donde venga en este archivo, y sigue
   valiendo cuando el ojo cambia el campo a type="text". */
.pw-eye-wrap{ position:relative; display:flex; align-items:center; }
.pw-eye-wrap > input[type]{ flex:1; min-width:0; padding-right:44px; }
.pw-eye-wrap > .pw-eye{ right:6px; }
.auth-submit{
  width:100%; justify-content:center; background:var(--rose-deep); color:var(--on-rose-deep-ink); border:none;
  border-radius:999px; padding:14px 18px; font-weight:600; font-size:0.9375rem; letter-spacing:.2px;
  margin-top:4px; transition:.15s; cursor:pointer; font-family:inherit;
}
.auth-submit:hover{filter:brightness(.95);}
.auth-trial-note{margin:10px 0 0; text-align:center; font-size:0.75rem; color:var(--ink-faint); line-height:1.4;}
.auth-submit:disabled{opacity:.6; cursor:default;}
.auth-msg{font-size:0.8125rem; line-height:1.45; text-align:center; min-height:1px; margin-top:2px;}
.auth-msg.error{color:var(--danger-ink);}
.auth-msg.info{color:var(--success-ink);}
.auth-divider{display:flex; align-items:center; gap:14px; margin:22px 0; color:var(--ink-faint); font-size:0.8125rem;}
.auth-divider::before, .auth-divider::after{content:""; flex:1; height:1px; background:var(--line);}
.auth-google{
  width:100%; justify-content:center; border:1.5px solid var(--line); background:var(--surface);
  border-radius:999px; padding:13px 18px; font-weight:600; font-size:0.9375rem; color:var(--ink);
  display:inline-flex; align-items:center; gap:11px; transition:.15s; cursor:pointer; font-family:inherit;
}
.auth-google:hover{border-color:var(--rose); background:var(--rose-tint);}
.auth-google svg{width:19px; height:19px; flex-shrink:0;}
/* Google renders its own button inside an iframe, so none of the styling above can reach it —
   only the theme/shape/size options passed to renderButton(). This wrapper does the two things
   we still control: centre it, and reserve its height so the card doesn't jump while the GIS
   script loads. min-height matches .auth-google's rendered height. */
.auth-gsi{display:flex; flex-direction:column; justify-content:center; align-items:center; gap:8px; min-height:48px;}
.auth-gsi > div{max-width:100%;}
/* The way out when Google's iframe swallows the click and we cannot tell. Quiet on purpose —
   it is for the reader who just watched nothing happen, not a second call to action. */
.auth-gsi-alt{
  background:none; border:none; padding:2px 4px; cursor:pointer; font-family:inherit;
  font-size:0.75rem; color:var(--ink-faint); text-decoration:underline; text-underline-offset:2px;
  transition:.15s;
}
.auth-gsi-alt:hover{color:var(--ink-soft);}
/* ===== Profile → Subscription ===== */
.sub-plan{display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; margin-bottom:6px;}
.sub-plan .sp-name{font-family:var(--font-display); font-weight:700; font-size:1.0625rem; color:var(--ink);}
/* --ink, not --ink-faint: this is the renewal date, the one line a paying reader came to read.
   Measured on the card surfaces, --ink-faint fell under the 4.5:1 floor PRODUCT.md commits to. */
.sub-plan .sp-when{font-size:0.8125rem; color:var(--ink); opacity:.75;}
.sub-notice{font-size:0.8125rem; line-height:1.5; color:var(--ink-soft); margin:0 0 10px;}
.sub-notice.warn{color:var(--danger-ink, #9C4A3C); font-weight:600;}
.sub-msg{font-size:0.8125rem; line-height:1.4; margin:0 0 8px; min-height:0;}
.sub-msg.error{color:var(--danger-ink, #9C4A3C);}
.sub-actions{display:flex; gap:10px; flex-wrap:wrap;}
.auth-forgot{
  display:block; width:100%; text-align:center; background:none; border:none; color:var(--rose-deep);
  font-size:0.84375rem; font-weight:600; margin-top:18px; cursor:pointer; font-family:inherit;
  text-decoration:underline; text-underline-offset:2px; transition:.15s;
}
.auth-forgot:hover{color:var(--ink);}
.auth-legal{text-align:center; margin-top:20px; font-size:0.75rem; color:var(--ink-faint);}
.auth-legal a{color:var(--ink-soft); text-decoration:underline; text-underline-offset:2px;}
.auth-legal a:hover{color:var(--ink);}
.auth-legal .sep{margin:0 6px;}
@media(max-width:480px){ .auth-card{padding:24px 20px; border-radius:22px;} .auth-title{font-size:1.5rem;} }

/* ===== plan selection (PASO D) — full screen, follows the active theme ===== */
#plansScreen{
  position:fixed; inset:0; z-index:1900; display:flex; align-items:center; justify-content:center;
  padding:40px 24px; overflow-y:auto;
  background:var(--app-gradient);
  transition:background 0.6s ease;
}
#plansScreen[hidden]{display:none;}
.plans-wrap{width:100%; max-width:1040px; margin:auto; display:flex; flex-direction:column; align-items:center;}
.plans-mark{display:flex; align-items:center; gap:9px; margin-bottom:18px; color:var(--ink);}
.plans-mark .pm-name{font-family:var(--font-display); font-weight:500; font-size:1.375rem; letter-spacing:.3px;}
.plans-head{text-align:center; margin-bottom:6px;}
.plans-title{font-family:var(--font-display); font-weight:500; font-size:2rem; color:var(--ink); margin:0;}
.plans-sub{font-size:0.95rem; color:var(--ink-soft); margin-top:8px;}
/* Flex y no grid a propósito: los planes son 2 o 3 según haya oferta de
   lanzamiento, y una rejilla de columnas fijas dejaba un hueco fantasma que
   descentraba el grupo. Con justify-content:center se centra cualquier número. */
.plans-grid{display:flex; flex-wrap:wrap; justify-content:center; gap:20px; width:100%; margin-top:32px; align-items:stretch;}
.plan-card{
  position:relative; display:flex; flex-direction:column; background:var(--surface);
  flex:1 1 260px; max-width:330px;
  border:1.5px solid var(--line); border-radius:var(--radius); padding:30px 26px 28px;
  box-shadow:var(--card-shadow); transition:transform .15s, box-shadow .15s, border-color .15s;
}
.plan-card:hover{transform:translateY(-3px); box-shadow:0 14px 32px rgba(0,0,0,0.10);}
/* highlighted (Annual) */
.plan-card.featured{border-color:var(--rose-deep); border-width:2px; box-shadow:0 16px 40px rgba(0,0,0,0.12);}
.plan-badge{
  position:absolute; top:-13px; left:50%; transform:translateX(-50%); white-space:nowrap;
  background:var(--rose-deep); color:var(--on-rose-deep-ink); font-size:0.6875rem; font-weight:700; letter-spacing:.4px;
  text-transform:uppercase; padding:5px 14px; border-radius:999px;
}
.plan-card.launch .plan-badge{background:var(--ink);}
.plan-name{font-family:var(--font-display); font-size:1.5rem; font-weight:500; color:var(--ink); margin:0 0 12px;}
.plan-price{display:flex; align-items:baseline; gap:5px; margin-bottom:4px;}
.plan-amount{font-size:2.375rem; font-weight:700; color:var(--ink); line-height:1;}
.plan-period{font-size:0.9rem; color:var(--ink-soft); font-weight:500;}
.plan-permonth{font-size:0.8125rem; color:var(--ink-faint); margin-bottom:14px; min-height:16px;}
.plan-tag{font-size:0.875rem; color:var(--ink-soft); margin-bottom:22px; flex:1;}
/* Seats remaining on a capped plan. Sits after .plan-tag, which is the flex:1 spacer, so it
   stays pinned just above the button no matter how long the tag copy runs. */
.plan-left{
  font-size:0.8125rem; font-weight:600; color:var(--ink); letter-spacing:.01em;
  margin:-14px 0 16px;
}
.plan-btn{
  border:none; border-radius:12px; padding:13px 18px; font-family:inherit; font-size:0.9375rem;
  font-weight:700; cursor:pointer; width:100%; transition:.15s; background:var(--rose-tint); color:var(--rose-deep-on-tint, var(--rose-deep));
}
.plan-btn:hover{filter:brightness(.97);}
.plan-card.featured .plan-btn{background:var(--rose-deep); color:var(--on-rose-deep-ink);}
.plan-card.launch .plan-btn{background:var(--ink); color:var(--surface);}
.plan-btn:disabled{opacity:.6; cursor:default; filter:none;}
.plans-foot{margin-top:26px; text-align:center; display:flex; flex-direction:column; gap:10px; align-items:center;}
.plans-close{border:none; background:none; font:inherit; font-size:0.875rem; color:var(--ink-soft); cursor:pointer; text-decoration:underline; text-underline-offset:2px;}
.plans-close:hover{color:var(--ink);}
.plans-secure{font-size:0.75rem; color:var(--ink-faint);}
.plans-error{font-size:0.85rem; color:var(--danger, #b23b4e); min-height:18px; margin-top:4px;}
@media(max-width:820px){
  /* una tarjeta por fila: el ancho del contenedor ya obliga al wrap */
  .plans-grid{max-width:420px; margin-left:auto; margin-right:auto; gap:26px;}
  .plan-card{max-width:none;}
  .plans-title{font-size:1.6rem;}
}
/* payment result (PASO E): welcome modal after a successful checkout */
.modal.pay-modal{max-width:400px;}
.pay-result{text-align:center; padding:6px 4px;}
.pay-emoji{font-size:2.6rem; line-height:1; margin-bottom:10px;}
.pay-title{font-family:var(--font-display); font-weight:500; font-size:1.6rem; color:var(--ink); margin:0 0 8px;}
.pay-msg{color:var(--ink-soft); font-size:0.95rem; line-height:1.5; margin:0 0 22px;}
.pay-cta{border:none; border-radius:12px; padding:13px 20px; width:100%; font-family:inherit; font-size:0.95rem; font-weight:700; cursor:pointer; background:var(--rose-deep); color:var(--on-rose-deep-ink); transition:.15s;}
.pay-cta:hover{filter:brightness(.96);}
/* legal links list (Settings) */
.legal-links{display:flex; flex-direction:column;}
.legal-link{display:flex; align-items:center; justify-content:space-between; gap:10px; width:100%; text-align:left; border:none; background:none; font-family:inherit; font-size:0.9rem; font-weight:500; color:var(--ink); padding:13px 4px; cursor:pointer;}
.legal-link:hover{color:var(--rose-deep);}
.legal-link svg{width:16px; height:16px; color:var(--ink-faint); flex-shrink:0;}
.legal-link:hover svg{color:var(--rose-deep);}
.legal-link + .legal-link{border-top:1px solid var(--line);}

/* contact modal: the same Edge Function as the public form, reached from the topbar */
.cm-status{font-size:0.85rem; margin:6px 0 0; min-height:1.2em; color:var(--ink-soft);}
.cm-status.bad{color:var(--danger-ink, #b3261e); font-weight:600;}
/* ===== the vault's working state =====
   Deriving the key is 600k PBKDF2 iterations — measured at ~1.7s on the workstation and 3-5s on a
   mid-range phone — and the library load follows it. A line of text alone left the reader unsure
   anything was happening at all, so the busy state gets a spinner and the dialog now stays up
   through BOTH waits instead of closing between them.
   ⚑ It genuinely spins: WebCrypto runs deriveKey off the main thread, so this keeps painting while
   the work happens. A spinner frozen mid-turn would be worse than showing nothing. */
.cm-status.busy{display:flex; align-items:center; gap:9px; color:var(--ink); font-weight:600;}
.vspin{
  width:15px; height:15px; flex-shrink:0; border-radius:50%;
  border:2px solid var(--line); border-top-color:var(--rose-deep);
  animation:vspin .7s linear infinite;
}
@keyframes vspin{to{transform:rotate(360deg);}}
/* Somebody who asked for less motion still needs to know it is working, so the ring stays and only
   slows — removing it entirely would take away the one signal this state has. */
@media (prefers-reduced-motion:reduce){ .vspin{animation-duration:2.6s;} }
/* The loading veil. Shown by bootWithCloud, so it covers the reader who was asked for a password
   AND the one who ticked "Remember this device" and is never asked at all — the second case had no
   feedback whatsoever, which is what made the wait feel like a fault.
   Deliberately a soft scrim rather than an opaque screen: the app behind it stays visible, so it
   reads as "still working" rather than "something went wrong and took over the page". */
#loadVeil{
  position:fixed; inset:0; z-index:80; display:none;
  align-items:center; justify-content:center;
  background:color-mix(in srgb, var(--bg) 62%, transparent);
  backdrop-filter:blur(2px); -webkit-backdrop-filter:blur(2px);
}
#loadVeil.open{display:flex;}
#loadVeil .lv-card{
  display:flex; align-items:center; gap:12px;
  background:var(--surface); border:1px solid var(--line); border-radius:999px;
  padding:14px 22px; box-shadow:var(--card-shadow);
  font-size:0.9rem; font-weight:600; color:var(--ink);
}
#loadVeil .lv-row{ display:flex; align-items:center; gap:12px; }
/* ⚑ Cuando la espera se alarga, la pastilla pasa a tarjeta: caben la frase y el botón, y el
   cambio de forma es en sí mismo la señal de que algo va distinto. */
#loadVeil .lv-card.lv-wide{
  flex-direction:column; align-items:center; gap:10px;
  border-radius:var(--radius); padding:18px 22px; max-width:min(92vw, 360px); text-align:center;
}
#loadVeil .lv-slow{ margin:0; font-size:0.85rem; font-weight:400; line-height:1.5; color:var(--ink-soft); }
#loadVeil .lv-reload{ font-size:0.8125rem; padding:7px 16px; }
/* color-mix is not everywhere yet; without the fallback the scrim would be fully transparent on an
   older browser and the veil would look like a floating pill with nothing behind it. */
@supports not (background:color-mix(in srgb, red 50%, transparent)){
  #loadVeil{ background:rgba(0,0,0,.18); }
}
/* ===== legal in a modal: styles for HTML lifted from the public pages =====
   The public pages carry their own stylesheet, which this modal does not load. These are the
   handful of element rules that document needs. It renders in the reader's CHOSEN theme here,
   unlike the public pages which pin Classic — inside the app it should look like the app. */
.modal.legal-doc-modal{max-width:660px; max-height:86vh; padding:0; display:flex; flex-direction:column; overflow:hidden;}
.legal-doc-head{display:flex; align-items:center; justify-content:space-between; gap:12px; padding:18px 22px; border-bottom:1px solid var(--line); flex-shrink:0;}
.legal-doc-head h2{margin:0; font-family:var(--font-display); font-weight:500; font-size:1.3rem; color:var(--ink);}
.legal-doc-x{border:none; background:none; font-size:1.4rem; line-height:1; color:var(--ink-soft); cursor:pointer; padding:4px 8px; border-radius:8px;}
.legal-doc-x:hover{background:var(--rose-tint); color:var(--ink);}
.legal-doc-body{overflow-y:auto; padding:18px 22px 24px; flex:1;}
.legal-doc-body h1{font-family:var(--font-display); font-weight:600; font-size:1.35rem; color:var(--ink); margin:0 0 .2em;}
.legal-doc-body h4{font-family:var(--font-body); font-size:0.95rem; font-weight:700; color:var(--ink); margin:18px 0 6px;}
.legal-doc-body p, .legal-doc-body li{font-size:0.875rem; line-height:1.6; color:var(--ink-soft); margin:0 0 10px;}
.legal-doc-body ul{margin:0 0 12px; padding-left:20px;}
.legal-doc-body strong{color:var(--ink); font-weight:600;}
.legal-doc-body a{color:var(--rose-deep); text-decoration:underline; text-underline-offset:2px;}
.legal-doc-body .updated{font-size:0.78rem; color:var(--ink-faint); margin-bottom:14px;}
/* The FR/DE translation notice travels with the section lifted from the public page. legal.css is
   not loaded in here, so without this rule it renders as ordinary body text and stops reading as a
   caveat — the one thing it has to do. */
/* El filete cuelga en el margen, igual que en legal.css: con `padding-left` empujaba el texto
   fuera de la columna del cuerpo. */
.legal-doc-body .xlate{position:relative; font-size:0.78rem; font-style:italic; color:var(--ink-faint);
  line-height:1.5; margin:0 0 12px;}
.legal-doc-body .xlate::before{content:""; position:absolute; left:calc(-.7em - 2px); top:.15em; bottom:.15em;
  width:2px; border-radius:1px; background:var(--line);}
.legal-doc-wait{color:var(--ink-faint);}
/* ===== vault: unlock, recovery, and the one-time recovery key =====
   The key is shown once and has to be transcribed correctly under mild stress, so it is set in a
   monospace face at a generous size with the groups visually separated. Everything here optimises
   for "can somebody copy this onto paper without a mistake", not for looking clever. */
.vault-rk{display:flex; flex-wrap:wrap; align-items:center; justify-content:center; gap:6px;
  margin:6px 0 14px; padding:16px 12px; border:1px dashed var(--line); border-radius:var(--radius);
  background:var(--rose-tint);}
.vault-rk span{font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size:1.05rem;
  font-weight:600; letter-spacing:.08em; color:var(--ink); user-select:all;}
.vault-rk i{color:var(--ink-faint); font-style:normal;}
/* The remember-this-device choice. Laid out as checkbox + two lines of text rather than a bare
   label, because the second line is the part that actually lets somebody decide. */
.vault-remember{display:flex; gap:10px; align-items:flex-start; margin:4px 0 2px; cursor:pointer;
  font-size:0.82rem; line-height:1.45; color:var(--ink-soft);}
.vault-remember input{margin-top:2px; flex-shrink:0;}
.vault-remember strong{color:var(--ink); font-weight:600;}
/* Sits ABOVE the buttons, not under them, and is set in full ink rather than as a hint. It is the
   sentence that decides whether somebody still has a diary a year from now, so it has to be read
   BEFORE the button is pressed — a consequence printed underneath an action is a footnote to a
   decision already made. Weighted, not alarmed: this is not an error, and styling it as one would
   make readers dismiss it. */
.set-enc-note{
  margin:14px 0 14px; padding:12px 14px; border-left:3px solid var(--rose-deep);
  background:var(--rose-tint); border-radius:0 var(--radius) var(--radius) 0;
  font-size:0.875rem; line-height:1.55; font-weight:600; color:var(--ink);
}
@media (max-width:420px){ .vault-rk span{font-size:.95rem;} }
/* ===== inbox (owner only): the contact form's queue ===== */
.ibx{background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:16px 18px; margin-bottom:12px; box-shadow:var(--card-shadow);}
.ibx-head{display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; margin-bottom:8px;}
.ibx-who{font-weight:700; color:var(--ink);}
.ibx-mail{font-size:0.85rem; color:var(--rose-deep); text-decoration:none;}
.ibx-mail:hover{text-decoration:underline;}
.ibx-when{font-size:0.8rem; color:var(--ink-faint); margin-left:auto;}
/* the message was saved but nobody was emailed about it — the one state that needs chasing */
.ibx-warn{font-size:0.72rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em; color:var(--danger-ink, #b3261e); border:1px solid currentColor; border-radius:999px; padding:2px 8px;}
.ibx-msg{font-size:0.94rem; line-height:1.6; color:var(--ink); white-space:pre-wrap; margin:0 0 12px;}
.ibx-acts{display:flex; gap:8px; flex-wrap:wrap; align-items:center;}
.ibx-acts .btn{text-decoration:none;}
/* ===== discreet sync toast (localStorage → Supabase) ===== */
.sync-toast{
  position:fixed; left:50%; bottom:24px; transform:translateX(-50%); z-index:1500;
  background:var(--surface); color:var(--ink); border:1px solid var(--line);
  box-shadow:var(--card-shadow); border-radius:999px; padding:10px 18px;
  font-size:0.84375rem; font-weight:500; max-width:90vw; text-align:center;
}
.sync-toast[hidden]{display:none;}

/* ============================================================
   MODO DE SERVICIO — la cinta de "solo lectura" y el apagon.
   Lo enciende `status.json`; lo pinta `pintarModoServicio()` en app.js.
   ⚑⚑ `.svc-dark[hidden]` HACE FALTA. `display:grid` gana al atributo `hidden`, asi que sin esta
   regla la tapa se queda puesta para siempre en cuanto se muestre una vez — y la app queda
   inservible justo despues de que el mantenimiento TERMINE, que es el peor momento posible.
   ============================================================ */
.svc-banner{
  position:sticky; top:0; z-index:60;
  background:var(--rose-tint); color:var(--ink);
  border-bottom:1px solid var(--line);
  padding:10px 16px; text-align:center;
  font-size:0.9rem; line-height:1.45; }
.svc-dark{
  position:fixed; inset:0; z-index:9999;
  display:grid; place-items:center;
  background:var(--bg); padding:24px; }
.svc-dark[hidden]{ display:none; }
.svc-dark-in{ max-width:36ch; text-align:center; }
.svc-dark-in h2{
  font-family:var(--font-display); font-weight:600; font-size:1.6rem;
  margin:0 0 0.5em; color:var(--ink); }
.svc-dark-in p{ color:var(--ink-soft); line-height:1.6; margin:0; }

/* ⚑⚑ LA TAPA DE UNA CUENTA SUSPENDIDA ES OSCURA DE VERDAD, no del color del tema. La de
   mantenimiento usa `var(--bg)` porque es la casa cerrada un rato y quiere seguir pareciendo la
   casa; esta dice otra cosa, y con los pasteles de siempre se lee como un aviso mas de la app.
   Colores escritos a pelo y no por variables: el tema lo elige la lectora, y este aviso no puede
   quedar a merced de que uno de los quince deje el texto ilegible sobre el fondo. */
.susp-dark{ background:#15131a; }
.susp-dark .svc-dark-in{ max-width:44ch; }
.susp-dark .svc-dark-in h2{ color:#f3efe8; }
.susp-dark .svc-dark-in p{ color:#cfc7bd; }
.susp-dark .susp-cta{ margin-top:1.25em; }
.susp-dark .susp-cta a{ color:#f3efe8; text-decoration:underline; text-underline-offset:3px; }
.susp-dark .susp-cta a:hover{ opacity:.85; }

/* El desplegable de suspension en Atencion al lector: apagado se ve como un campo mas —no es una
   zona de peligro hasta que lo es— y encendido se marca, para que la ficha de una cuenta
   suspendida no se pueda leer por encima sin verlo. */
.adm-u-susp.on select{ border-color:#a33; }
.adm-u-susp.on label{ color:#a33; font-weight:600; }

/* ---- Administración ---- */
/* ⚑⚑ DOS COLUMNAS DE VERDAD, no una etiqueta con un ancho minimo a ojo. Cada fila era su propio
   flex, asi que el valor empezaba donde acabara SU etiqueta: con «Cifrado activo» y «Tiene foto
   (cifrada)» en la misma lista, los «sí» salian en cuatro sitios distintos y no habia forma de
   recorrerlos con la vista. `min-width:11ch` era un parche — acertaba mientras las etiquetas
   fueran cortas y fallaba en cuanto una crecia, que es justo lo que paso.
   ⚑ `display:contents` en la fila: el `div` desaparece a efectos de maquetacion y sus dos `span`
   pasan a ser celdas de la MISMA rejilla. Sin eso, cada fila seria su propia rejilla y estariamos
   igual. Vale para las dos listas que hay (el modo de servicio y la ficha de la lectora) porque
   las dos tienen exactamente dos celdas por fila.
   ⚑ `max-content` en la primera: la columna se ajusta a la etiqueta MAS LARGA de esa lista, no a
   un numero fijo — asi «Cifrado» y «Datos guardados» quedan cada una tan ancha como necesite. */
.adm-estado{
  display:grid; grid-template-columns:max-content minmax(0, 1fr);
  gap:6px 14px; margin:10px 0 14px; font-size:0.9rem; align-items:baseline;
}
.adm-estado > div{ display:contents; }
.adm-k{ min-width:11ch; color:var(--ink-soft); }
.adm-v{ font-weight:700; }
/* ⚑ `--danger-ink` y no un rojo suelto: es el token que ya usan los avisos de la app, así que
   sigue al tema y no hay que comprobar quince contrastes a mano. */
.adm-v.off{ color:var(--danger-ink); }
.adm-ro .adm-v{ font-weight:600; opacity:0.85; }
.adm-flag{ padding:10px 0; border-top:1px solid var(--line); }
.adm-flag:first-of-type{ border-top:0; }
.adm-flag-h{ display:flex; gap:10px; align-items:baseline; flex-wrap:wrap; }
/* ⚑ Los valores posibles, en el hueco que queda a la derecha: dicen que se puede poner sin
   obligar a abrir un fichero para averiguarlo. */
.adm-vals{ margin-left:auto; font-size:0.8rem; color:var(--ink-faint); font-variant-numeric:tabular-nums; }
.adm-reach{ margin:4px 0 0; font-size:0.85rem; line-height:1.5; color:var(--ink-soft); }
.adm-sel{ margin-left:0; font-size:0.85rem; padding:3px 6px; border-radius:8px;
  border:1px solid var(--line); background:var(--surface); color:var(--ink); }
.adm-cmd{ display:flex; gap:8px; align-items:center; margin-top:8px; }
/* ⚑ La orden se puede seleccionar con el ratón aunque el botón de copiar no funcione: en un
   contexto no seguro `navigator.clipboard` no existe, y quedarse sin la orden Y sin poder
   marcarla sería quedarse sin nada. */
.adm-cmd code{ flex:1; min-width:0; overflow-x:auto; white-space:nowrap; user-select:all;
  font-size:0.8rem; padding:7px 9px; border-radius:8px;
  background:var(--rose-tint); color:var(--ink); }
.adm-copy{ flex:none; font-size:0.8rem; padding:6px 12px; }
/* ⚑ La cinta de la dueña se distingue de la que leen las lectoras: es la misma barra, pero dice
   una cosa distinta y conviene que no se confundan de un vistazo. */
.svc-banner-owner{ background:var(--rose-deep); color:#fff; }

/* ---- Atencion al lector (Administracion) ------------------------------------------------ */
.adm-busca{ display:flex; gap:8px; align-items:center; }
.adm-busca-in{ position:relative; flex:1; min-width:0; }
.adm-busca-in input{ width:100%; padding-right:34px; }
.adm-busca .btn{ flex:none; }
/* ⚑ La x se esconde sola cuando la caja esta vacia, con `:placeholder-shown` y sin JavaScript:
   una x que no borra nada solo invita a pulsarla. */
.adm-busca-in input:placeholder-shown + .adm-clear{ display:none; }
.adm-clear{
  position:absolute; right:6px; top:50%; transform:translateY(-50%);
  width:24px; height:24px; border:0; border-radius:50%; background:transparent;
  color:var(--ink-soft); font-size:1.15rem; line-height:1; cursor:pointer;
  display:flex; align-items:center; justify-content:center; padding:0;
}
.adm-clear:hover{ background:var(--rose-tint); color:var(--ink); }
.adm-u-none{ margin-top:14px; }
/* ---- Atencion al lector: dos columnas y pestanias ---------------------------------------- */
/* ⚑ `minmax(0, …)` en las dos: sin el, una fila con un identificador de Paddle largo estira su
   columna y la otra se encoge. Es la trampa de siempre de grid — `auto` no es «lo que quepa»,
   es «lo que el contenido pida». */
.adm-u-cols{ display:grid; grid-template-columns:minmax(0, 320px) minmax(0, 1fr); gap:22px; align-items:start; }
/* ⚑ Una sola columna en cuanto no caben las dos: en un movil, 320px de formulario al lado de una
   tabla de datos deja las dos ilegibles. Y lo editable va PRIMERO tambien apilado. */
@media (max-width: 760px){ .adm-u-cols{ grid-template-columns:minmax(0, 1fr); gap:18px; } }
.adm-u-cols > .adm-u-edit{ margin-top:20px; }
.adm-u-ver{ margin-top:20px; padding-top:16px; border-top:1px solid var(--line); min-width:0; }
/* ⚑ Aire entre la tira de pestanias y la primera fila: pegados, la pestania elegida y el primer
   dato se leian como una sola cosa y costaba ver donde empezaba la tabla. */
.adm-u-tabs{ display:flex; flex-wrap:wrap; gap:6px; margin-bottom:4px; }
.adm-u-tab{
  border:1px solid var(--line); background:transparent; border-radius:999px;
  padding:6px 14px; font-size:0.8125rem; font-weight:500; color:var(--ink-soft);
  cursor:pointer; transition:.15s;
}
.adm-u-tab:hover{ border-color:var(--ink); color:var(--ink); }
.adm-u-tab.on{ background:var(--ink); border-color:var(--ink); color:var(--surface); }
/* ⚑ El titulo de cada seccion ocupa las DOS columnas: es un encabezado de la lista, no una
   etiqueta con su valor al lado. Asi varias secciones caben en una sola rejilla —que es lo que
   hace que sus valores se alineen entre si— sin que el titulo se coma la columna de etiquetas. */
.adm-u-h{ grid-column:1 / -1; margin:0; font-size:0.9375rem; }
.adm-u-h:not(:first-child){ margin-top:12px; }
.adm-u-panel > .adm-estado{ margin-top:18px; }

.adm-u-grp{ margin-top:20px; padding-top:16px; border-top:1px solid var(--line); }
.adm-u-grp h4{ margin:0 0 4px; font-size:0.9375rem; }
.adm-u-grp .adm-estado{ margin-bottom:0; }
/* ⚑ El valor puede ser un identificador de Paddle: se deja seleccionar y romper linea, porque lo
   que se hace con el es copiarlo y pegarlo en el panel de Paddle. */
.adm-uv{ font-weight:600; word-break:break-word; user-select:all; }
.adm-u-chk{ display:flex; gap:9px; align-items:center; margin:12px 0 4px; cursor:pointer; }
.adm-u-chk input{ width:16px; height:16px; flex:none; }
/* La mitad editable se separa de la que solo se mira: es la unica que escribe en la cuenta de
   otra persona, y conviene que no se confunda con el resto de un vistazo. */
.adm-u-edit{ background:var(--rose-tint); border-radius:var(--radius); padding:14px 16px 16px; }
.adm-u-edit h4{ margin-bottom:2px; }
.adm-u-edit .field{ margin-top:10px; }
/* La letra pequeña bajo un campo del perfil: explica la consecuencia, no repite la etiqueta. */
.prof-note{ margin:6px 0 0; font-size:0.75rem; line-height:1.5; color:var(--ink-faint); }
/* El correo de la cuenta en el perfil: una direccion larga parte donde haga falta, no desborda. */
.prof-email-now{ font-size:0.9rem; line-height:1.4; overflow-wrap:anywhere; }
/* Atencion al lector: el correo de la cuenta, debajo de las preferencias y separado de ellas —
   tiene su propio boton y no se guarda con «Guardar». */
.adm-mail{ margin-top:18px; padding-top:14px; border-top:1px solid var(--line); }
.adm-mail-pend{ margin:8px 0 6px; font-size:0.8125rem; line-height:1.5; overflow-wrap:anywhere; }
.adm-u-old{ margin:10px 0 0; }
/* ---- El globo de ayuda del Resumen ------------------------------------------------------- */
.adm-u-edit-h{ display:flex; align-items:center; gap:8px; }
.adm-u-edit-h h4{ margin:0; }
.adm-help-wrap{ position:relative; margin-left:auto; display:inline-flex; }
.adm-help{
  width:20px; height:20px; flex:none; border-radius:50%; cursor:help; padding:0;
  border:1px solid var(--line-strong); background:transparent; color:var(--ink-soft);
  font-size:0.75rem; font-weight:700; line-height:1; display:flex; align-items:center; justify-content:center;
}
.adm-help:hover, .adm-help:focus-visible{ border-color:var(--ink); color:var(--ink); }
/* ⚑ `visibility` y no `display`: asi se puede animar y, sobre todo, el globo sigue existiendo para
   `aria-describedby` — un lector de pantalla lo anuncia al enfocar el boton aunque no se vea. */
.adm-tip{
  position:absolute; top:calc(100% + 8px); right:0; z-index:40;
  width:min(300px, 76vw); padding:12px 14px; border-radius:var(--radius);
  background:var(--surface); border:1px solid var(--line); box-shadow:var(--card-shadow);
  font-size:0.8125rem; font-weight:400; line-height:1.55; color:var(--ink-soft);
  display:flex; flex-direction:column; gap:10px; text-align:left;
  visibility:hidden; opacity:0; transition:opacity .12s;
}
.adm-help-wrap:hover .adm-tip, .adm-help-wrap:focus-within .adm-tip{ visibility:visible; opacity:1; }
/* La cabecera de cada tarjeta de Administracion: el titulo a la izquierda y la interrogacion en
   la esquina de arriba a la derecha (`.adm-help-wrap` ya lleva `margin-left:auto`). */
.adm-card-h{ display:flex; align-items:center; gap:10px; margin-bottom:16px; }
.adm-card-h .section-title{ margin:0; }
/* Un panel por bandera: el mismo aire bajo la tira que en la ficha, y sin el filo de separacion
   que tenia cada bandera cuando iban una debajo de otra. */
.adm-flag-panel{ margin-top:18px; }
.adm-flag-panel > .adm-flag{ border-top:0; padding-top:0; }
/* El estado real de cada bandera, a la derecha del desplegable. En rojo si no esta en marcha:
   es lo que hay que ver de un vistazo antes de copiar ninguna orden. */
.adm-now{ font-size:0.85rem; color:var(--ink-soft); }
.adm-now strong{ color:var(--ink); font-weight:700; }
.adm-now strong.off{ color:var(--danger-ink); }
