/* ============================================================
   Visuala Studio — Component primitives
   Reusable building blocks shared across every section. Each
   primitive uses BEM-style modifiers (.btn--primary, .card--dark)
   so HTML stays declarative and CSS stays scoped.
   ============================================================ */


/* ============================================================
   1 · BUTTON  .btn
   Pill-shaped CTA with three variants × three sizes × two tones.
   Primary = lime fill (brand CTA, never anywhere else).
   Secondary = outlined ink/cream pill.
   Ghost = text-only, no border, no fill.
   Add .btn--arrow to tuck an SVG arrow that nudges on hover.
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 16px 22px;          /* size: md (default) */
  font-family: var(--font-sans);
  font-size: var(--text-body);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition:
    box-shadow var(--dur-base) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out),
    background-color var(--dur-base) var(--ease-out);
}

/* Sizes */
.btn--sm { padding: 9px 16px;  font-size: var(--text-sm); gap: 7px; }
.btn--md { padding: 16px 22px; font-size: var(--text-body); gap: 9px; }
.btn--lg { padding: 17px 32px; font-size: 1.125rem;  gap: 10px; }

/* Width */
.btn--full { width: 100%; }

/* Disabled */
.btn[disabled],
.btn--disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

/* Arrow tucked at the end of the button (use an inline <svg>). */
.btn__arrow {
  width: 17px;
  height: 17px;
  transition: transform var(--dur-base) var(--ease-reveal);
}

/* Variants × tones */

/* Primary — the lime CTA. Never used outside the primary CTA role. */
.btn--primary {
  background: var(--color-accent);
  color: var(--on-accent);
  border-color: var(--color-accent);
}
.btn--primary:hover:not([disabled]) {
  box-shadow: 0 10px 24px rgba(29, 33, 37, 0.18);
  transform: translateY(-2px);
}
.btn--primary:active:not([disabled]) { transform: translateY(0) scale(0.98); }

/* Secondary — outlined. Two tones for cream vs dark backgrounds. */
.btn--secondary {
  background: transparent;
  color: var(--fg-1);
  border-color: var(--border-strong);
}
.btn--secondary:hover:not([disabled]) { border-color: var(--fg-1); }

.btn--secondary.btn--dark {
  color: var(--fg-on-dark);
  border-color: var(--border-on-dark);
}
.btn--secondary.btn--dark:hover:not([disabled]) { border-color: var(--fg-on-dark); }

/* Ghost — text-only, no fill, no border. Small horizontal padding so the
   focus ring stays tight. */
.btn--ghost {
  background: transparent;
  color: var(--fg-1);
  border-color: transparent;
  padding-left: 4px;
  padding-right: 4px;
}
.btn--ghost.btn--dark { color: var(--fg-on-dark); }

/* Arrow nudge on hover (works across variants) */
.btn:hover:not([disabled]) .btn__arrow { transform: translateX(3px); }


/* ============================================================
   2 · PILL  .pill
   Small rounded label. Four variants — outline (default), frosted
   (overlaid on imagery), solid (filled emphasis), accent (subtle
   tinted fill). Tones for cream / dark.
   Pills NEVER use the lime accent fill — that's reserved for CTAs.

   --pill-scale multiplies padding + font-size across every pill-like
   element (.pill, .pill--uppercase, .svc-card__label, .hero__pills
   .pill). Tweak it in :root from DevTools to preview different sizes
   live without editing each rule.

   The frosted variant (and its siblings .svc-card__label /
   .cases__label) share ONE recipe — see the "FROSTED CHIP" block
   right after the variants. The legacy dark-tinted glass was
   deprecated 2026-06-24; everything is white "vidrio esmerilado"
   now, driven by --chip-glass-* + --chip-blur tokens.
   ============================================================ */
:root {
  --pill-scale: 0.9;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: calc(6px * var(--pill-scale));
  padding: calc(7px * var(--pill-scale)) calc(13px * var(--pill-scale));
  font-family: var(--font-sans);
  font-size: calc(var(--text-xs) * var(--pill-scale));
  font-weight: 500;
  letter-spacing: -0.005em;
  line-height: 1;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  white-space: nowrap;
}

/* ALL CAPS pills (category chips, frosted media labels).
   Geometry (padding + font-size) is shared with .pill base — only
   typographic treatment changes. */
.pill--uppercase {
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* Variants × tones */
.pill--outline {
  color: var(--fg-2);
  border-color: var(--border-strong);
  background: transparent;
}
.pill--outline.pill--dark {
  color: var(--fg-on-dark);
  border-color: var(--border-on-dark);
}

.pill--frosted {
  /* Glass treatment (color + background + border + backdrop-filter)
     lives in the shared "FROSTED CHIP" recipe below — single source
     of truth across .pill--frosted, .svc-card__label, .cases__label.
     This local rule only widens the tracking from .pill--uppercase's
     0.1em to the chip standard. */
  letter-spacing: var(--ls-chip);
}

.pill--solid {
  color: var(--cream-100);
  background: var(--ink-900);
  border-color: transparent;
}
.pill--solid.pill--dark {
  color: var(--green-900);
  background: var(--cream-100);
}

.pill--accent {
  color: var(--fg-1);
  background: rgba(29, 33, 37, 0.06);
  border-color: rgba(29, 33, 37, 0.10);
}
.pill--accent.pill--dark {
  color: var(--fg-on-dark);
  background: rgba(247, 246, 239, 0.12);
  border-color: rgba(247, 246, 239, 0.22);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}


/* ============================================================
   2b · FROSTED CHIP — shared recipe
   Single source of truth for the "vidrio esmerilado" pill that
   floats over imagery. Three call sites:
   · .pill--frosted    — testimonial cards (composed on .pill)
   · .svc-card__label  — services carousel
   · .cases__label     — cases banners
   Each call site adds its own positioning + per-section quirks.
   Consumes --chip-glass-fill / --chip-glass-border (colors.css)
   and --chip-blur (spacing.css). Replaces the legacy dark glass
   recipe deprecated 2026-06-24.
   ============================================================ */

/* Geometry + uppercase typography — applied to the two chips that
   are NOT built on .pill base (.pill--frosted inherits geometry
   from .pill + .pill--uppercase). */
.svc-card__label,
.cases__label {
  display: inline-flex;
  align-items: center;
  padding: calc(7px * var(--pill-scale)) calc(13px * var(--pill-scale));
  line-height: 1;
  font-family: var(--font-sans);
  font-size: calc(var(--text-xs) * var(--pill-scale));
  font-weight: 600;
  letter-spacing: var(--ls-chip);
  text-transform: uppercase;
  border-radius: var(--radius-pill);
}

/* Glass treatment — all three call sites. */
.pill--frosted,
.svc-card__label,
.cases__label {
  color: var(--fg-on-dark);
  background: var(--chip-glass-fill);
  border: 1px solid var(--chip-glass-border);
  backdrop-filter: var(--chip-blur);
  -webkit-backdrop-filter: var(--chip-blur);
}


/* ============================================================
   3 · CARD  .card
   Flexible surface container. Six surfaces, soft canonical radius,
   pad steps, optional hairline border + warm shadow.
   ============================================================ */
.card {
  background: var(--surface-raised);
  color: var(--fg-1);
  border-radius: var(--radius-card);
  padding: var(--space-6);
  border: none;
  box-shadow: none;
}

/* Surfaces */
.card--page    { background: var(--surface-page);     color: var(--fg-1); }
.card--raised  { background: var(--surface-raised);   color: var(--fg-1); }
.card--warm    { background: var(--surface-warm);     color: var(--fg-1); }
.card--dark    { background: var(--surface-dark);     color: var(--fg-on-dark); }
.card--darkest { background: var(--surface-darkest);  color: var(--fg-on-dark); }
.card--accent  { background: var(--color-accent);     color: var(--on-accent); }

/* Pads */
.card--pad-none { padding: 0; }
.card--pad-sm   { padding: var(--space-4); }
.card--pad-md   { padding: var(--space-5); }
.card--pad-lg   { padding: var(--space-6); }
.card--pad-xl   { padding: var(--space-7); }

/* Radii overrides */
.card--radius-sm  { border-radius: var(--radius-sm); }
.card--radius-md  { border-radius: var(--radius-md); }
.card--radius-lg  { border-radius: var(--radius-lg); }
.card--radius-2xl { border-radius: var(--radius-2xl); }

/* Borders + shadows */
.card--border             { border: 1px solid var(--border-soft); }
.card--dark.card--border,
.card--darkest.card--border { border-color: var(--border-on-dark); }

.card--shadow-sm { box-shadow: var(--shadow-sm); }
.card--shadow-md { box-shadow: var(--shadow-md); }
.card--shadow-lg { box-shadow: var(--shadow-lg); }


/* ============================================================
   4 · SERVICE CARD  .svc-card
   Cinematic media card with frosted label chip. Two layouts:
   · overlay — title sits over the image on a protection scrim
                (services carousel).
   · stacked — label on the image; title + copy + link below it
                (selected cases).
   ============================================================ */
.svc-card {
  display: block;
  text-decoration: none;
  color: inherit;
}
.svc-card--stacked {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.svc-card__media {
  position: relative;
  aspect-ratio: 3 / 4;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--green-900);
}
.svc-card__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The services track animates with translate3d, which promotes
     the whole stage to a GPU compositor layer. Chrome's default
     compositor downscale is bilinear and looks harsh on photos
     scaled ~5× down (1856px source → ~320px slot). These hints
     opt the image into the high-quality downscale path and pin it
     to a stable backface so the compositor doesn't re-sample at
     sub-pixel offsets during the drift. */
  image-rendering: -webkit-optimize-contrast;
  image-rendering: high-quality;
  image-rendering: smooth;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  transform: translateZ(0);
}

/* Row of frosted chip pills across the top of the media.
   Each pill = 1 concept. Pills share the row width equally
   (flex: 1 1 0) and wrap to a 2nd row when the sum of their
   intrinsic widths would overflow. Inset is off the 8px scale
   on purpose — see --chip-inset in tokens/spacing.css. */
.svc-card__labels {
  position: absolute;
  top: var(--chip-inset);
  left: var(--chip-inset);
  right: var(--chip-inset);
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
@media (max-width: 767px) {
  .svc-card__labels {
    top: var(--chip-inset-mobile);
    left: var(--chip-inset-mobile);
    right: var(--chip-inset-mobile);
  }
}

/* Frosted chip label. Geometry + glass treatment come from the
   shared "FROSTED CHIP" recipe below; this rule only lifts the
   chip above the media's bottom scrim ::after pseudo. */
.svc-card__label {
  z-index: 2;
  flex: 0 0 auto;
}

/* Overlay-layout scrim and title (title sits over the image).
   The scrim is a ::after pseudo, which always comes AFTER all real
   children in source order. To keep the label + title legible we
   give them an explicit z-index that beats the pseudo's default. */
.svc-card--overlay .svc-card__media::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(
    to top,
    rgba(10, 16, 12, 0.82) 0%,
    rgba(10, 16, 12, 0.18) 42%,
    rgba(10, 16, 12, 0) 70%
  );
  pointer-events: none;
}
.svc-card--overlay .svc-card__title {
  position: absolute;
  left: 22px;
  right: 22px;
  bottom: 22px;
  z-index: 2;
  margin: 0;
  color: var(--fg-on-dark);
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--text-h3);
  line-height: 1.12;
  letter-spacing: -0.02em;
}

/* Stacked-layout copy block (title + meta + arrow link) */
.svc-card__body { /* extra inset hook for stacked layout */ }
.svc-card__title {
  margin: 0;
  color: var(--fg-1);
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--text-h3);
  line-height: 1.14;
  letter-spacing: -0.02em;
}
.svc-card--dark .svc-card__title { color: var(--fg-on-dark); }

/* Mobile: bump card title font-size so it reads at the same visual
   weight as desktop and naturally wraps to 2 lines inside the 320px
   carousel cell. text-wrap: balance produces an even 2-line break. */
@media (max-width: 767px) {
  .svc-card--overlay .svc-card__title,
  .svc-card__title {
    font-size: 2rem;
    line-height: 1.1;
    text-wrap: balance;
  }
}

.svc-card__meta {
  margin: 10px 0 0;
  max-width: 42ch;
  color: var(--fg-2);
  font-size: var(--text-body);
  line-height: 1.55;
}
.svc-card--dark .svc-card__meta { color: var(--fg-on-dark-muted); }

/* Arrow link with growing underline on hover */
.svc-card__link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 18px;
  color: var(--fg-1);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--text-body);
  text-decoration: none;
  transition: gap var(--dur-base) var(--ease-out);
}
.svc-card--dark .svc-card__link { color: var(--fg-on-dark); }
.svc-card__link svg { transition: transform var(--dur-base) var(--ease-out); }
.svc-card__link-label {
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1.5px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  transition: background-size var(--dur-base) var(--ease-out);
  padding-bottom: 2px;
}
.svc-card__link:hover { gap: 13px; }
.svc-card__link:hover svg { transform: translateX(5px); }
.svc-card__link:hover .svc-card__link-label { background-size: 100% 1.5px; }


/* ============================================================
   5 · STAT BAR  .stat-bar
   Single tracked row of proof metrics, middot-separated.
   "+150 activos · 72h turnaround · 8x output"
   ============================================================ */
.stat-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 16px;
  justify-content: flex-start;
  font-family: var(--font-sans);
  font-size: var(--text-lead);
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--fg-1);
}
.stat-bar--sm { font-size: var(--text-sm); }
.stat-bar--center { justify-content: center; }
.stat-bar--end { justify-content: flex-end; }
.stat-bar--dark { color: var(--fg-on-dark); }

.stat-bar__sep {
  color: var(--ink-300);
  user-select: none;
}
.stat-bar--dark .stat-bar__sep { color: var(--green-200); }

.stat-bar__item { display: inline-flex; align-items: baseline; gap: 6px; }
.stat-bar__value { font-weight: 600; }
.stat-bar__label { color: var(--fg-muted); }
.stat-bar--dark .stat-bar__label { color: var(--fg-on-dark-muted); }


/* ============================================================
   6 · FAQ ITEM  .faq-item
   Cream card with question + rotating plus toggle. Answer reveal
   uses the canonical brand transition: height (grid-template-rows
   0fr→1fr) + fade + slide ~12px from the right. ease-reveal, dur-slow.
   Single-open behaviour is controlled by JS (Fase 3h) toggling
   .faq-item--open.
   ============================================================ */
.faq-item {
  background: var(--surface-raised);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  padding: 4px 6px;
  cursor: pointer;
  transition: border-color var(--dur-base) var(--ease-out);
}

.faq-item__head {
  width: 100%;
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 22px 18px;
  text-align: left;
}

.faq-item__question {
  flex: 1;
  font-family: var(--font-sans);
  font-weight: 450;
  font-size: var(--text-h3);
  line-height: 1.2;
  letter-spacing: -0.02em;
  color: var(--fg-1);
}

.faq-item__toggle {
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--ink-900);
  transition:
    background var(--dur-base) var(--ease-out),
    border-color var(--dur-base) var(--ease-out),
    transform var(--dur-base) var(--ease-reveal);
}
.faq-item--open .faq-item__toggle {
  background: var(--ink-900);
  border-color: var(--ink-900);
  color: var(--cream-50);
  transform: rotate(135deg);
}
.faq-item__toggle svg { width: 16px; height: 16px; }

.faq-item__body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-slow) var(--ease-reveal);
}
.faq-item--open .faq-item__body { grid-template-rows: 1fr; }

.faq-item__body-inner { overflow: hidden; }

.faq-item__answer {
  padding: 0 18px 24px;
  font-family: var(--font-sans);
  font-size: var(--text-body);
  line-height: 1.6;
  color: var(--fg-2);
  max-width: 60ch;
  opacity: 0;
  transform: translateX(12px);
  transition:
    opacity var(--dur-slow) var(--ease-reveal),
    transform var(--dur-slow) var(--ease-reveal);
}
.faq-item--open .faq-item__answer {
  opacity: 1;
  transform: translateX(0);
}


/* ============================================================
   7 · MEDIA PLAY BUTTON  .media-play
   Circular frosted play affordance, centered over a video preview.
   Shared by every "click to open testimonial modal" component:
   the two testimonial integrations (Conversion Gaps Sesión 4 —
   .diff-card--testimonial inside Diferenciadores, .proc-testi__card
   after Proceso). Each call site wires its own :hover trigger
   (parent doesn't share a common class), matching the "each call
   site adds its own positioning + quirks" convention used by the
   frosted chip recipe above.
   ============================================================ */
.media-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 64px;
  height: 64px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  pointer-events: none;
  z-index: 2;
  color: var(--cream-100);
  border: 1px solid var(--chip-glass-border);
  background: var(--chip-glass-fill);
  backdrop-filter: var(--chip-blur);
  -webkit-backdrop-filter: var(--chip-blur);
  transition:
    background var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    transform var(--dur-base) var(--ease-out);
}
.media-play svg path { transition: fill var(--dur-fast) var(--ease-out); }

/* Modal — darkens everything, shows ONLY the video with sound. */
@keyframes testi-modal-in { from { opacity: 0; } to { opacity: 1; } }
.testi-modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  padding: clamp(16px, 4vw, 52px);
  background: rgba(7, 10, 8, 0.94);
  animation: testi-modal-in var(--dur-base) var(--ease-out);
}
/* The HTML `hidden` attribute sets display:none in the UA stylesheet,
   but our `display: grid` above wins on specificity — force the hide
   back explicitly so the modal stays closed at boot. */
.testi-modal[hidden] { display: none; }
.testi-modal video {
  width: auto;
  height: auto;
  max-width: min(1120px, 92vw);
  max-height: 84vh;
  border-radius: var(--radius-card);
  background: #000;
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5);
}
.testi-modal__close {
  position: absolute;
  top: clamp(16px, 3vw, 28px);
  right: clamp(16px, 3vw, 28px);
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  cursor: pointer;
  color: var(--fg-on-dark);
  border: 1px solid rgba(247, 246, 239, 0.4);
  background: rgba(18, 22, 20, 0.5);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition:
    background var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}
.testi-modal__close:hover {
  background: var(--cream-100);
  color: var(--ink-900);
  transform: scale(1.06);
}


/* ============================================================
   8 · INPUT (FIELD)  .field
   Minimalist underline field. Uppercase eyebrow-style label, single
   1.5px bottom border that shifts to lime on focus. Works on cream
   (default) or dark green (.field--dark).
   ============================================================ */
.field {
  display: block;
  width: 100%;
}
.field__label {
  display: block;
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-muted);
  margin-bottom: 10px;
}
.field--dark .field__label { color: var(--green-200); }

.field__input {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 2px;
  background: transparent;
  border: none;
  border-bottom: 1.5px solid var(--border-strong);
  color: var(--fg-1);
  font-family: var(--font-sans);
  font-size: var(--text-lead);
  font-weight: 400;
  letter-spacing: -0.01em;
  outline: none;
  transition: border-color var(--dur-base) var(--ease-out);
}
.field--dark .field__input {
  border-bottom-color: var(--border-on-dark);
  color: var(--fg-on-dark);
}
/* Focus = full ink on cream / cream on dark. Lima is reserved for the
   primary CTA only; on the thin baseline it also loses contrast against
   the cream surface, so we go for the strongest neutral instead. */
.field__input:focus { border-bottom-color: var(--fg-1); }
.field--dark .field__input:focus { border-bottom-color: var(--fg-on-dark); }
.field__input::placeholder { color: var(--ink-300); }
.field--dark .field__input::placeholder { color: var(--green-200); opacity: 0.6; }

.field__hint {
  display: block;
  margin-top: 8px;
  font-size: var(--text-sm);
  color: var(--fg-muted);
}
.field--dark .field__hint { color: var(--green-200); }


/* ============================================================
   9 · SEGMENTED CONTROL  .segmented
   Row of pill options, one selected. Used for the "tamaño de equipo"
   step (1–10 · 11–20 · 21–50 · +50). Active option in lime.

   Grid (not flex) + auto-fit so 4 options never end with a lonely
   stretched pill on the second row at narrow widths — they fall
   into a 2×2 instead. minmax(110px, 1fr) lets each cell shrink to
   ~110px before reducing the column count, giving us 4×1 on wide
   cards and 2×2 on the mobile single-column layout.
   ============================================================ */
.segmented {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 10px;
}
.segmented__opt {
  min-width: 0;
  padding: 13px 18px;
  font-family: var(--font-sans);
  font-size: var(--text-body);
  font-weight: 500;
  letter-spacing: -0.01em;
  border-radius: var(--radius-pill);
  cursor: pointer;
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--fg-1);
  transition: all var(--dur-fast) var(--ease-out);
}
.segmented--dark .segmented__opt {
  border-color: var(--border-on-dark);
  color: var(--fg-on-dark);
}
.segmented__opt[aria-checked="true"],
.segmented__opt.is-active {
  background: var(--color-accent);
  color: var(--on-accent);
  border-color: var(--color-accent);
}


/* ============================================================
   10 · CTA BANNER  .cta-banner
   Full-bleed cinematic banner that closes a section with a single
   image + title + button lockup. The base below carries ONLY what
   every banner shares (stage, image plane, scrim plane, content
   plane); each variant owns its own proportions, scrim and lockup.

   Variants:
   · --mid                 landing mid-scroll banner (design "2a",
                           2026-07-24). Desktop: slim 340px bar, one
                           panoramic image full-bleed, title + button as
                           one left-hand block (46% column) vertically
                           centred in a single light weight, over a
                           horizontal green wipe (to right). Mobile: 4/5
                           poster with the same lockup stacked over the
                           bottom band (the mockup only specced desktop;
                           the portrait poster is preserved from 3b).
   · --case-study          wider banner (16/6) with horizontal
                           title + button bottom row. Tuned for the
                           longer editorial copy that closes a case
                           study page.

   Both variants share the mobile-portrait → desktop-landscape
   <picture> swap. Titles wrap naturally with text-wrap: balance
   plus a ch cap — no <br>, no NBSP, no nowrap (Android Chrome
   line-break history: see the text-size-adjust reset in base.css).

   Section-scoped positioning (e.g. margin-top relative to siblings)
   lives in the section CSS that hosts the banner (see .cases in
   cases.css), not on the primitive — so the same primitive can be
   reused in any context without inheriting a neighbour offset.
   ============================================================ */
.cta-banner {
  position: relative;
  border-radius: var(--radius-2xl);
  overflow: hidden;
  display: flex;
}
.cta-banner__media { display: contents; }
.cta-banner__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.cta-banner__scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.cta-banner__content {
  position: relative;
  display: flex;
}
.cta-banner__title {
  margin: 0;
  font-family: var(--font-sans);
  color: var(--fg-on-dark);
  /* Balanced wrap on supporting browsers (Chrome 114+, Safari 17.5+,
     Firefox 121+); graceful fallback to natural wrap elsewhere. Each
     variant adds the ch cap that decides how many lines it breaks into. */
  text-wrap: balance;
}

/* --- Variant: --mid — landing mid-scroll banner (design 2a) ---
   Mobile: tall 3/5 poster. The photo runs full-bleed (object-fit
   cover): the mobile source is 0.8 portrait, so in a narrower 3/5
   frame its FULL height still shows (head → feet) and only the couch
   sides trim — the subject is never cropped. The pose fills the upper
   two thirds and its lower body falls under a bottom-anchored green
   gradient that carries the copy, button and trust row. */
.cta-banner--mid {
  aspect-ratio: 3 / 5;
  flex-direction: column;
  justify-content: flex-end;
}
.cta-banner--mid .cta-banner__scrim {
  /* Bottom-anchored vertical wipe: --green-900 solid across the base,
     holding dense through the copy band, then easing to clear glass by
     ~84% so the top two thirds read as photo. Replaces the earlier
     bottom-left radial, which left the copy over half-lit image. The
     desktop 2a wipe (to right) can't carry a bottom-stacked lockup on a
     portrait crop, so mobile owns this vertical variant. */
  background: linear-gradient(
    to top,
    var(--green-900) 0%,
    var(--green-900) 12%,
    color-mix(in srgb, var(--green-900) 82%, transparent) 34%,
    color-mix(in srgb, var(--green-900) 42%, transparent) 54%,
    color-mix(in srgb, var(--green-900) 12%, transparent) 70%,
    transparent 84%
  );
}
.cta-banner--mid .cta-banner__img {
  /* Mobile reframe. At 3/5 the source shows its full height (only the
     couch sides crop), so object-position can't move it vertically —
     there's no vertical overflow to shift. This gentle zoom, anchored
     low, is what pulls the subject up: her head rides toward the top
     edge and the ceiling headroom trims away, while the MacBook
     (lower-left) stays in frame and the extra crop below her feet lands
     under the gradient. transform-origin sits near the bottom so the
     scale grows the frame upward rather than downward.
     Tuning knobs: raise the scale to enlarge her more; lower the origin
     Y (toward 50%) to keep more ceiling, raise it (toward 100%) to trim
     more. Desktop resets both (transform: none) in the @media below. */
  transform: scale(1.2);
  transform-origin: 50% 72%;
}
.cta-banner--mid .cta-banner__content {
  flex-direction: column;
  gap: 1.75rem;
  padding: clamp(1.75rem, 4vw, 2.5rem);
  /* Extra bottom inset so the stack clears the lower band of the
     photo (feet + floor). Desktop resets it via the padding
     shorthand in the @media below. */
  padding-bottom: clamp(2.5rem, 7vw, 3.5rem);
}
.cta-banner--mid .cta-banner__title {
  /* One weight, one colour. Light (300) against the photo — the
     banner is punctuation between two sections, not a second hero.
     Mobile runs a hair larger than --text-h3 so the phrase lands on
     three lines in the poster's copy band (owner's call, 2026-07-24);
     the clamp still eases down on the narrowest phones so the longest
     line ("una primera llamada.") stays whole. Desktop resets to
     --text-h3 in the @media below to keep the slim bar's proportions. */
  font-weight: var(--fw-light);
  font-size: clamp(1.7rem, 1.25rem + 2.4vw, 2rem);
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-tight);
  /* Caps the mobile column so the title can never stretch into one
     long line. Desktop hands the job to the 46% content column. */
  max-width: 26ch;
}
/* Button + trust row read as one action group. --space-4 (16px)
   between them — same as the Process step-01 aside — keeps the trust
   as a caption to the button, tighter than the content column's
   title→cta gap. The group's own width is governed by the content
   column: full-width on mobile (content stretches its children),
   shrunk to its contents on desktop (content is flex-start). */
.cta-banner--mid .cta-banner__cta {
  display: flex;
  flex-direction: column;
  /* Mobile: centre the group's cross axis so the trust row sits centred
     under the button (the button itself stays full-width via its own
     align-self: stretch). Desktop reverts to flex-start in the @media. */
  align-items: center;
  gap: var(--space-4);
}
/* Mobile: button stretches full-width inside the action group
   for visual prominence over the photo. Desktop reverts to
   intrinsic width via the @media below.
   Size: btn--sm + the hero booking CTA's padding (13px 21px, see
   .hero__cta in hero.css), so every "Agendá una llamada" pill on the
   page reads at one size. Was btn--lg — too heavy next to the hero. */
.cta-banner--mid .cta-banner__btn {
  padding: 13px 21px;
  align-self: stretch;
  justify-content: center;
}
/* Trust row — the same three items as Hero, Process step-01 and the
   booking form, in the over-dark tone (a muted echo of the title's
   --fg-on-dark, matching .proc-step__trust-item) so it reads as a
   caption rather than a second heading.

   Mobile default: the three icons + labels don't fit one line inside
   the poster's copy column, so mobile drops the icons and joins the
   labels with the brand middot (·) into a single compact row. Desktop
   restores the icon-per-item layout in the @media below, where the
   wider bar has room. */
.cta-banner--mid .cta-banner__trust {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  /* Centre the items so, on the narrowest phones where the row wraps,
     both lines stay centred under the button. Desktop resets to
     flex-start in the @media. */
  justify-content: center;
  gap: 0;
  font-size: var(--text-xs);
  font-weight: var(--fw-medium);
  color: color-mix(in srgb, var(--fg-on-dark) 78%, transparent);
}
.cta-banner--mid .cta-banner__trust-item {
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
}
/* Mobile: hide the per-item icon, separate labels with a middot. The
   separator is a trailing ::after glued INSIDE each item (not a leading
   ::before), so at the narrowest widths (≤~330px) where the row wraps,
   the middot travels with its item and never dangles at a line start. */
.cta-banner--mid .cta-banner__trust-item svg { display: none; }
.cta-banner--mid .cta-banner__trust-item:not(:last-child)::after {
  content: "·";
  margin: 0 0.5em;
  color: color-mix(in srgb, var(--fg-on-dark) 50%, transparent);
}

/* --- Variant: --case-study — closing banner of a case study ---
   Mobile portrait at 9:16 (phone-screen ratio) so the source image
   (full pose: head → laptop → legs → feet → floor) has room to
   breathe with the copy stack overlaid on top. */
.cta-banner--case-study {
  aspect-ratio: 9 / 16;
  flex-direction: column;
  justify-content: flex-end;
}
.cta-banner--case-study .cta-banner__img {
  /* Anchor the source's bottom to the banner floor so feet/floor
     never crop out, and scale up from that pivot — the parent's
     overflow:hidden clips the extra height off the top, trimming
     the ceiling band above the subject. Mobile (9/16 container,
     0.806 source) needs a smaller scale than desktop because its
     portrait crop already places the head higher in frame. */
  object-position: 50% 100%;
  transform: scale(1.15);
  transform-origin: 50% 100%;
}
.cta-banner--case-study .cta-banner__scrim {
  /* The editorial copy here runs 4 lines, so the title sits high in
     the frame — into the bright sky/subject band. A tall, dense
     bottom ramp keeps the white sans readable across all of it. */
  background: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.85) 0%,
    rgba(0, 0, 0, 0.72) 30%,
    rgba(0, 0, 0, 0.55) 55%,
    rgba(0, 0, 0, 0.30) 80%,
    rgba(0, 0, 0, 0.00) 100%
  );
}
.cta-banner--case-study .cta-banner__content {
  flex-direction: column;
  gap: 1.75rem;
  padding: clamp(1.75rem, 4vw, 2.5rem);
  padding-bottom: clamp(3.25rem, 9vw, 4.5rem);
}
.cta-banner--case-study .cta-banner__title {
  font-weight: 550;
  font-size: var(--text-h2);
  line-height: 1.08;
  letter-spacing: -0.02em;
  max-width: 32ch;
  /* Soft 1px darken under the type — invisible over the scrim's
     dense zone, but rescues the brightest pixels (sky band) where
     individual letterforms can still pick up the underlying hue. */
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}
.cta-banner--case-study .cta-banner__btn {
  align-self: stretch;
  justify-content: center;
}

@media (min-width: 880px) {
  /* --- Mid-scroll variant: desktop overrides (design 2a —
     "Horizontal corto · panorámica entera", 2026-07-24) --- */
  .cta-banner--mid {
    /* Slim horizontal bar. Height comes from content + padding with a
       340px floor, so the banner stays "puntuación entre secciones"
       instead of inflating with the viewport the way a fixed
       aspect-ratio did (12/4.5 ≈ 525px at a 1400px wrap).
       Row direction + flex-start: the content block is a single
       left-hand column that stretches to the full bar height (which
       is what lets it centre its own lockup vertically). */
    aspect-ratio: auto;
    min-height: 340px;
    flex-direction: row;
    justify-content: flex-start;
  }
  .cta-banner--mid .cta-banner__img {
    /* One panoramic frame across the whole bar (no split). 62% pushes
       the subject into the right third — clear of the copy column — and
       keeps the wide shot uncropped: the least aggressive reframe of the
       four 2a options. transform: none drops the mobile zoom reframe. */
    object-position: 62% 50%;
    transform: none;
  }
  .cta-banner--mid .cta-banner__scrim {
    /* 2a: a single horizontal wipe. Opaque --green-900 through the copy
       column (holding at 92% out to 22%), then easing to clear glass by
       64% so the right of the photo reads at full strength. One green,
       no split, no seam. */
    background: linear-gradient(
      to right,
      var(--green-900) 0%,
      color-mix(in srgb, var(--green-900) 92%, transparent) 22%,
      color-mix(in srgb, var(--green-900) 55%, transparent) 38%,
      color-mix(in srgb, var(--green-900) 14%, transparent) 52%,
      transparent 64%
    );
  }
  .cta-banner--mid .cta-banner__content {
    /* Copy lockup in the left 46%, vertically centred in the bar. The
       column is proportional (not a fixed px width) so it tracks the
       scrim's opaque zone at every wrap width; the copy must stay inside
       it — past ~52% the photo lightens and contrast drops. */
    flex: 0 0 46%;
    max-width: 46%;
    align-items: flex-start;
    justify-content: center;
    gap: 1.75rem;
    padding: clamp(40px, 4vw, 64px);
  }
  .cta-banner--mid .cta-banner__title {
    /* The 46% column governs the wrap here — no ch cap on top of it, and
       back to --text-h3 (the mobile bump is poster-only). */
    font-size: var(--text-h3);
    max-width: none;
  }
  /* Left-align the action group again on the wide bar. */
  .cta-banner--mid .cta-banner__cta {
    align-items: flex-start;
  }
  .cta-banner--mid .cta-banner__btn {
    align-self: flex-start;
  }
  /* Desktop trust row: restore the icon-per-item layout (there's room
     in the wide bar), left-align it, and drop the mobile middot
     separators. */
  .cta-banner--mid .cta-banner__trust {
    justify-content: flex-start;
    gap: 6px 16px;
  }
  .cta-banner--mid .cta-banner__trust-item {
    gap: 6px;
  }
  .cta-banner--mid .cta-banner__trust-item svg { display: block; }
  .cta-banner--mid .cta-banner__trust-item:not(:last-child)::after {
    content: none;
  }

  /* --- Case-study variant: desktop overrides --- */
  .cta-banner--case-study {
    /* Wider landscape than the base 12/4.5 so the long title and
       the button fit on a single bottom row without forcing the
       title to wrap into too many lines. */
    aspect-ratio: 16 / 6;
    justify-content: flex-end;
  }
  .cta-banner--case-study .cta-banner__img {
    /* Top-aligned crop for the case-study banner image (composed with
       its subject toward the top). Drops the base bottom-anchor +
       scale recipe, which was tuned for the full-pose source. */
    object-position: 50% 0%;
    transform: none;
  }
  .cta-banner--case-study .cta-banner__scrim {
    /* Bottom-anchored scrim — copy sits across the full bottom band
       on desktop, not just the left third. Stronger ramp than the
       mobile one to rescue the editorial title from the bright sky
       band that sits behind its upper lines. */
    background: linear-gradient(
      to top,
      rgba(0, 0, 0, 0.85) 0%,
      rgba(0, 0, 0, 0.72) 30%,
      rgba(0, 0, 0, 0.45) 60%,
      rgba(0, 0, 0, 0.18) 82%,
      rgba(0, 0, 0, 0.00) 100%
    );
  }
  .cta-banner--case-study .cta-banner__content {
    /* Take the full banner width and arrange title + button on a
       single bottom-aligned row, matching the editorial design. */
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-7);
    padding-inline: var(--space-8);
    padding-block: var(--space-7);
  }
  .cta-banner--case-study .cta-banner__title {
    /* Title spans the editorial column (~720px in the design). */
    max-width: 45ch;
    font-size: var(--text-h2);
  }
  .cta-banner--case-study .cta-banner__btn {
    flex: 0 0 auto;
    align-self: flex-end;
  }
}


/* ============================================================
   11 · WORDMARK  .wordmark
   The brand wordmark as a CSS mask instead of an <img>, so the
   mark takes its colour from --tone-strong and rides the global
   light→dark ramp in lockstep with the copy around it.

   Replaces the two-PNG opacity crossfade deprecated 2026-07-20:
   that recipe stacked a black and a white copy and cross-faded
   them, which left the mark washed out and thin at
   --text-progress .5 (both layers sitting at half opacity).
   One element, one file, no mid-scroll dip.

   Dos call sites — .nav__wordmark y .footer__wordmark — y desde
   2026-08-18 los dos miden LO MISMO, así que --wordmark-h se define
   acá una sola vez en vez de en cada sección:
   · <880px  → 17.6px  (los 22 de desktop menos 20%)
   · ≥880px  → 22px

   Antes el footer iba a 32px y el nav a 22px, cada uno declarado en
   su propio archivo. Se unifica acá porque el requisito es que sean
   iguales: con el valor duplicado en nav.css y footer.css alcanza
   con que alguien toque uno solo para que dejen de coincidir y no
   lo note nadie. El mecanismo de override sigue intacto — si alguna
   vez tienen que divergir, el call site declara su propio
   --wordmark-h y gana por especificidad.

   El breakpoint es 880px, el mismo con el que el nav pasa a layout
   desktop (nav.css). Con 768px el wordmark saltaría a tamaño grande
   mientras el nav todavía muestra la hamburguesa.

   The source SVG carries no fill attribute anywhere, so it works
   as a pure alpha mask. Mask URLs resolve against THIS stylesheet
   rather than the host document, so the one rule serves /,
   /bienvenida and /casos/* without per-page path juggling.

   No transition here on purpose: --text-progress is already an
   @property that interpolates smoothly, and a transition on top
   of a scroll-driven value only adds lag.
   ============================================================ */
.wordmark {
  /* The SVG's own 301.55 × 71.92. Width is derived rather than
     left to `auto` because a masked box has no intrinsic ratio to
     fall back on the way an <img> does.

     Historial: 3.8757 (278.74 × 71.92, "l" recta) hasta 2026-08-18,
     después 4.0587 (291.9 × 71.92) al aparecer el swash en la "l".
     Desde 2026-08-23 el swash se redibuja más abierto y con la cola
     más larga, y eso suma 9.65 unidades: 301.55 de ancho. Si se toca
     el SVG hay que recalcular esto a mano — una máscara no tiene
     ratio intrínseco del que agarrarse. */
  --wordmark-ratio: 4.1929;
  --wordmark-h: 17.6px;

  display: block;
  height: var(--wordmark-h);
  width: calc(var(--wordmark-h) * var(--wordmark-ratio));

  background-color: var(--tone-strong);
  -webkit-mask: url("../assets/images/visuala_wordmark.svg")
                center / contain no-repeat;
          mask: url("../assets/images/visuala_wordmark.svg")
                center / contain no-repeat;
}

/* Desktop — mismo breakpoint con el que el nav muestra la fila de
   links y esconde la hamburguesa. */
@media (min-width: 880px) {
  .wordmark {
    --wordmark-h: 22px;
  }
}
