/* ---- variable contract (render.js sets accent/radius/font; themes set bg/fg/muted) ---- */
.wb-root {
  --wb-accent: #1F7DFE;
  --wb-graphite: #2C2A2A;
  --wb-radius: 18px;
  --wb-font: 'Geist', system-ui, sans-serif;
  --wb-font-heading: 'Bricolage Grotesque', system-ui, sans-serif;
  --wb-bg: #F4F8FE;
  --wb-fg: #2C2A2A;
  /* muted pasa AA (4.5:1) sobre los fondos claros de los cuatro temas; el tono
     anterior (#6B7585) se quedaba en 4.2-4.4 y el texto secundario no cumplía */
  --wb-muted: #5A6270;
  /* velo de la imagen de fondo: cada modo aclara u oscurece la foto lo justo para
     que el texto conserve el contraste que el tema diseñó */
  --wb-scrim: rgba(255, 255, 255, .62);
  /* logo box: the theme owns the size, the shape (data-logo) owns ratio + radius */
  --wb-logo-w: 76px;
  --wb-logo-r: 50%;
}
.wb-root.mode-dark { --wb-scrim: rgba(8, 10, 14, .66); }
.wb-root, .wb-root *, .wb-root *::before, .wb-root *::after { box-sizing: border-box; }

.wb-root {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  font-family: var(--wb-font);
  background: var(--wb-bg);
  color: var(--wb-fg);
  /* svh: la pantalla CON la barra de URL visible — si el contenido cabe, jamás hay scroll.
     (dvh no sirve: Android lo calcula expandido al cargar y deja scroll fantasma.) */
  min-height: 100vh;
  min-height: 100svh;
  padding: 44px 24px 22px;
  /* edge-to-edge: keep the footer clear of the phone's gesture strip */
  padding-bottom: calc(22px + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  gap: 15px;
}

/* imagen de fondo opcional (theme.bgImage → data-bgimage): longhands para conservar
   el background-color del tema debajo; pinta bajo el contenido y bajo las capas
   animadas de backgrounds.css (z-index -1 sobre el fondo propio del elemento) —
   la consola típicamente la empareja con background: 'none'.
   El velo (--wb-scrim) va como capa SUPERIOR de la misma propiedad: sin él una foto
   clara bajo un tema oscuro (o al revés) deja ilegibles el nombre y los bloques de
   contenido, que por diseño no llevan tarjeta detrás. Es rgba fijo a propósito: un
   color-mix aquí invalidaría la declaración entera en navegadores sin soporte y la
   tarjeta se quedaría sin foto. */
.wb-root[data-bgimage] {
  background-image: linear-gradient(var(--wb-scrim), var(--wb-scrim)), var(--wb-bg-image);
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

/* the background is full-bleed; the content stays a phone-width column on any viewport */
.wb-header, .wb-blocks, .wb-branches, .wb-addblock, .wb-footer {
  width: 100%; max-width: 480px; margin-inline: auto;
}

/* ---- header ---- */
.wb-header { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.wb-logo {
  /* the height falls back to the width, so every 1:1 shape stays square on any theme */
  width: var(--wb-logo-w); height: var(--wb-logo-h, var(--wb-logo-w));
  border-radius: var(--wb-logo-r); object-fit: cover;
  display: block; background: rgba(31,125,254,.12);
}
/* logo shape (theme.logoShape → data-logo). It decides the crop: `object-fit: cover`
   trims whatever the photo's aspect ratio does not fill. Keyed on the attribute so
   it always outweighs a theme's class selector. */
.wb-root[data-logo="circle"] { --wb-logo-r: 50%; }
.wb-root[data-logo="square"] { --wb-logo-r: 0; }
.wb-root[data-logo="rounded"] { --wb-logo-r: 18px; }
/* the arch is the only portrait shape — 96 → 130, spa's original proportion */
.wb-root[data-logo="arch"] {
  --wb-logo-r: 999px 999px 6px 6px;
  --wb-logo-h: calc(var(--wb-logo-w) * 130 / 96);
}
.wb-eyebrow { font-size: 11px; letter-spacing: .2em; text-transform: uppercase; color: var(--wb-accent); }
.wb-name { margin: 0; font-size: 23px; font-weight: 700; color: var(--wb-fg); font-family: var(--wb-font-heading); }
.wb-tagline { margin: 0; font-size: 12.5px; color: var(--wb-muted); }
/* long unbroken words (names, pasted urls as labels) must wrap, not clip against overflow:hidden */
.wb-name, .wb-tagline, .wb-eyebrow, .wb-link-label, .wb-membership-label, .wb-schedule-hours,
.wb-heading, .wb-text {
  overflow-wrap: anywhere;
}

/* ---- blocks container ---- */
.wb-blocks { display: flex; flex-direction: column; gap: 11px; }
.wb-blocks[data-layout="grid"] { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
/* in grid, only links are cells; banner-like blocks keep the full row */
.wb-blocks[data-layout="grid"] > :not(.wb-link) { grid-column: 1 / -1; }
/* Location carries the same business context as the schedule and needs the
   same readable width on narrow public profiles. */
.wb-blocks[data-layout="grid"] > .wb-link-location { grid-column: 1 / -1; }

/* ---- block primitives ---- */
.wb-block, .wb-cta, .wb-addblock {
  display: flex; align-items: center; gap: 12px;
  padding: 15px 17px;
  border-radius: var(--wb-radius);
  border: 1px solid rgba(127,127,127,.25);
  text-decoration: none;
  color: inherit;
  font-size: 14.5px;
  font-weight: 500;
}
.wb-cta {
  justify-content: center;
  padding: 18px;
  background: var(--wb-accent);
  color: #fff;
  font-weight: 700;
  font-size: 17px;
  border: none;
}
.wb-link-icon { font-size: 17px; color: var(--wb-accent); }
.wb-link-label { flex: 1; }
/* optional button thumbnail (link/cta.image) */
.wb-btn-img {
  width: 42px; height: 42px; flex: none;
  border-radius: min(calc(var(--wb-radius) / 2), 12px);
  object-fit: cover; display: block;
}
.wb-cta .wb-btn-img { width: 34px; height: 34px; }

.wb-socials { display: flex; justify-content: center; gap: 9px; border: none; padding: 0; background: none; }
.wb-social {
  width: 36px; height: 36px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 12px; text-decoration: none; color: inherit;
  border: 1px solid rgba(127,127,127,.25);
}

.wb-membership { justify-content: space-between; }
.wb-membership-text { display: flex; flex-direction: column; gap: 3px; align-items: flex-start; }
.wb-membership-label { font-size: 13px; }
.wb-membership-note { font-size: 11.5px; color: var(--wb-muted); }
.wb-membership-price { font-size: 15px; font-weight: 700; color: var(--wb-accent); }

.wb-schedule { flex-direction: column; align-items: flex-start; gap: 6px; }
.wb-schedule-label { font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: var(--wb-accent); }
.wb-schedule-hours { font-size: 19px; }

.wb-marquee { overflow: hidden; padding: 9px 0; border: none; border-radius: 0; background: var(--wb-accent); }
.wb-marquee-track { display: flex; gap: 24px; width: max-content; animation: wb-marquee 14s linear infinite; }
.wb-marquee-text {
  font-size: 13px; font-weight: 700; letter-spacing: .16em;
  text-transform: uppercase; color: #fff; white-space: nowrap;
}
@keyframes wb-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* ---- content blocks (heading / text / spacer) ----
   They carry .wb-block for the column width only: the button skin (border,
   background, padding, radius) is neutralized here, and every theme's generic
   .wb-block decorator excludes them — together with .wb-socials and .wb-marquee,
   the two blocks this file also declares chrome-less — inside a zero-specificity
   :not(:where(…)), so these rules and the per-block ones keep winning. */
.wb-heading, .wb-text, .wb-spacer {
  display: block;
  margin: 0; padding: 4px 0;
  border: none; border-radius: 0; background: none;
}
.wb-heading {
  font-family: var(--wb-font-heading);
  font-size: 17px; font-weight: 700; line-height: 1.25;
  text-align: left;
}
/* body copy, not a caption: --wb-muted is a 4.3:1 label grey and fails AA at this
   size. A softened foreground keeps the hierarchy and stays above 7:1 in the four
   themes; where color-mix is missing the declaration drops to the plain foreground. */
.wb-text {
  font-size: 13.5px; font-weight: 400; line-height: 1.55;
  color: color-mix(in srgb, var(--wb-fg) 80%, var(--wb-bg));
}
.wb-spacer { padding: 0; height: 24px; }
.wb-spacer[data-size="sm"] { height: 12px; }
.wb-spacer[data-size="lg"] { height: 40px; }

/* ---- selector de sedes (renderBranchPicker) ----
   Cada sede es una tarjeta .wb-block: hereda la piel del tema tal cual, así que
   el selector se ve como el mismo producto que la bio. El chevron va dibujado en
   CSS —monocromo, sin asset ni emoji— y apunta a "entrar aquí". */
.wb-branches { display: flex; flex-direction: column; gap: 11px; }
.wb-branch { justify-content: space-between; gap: 14px; }
.wb-branch-text { display: flex; flex-direction: column; gap: 3px; align-items: flex-start; min-width: 0; }
.wb-branch-name { font-size: 15px; font-weight: 600; overflow-wrap: anywhere; }
.wb-branch-address { font-size: 12px; line-height: 1.4; color: var(--wb-muted); overflow-wrap: anywhere; }
.wb-branch::after {
  content: ''; flex: none; width: 9px; height: 9px; margin-inline-end: 3px;
  border-top: 2px solid var(--wb-accent); border-right: 2px solid var(--wb-accent);
  transform: rotate(45deg);
}

.wb-addblock {
  justify-content: center; gap: 9px;
  background: transparent;
  border: 2px dashed var(--wb-accent);
  color: var(--wb-accent);
  font-weight: 700;
  cursor: pointer;
  font-family: inherit;
}

/* ---- footer ---- */
.wb-footer {
  margin-top: auto;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  font-size: 11px; color: var(--wb-muted);
}
.wb-footer img { height: 14px; width: auto; display: block; }
/* "Hecho con [logo]" viaja junto: así el handle puede irse al lado opuesto cuando
   el tema separa el footer (brutalista) sin partir la firma en dos */
.wb-made { display: inline-flex; align-items: center; gap: 6px; }
.wb-handle { color: var(--wb-muted); }
/* separador solo cuando van seguidos; el tema que los separa lo anula */
.wb-handle + .wb-made::before { content: '·'; margin-right: 6px; }

/* ---- esqueleto de carga (renderSkeleton) ----
   El hueco mientras el API contesta. Dibuja la MISMA forma que va a aparecer
   —la marca donde irá la foto, el nombre, unos botones— sobre la piel real del
   widget: el header, los .wb-block y el footer son los de arriba, así que cuando
   llegan los datos la tarjeta se rellena en vez de cambiar de página. Aquí solo
   se declaran las barras y la marca.
   El color sale de currentColor: la barra es el propio texto del tema a muy
   baja opacidad, y así se lee igual sobre una tarjeta clara que sobre una
   oscura sin declarar un gris por modo. La opacidad va en la regla base y el
   pulso solo la mueve: con prefers-reduced-motion el bloque de abajo apaga la
   animación y las barras se quedan quietas y visibles. */
.wb-skel {
  background: currentColor;
  opacity: .11;
  animation: wb-skel-pulse 1.5s ease-in-out infinite;
}

/* todas son barras de texto: píldora. La marca de arriba trae su propia forma */
.wb-skel-name, .wb-skel-tagline, .wb-skel-line, .wb-skel-made {
  display: block;
  border-radius: 999px;
}
.wb-skel-name { width: 58%; max-width: 210px; height: 21px; }
.wb-skel-tagline { width: 38%; max-width: 140px; height: 11px; }
.wb-skel-line { width: 62%; height: 15px; }
/* anchos alternos: cuatro barras idénticas se leen como una tabla, no como una
   lista de botones a medio cargar */
.wb-skel-block:nth-child(even) .wb-skel-line { width: 46%; }
.wb-skel-made { width: 104px; height: 10px; }
@keyframes wb-skel-pulse {
  0%, 100% { opacity: .11; }
  50% { opacity: .05; }
}

/* El esqueleto entra desvanecido, nunca de golpe. bio.html ya espera
   SKELETON_DELAY_MS (200 ms) antes de pedirlo; esta entrada de 240 ms hace la
   otra mitad del trabajo contra el parpadeo. La curva es ease-IN a propósito:
   arranca lenta, así que una respuesta de ~350 ms borra un fantasma al 20-30 %
   —no una pantalla entera— y una espera de verdad lo tiene entero a los 440 ms,
   que nadie percibe como tarde. La salida es instantánea, también a propósito:
   lo que lo sustituye es la bio, y retenerla para desvanecer un relleno sería
   cobrarle al visitante justo el tiempo que se quería ahorrar. */
.wb-root[data-skeleton] { animation: wb-skel-in .24s ease-in both; }
@keyframes wb-skel-in { from { opacity: 0; } }

/* La marca de WeiBook (js/render.js la dibuja en SVG) ocupa la caja de la foto:
   el único punto de color de toda la espera. El azul es FIJO y no var(--wb-accent),
   porque el acento es del negocio y aquí todavía no ha llegado ninguno — este es
   el azul de WeiBook. Sobre la tarjeta clara del tema por defecto da 3.6:1, y si
   alguna vez se pintara sobre una oscura, 4.7:1: AA para gráficos (3:1) en los dos. */
.wb-skel-mark {
  width: var(--wb-logo-w); height: var(--wb-logo-h, var(--wb-logo-w));
  color: #1F7DFE;
  animation: wb-mark-pulse 1.5s ease-in-out infinite;
}
/* el mismo compás que las barras (1.5s), otra amplitud: un logo al 11% no se
   vería. Late con la opacidad y una escala mínima — respira, no rebota ni gira */
@keyframes wb-mark-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: .45; transform: scale(.965); }
}

/* solo para lectores de pantalla (el "Cargando…" que ya no se dibuja) */
.wb-sr {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  .wb-root, .wb-root::before, .wb-root::after,
  .wb-root *, .wb-root *::before, .wb-root *::after { animation: none !important; transition: none !important; }
}
