/* ═══════════════════════════════════════════════════════════════════════════
   Core 30 — base neutra

   ESTA HOJA VIAJA A TODAS LAS COPIAS. La identidad visual NO: la escribe la
   skill `disenar`, una vez por repo, en plantillas/estilo.css.

   Acá no hay ni una decisión de marca. Ni un color de acento, ni una
   tipografía elegida, ni una composición. Solo lo que hace falta para que el
   sitio se lea mientras se escribe el contenido — que es el paso más largo del
   proyecto y el que más tiempo se pasa mirando estas páginas.

   Todo lo de acá está pensado para que una piel lo pise sin pelear: valores
   sobrios, especificidad de un solo selector, cero !important.

   Se carga SIEMPRE, antes que estilo.css.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* Sin color de marca a propósito. Gris neutro hasta que corra `disenar`. */
  --b-tinta:    #16181d;
  --b-cuerpo:   #33363d;
  --b-mute:     #5c6068;   /* 4.9:1 sobre --b-papel */
  --b-papel:    #ffffff;
  --b-hunde:    #f4f4f6;
  --b-pelo:     #dcdde1;
  --b-enlace:   #1b4fa8;   /* 7.4:1 sobre --b-papel */
  --b-foco:     #1b4fa8;

  --b-medida:   68ch;      /* medida de línea legible */
  --b-ritmo:    1.6;
  --b-radio:    4px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --b-tinta:  #f2f3f5;
    --b-cuerpo: #d3d5da;
    --b-mute:   #a1a5ad;   /* 7.1:1 sobre --b-papel oscuro */
    --b-papel:  #16181d;
    --b-hunde:  #1f222a;
    --b-pelo:   #333740;
    --b-enlace: #8fb4f2;   /* 7.3:1 sobre --b-papel oscuro */
    --b-foco:   #8fb4f2;
  }
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--b-papel);
  color: var(--b-cuerpo);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: 1rem;
  line-height: var(--b-ritmo);
}

/* ── Ritmo vertical ────────────────────────────────────────────────────────
   El margen va SOLO hacia abajo: así dos bloques seguidos no acumulan. */
h1, h2, h3, p, ul, ol, table, figure, blockquote { margin: 0 0 1rem; }
h1, h2, h3 { margin-top: 2rem; }

h1, h2, h3 {
  color: var(--b-tinta);
  line-height: 1.25;
  font-weight: 650;
  text-wrap: balance;
}

/* El H1 lleva el ancla completa y suele ser largo: no se lo aprieta. */
h1 { font-size: 1.9rem; margin-top: 0; }
h2 { font-size: 1.45rem; }
h3 { font-size: 1.15rem; }

p { max-width: var(--b-medida); text-wrap: pretty; }

/* ── Contenedores ─────────────────────────────────────────────────────────
   Sin composición: una columna centrada. La piel decide lo demás. */
header, main, footer, section { padding-inline: 1.25rem; }

main {
  max-width: 74rem;
  margin-inline: auto;
  padding-block: 1.5rem 3rem;
}

section { padding-inline: 0; margin-block: 2.5rem; }

/* ── Enlaces y foco ───────────────────────────────────────────────────────
   El foco es visible SIEMPRE. Es lo único acá que no se negocia: sin él el
   sitio no se puede usar con teclado, y eso no espera al diseño. */
a { color: var(--b-enlace); text-underline-offset: 2px; }
a:hover { text-decoration: none; }

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

/* ── Barra superior y pie ─────────────────────────────────────────────────── */
header, footer {
  border-color: var(--b-pelo);
  border-style: solid;
  border-width: 0;
  padding-block: 1rem;
}
header { border-bottom-width: 1px; }

/* EL BOTÓN DE LA CABECERA LLEVA DOS RÓTULOS Y SE VE UNO — 2026-08-20.
   `_base.html` escribe el largo (`cta_texto`) y el corto ("Call"); acá se
   decide cuál. Por defecto gana el largo, y en pantalla angosta se cambian,
   que es donde un `cta_texto` de seis palabras más el número se sale del
   viewport. VA EN LA HOJA NEUTRA Y NO EN LA PIEL: el nodo lo emite la
   plantilla, así que un sitio SIN design system tiene que verse bien igual —
   sin esta regla se publican los dos rótulos a la vez. La piel puede mover el
   corte; lo que no puede es olvidarse, porque el default ya es correcto. */
.site-header__cta-corto { display: none; }

@media (max-width: 40rem) {
  .site-header__cta-label { display: none; }
  .site-header__cta-corto { display: inline; }
}
footer { border-top-width: 1px; margin-top: 3rem; color: var(--b-mute); }

header nav ul, footer nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

/* El mapa del pie llega como el snippet que Google da para copiar: con
   `width="600" height="450"` en atributos. Sin nadie que lo mida se desborda
   del viewport en móvil, y en el pie eso rompe las ~40 páginas a la vez. Se
   mide acá para que la copia pueda pegar el embed sin editarlo. Alto y lugar
   los decide la piel; esto es solo que entre. */
/* El formulario de afuera (`form_embed`, GoHighLevel y similares). Sin el
   script del proveedor el iframe no se estira solo: el alto es FIJO y la piel
   lo ajusta medido contra el formulario publicado. Este es el piso neutro. */
.form__embed {
  display: block;
  width: 100%;
  height: 1000px;
  border: 0;
}
@media (max-width: 760px) { .form__embed { height: 1180px; } }

/* El gracias del formulario (`form_gracias`): oculto hasta que el puente
   devuelve a /contact/#gracias. Sin JavaScript. */
.form__gracias { display: none; }
.form__gracias:target { display: block; }

.site-footer__mapa { max-width: 34rem; margin-bottom: 1rem; }
.site-footer__mapa iframe {
  display: block;
  width: 100%;
  height: 260px;
  border: 0;
  border-radius: var(--b-radio);
}

/* El teléfono es la conversión del rubro: se ve antes que el diseño exista. */
.tel, a[href^="tel:"] { font-weight: 650; white-space: nowrap; }
/* Una TARJETA entera que es un enlace tel: (la de /contact/) lleva adentro el
   horario y el rótulo: el nowrap de arriba le impedía partir el renglón y a 390px
   la página scrolleaba de costado (layout.py I2, 2026-11-09). Sólo el número no
   se parte. */
.contacto__dato[href^="tel:"] { white-space: normal; }
.contacto__dato[href^="tel:"] .contacto__valor { white-space: nowrap; }

/* El video de la página. Mismo problema que el mapa y la misma cura: el embed
   de YouTube trae `width="560" height="315"` en atributos y sin medirlo se
   desborda en móvil. `aspect-ratio` los pisa, así que el marco es fluido y
   guarda el 16/9 sin que nadie calcule un padding-top. Alto, ancho y lugar los
   decide la piel; esto es solo que entre y que no salte al cargar. */
/* SIN ANCHO PROPIO, y es la corrección de la primera vez que se miró esto con
   una piel puesta. Un `max-width: 48rem` acá dejaba el marco 440px más angosto
   que el párrafo de arriba, con un hueco a la derecha que se lee como un error
   de maquetado. La medida la da el contenedor, igual que a las imágenes: es la
   misma regla que evita declarar una grilla propia y pisar la del sitio. */
.video__marco { margin-top: 1.5rem; }
.video__marco iframe {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: var(--b-radio);
  background: var(--b-hunde);
}
.video__rotulo { color: var(--b-mute); margin-bottom: .25rem; }

/* ⛔ EL RÓTULO, EL H2 Y LA PROSA VAN CENTRADOS, SIEMPRE — operador, 2026-09-07.
   Es un PRECEDENTE del modelo y no una preferencia de un cliente: donde se
   embebe un video, el texto de esa sección va centrado.

   POR QUÉ VIVE ACÁ Y NO EN LA PIEL. `base.css` es la hoja neutra, así que la
   tentación es dejar la alineación para `diseno/estilo.css` de cada copia. Pero
   una regla que vale para todos los clientes puesta en la piel de uno es una
   regla que el próximo no hereda, y se vuelve a decidir de cero cada vez. Acá
   es cierta por defecto, y una piel que quiera otra cosa la pisa como pisa
   cualquier otra declaración.

   QUÉ PROBLEMA RESUELVE, medido el 2026-09-07 en el Homepage de Boca Raton a
   1440px: el marco del video ocupa el ancho del contenedor —1240px, que es la
   corrección de arriba— y el bloque de texto alineado a la izquierda se cortaba
   a la mitad del ancho, con media franja vacía a la derecha y el video enorme
   debajo. Los dos son correctos por separado y juntos se leen como un error de
   maquetado. Centrar el texto es lo que los ata.

   La medida sale de `--b-medida` y no de un número nuevo: es la misma línea
   legible que el resto del sitio. */
.video__texto {
  text-align: center;
  max-width: var(--b-medida);
  margin-left: auto;
  margin-right: auto;
}

/* ── Medios ───────────────────────────────────────────────────────────────── */
img, svg, video { max-width: 100%; height: auto; display: block; }
figure { margin-inline: 0; }
figcaption { color: var(--b-mute); font-size: .9rem; }

/* ── Tablas y código: que no desborden la página ──────────────────────────── */
table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: .5rem .75rem; border-bottom: 1px solid var(--b-pelo); }
pre { overflow-x: auto; background: var(--b-hunde); padding: 1rem; border-radius: var(--b-radio); }

/* ── Formulario ───────────────────────────────────────────────────────────── */
label { display: block; margin-bottom: .25rem; color: var(--b-tinta); }

input, select, textarea, button {
  font: inherit;
  color: inherit;
  max-width: 100%;
}

input, select, textarea {
  width: 100%;
  padding: .6rem .7rem;
  background: var(--b-papel);
  border: 1px solid var(--b-pelo);
  border-radius: var(--b-radio);
  margin-bottom: 1rem;
}

/* El placeholder necesita el mismo contraste que el cuerpo: se lee con sol de
   costado en un teléfono, que es exactamente cuando se usa. */
::placeholder { color: var(--b-mute); opacity: 1; }

button, .boton {
  display: inline-block;
  padding: .6rem 1.1rem;
  background: var(--b-tinta);
  color: var(--b-papel);
  border: 1px solid var(--b-tinta);
  border-radius: var(--b-radio);
  text-decoration: none;
  cursor: pointer;
  /* Área táctil mínima. */
  min-height: 44px;
}

/* ── Grillas ──────────────────────────────────────────────────────────────
   auto-fit y no un número fijo de columnas: una categoría con un solo hijo
   no puede dejar la grilla huérfana. */
.grilla {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: 1rem;
  padding: 0;
  list-style: none;
}

.grilla > * {
  border: 1px solid var(--b-pelo);
  border-radius: var(--b-radio);
  padding: 1rem;
}

/* ── Fila de specs del hero ───────────────────────────────────────────────
   La dibuja el parcial `hero` cuando la página declara `spec_1`. Acá va lo
   mínimo para que la fila se lea como fila: sin esto son cuatro <dt>/<dd>
   apilados, que es peor que no tenerla. La piel de cada cliente la pisa con
   un selector y le pone su tipografía; esto no elige ninguna.

   auto-fit y no `repeat(4, …)`: la fila acepta de dos a cuatro pares —el
   parcial los dibuja condicionales— y con cuatro columnas fijas dos pares
   dejan media fila vacía. */
.hero__specs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr));
  gap: 0;
  margin: 1.5rem 0 0;
  border-top: 1px solid var(--b-pelo);
}

.hero__spec {
  padding: .85rem 1rem;
  border-right: 1px solid var(--b-pelo);
}

.hero__spec:last-child { border-right: 0; }
.hero__spec dt { color: var(--b-mute); font-size: .75rem; }
.hero__spec dd { margin: .25rem 0 0; color: var(--b-tinta); font-weight: 650; }

/* ── Galería de trabajos: el carrusel sin JavaScript ──────────────────────
   Cuatro piezas a la vez en escritorio, con flechas; una tira que se desliza
   con el dedo en móvil. Ningún script: la página activa la guarda un radio y
   estas reglas leen su `:checked`. El HTML está en _componentes.html.

   EL 4 DE `repeat(4, 1fr)` ES LA MISMA CONSTANTE QUE GALERIA_POR_PAGINA en
   scripts/build.py. Si una cambia y la otra no, cada clic corre la pista por
   una fracción de columna. Están las dos porque el CSS no sabe partir listas.

   Acá tampoco hay marca: proporciones, tokens neutros y nada más. Una piel
   pisa esto con un solo selector, que es la regla de toda la hoja. */
.galeria__estado {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.galeria__cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  justify-content: space-between;
  gap: 1rem;
}

/* Móvil primero: la tira se desliza y las flechas no existen. Un carrusel con
   botones de 44px en una pantalla de 360 es un pulgar tapando la foto. */
.galeria__flechas { display: none; }

.galeria__viewport {
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
}

.galeria__pista { display: flex; gap: 1rem; }

.galeria__pagina {
  display: flex;
  gap: 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.galeria__item {
  flex: 0 0 min(78vw, 22rem);
  scroll-snap-align: start;
}

.galeria__figura { margin: 0; }

.galeria__figura img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--b-radio);
}

.galeria__pie { display: block; margin-top: .5rem; }
.galeria__titulo { display: block; color: var(--b-tinta); }
.galeria__detalle { color: var(--b-mute); font-size: .9rem; }

@media (min-width: 60rem) {
  /* La pista pasa a ser una fila de PÁGINAS, cada una del ancho exacto del
     viewport. Por eso `gap: 0` acá: con separación entre páginas, un
     translateX(-100%) queda corto por ese gap y el desfase se acumula. */
  .galeria__viewport { overflow: hidden; }

  .galeria__pista {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 100%;
    gap: 0;
    transition: transform .45s ease;
  }

  .galeria__pagina { display: grid; grid-template-columns: repeat(4, 1fr); }

  .galeria__flechas { display: flex; }
  .galeria__nav { display: none; gap: .5rem; }

  .galeria__flecha {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin: 0;
    border: 1px solid var(--b-pelo);
    border-radius: 50%;
    background: var(--b-papel);
    color: var(--b-tinta);
    cursor: pointer;
  }

  /* La flecha del extremo no se saca: se apaga. Sacarla mueve la otra de lugar
     en cada clic, y un control que salta es un control que no se aprende. */
  .galeria__flecha--off { opacity: .35; cursor: default; }

  /* ── El estado ──────────────────────────────────────────────────────────
     Una regla por página, escritas a mano hasta GALERIA_PAGINAS_MAX = 4.
     `:nth-of-type` cuenta entre los <input> hermanos, que son solo estos. */
  .galeria__estado:nth-of-type(2):checked ~ .galeria__viewport .galeria__pista { transform: translateX(-100%); }
  .galeria__estado:nth-of-type(3):checked ~ .galeria__viewport .galeria__pista { transform: translateX(-200%); }
  .galeria__estado:nth-of-type(4):checked ~ .galeria__viewport .galeria__pista { transform: translateX(-300%); }

  .galeria__estado:nth-of-type(1):checked ~ .galeria__cabecera .galeria__nav:nth-child(1),
  .galeria__estado:nth-of-type(2):checked ~ .galeria__cabecera .galeria__nav:nth-child(2),
  .galeria__estado:nth-of-type(3):checked ~ .galeria__cabecera .galeria__nav:nth-child(3),
  .galeria__estado:nth-of-type(4):checked ~ .galeria__cabecera .galeria__nav:nth-child(4) { display: flex; }

  /* El radio es invisible y sigue siendo lo ÚNICO que el teclado alcanza: los
     <label> no reciben foco. Sin esto, tabular hasta el carrusel no se ve, y
     las flechas del teclado cambian de página sin que nada lo muestre. */
  .galeria__estado:focus-visible ~ .galeria__cabecera .galeria__flechas {
    outline: 2px solid var(--b-foco);
    outline-offset: 4px;
    border-radius: 2px;
  }
}

/* ── Accesibilidad ────────────────────────────────────────────────────────── */
.visualmente-oculto {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}
