/* =============================================================================
   vendorsify.css — el lenguaje visual de vendorsify.com aplicado a
   estudioplatano.

   Origen: `clivaje/producto/frontend/src/app/styles/vendorsify-tokens.css`, el
   port documentado del sitio (captura 2026-08-06). La extraccion literal vive en
   `clivaje/design/tokens/vendorsify-raw.css`.

   CONTRATO DE NOMBRES — dos capas, a proposito:
     `--vs-*`  paleta CRUDA, con el nombre original de vendorsify. No se toca:
               es lo que permite rastrear cualquier valor hasta la fuente.
     `--ep-*`  capa SEMANTICA de estudioplatano (fondo, texto, acento, atmosfera).
               Es la unica que consumen las clases y el resto del sitio.

   Este sitio es OSCURO Y SOLO OSCURO — igual que el origen, que no tiene tema
   claro. Las secciones 3 y 4 quedan separadas de todos modos para que un
   `:root[data-theme='light']` pueda entrar despues sin reescribir los efectos.

   ORDEN DE LAS SECCIONES (importa):
     1. fuentes       Geist / Geist Mono / Clash Display, autoalojadas
     2. paleta cruda  los `--vs-*`
     3. semantica     el mapeo a `--ep-*`
     4. atmosfera     los tokens que hacen posible la seccion 5
     5. efectos       superficies, fondos, texto, accion, ambiente
     6. keyframes
     7. reduced-motion

   La seccion 4 es la bisagra: las clases del origen traen el blanco y el negro
   cableados (`rgb(255 255 255 / .055)` para la luz cenital, `#000` para la
   sombra). Aca esos valores salen de tokens, no de literales, para que el dia
   que exista un tema claro cambien los tokens y no las 20 reglas de la 5.

   Tailwind (Play CDN) aporta layout, espaciado y color plano via `tailwind.config`
   en index.html. Este archivo aporta lo que Tailwind no puede: las superficies
   compuestas. Regla para no pelear especificidad: si un elemento lleva una clase
   `.vs-*`, NO debe llevar la utilidad Tailwind del mismo propiedad (nada de
   `bg-*` ni `border-*` junto a `.vs-surface-lit`).
   ============================================================================= */

/* -----------------------------------------------------------------------------
   1. FUENTES — autoalojadas en `fonts/`, hermanas de `img/` en la raiz publicada.

   Geist / Geist Mono: SIL OFL 1.1 (Vercel), variables 100-900, subsets latin y
   latin-ext (latin cubre es/en/de completo; latin-ext entra por si acaso).
   Clash Display: Indian Type Foundry via Fontshare, gratis para uso comercial.
   Solo los tres pesos que el origen usa (500/600/700).

   Si falta cualquiera, la cascada cae a system-ui: degrada sin romper.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Geist Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url('fonts/Geist/geist-latin-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url('fonts/Geist/geist-latin-ext-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
    U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Geist Mono Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url('fonts/Geist_Mono/geist-mono-latin-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist Mono Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url('fonts/Geist_Mono/geist-mono-latin-ext-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
    U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Clash Display';
  src: url('fonts/Clash_Display/clash-display-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Clash Display';
  src: url('fonts/Clash_Display/clash-display-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Clash Display';
  src: url('fonts/Clash_Display/clash-display-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* -----------------------------------------------------------------------------
   2. PALETA CRUDA — los tokens tal como vendorsify los declara.
   -------------------------------------------------------------------------- */
:root {
  /* neutrales: la escala de profundidad (fondo -> panel -> superficie -> borde) */
  --vs-void: #090c10;
  --vs-charcoal: #111318;
  --vs-surface: #181c24;
  --vs-border: #242933;

  /* azul: el eje de marca */
  --vs-blue-deep: #0a1628;
  --vs-blue-core: #1a6fe8;
  --vs-blue-bright: #4d9fff;
  --vs-blue-glow: #1a6fe826;
  --vs-border-glow: #1e3a5f;
  --vs-accent: #00d4ff;

  /* texto */
  --vs-text-primary: #f0f4ff;
  --vs-text-secondary: #a8b4c8;
}

/* -----------------------------------------------------------------------------
   2b. RAMPA GRIS — la variante que consume hoy la capa semantica.

   Los `--vs-*` de arriba NO se tocan: son el registro de lo que declara
   vendorsify, y borrarlos perderia la trazabilidad al origen. Lo que cambia es
   A QUE APUNTA la seccion 3. Volver al azulado es repuntar `--ep-*` a `--vs-*`,
   sin tocar una sola regla de efecto.

   Es una NEUTRALIZACION, no un desaturado mecanico: se le quita el tinte frio a
   los neutrales conservando la MISMA escala de profundidad y los mismos ratios
   de contraste (anotados donde importan).
   -------------------------------------------------------------------------- */
:root {
  --gr-void: #0b0b0c;
  --gr-charcoal: #141415;
  --gr-surface: #1c1c1e;
  --gr-border: #2b2b2e;

  --gr-text-primary: #f4f4f5; /* 18.1:1 sobre --gr-void */
  --gr-text-secondary: #b0b0b6; /*  9.1:1 (antes 9.4 con el azulado) */
  --gr-text-meta: #86868c; /*  5.4:1 (antes 5.6) */

  /* Acento neutro. Los TRES ROLES del origen se conservan enteros — lo unico
     que cambia es que dejan de ser azules. Sin esto el sitio quedaba gris con
     manchas azules sueltas (pildora de nav activa, LED, boton de play, thumb
     del volumen), que es peor que cualquiera de los dos extremos. */
  --gr-accent: #e4e4e7; /* 16.3:1 — texto, enlace, borde, indicador */
  --gr-accent-solid: #3f3f46; /* relleno; blanco encima da 10.4:1 */
  --gr-accent-hi: #ffffff; /* 19.7:1 — hover, remate */
  --gr-accent-wash: rgb(255 255 255 / 0.08); /* relleno tenue */
}

/* -----------------------------------------------------------------------------
   3. SEMANTICA — el mapeo a `--ep-*`. Los ratios anotados son contra `--ep-bg`
   (#090c10) y no deben degradarse sin que se note.

   EL ACENTO VA PARTIDO EN TRES porque el origen usa tres azules con roles
   distintos y colapsarlos rompe contraste:
     claro    texto, enlace, borde, indicador
     medio    superficie llena, con texto blanco encima
     maximo   hover de enlace, remate
   La regla que sobrevive al cambio de color: EL TONO MEDIO NUNCA ES TEXTO, solo
   relleno. En el original eso era porque #1a6fe8 da 4.2:1 y no llega a 4.5:1;
   aca #3f3f46 es todavia mas oscuro, asi que la prohibicion sigue en pie.
   -------------------------------------------------------------------------- */
:root {
  --ep-bg: var(--gr-void); /* #0b0b0c */
  --ep-surface: var(--gr-charcoal); /* #141415 */
  --ep-surface-2: var(--gr-surface); /* #1c1c1e */
  --ep-border: var(--gr-border); /* #2b2b2e */
  --ep-text: var(--gr-text-primary); /* #f4f4f5 — 18.1:1 */
  --ep-text-dim: var(--gr-text-secondary); /* #b0b0b6 —  9.1:1 */
  --ep-text-meta: var(--gr-text-meta); /* #86868c — 5.4:1, la voz mono */

  --ep-accent: var(--gr-accent); /* #e4e4e7 — texto, enlace, borde, barra */
  --ep-accent-solid: var(--gr-accent-solid); /* #3f3f46 — superficie llena + blanco */
  --ep-accent-hi: var(--gr-accent-hi); /* #ffffff — hover, remate */
  --ep-accent-wash: var(--gr-accent-wash); /* blanco 8% — relleno tenue */

  /* Forma y ritmo. El origen apila radios y usa `rounded-full` en chips y botones. */
  --ep-radius-sm: 0.375rem;
  --ep-radius: 0.75rem;
  --ep-radius-lg: 1rem;
  --ep-radius-pill: 999px;

  /* Curva de movimiento: la misma en cada transicion de superficie. */
  --ep-ease-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --ep-dur-fast: 0.15s;
  --ep-dur: 0.35s;

  /* Tipografia. Geist para todo; Clash Display solo para titulos. */
  --ep-font-sans: 'Geist Variable', 'Geist', system-ui, -apple-system, sans-serif;
  --ep-font-display: 'Clash Display', 'Geist Variable', 'Geist', sans-serif;
  --ep-font-mono: 'Geist Mono Variable', 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  color-scheme: dark;
}

/* -----------------------------------------------------------------------------
   4. ATMOSFERA — la bisagra que hace tematizable la seccion 5.
   `--ep-lift` es la luz que cae desde arriba; `--ep-hairline` el filo del borde;
   `--ep-shade` la sombra proyectada; `--ep-panel-*` los tramos del gradiente de
   una superficie.
   -------------------------------------------------------------------------- */
:root {
  --ep-lift: rgb(255 255 255 / 0.055);
  --ep-hairline: rgb(255 255 255 / 0.12);
  --ep-hairline-hi: rgb(255 255 255 / 0.2);
  --ep-inset: rgb(255 255 255 / 0.07);
  --ep-shade: rgb(0 0 0 / 0.92);
  --ep-shade-soft: rgb(0 0 0 / 0.5);
  /* Los tramos del gradiente de panel van CON ALPHA: la carta deja pasar el
     fondo, asi que el halo y los orbes se ven a traves y la superficie queda
     atada a la atmosfera en vez de flotar como un rectangulo opaco.
     El alpha es una sola perilla; el color de cada tramo no cambia. */
  --ep-panel-alpha: 0.3;
  --ep-panel-1: rgb(26 26 28 / var(--ep-panel-alpha));
  --ep-panel-2: rgb(16 16 17 / var(--ep-panel-alpha));
  --ep-panel-3: rgb(12 12 13 / var(--ep-panel-alpha));

  /* Version opaca del tramo mas oscuro. La usan las piezas donde la
     transparencia resta en vez de sumar: los campos del formulario (viven
     dentro del modal de vidrio y necesitan separarse de el) y el marco de las
     caratulas (es un soporte de imagen, tiene que ser solido). */
  --ep-panel-opaque: #0c0c0d;
  --ep-glass-1: rgb(20 20 21 / 0.62);
  --ep-glass-2: rgb(20 20 21 / 0.42);
  --ep-wash: rgb(255 255 255 / 0.07);
  --ep-wash-base: #0f0f11;
  --ep-fade-1: #ffffff;
  --ep-fade-2: #d6d6d8;
  --ep-fade-3: #9d9da2;

  /* EL BRILLO POR DETRAS. En el origen no es un filtro ni un pseudo-elemento: es
     un box-shadow de COLOR sin desplazamiento. Esa es la tecnica correcta porque
     el box-shadow se pinta SIEMPRE por fuera y por debajo de la caja — un
     ::before con z-index negativo dentro de un elemento que crea contexto de
     apilado se pintaria ENCIMA de su propio fondo. */
  --ep-glow: rgb(255 255 255 / 0.16);
  --ep-glow-soft: rgb(255 255 255 / 0.08);

  /* Centro del halo cenital. Lo escribe AmbientPointer y nada mas. Van en :root
     —no en .vs-ambient— porque el splash es HERMANO de la capa ambiental, no
     descendiente: desde alli no las veria y se quedaria con su gradiente fijo.
     El default es la posicion que estaba cableada, asi que sin JS, en pantalla
     tactil o con reduced-motion, se ve exactamente igual que antes. */
  --ep-wash-x: 50%;
  --ep-wash-y: -10%;
}

/* -----------------------------------------------------------------------------
   5. CAPA DE EFECTOS
   -------------------------------------------------------------------------- */

/* --- base ---------------------------------------------------------------- */

/* El fondo vive en `html`, no en `body`: asi la capa ambiental (fija, z-index -1,
   hija de body) se pinta ENCIMA del fondo y DEBAJO del contenido. */
html {
  background-color: var(--ep-bg);
}

body {
  background: transparent;
  color: var(--ep-text);
  font-family: var(--ep-font-sans);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

::selection {
  background: var(--ep-accent-wash);
  color: var(--ep-text);
}

:focus-visible {
  outline: 2px solid var(--ep-accent);
  outline-offset: 2px;
}

/* --- ambiente de pagina --------------------------------------------------- */

/* Capa fija bajo todo: lavado cenital + dos orbes a la deriva.
   `pointer-events: none` porque es decoracion, no superficie. */
.vs-ambient {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

/* Lavado cenital: halo neutro arriba, cae al fondo del tema. */
.vs-ambient__wash {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 90% at var(--ep-wash-x) var(--ep-wash-y), var(--ep-wash) 0%, transparent 55%),
    linear-gradient(var(--ep-wash-base) 0%, var(--ep-bg) 70%);
}

/* Orbes en DOS capas, y la division importa:
     .vs-orb-wrap  posicion + paralaje del puntero (transform escrito por JS)
     .vs-orb       tamano, color y deriva propia (transform de los keyframes)
   Un solo elemento no puede llevar las dos cosas: `transform` es una sola
   propiedad y la animacion le ganaria al translate en cada frame. */
.vs-orb-wrap {
  position: absolute;
  will-change: transform;
}

.vs-orb-wrap--a {
  top: -14vh;
  left: -10vw;
  width: 54vw;
  height: 54vw;
  max-width: 680px;
  max-height: 680px;
}

.vs-orb-wrap--b {
  top: 34vh;
  right: -14vw;
  width: 46vw;
  height: 46vw;
  max-width: 560px;
  max-height: 560px;
}

/* Radial de color + blur grande. El desfase 18s/23s evita el loop obvio. */
.vs-orb {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  filter: blur(90px);
  will-change: transform;
}

.vs-orb--a {
  background: radial-gradient(circle, rgb(255 255 255 / 0.10) 0%, transparent 68%);
  animation: vs-orb-a 18s ease-in-out infinite;
}

.vs-orb--b {
  background: radial-gradient(circle, rgb(255 255 255 / 0.06) 0%, transparent 70%);
  animation: vs-orb-b 23s ease-in-out infinite;
}

/* --- superficies ---------------------------------------------------------- */

/* Carta con brillo superior: el gradiente de 0->16% simula luz cenital. Es la
   superficie protagonista (paneles de contenido, cartas de release). */
.vs-surface-lit {
  position: relative;
  background:
    linear-gradient(var(--ep-lift) 0%, transparent 16%),
    linear-gradient(165deg, var(--ep-panel-1) 0%, var(--ep-panel-2) 58%, var(--ep-panel-3) 100%);
  border: 1px solid var(--ep-hairline);
  box-shadow:
    inset 0 1px var(--ep-inset),
    0 24px 60px -28px var(--ep-shade);
  transition:
    border-color var(--ep-dur) var(--ep-ease-expo),
    transform var(--ep-dur) var(--ep-ease-expo),
    box-shadow var(--ep-dur) var(--ep-ease-expo);
}

.vs-surface-lit:hover {
  border-color: var(--ep-hairline-hi);
}

/* Elevacion al hover, separada de la superficie para poder aplicarla suelta. */
.vs-lift:hover {
  transform: translateY(-2px);
  box-shadow:
    inset 0 1px var(--ep-inset),
    0 32px 70px -30px var(--ep-shade),
    0 0 50px -12px var(--ep-glow-soft);
}

/* Superficie secundaria, sin protagonismo: para lo que acompana. */
.vs-surface-quiet {
  background: linear-gradient(165deg, var(--ep-lift) 0%, transparent 100%);
  border: 1px solid var(--ep-hairline);
  box-shadow: inset 0 1px var(--ep-inset);
}

/* Vidrio: para lo que FLOTA sobre contenido (modal, popovers). */
.vs-glass {
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  background: linear-gradient(var(--ep-glass-1) 0%, var(--ep-glass-2) 100%);
  border: 1px solid var(--ep-hairline);
  box-shadow:
    0 4px 24px -8px var(--ep-shade-soft),
    inset 0 1px var(--ep-inset);
}

/* Vidrio de barra fija. El ::before dibuja un borde con gradiente via
   mask-composite — el truco que le da el filo "liquido" al canto. */
.vs-glass-bar {
  position: relative;
  isolation: isolate;
  backdrop-filter: blur(80px) saturate(120%);
  -webkit-backdrop-filter: blur(80px) saturate(120%);
  background: linear-gradient(165deg, var(--ep-glass-1) 0%, var(--ep-glass-2) 100%);
  border-bottom: 1px solid var(--ep-hairline);
  box-shadow: 0 8px 40px var(--ep-shade-soft);
}

.vs-glass-bar::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  padding: 1px;
  pointer-events: none;
  background: linear-gradient(
    145deg,
    var(--ep-hairline-hi) 0%,
    transparent 32%,
    transparent 58%,
    var(--ep-hairline) 100%
  );
  -webkit-mask-image: linear-gradient(#fff 0 0), linear-gradient(#fff 0 0);
  mask-image: linear-gradient(#fff 0 0), linear-gradient(#fff 0 0);
  -webkit-mask-clip: content-box, border-box;
  mask-clip: content-box, border-box;
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

/* Variante inferior: misma materia, el filo va arriba (barra del reproductor). */
.vs-glass-bar--bottom {
  border-bottom: 0;
  border-top: 1px solid var(--ep-hairline);
  box-shadow: 0 -8px 40px var(--ep-shade-soft);
}

/* Marco de imagen: el chrome de "producto dentro de la pagina" del origen,
   aplicado aca a la caratula de un disco. */
.vs-frame {
  overflow: hidden;
  background-color: var(--ep-panel-opaque);
  border: 1px solid var(--ep-hairline);
  box-shadow:
    0 12px 32px -16px var(--ep-shade),
    inset 0 1px var(--ep-inset);
  transition:
    border-color var(--ep-dur) var(--ep-ease-expo),
    box-shadow var(--ep-dur) var(--ep-ease-expo);
}

a:hover > .vs-frame,
.group:hover .vs-frame {
  border-color: var(--ep-accent);
  box-shadow:
    0 16px 40px -16px var(--ep-shade),
    0 0 34px -10px var(--ep-glow);
}

/* Filo de luz arriba y abajo de una franja (separadores de seccion). */
.vs-edge-lit {
  position: relative;
}

.vs-edge-lit::before,
.vs-edge-lit::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--ep-hairline-hi), transparent);
}

.vs-edge-lit::before {
  top: 0;
}

.vs-edge-lit::after {
  bottom: 0;
}

/* Regla de seccion: hairline que se apaga hacia la derecha. */
.vs-rule {
  height: 1px;
  background: linear-gradient(90deg, var(--ep-hairline-hi), transparent);
}

/* --- texto ---------------------------------------------------------------- */

/* La voz de Clash Display, con el tracking cerrado del origen. */
.vs-display {
  font-family: var(--ep-font-display);
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1.25;
}

/* Titular que se apaga hacia abajo — el gesto tipografico principal del origen. */
.vs-text-fade {
  background: linear-gradient(var(--ep-fade-1) 0%, var(--ep-fade-2) 55%, var(--ep-fade-3) 100%);
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* Variante suave sobre el color de texto propio. */
.vs-text-gradient {
  background: linear-gradient(180deg, var(--ep-text) 0%, var(--ep-text-dim) 100%);
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* Chip: pildora mono para etiquetas sueltas (generos, herramientas, sellos). */
.vs-chip {
  display: inline-flex;
  align-items: center;
  font-family: var(--ep-font-mono);
  font-size: 0.6rem;
  line-height: 1.4;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ep-text-dim);
  background: var(--ep-lift);
  border: 1px solid var(--ep-hairline);
  border-radius: var(--ep-radius-pill);
  padding: 0.15rem 0.55rem;
  transition:
    border-color var(--ep-dur-fast) var(--ep-ease-expo),
    color var(--ep-dur-fast) var(--ep-ease-expo);
}

.vs-chip--accent {
  color: var(--ep-accent);
  background: var(--ep-accent-wash);
  border-color: rgb(255 255 255 / 0.22);
}

/* Enlace de contenido: acento en reposo, el mas brillante al hover. */
.vs-link {
  color: var(--ep-text);
  transition: color var(--ep-dur-fast) var(--ep-ease-expo);
}

.vs-link:hover {
  color: var(--ep-accent-hi);
}

/* --- accion --------------------------------------------------------------- */

/* Boton secundario: pildora de superficie tenue. El grueso de los botones. */
.vs-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  color: var(--ep-text-dim);
  background: linear-gradient(165deg, var(--ep-lift) 0%, transparent 100%);
  border: 1px solid var(--ep-hairline);
  border-radius: var(--ep-radius-pill);
  box-shadow: inset 0 1px var(--ep-inset);
  transition: all var(--ep-dur) var(--ep-ease-expo);
}

.vs-btn:hover {
  color: var(--ep-text);
  border-color: var(--ep-hairline-hi);
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px var(--ep-inset),
    0 0 30px -10px var(--ep-glow);
}

/* Estado activo de navegacion: tinte de acento + borde de acento. */
.vs-btn--active,
.vs-btn[aria-current='true'] {
  color: var(--ep-text);
  background: var(--ep-accent-wash);
  border-color: rgb(255 255 255 / 0.3);
}

/* Boton primario con barrido de brillo cada 3s y elevacion al hover.
   UNO por pantalla: es el CTA, y dos compitiendo anulan el gesto. */
.vs-btn-shimmer {
  position: relative;
  overflow: hidden;
  color: #fff;
  background: var(--ep-accent-solid);
  border: 1px solid var(--ep-hairline);
  box-shadow:
    0 0 20px rgb(255 255 255 / 0.10),
    inset 0 1px rgb(255 255 255 / 0.15);
  transition: all 0.3s var(--ep-ease-expo);
}

.vs-btn-shimmer:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow:
    0 0 30px rgb(255 255 255 / 0.18),
    inset 0 1px rgb(255 255 255 / 0.15);
}

.vs-btn-shimmer:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.vs-btn-shimmer::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, rgb(255 255 255 / 0.25), transparent);
  animation: vs-shimmer 3s infinite;
}

/* Destello diagonal al pasar por encima del contenedor `.group`. */
.vs-sheen {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(100deg, transparent 20%, var(--ep-hairline-hi) 50%, transparent 80%);
  transform: translateX(-120%) skew(-12deg);
}

.group:hover .vs-sheen {
  animation: vs-sheen 0.9s ease-out;
}

/* Campo de formulario. */
.vs-input {
  width: 100%;
  color: var(--ep-text);
  background: var(--ep-panel-opaque);
  border: 1px solid var(--ep-hairline);
  border-radius: var(--ep-radius-sm);
  transition: all var(--ep-dur-fast) var(--ep-ease-expo);
}

.vs-input::placeholder {
  color: var(--ep-text-meta);
}

.vs-input:focus {
  outline: none;
  border-color: var(--ep-accent);
  box-shadow: 0 0 0 3px var(--ep-accent-wash);
}

/* Bandera del selector de idioma. */
.vs-flag {
  border: 1px solid var(--ep-hairline);
  border-radius: var(--ep-radius-sm);
  overflow: hidden;
  opacity: 0.5;
  transition: all var(--ep-dur-fast) var(--ep-ease-expo);
}

.vs-flag:hover {
  opacity: 0.9;
  border-color: var(--ep-hairline-hi);
}

.vs-flag.active {
  opacity: 1;
  border-color: var(--ep-accent);
  box-shadow: 0 0 12px -2px var(--ep-glow);
}

/* Barra de progreso del reproductor. */
.vs-progress {
  background: var(--ep-lift);
  border-radius: var(--ep-radius-pill);
  overflow: hidden;
}

.vs-progress__fill {
  height: 100%;
  background: linear-gradient(90deg, var(--ep-accent-solid), var(--ep-accent));
  box-shadow: 0 0 12px -2px var(--ep-glow);
  transition: width 0.1s linear;
}

/* --- ambiente puntual ------------------------------------------------------ */

/* LED: el indicador vivo de la barra. */
.vs-pulse {
  animation: vs-pulse-soft 3.4s ease-in-out infinite;
}

/* -----------------------------------------------------------------------------
   6. KEYFRAMES
   -------------------------------------------------------------------------- */
@keyframes vs-shimmer {
  0% {
    transform: translateX(-100%) skew(-15deg);
  }
  100% {
    transform: translateX(200%) skew(-15deg);
  }
}

@keyframes vs-sheen {
  0% {
    transform: translateX(-120%) skew(-12deg);
  }
  100% {
    transform: translateX(240%) skew(-12deg);
  }
}

@keyframes vs-orb-a {
  0%,
  100% {
    transform: translate(0) scale(1);
  }
  50% {
    transform: translate(48px, -34px) scale(1.1);
  }
}

@keyframes vs-orb-b {
  0%,
  100% {
    transform: translate(0) scale(1);
  }
  50% {
    transform: translate(-38px, 36px) scale(1.06);
  }
}

@keyframes vs-pulse-soft {
  0%,
  100% {
    opacity: 0.35;
  }
  50% {
    opacity: 0.8;
  }
}

/* -----------------------------------------------------------------------------
   7. Nada se mueve si el usuario pidio que nada se mueva. El origen NO trae esta
   regla; aca la animacion ambiental es decoracion y debe poder apagarse.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .vs-orb--a,
  .vs-orb--b,
  .vs-pulse,
  .vs-btn-shimmer::after,
  .group:hover .vs-sheen {
    animation: none;
  }

  .vs-btn:hover,
  .vs-lift:hover,
  .vs-btn-shimmer:hover:not(:disabled) {
    transform: none;
  }
}

/* =============================================================================
   NOTAS AL PIE

   PROCEDENCIA — esto es lenguaje visual de terceros. Los valores (paleta,
   escalas, radios) son datos y se reusan sin friccion; las clases de efecto de
   la seccion 5 son composicion autoral de vendorsify, reescrita aca pero
   reconocible. Vendorsify es donde trabaja el dueno del sitio, y la adopcion
   esta registrada en `context/web-styling.md`.

   LICENCIAS DE FUENTE — Geist / Geist Mono: SIL OFL 1.1 (Vercel), copia en
   `fonts/Geist*/LICENSE`. Clash Display: Indian Type Foundry via Fontshare,
   gratis para uso comercial; los .woff2 vienen del CDN oficial, NO del sitio.

   RUTAS — ojo con la asimetria: los `url()` de la seccion 1 se resuelven contra
   ESTE ARCHIVO (asi funciona CSS), mientras que los `src="img/..."` del HTML se
   resuelven contra el DOCUMENTO. Coinciden solo porque en la raiz publicada
   `index.html`, `vendorsify.css`, `img/` y `fonts/` son todos hermanos. Servir
   `portafolio/` sin `fonts/` al lado deja el sitio en system-ui: se ve mal pero
   no se rompe. Ver CLAUDE.md > Local preview.
   ============================================================================= */
