/* ==========================================================================
   Open Banking — Conciliación Bancaria Automática
   Design system: DESIGN.md. Tokens below are that file, verbatim.
   ========================================================================== */

/* ---------- Face ---------------------------------------------------------- */

@font-face {
  font-family: "Jost";
  font-style: normal;
  font-weight: 300 600;
  font-display: swap;
  src: url("../fonts/jost-latin.woff2") format("woff2");
  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;
}

/* Latin-ext. Not currently exercised — currency is set as "Gs.", not ₲ — but
   unicode-range means the browser only fetches this subset if a character in
   it is actually rendered, so it costs nothing and covers future copy. */
@font-face {
  font-family: "Jost";
  font-style: normal;
  font-weight: 300 600;
  font-display: swap;
  src: url("../fonts/jost-latin-ext.woff2") format("woff2");
  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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---------- Tokens -------------------------------------------------------- */

:root {
  --ink-900: #0a0a0b;
  --ink-700: #1f2330;
  --navy-900: #0b1324;
  --navy-700: #1b2136;
  --navy-600: #26304a; /* DESIGN.md §7: primary button hover */
  --white: #fff;
  --surface-subtle: #ebf1f8;
  --surface-mist: #f4f6fa;
  --blue-500: #4a5aff;
  --blue-100: #e6e9ff;
  --yellow-400: #ffdb5c;
  --slate-500: #60738a;
  --slate-300: #8eadd9;
  --slate-200: #c9d3e3;

  /* Sampled from the product itself (assets/img/consola-movimientos.png) so the
     ribbon agrees with the badges in the screenshot it labels. */
  --state-pending-fill: #fef3ad;
  --state-pending-ink: #7b3306;
  --state-done-fill: #d0fae5;
  --state-done-ink: #004f3b;

  /* Secondary text sits on tinted grounds as often as on white, where
     --slate-500 lands just under 4.5:1. This is that token, darkened to clear
     it everywhere; --slate-500 keeps its DESIGN.md job on large marks. */
  --ink-muted: #566879;

  /* Error state. Not in DESIGN.md; declared here and recorded with the other
     extensions rather than hardcoded at the point of use. */
  --danger-ink: #a4231a;
  --danger-line: #c8493d;
  --danger-fill: #fdf3f2;
  --danger-soft: #f6dedb;


  --r-pill: 999px;
  --r-lg: 24px;
  --r-md: 16px;
  --r-sm: 10px;

  --shadow-card: 0 24px 48px -12px rgba(40, 60, 110, 0.18),
    0 4px 12px rgba(40, 60, 110, 0.06);
  --shadow-strip: 0 16px 40px -16px rgba(40, 60, 110, 0.14);
  /* --shadow-card, raised: the hovered state of a lifted card. */
  --shadow-card-lift: 0 34px 60px -16px rgba(40, 60, 110, 0.26),
    0 6px 16px rgba(40, 60, 110, 0.08);

  --font-sans: "Jost", "Futura PT", "Avenir Next", "TT Commons", system-ui,
    sans-serif;

  --gutter: clamp(24px, 4.4vw, 64px);
  --measure: 1280px;
  --head-h: 88px;
  --section-pad: 112px;
  --row-pad: 32px;
  --gap-pad: 56px;

  /* Exponential ease-out: everything that moves settles on this curve. */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* ---------- Reset --------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--head-h) + 16px);
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  background: var(--white);
  color: var(--ink-700);
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

h1,
h2,
h3,
p,
figure,
ul,
ol {
  margin: 0;
  padding: 0;
}

ul,
ol {
  list-style: none;
}

img,
svg {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

/* ---------- Browser surfaces ---------------------------------------------
   The parts we did not draw still belong to the system.
   -------------------------------------------------------------------------- */

::selection {
  background: var(--blue-100);
  color: var(--ink-900);
}

:root {
  caret-color: var(--blue-500);
  accent-color: var(--blue-500);
  scrollbar-color: var(--slate-200) var(--surface-mist);
  scrollbar-width: thin;
}

::-webkit-scrollbar {
  width: 12px;
  height: 12px;
}

::-webkit-scrollbar-track {
  background: var(--surface-mist);
}

::-webkit-scrollbar-thumb {
  background: var(--slate-200);
  border: 3px solid var(--surface-mist);
  border-radius: var(--r-pill);
}

::-webkit-scrollbar-thumb:hover {
  background: var(--slate-300);
}

:focus-visible {
  outline: 3px solid var(--blue-100);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

.footer :focus-visible {
  outline-color: var(--slate-300);
}

/* Figures and times read as data, not as prose. */
.tnum {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.skip {
  position: absolute;
  left: 50%;
  top: 8px;
  transform: translate(-50%, -140%);
  z-index: 99;
  background: var(--navy-900);
  color: var(--white);
  padding: 12px 24px;
  border-radius: var(--r-pill);
  font-size: 15px;
  transition: transform 0.24s var(--ease-out);
}

.skip:focus {
  transform: translate(-50%, 0);
}

/* ---------- Drawer scrim ---------------------------------------------------
   Below the masthead (z 40) and above the page, so the bar and the open
   drawer stay lit while everything behind them dims.
   -------------------------------------------------------------------------- */

.scrim {
  position: fixed;
  inset: 0;
  z-index: 30;
  background: rgba(11, 19, 36, 0.55);
  backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.32s var(--ease-out);
}

:root[data-nav-open] .scrim {
  opacity: 1;
  pointer-events: auto;
}

/* ---------- Mobile conversion dock -----------------------------------------
   Below 880px the masthead CTA is gone, so this is the only persistent route
   to the demo. Sits under the scrim (z 30) so the open drawer covers it, and
   the native <dialog> renders in the top layer above both. */
.cta-dock {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 25;
  justify-content: center;
  padding: 14px 16px calc(14px + env(safe-area-inset-bottom, 0px));
  transform: translateY(130%);
  transition: transform 0.42s var(--ease-out);
  pointer-events: none;
}

@media (max-width: 880px) {
  .cta-dock {
    display: flex;
  }
}

.cta-dock[data-show] {
  transform: none;
}

/* No backing band: the sections alternate white, mist and navy, so any fixed
   tint clashes on one of them and a translucent one lets copy bleed through.
   A floating pill on its own shadow reads correctly over all three. */
.cta-dock__btn {
  width: 100%;
  max-width: 420px;
  pointer-events: auto;
  box-shadow: var(--shadow-card);
}

/* The drawer owns the screen while it is open. */
:root[data-nav-open] .cta-dock {
  transform: translateY(130%);
}

@media (prefers-reduced-motion: reduce) {
  .cta-dock {
    transition: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .scrim {
    transition-duration: 0.01ms;
  }
}

/* ---------- Shell --------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: calc(var(--measure) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ---------- Buttons ------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 48px;
  padding: 0 28px;
  border-radius: var(--r-pill);
  font-size: 15px;
  line-height: 1;
  white-space: nowrap;
  transition: background-color 0.22s var(--ease-out),
    border-color 0.22s var(--ease-out), transform 0.12s var(--ease-out),
    color 0.22s var(--ease-out);
}

.btn--lg {
  height: 52px;
}

.btn--primary {
  background-image: linear-gradient(180deg, var(--navy-700) 0%, var(--navy-900) 100%);
  background-color: var(--navy-900);
  color: var(--white);
}

.btn--primary:hover {
  background-image: none;
  background-color: var(--navy-600);
}

.btn--ghost {
  border: 1px solid var(--ink-700);
  color: var(--ink-900);
}

.btn--ghost:hover {
  background-color: var(--surface-subtle);
  border-color: var(--ink-900);
}

.btn:active {
  transform: scale(0.98);
}

.btn[aria-disabled="true"],
.btn:disabled {
  opacity: 0.45;
  pointer-events: none;
}

/* ---------- Masthead ------------------------------------------------------ */

.masthead {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: saturate(140%) blur(14px);
  transition: box-shadow 0.3s var(--ease-out);
}

/* Supports-less browsers get an opaque bar rather than unreadable text. */
@supports not (backdrop-filter: blur(4px)) {
  .masthead {
    background: var(--white);
  }
}

.masthead[data-stuck="true"] {
  box-shadow: 0 1px 0 rgba(31, 35, 48, 0.08), var(--shadow-strip);
}

.masthead__inner {
  height: var(--head-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(16px, 2vw, 40px);
}

/* Wordmark */
.mark {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
}

.mark__glyph {
  width: 34px;
  height: 34px;
  flex-shrink: 0;
}

.mark__text {
  display: flex;
  flex-direction: column;
  line-height: 1;
}

.mark__name {
  font-size: 23px;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--ink-900);
}

.mark__name b {
  font-weight: 500;
  color: var(--blue-500);
}

/* The approved copy's "Cobranzas por transferencia" lives here, as the
   product descriptor, rather than floating above the headline as a kicker. */
.mark__sub {
  font-size: 13px;
  letter-spacing: 0.055em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-top: 5px;
}

/* Nav pill */
.nav {
  display: flex;
  align-items: center;
  gap: clamp(20px, 2.3vw, 48px);
  height: 52px;
  padding: 0 clamp(24px, 2.4vw, 40px);
  background: var(--surface-subtle);
  border-radius: var(--r-pill);
}

.nav a:not(.btn) {
  font-size: 15px;
  color: var(--ink-700);
  white-space: nowrap;
  position: relative;
  padding-block: 4px;
  transition: color 0.2s var(--ease-out);
}

.nav a:not(.btn)::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1.5px;
  background: var(--ink-900);
  border-radius: var(--r-pill);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.28s var(--ease-out);
}

.nav a:not(.btn):hover {
  color: var(--ink-900);
}

.nav a:not(.btn):hover::after {
  transform: scaleX(1);
}

.masthead__cta {
  flex-shrink: 0;
}

.nav__cta {
  display: none;
}

.nav-toggle {
  display: none;
  width: 48px;
  height: 48px;
  border-radius: var(--r-pill);
  background: var(--surface-subtle);
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.nav-toggle svg {
  width: 20px;
  height: 20px;
}

.nav-toggle__bar {
  transition: transform 0.3s var(--ease-out), opacity 0.2s var(--ease-out);
  transform-origin: center;
}

[aria-expanded="true"] .nav-toggle__bar--t {
  transform: translateY(5px) rotate(45deg);
}

[aria-expanded="true"] .nav-toggle__bar--m {
  opacity: 0;
}

[aria-expanded="true"] .nav-toggle__bar--b {
  transform: translateY(-5px) rotate(-45deg);
}

/* ---------- Hero ---------------------------------------------------------- */

.hero {
  position: relative;
  min-height: calc(100svh - var(--head-h));
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: linear-gradient(
    180deg,
    var(--white) 0%,
    var(--white) 52%,
    var(--surface-mist) 100%
  );
}

.hero__copy {
  text-align: center;
  padding-top: clamp(22px, 6.2vh, 72px);
  padding-bottom: clamp(18px, 4vh, 56px);
}

.hero h1 {
  font-size: clamp(31px, min(5vw, 7.2vh), 62px);
  line-height: 1.04;
  letter-spacing: -0.02em;
  font-weight: 400;
  color: var(--ink-900);
  max-width: 22ch;
  margin-inline: auto;
  text-wrap: balance;
}

/* The sentence reads in two beats: the condition, then the consequence. */
.hero h1 .lead-in {
  display: block;
  color: var(--ink-900);
}

.hero h1 em {
  font-style: normal;
  color: var(--slate-500);
}

.hero__lede {
  font-size: clamp(17px, 1.6vw, 21px);
  line-height: 1.5;
  color: var(--ink-700);
  max-width: 60ch;
  margin: clamp(12px, 2.6vh, 30px) auto 0;
  text-wrap: pretty;
}

.hero__ctas {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
  margin-top: clamp(16px, 3.4vh, 36px);
}

.hero__note {
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink-muted);
  max-width: 62ch;
  margin: clamp(10px, 1.8vh, 18px) auto 0;
  text-wrap: balance;
}

/* ---------- The console, rising into the fold ----------------------------- */

.stage {
  position: relative;
  margin-top: auto;
  display: flex;
  justify-content: center;
  padding-top: 21px; /* a 42px chip half-overlaps the console's edge */
  padding-bottom: clamp(40px, 7vh, 104px);
}

.ribbon {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  z-index: 3;
  display: flex;
  gap: 10px;
  padding-inline: var(--gutter);
  max-width: 100%;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 42px;
  padding: 0 14px;
  background: var(--white);
  border-radius: var(--r-pill);
  box-shadow: var(--shadow-card);
  font-size: 14px;
  line-height: 1;
  color: var(--ink-900);
  white-space: nowrap;
}

.chip b {
  font-weight: 400;
  font-size: 14px;
  line-height: 1;
  padding: 5px 11px 6px;
  border-radius: var(--r-pill);
  flex-shrink: 0;
}

/* The ribbon is a legend, so each badge is the one the console draws. */
.chip--pending b {
  background: var(--state-pending-fill);
  color: var(--state-pending-ink);
}

.chip--done b {
  background: var(--state-done-fill);
  color: var(--state-done-ink);
}

.chip--none b {
  background: var(--surface-subtle);
  color: var(--ink-muted);
}

.console {
  width: min(1180px, 100% - var(--gutter) * 2);
  position: relative;
  border-radius: var(--r-md) var(--r-md) 0 0;
  overflow: hidden;
  background: var(--white);
  box-shadow: var(--shadow-card);
}

.console img {
  width: 100%;
  height: auto;
}

@media (min-width: 761px) {
  .console::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 96px;
    pointer-events: none;
    background: linear-gradient(
      180deg,
      rgba(244, 246, 250, 0) 0%,
      var(--surface-mist) 92%
    );
  }
}

/* The one authored moment: the console arrives, the ribbon settles onto it.
   Resting state is the visible one, so nothing depends on the animation. */
@media (prefers-reduced-motion: no-preference) {
  .console {
    animation: rise 1.05s var(--ease-out) both;
  }

  .ribbon .chip {
    animation: settle 0.7s var(--ease-out) both;
    animation-delay: calc(0.42s + var(--i) * 0.08s);
  }

  @keyframes rise {
    from {
      transform: translateY(42px);
      opacity: 0;
    }
  }

  @keyframes settle {
    from {
      transform: translateY(14px);
      opacity: 0;
    }
  }
}

/* ---------- Footer -------------------------------------------------------- */

.footer {
  background: var(--navy-900);
  color: var(--surface-subtle);
  padding-top: clamp(56px, 7vw, 96px);
  padding-bottom: 32px;
}

.footer__top {
  display: grid;
  grid-template-columns: minmax(260px, 1.25fr) repeat(3, minmax(130px, 1fr));
  gap: clamp(32px, 4vw, 64px);
  padding-bottom: clamp(40px, 5vw, 64px);
}

.footer .mark__name {
  color: var(--white);
  font-size: 22px;
}

.footer .mark__name b {
  color: var(--slate-300);
}

.footer .mark__sub {
  color: var(--slate-300);
}

.footer__note {
  margin-top: 20px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--slate-300);
  max-width: 42ch;
}

.footer__banks {
  margin-top: 18px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--slate-300);
  max-width: 42ch;
}

.footer__banks b {
  color: var(--surface-subtle);
  font-weight: 400;
}

.col__title {
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--slate-300);
  margin-bottom: 20px;
}

.col ul {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.col a {
  font-size: 16px;
  color: var(--surface-subtle);
  transition: color 0.2s var(--ease-out);
}

.col a:hover {
  color: var(--white);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--slate-300);
}

/* Anything still waiting on a real value names itself in the text, so it reads
   as a placeholder on a phone too, and is inert so nobody taps a dead link. */
.ph {
  display: inline-block;
  font-size: 15px;
  color: var(--slate-300);
  opacity: 0.72;
  text-decoration: underline dotted currentColor;
  text-underline-offset: 4px;
  cursor: default;
}

.footer__bottom {
  border-top: 1px solid rgba(142, 173, 217, 0.18);
  padding-top: 28px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px 32px;
  font-size: 14px;
  color: var(--slate-300);
}

/* ---------- Responsive ---------------------------------------------------- */

/* Compact desktop: full nav and CTA, tightened to fit. */
@media (max-width: 1180px) and (min-width: 881px) {
  .mark__sub {
    display: none;
  }

  .nav {
    gap: 18px;
    padding: 0 18px;
  }

  .nav a:not(.btn) {
    font-size: 14px;
  }

  .btn {
    padding: 0 20px;
  }
}

/* Short viewports (a scaled 1080p panel lands near 480px of CSS height).
   Trim the chrome and the hero's vertical rhythm so the proof still shows. */
@media (max-height: 560px) and (min-width: 881px) {
  :root {
    --head-h: 64px;
  }

  .hero h1 {
    font-size: clamp(31px, min(5vw, 6.4vh), 62px);
  }

  .hero__lede {
    font-size: 16.5px;
    max-width: 78ch;
    margin-top: 10px;
  }

  .hero__copy {
    padding-top: 12px;
    padding-bottom: 8px;
  }

  .hero__ctas {
    margin-top: 14px;
  }

  .hero__note {
    margin-top: 8px;
    max-width: 78ch;
  }

  .btn--lg {
    height: 46px;
  }

  .stage {
    padding-bottom: 32px;
  }
}

@media (max-width: 880px) {
  .nav-toggle {
    display: inline-flex;
  }

  .masthead__inner {
    position: relative;
  }

  .nav {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    right: 0;
    height: auto;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 12px;
    border-radius: var(--r-lg);
    background: var(--white);
    box-shadow: var(--shadow-card);
  }

  .nav a:not(.btn) {
    padding: 14px 16px;
    border-radius: var(--r-sm);
    font-size: 16px;
  }

  .nav a:not(.btn)::after {
    display: none;
  }

  .nav a:not(.btn):hover {
    background: var(--surface-subtle);
  }

  /* Without JS the links stay reachable; the script adds the collapsed state. */
  [data-js] .nav[hidden] {
    display: none;
  }

  .masthead__cta {
    display: none;
  }

  /* The primary action follows the nav into the panel rather than vanishing. */
  .nav__cta {
    display: inline-flex;
    margin-top: 8px;
  }
}

@media (max-width: 1100px) {
  :root {
    --section-pad: 80px;
    --row-pad: 26px;
  }
}

@media (max-width: 760px) {
  :root {
    --head-h: 76px;
    --section-pad: 56px;
    --row-pad: 22px;
  }

  .hero {
    min-height: 0;
  }

  .hero__copy {
    padding-top: 36px;
    padding-bottom: 36px;
  }

  .hero__ctas .btn {
    width: 100%;
  }

  .ribbon {
    gap: 6px;
  }

  .chip {
    height: 34px;
    padding: 0 6px;
  }

  .chip b {
    font-size: 13px;
    padding: 4px 7px 5px;
  }
}

/* Small phones: the three state chips must still sit inside the console they
   label, so the padding gives way before the type does. */
@media (max-width: 400px) {
  .ribbon {
    gap: 4px;
  }

  .chip {
    padding: 0 4px;
  }

  .chip b {
    padding: 4px 6px 5px;
  }

  /* Scaled to fit, the console is a grey thumbnail. It stays at full size
     inside a window the reader swipes instead, so the rows stay legible. */
  .console {
    width: calc(100% - 24px);
    height: 560px;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }

  .console::-webkit-scrollbar {
    display: none;
  }

  .console img {
    width: 1540px;
    max-width: none;
    margin-top: -206px; /* lands on the table header, not mid-filter-row */
  }

  /* Both edges fade, so a column clipped by the window reads as continuing
     rather than as a hard cut through the middle of a word. */
  .stage::after,
  .stage::before {
    content: "";
    position: absolute;
    bottom: clamp(40px, 7vh, 104px);
    width: 16px;
    height: 560px;
    pointer-events: none;
    z-index: 2;
  }

  .stage::after {
    right: 12px;
    border-radius: 0 var(--r-md) var(--r-md) 0;
    background: linear-gradient(90deg, rgba(255, 255, 255, 0), #fff 88%);
  }

  .stage::before {
    left: 12px;
    border-radius: var(--r-md) 0 0 var(--r-md);
    background: linear-gradient(270deg, rgba(255, 255, 255, 0), #fff 88%);
  }

  .footer__top {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .footer__brand {
    grid-column: 1 / -1;
  }
}

/* ---------- Demo request dialog -------------------------------------------
   A modal is normally the lazy answer, but a short capture triggered by an
   explicit action is exactly the case protected focus exists for. Native
   <dialog> carries the focus trap, Escape and the inert background.
   -------------------------------------------------------------------------- */

.modal {
  width: min(680px, calc(100vw - 32px));
  max-height: calc(100svh - 32px);
  padding: 0;
  border: 0;
  background: transparent;
  overflow: visible;
  color: var(--ink-700);
}

.modal::backdrop {
  background: rgba(11, 19, 36, 0.52);
  backdrop-filter: blur(6px) saturate(120%);
}

@media (prefers-reduced-motion: no-preference) {
  .modal[open] {
    animation: modal-in 0.42s var(--ease-out) both;
  }

  .modal[open]::backdrop {
    animation: fade-in 0.42s var(--ease-out) both;
  }

  @keyframes modal-in {
    from {
      transform: translateY(16px) scale(0.985);
      opacity: 0;
    }
  }

  @keyframes fade-in {
    from {
      opacity: 0;
    }
  }
}

.modal__card {
  position: relative;
  background: var(--white);
  border-radius: var(--r-lg);
  box-shadow: 0 40px 80px -24px rgba(11, 19, 36, 0.4),
    0 8px 24px rgba(11, 19, 36, 0.12);
  padding: clamp(28px, 3.4vw, 44px);
  max-height: calc(100svh - 32px);
  overflow-y: auto;
}

.modal__close {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: var(--r-pill);
  color: var(--ink-700);
  transition: background-color 0.2s var(--ease-out), color 0.2s var(--ease-out);
}

.modal__close svg {
  width: 18px;
  height: 18px;
}

.modal__close:hover {
  background: var(--surface-subtle);
  color: var(--ink-900);
}

.modal__title {
  font-size: clamp(22px, 2.3vw, 29px);
  line-height: 1.16;
  letter-spacing: -0.015em;
  font-weight: 400;
  color: var(--ink-900);
  max-width: 38ch;
  padding-right: 44px;
  text-wrap: balance;
}

.modal__lede {
  font-size: 17px;
  line-height: 1.55;
  color: var(--ink-muted);
  margin-top: 14px;
  max-width: 54ch;
  text-wrap: pretty;
}

/* ---------- Fields --------------------------------------------------------- */

.field-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px 16px;
  margin-top: clamp(24px, 2.6vw, 34px);
}

.field {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.field label {
  font-size: 13px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.field input {
  height: 52px;
  padding: 0 22px;
  font: inherit;
  font-size: 17px;
  color: var(--ink-900);
  background: var(--surface-mist);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  transition: border-color 0.2s var(--ease-out),
    background-color 0.2s var(--ease-out), box-shadow 0.2s var(--ease-out);
}

.field input::placeholder {
  color: var(--ink-muted);
  opacity: 1; /* Firefox dims placeholders by default */
}

.field input:hover {
  background: var(--surface-subtle);
}

.field input:focus {
  outline: none;
  background: var(--white);
  border-color: var(--blue-500);
  box-shadow: 0 0 0 3px var(--blue-100);
}

/* Chrome paints autofill a flat yellow that belongs to no design system. */
.field input:-webkit-autofill,
.field input:-webkit-autofill:hover,
.field input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--ink-900);
  caret-color: var(--blue-500);
  box-shadow: 0 0 0 1000px var(--surface-mist) inset;
  transition: background-color 9999s;
}

.field__error {
  font-size: 14px;
  line-height: 1.4;
  color: var(--danger-ink);
  padding-left: 22px;
  display: none;
}

.field[data-invalid] input {
  background: var(--danger-fill);
  border-color: var(--danger-line);
}

.field[data-invalid] input:focus {
  box-shadow: 0 0 0 3px var(--danger-soft);
}

.field[data-invalid] .field__error {
  display: block;
}

/* ---------- reCAPTCHA (v2 invisible) ---------------------------------------
   The widget draws nothing in the form; it draws a badge. Google's terms let
   that badge be hidden exactly when the attribution line is shown instead,
   which is why .modal__legal sits in the dialog and why these two rules are a
   pair: hiding one without the other breaks the terms the keys are issued
   under.

   The badge is rendered inline, into #captcha-box inside the dialog, rather
   than floating bottom-right over every page — where on a phone it would land
   on top of the conversion dock.
   -------------------------------------------------------------------------- */

/* Not display:none — the widget is live, it just has nothing to show. A
   display:none ancestor is the documented way to break it, and Google's own
   guidance for hiding the badge is visibility, not display. */
#captcha-box {
  height: 0;
  overflow: hidden;
}

.grecaptcha-badge {
  visibility: hidden;
}

.modal__legal {
  margin-top: 18px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-muted);
  max-width: 52ch;
  text-wrap: pretty;
}

.modal__legal a {
  color: var(--ink-muted);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--slate-200);
  transition: color 0.2s var(--ease-out),
    text-decoration-color 0.2s var(--ease-out);
}

.modal__legal a:hover {
  color: var(--ink-900);
  text-decoration-color: var(--ink-900);
}

@media (prefers-reduced-motion: reduce) {
  .modal__legal a {
    transition: none;
  }
}

/* ---------- Foot ----------------------------------------------------------- */

.modal__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 20px;
  margin-top: clamp(24px, 2.6vw, 32px);
}

.modal__foot .btn {
  flex-shrink: 0;
}

.modal__note {
  font-size: 14px;
  line-height: 1.45;
  color: var(--ink-muted);
  max-width: 30ch;
}

.modal__alert {
  margin-top: 18px;
  padding: 14px 20px;
  border-radius: var(--r-md);
  background: var(--danger-fill);
  color: var(--danger-ink);
  font-size: 15px;
  line-height: 1.5;
}

/* The fallback message can carry a mailto when no endpoint is set yet. */
.modal__alert a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.btn[data-busy] {
  pointer-events: none;
  opacity: 0.6;
}

/* ---------- Success -------------------------------------------------------- */

.modal__done {
  text-align: center;
}

.modal__done .modal__title,
.modal__done .modal__lede {
  max-width: 26ch;
  margin-inline: auto;
  padding-right: 0;
}

.modal__done .btn {
  margin-top: 28px;
}

.modal__tick {
  color: var(--blue-500);
  margin: 0 auto 22px;
  width: 56px;
}

@media (max-width: 560px) {
  .field-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .modal__foot .btn {
    width: 100%;
  }

  .modal__note {
    max-width: none;
  }
}

/* ---------- La espera -----------------------------------------------------
   The argument is the shape: both tracks open on the same two beats, then one
   stalls through a weekend the reader has to scroll past while the other has
   already finished. Height is the message, so nothing equalises these columns.
   -------------------------------------------------------------------------- */

.wait {
  background: var(--surface-mist);
  padding: var(--section-pad) 0;
}

.wait__intro {
  max-width: none;
}

.wait__title {
  font-size: clamp(29px, 3.8vw, 48px);
  line-height: 1.1;
  letter-spacing: -0.015em;
  font-weight: 400;
  color: var(--ink-900);
  max-width: none;
  text-wrap: balance;
}

/* The 24/7 claim sits under the heading, not above it: a statement the
   heading hands off to, never a label floating over it. */
.wait__claim {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-top: 22px;
  font-size: 17px;
  color: var(--state-done-ink);
}

.wait__claim::before {
  content: "";
  width: 28px;
  height: 2px;
  border-radius: var(--r-pill);
  background: var(--state-done-ink);
  flex-shrink: 0;
  transform: translateY(-6px);
}

.wait__lede {
  font-size: clamp(17px, 1.6vw, 21px);
  line-height: 1.6;
  color: var(--ink-700);
  margin-top: 20px;
  max-width: none;
  text-wrap: pretty;
}

.wait__case {
  font-size: clamp(17px, 1.6vw, 21px);
  line-height: 1.6;
  color: var(--ink-muted);
  margin: clamp(30px, 3.2vw, 44px) auto 0;
  text-align: center;
  text-wrap: balance;
}

.wait__case b {
  font-weight: 400;
  color: var(--ink-900);
}

.wait__close {
  font-size: clamp(17px, 1.6vw, 21px);
  line-height: 1.6;
  color: var(--ink-muted);
  max-width: 76ch;
  margin: clamp(34px, 3.6vw, 52px) auto 0;
  text-align: center;
  text-wrap: balance;
}

/* ---------- The two tracks ------------------------------------------------- */

.tracks {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 28px);
  align-items: start; /* never equalise: the height difference is the point */
  margin-top: clamp(40px, 4.6vw, 64px);
}

.track {
  background: var(--white);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
  padding: clamp(22px, 2.3vw, 32px);
}

.track__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--surface-subtle);
}

.track__name {
  font-size: clamp(17px, 1.5vw, 20px);
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--ink-900);
}

.tag {
  display: inline-flex;
  align-items: center;
  height: 30px;
  padding: 0 14px;
  border-radius: var(--r-pill);
  font-size: 14px;
  line-height: 1;
  white-space: nowrap;
}

/* 41 hours of waiting is a failure, not a pending status — so it reads red
   rather than amber, unlike the Pendiente badge inside the console. */
.tag--wait {
  background: var(--danger-soft);
  color: var(--danger-ink);
}

.tag--done {
  background: var(--state-done-fill);
  color: var(--state-done-ink);
}

/* ---------- Days and beats -------------------------------------------------
   Beats are grouped into tinted day bands, so the count of bands is the
   comparison: one day on the right, two plus a dead weekend on the left.
   -------------------------------------------------------------------------- */

.beats {
  margin-top: 22px;
}

.day {
  background: var(--surface-mist);
  border-radius: var(--r-md);
  padding: 15px 18px 17px;
}

.day__name {
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 14px;
}

.beat {
  position: relative;
  padding-left: 26px;
  padding-bottom: 20px;
}

.beat--last {
  padding-bottom: 0;
}

/* the rail */
.beat::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 7px;
  bottom: -7px;
  width: 1.5px;
  background: var(--slate-300);
  opacity: 0.55;
}

.beat--last::before {
  display: none;
}

/* the node */
.beat::after {
  content: "";
  position: absolute;
  left: 0;
  top: 5px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--slate-300);
}

.track--ob .beat::after {
  background: var(--state-done-ink);
}

.track--ob .beat::before {
  background: var(--state-done-ink);
  opacity: 0.32;
}

/* The last beat of the Open Banking track is the resolution — the point the
   whole comparison is driving at — so it carries the confirmed colour. */
.track--ob .beat--last .beat__what {
  color: var(--state-done-ink);
}

.beat__done {
  display: inline-block;
  width: 17px;
  height: 17px;
  margin-left: 7px;
  /* nudged onto the x-height so it sits with the words, not above them */
  vertical-align: -3px;
  color: var(--state-done-ink);
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.beat__at {
  display: block;
  font-size: 13.5px;
  letter-spacing: 0.03em;
  color: var(--ink-muted);
}

.beat__what {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 16.5px;
  line-height: 1.45;
  color: var(--ink-900);
  margin-top: 4px;
}

.beat__what > span {
  text-wrap: pretty;
}

.beat__icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  /* sits on the cap of the first line, not the line box */
  margin-top: 2px;
  color: var(--ink-muted);
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Each track's beats carry that track's colour, so the two read apart even
   where the wording is identical. */
.track--ob .beat__icon {
  color: var(--state-done-ink);
  opacity: 0.8;
}

/* ---------- The dead weekend ------------------------------------------------
   Deliberately untinted: it is not a working day, so it gets no band. The
   empty stretch between two bands is the thing doing the arguing.
   -------------------------------------------------------------------------- */

.gap {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-block: var(--gap-pad);
}

/* The dashed line now runs down the middle, and the note sits on it with the
   card's own ground behind, so the line reads as paused rather than struck
   through the words. */
.gap::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  border-left: 1.5px dashed var(--state-pending-ink);
  opacity: 0.5;
  transform-origin: top;
}

.gap__note {
  position: relative;
  background: var(--white);
  padding: 10px 14px;
  max-width: 30ch;
  text-align: center;
  font-size: 16px;
  line-height: 1.5;
  color: var(--state-pending-ink);
  text-wrap: balance;
}

.gap__clock {
  display: block;
  width: 24px;
  height: 24px;
  margin: 0 auto 10px;
  color: var(--state-pending-ink);
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Scroll-driven, where it exists: the dead stretch draws itself as the reader
   scrolls through it, so the wait is spent rather than described. The resting
   state above is the complete one, so nothing depends on this. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .gap::before {
      animation: stall linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 48%;
    }
  }

  @keyframes stall {
    from {
      transform: scaleY(0);
    }
  }
}

@media (max-width: 760px) {
  .tracks {
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
  }

  /* Stacked, the two tracks no longer sit side by side, so the dead stretch
     has to carry the comparison on its own. Keep it felt. */
  :root {
    --gap-pad: 46px;
  }
}

/* ---------- Si esto pasa en tu empresa -------------------------------------
   Six independent symptoms, not an ordered process, so the source's 01–06 are
   dropped: numbering a set the reader can enter anywhere is decoration.
   White ground and open ruled rows, answering the mist-and-cards density of
   the section above rather than repeating it.
   -------------------------------------------------------------------------- */

.signs {
  background: var(--white);
  padding: var(--section-pad) 0;
}

.signs__title {
  font-size: clamp(29px, 3.8vw, 48px);
  line-height: 1.1;
  letter-spacing: -0.015em;
  font-weight: 400;
  color: var(--ink-900);
  text-wrap: balance;
}

/* The source sets this above the heading; the craft floor rules that out, and
   it reads better where it actually belongs — as the condition the list
   answers, immediately before the list. */
.signs__cue {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-top: 22px;
  font-size: 17px;
  color: var(--ink-muted);
}

.signs__cue::before {
  content: "";
  width: 28px;
  height: 2px;
  border-radius: var(--r-pill);
  background: var(--slate-300);
  flex-shrink: 0;
  transform: translateY(-6px);
}

.sign-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(32px, 5vw, 88px);
  margin-top: clamp(34px, 3.8vw, 52px);
}

.sign {
  display: flex;
  align-items: center;
  padding: var(--row-pad) 0;
  border-top: 1px solid var(--surface-subtle);
}

.sign:nth-last-child(-n + 2) {
  border-bottom: 1px solid var(--surface-subtle);
}

.sign__title {
  display: flex;
  align-items: center;
  gap: 16px;
  font-size: clamp(20px, 1.6vw, 22px);
  line-height: 1.25;
  letter-spacing: -0.01em;
  font-weight: 400;
  color: var(--ink-900);
}

.sign__title > span {
  text-wrap: balance;
}

.sign__badge {
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  /* DESIGN.md §7 draws this badge as a solid blue disc with a white glyph.
     Six solid discs read as a feature list; this section is a list of
     problems, so it takes the tinted form of the same component. */
  background: var(--blue-100);
}

.sign__icon {
  width: 23px;
  height: 23px;
  color: var(--blue-500);
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.sign__body {
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink-muted);
  text-wrap: pretty;
}

@media (max-width: 760px) {
  .sign-list {
    grid-template-columns: minmax(0, 1fr);
  }

  .sign:nth-last-child(-n + 2) {
    border-bottom: 0;
  }

  .sign:last-child {
    border-bottom: 1px solid var(--surface-subtle);
  }
}

/* ---------- Cómo funciona --------------------------------------------------
   "Cuatro pasos, y uno solo lo hace una persona." The fourth step is the only
   one a human performs, so it takes the navy action colour while the three
   automatic ones stay quiet. Numbers are kept here — unlike the six symptoms,
   this really is a sequence and the order is information.
   -------------------------------------------------------------------------- */

.how {
  background: var(--surface-mist);
  padding: var(--section-pad) 0;
}

.how__title {
  font-size: clamp(29px, 3.8vw, 48px);
  line-height: 1.1;
  letter-spacing: -0.015em;
  font-weight: 400;
  color: var(--ink-900);
  text-wrap: balance;
}

.steps {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 20px;
  margin-top: clamp(34px, 3.8vw, 52px);
}

.step {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  background-color: var(--white);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
  padding: 26px 24px 28px;
}

/* The dark face is a layer that cross-fades, because background-image does
   not animate. Step 04 simply wears it at rest; the others on hover. */
.step::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background-image: linear-gradient(180deg, var(--navy-700) 0%, var(--navy-900) 100%);
  opacity: 0;
  transition: opacity 0.45s var(--ease-out);
}

.step:hover::before {
  opacity: 1;
}

.step__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 14px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--surface-subtle);
}

/* Authored, one stroke weight across all four, in the system's accent — which
   the page otherwise barely spends. */
.step__icon {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  color: var(--blue-500);
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.step__n {
  font-size: 13px;
  letter-spacing: 0.1em;
  color: var(--slate-500);
}

.step__title {
  font-size: clamp(17px, 1.5vw, 21px);
  line-height: 1.25;
  letter-spacing: -0.01em;
  font-weight: 400;
  color: var(--ink-900);
  text-wrap: balance;
}

.step__body {
  font-size: 17px;
  line-height: 1.55;
  color: var(--ink-muted);
  margin-top: 10px;
  text-wrap: pretty;
}

/* The dark reading, worn by whichever card the reader is pointing at. Every
   card rests white. */
.step:hover .step__head {
  border-bottom-color: rgba(142, 173, 217, 0.26);
}

.step:hover .step__icon,
.step:hover .step__n {
  color: var(--blue-100);
}

.step:hover .step__title {
  color: var(--white);
}

.step:hover .step__body {
  color: var(--slate-300);
}

.step__head,
.step__icon,
.step__n,
.step__title,
.step__body {
  transition: color 0.45s var(--ease-out),
    border-bottom-color 0.45s var(--ease-out);
}

@media (max-width: 1100px) {
  .steps {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 620px) {
  .steps {
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
  }
}

/* ---------- Funcionalidades ------------------------------------------------
   Ten items, so the device has to survive volume. A two-column spec grid on
   hairlines, no cards and no icons: the reader is scanning for a capability
   name, which is why the name leads and the benefit line follows it.
   -------------------------------------------------------------------------- */

.feats {
  background: var(--white);
  padding: var(--section-pad) 0;
}

/* The dedicated page clears the sticky masthead itself, since it opens on
   this section rather than arriving at it mid-scroll. */
.feats--page {
  padding-top: calc(var(--section-pad) + var(--head-h));
}

.feats__lede {
  font-size: clamp(17px, 1.6vw, 21px);
  line-height: 1.6;
  color: var(--ink-700);
  margin-top: 20px;
  max-width: 68ch;
  text-wrap: pretty;
}

.feats__more {
  margin-top: clamp(28px, 3vw, 40px);
}

.more-link {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 17px;
  color: var(--state-done-ink);
  transition: gap 0.3s var(--ease-out);
}

.more-link svg {
  width: 20px;
  height: 20px;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.more-link:hover {
  gap: 16px;
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-thickness: 1px;
}

@media (prefers-reduced-motion: reduce) {
  .more-link {
    transition: none;
  }
}

/* The base link is dark green for light sections; on navy it needs its own ink. */
.more-link--light {
  color: var(--blue-100);
}

.feats__lede a {
  color: var(--state-done-ink);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
}

.feats__title {
  font-size: clamp(29px, 3.8vw, 48px);
  line-height: 1.1;
  letter-spacing: -0.015em;
  font-weight: 400;
  color: var(--ink-900);
  text-wrap: balance;
}

.feat-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(32px, 5vw, 88px);
  margin-top: clamp(34px, 3.8vw, 52px);
}

.feat {
  padding: var(--row-pad) 0;
  border-top: 1px solid var(--surface-subtle);
}

/* The last row in each column closes the grid off. */
.feat:nth-last-child(-n + 2) {
  border-bottom: 1px solid var(--surface-subtle);
}

.feat__title {
  font-size: clamp(20px, 1.5vw, 22px);
  line-height: 1.25;
  letter-spacing: -0.01em;
  font-weight: 400;
  color: var(--ink-900);
}

.feat__benefit {
  font-size: 17px;
  line-height: 1.45;
  color: var(--state-done-ink);
  margin-top: 6px;
  text-wrap: pretty;
}

.feat__body {
  transition: margin-top 0.45s var(--ease-out);
}

@media (prefers-reduced-motion: reduce) {
  .feat__title,
  .feat__benefit,
  .feat__body {
    transition-duration: 0.01ms;
  }
}

.feat__body {
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink-muted);
  margin-top: 10px;
  text-wrap: pretty;
}

@media (max-width: 760px) {
  .feat-list {
    grid-template-columns: minmax(0, 1fr);
  }

  .feat:nth-last-child(-n + 2) {
    border-bottom: 0;
  }

  .feat:last-child {
    border-bottom: 1px solid var(--surface-subtle);
  }
}

/* ---------- Integración ----------------------------------------------------
   Three routes on the open mist, divided by rules rather than boxed: Cómo
   funciona already spends the card device on this ground.
   -------------------------------------------------------------------------- */

.integra {
  background: var(--surface-mist);
  padding: var(--section-pad) 0;
}

.integra__title,
.secure__title {
  font-size: clamp(29px, 3.8vw, 48px);
  line-height: 1.1;
  letter-spacing: -0.015em;
  font-weight: 400;
  text-wrap: balance;
}

.integra__title {
  color: var(--ink-900);
}

.integra__lede {
  font-size: clamp(17px, 1.6vw, 21px);
  line-height: 1.6;
  color: var(--ink-700);
  margin-top: 20px;
  text-wrap: pretty;
}

.route-list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(28px, 4vw, 64px);
  margin-top: clamp(34px, 3.8vw, 52px);
}

.route {
  position: relative;
  padding-top: var(--row-pad);
  border-top: 1px solid var(--slate-200);
}

/* The rule above each route draws itself in the confirmed colour, so the
   hovered column is readable from across the section, not only at the title. */
.route::after {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  width: 100%;
  height: 1px;
  background: var(--state-done-ink);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.55s var(--ease-out);
}

.route:hover::after {
  transform: scaleX(1);
}

.route__title {
  position: relative;
  font-size: clamp(20px, 1.5vw, 22px);
  line-height: 1.25;
  letter-spacing: -0.01em;
  font-weight: 400;
  color: var(--ink-900);
}

/* The icon arrives from the left and the name steps aside to admit it. */
.route__icon {
  position: absolute;
  left: 0;
  top: 50%;
  width: 24px;
  height: 24px;
  margin-top: -12px;
  color: var(--state-done-ink);
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  transform: translateX(-8px) scale(0.92);
  transition: opacity 0.45s var(--ease-out), transform 0.45s var(--ease-out);
}

.route__name {
  display: inline-block;
  transition: transform 0.45s var(--ease-out);
}

.route:hover .route__icon {
  opacity: 1;
  transform: translateX(0) scale(1);
}

.route:hover .route__name {
  transform: translateX(34px);
}

@media (prefers-reduced-motion: reduce) {
  .route::after,
  .route__icon,
  .route__name {
    transition-duration: 0.01ms;
  }

  .route:hover .route__name {
    transform: none;
  }

  .route:hover .route__icon {
    opacity: 0;
  }
}

/* The hover icon is absolutely positioned inside this label, so the label has
   to be its containing block. Without this it resolves against .route and
   centres on the whole row, landing on top of the body copy. */
.route__kind {
  position: relative;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--state-done-ink);
  margin-top: 9px;
}

.route__body {
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink-muted);
  margin-top: 11px;
  text-wrap: pretty;
}

.integra__more {
  margin-top: clamp(28px, 3vw, 40px);
}

/* integracion.html clears the fixed masthead, as the other dedicated pages do. */
.integra--page {
  padding-top: calc(var(--section-pad) + var(--head-h));
}

/* ---------- Seguridad ------------------------------------------------------
   The page's one dark beat. Security is the differentiator here — official
   bank channel, not a robot in the home banking — so it gets its own ground
   rather than another tinted panel.
   -------------------------------------------------------------------------- */

.secure {
  background: var(--navy-900);
  padding: var(--section-pad) 0;
}

.secure__title {
  color: var(--white);
}

.secure__lede {
  font-size: clamp(17px, 1.6vw, 21px);
  line-height: 1.6;
  color: var(--slate-300);
  margin-top: 20px;
  max-width: 76ch;
  text-wrap: pretty;
}

.guard-list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(14px, 1.6vw, 20px);
  margin-top: clamp(34px, 3.8vw, 52px);
}

/* Two faces on one card. The front is a centred summary; the back is the full
   entry, which is also the only copy exposed to assistive tech (the front
   carries aria-hidden), so nothing is announced twice. */
.guard {
  perspective: 1400px;
  min-height: 268px;
  border-radius: var(--r-lg);
}

.guard:focus-visible {
  outline: 3px solid var(--slate-300);
  outline-offset: 3px;
}

.guard__flip {
  position: relative;
  display: grid;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.72s var(--ease-out);
}

.guard:hover .guard__flip,
.guard:focus-visible .guard__flip {
  transform: rotateY(180deg);
}

.guard__face {
  grid-area: 1 / 1;
  display: flex;
  flex-direction: column;
  padding: 28px 26px;
  border-radius: var(--r-lg);
  background: rgba(142, 173, 217, 0.07);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

.guard__face--front {
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 2px;
}

.guard__face--back {
  justify-content: center;
  transform: rotateY(180deg);
  background: rgba(142, 173, 217, 0.12);
}

.guard__icon {
  width: 34px;
  height: 34px;
  margin-bottom: 18px;
  color: var(--blue-100);
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.guard__front-title {
  font-size: clamp(20px, 1.5vw, 22px);
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--white);
  max-width: 20ch;
  text-wrap: balance;
}

.guard__front-sub {
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--blue-100);
  margin-top: 10px;
}

.guard__title {
  font-size: clamp(20px, 1.5vw, 22px);
  line-height: 1.25;
  letter-spacing: -0.01em;
  font-weight: 400;
  color: var(--white);
}

.guard__kind {
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--blue-100);
  margin-top: 9px;
}

.guard__body {
  font-size: 17px;
  line-height: 1.6;
  color: var(--slate-300);
  margin-top: 12px;
  text-wrap: pretty;
}

.secure__more {
  margin-top: clamp(28px, 3vw, 40px);
}

/* seguridad.html clears the fixed masthead the same way funcionalidades.html does. */
.secure--page {
  padding-top: calc(var(--section-pad) + var(--head-h));
}

/* On its own page every entry is already open, so there is no front face and no
   flip: the icon sits beside the text instead of above it. */
.guard-list--page {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(14px, 1.8vw, 24px);
}

.guard-entry {
  display: flex;
  gap: 20px;
  padding: 28px 26px;
  border-radius: var(--r-lg);
  background: rgba(142, 173, 217, 0.12);
}

.guard-entry .guard__icon {
  margin-bottom: 0;
  flex-shrink: 0;
}

.guard-entry .guard__title {
  margin-top: -2px;
}

@media (max-width: 860px) {
  .guard-list--page {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 520px) {
  .guard-entry {
    flex-direction: column;
    gap: 14px;
  }
}

/* No hover to give, so nothing can flip. This used to print the full entry
   outright, which made the phone read six paragraphs where the desktop reads
   six titles — the one place scanning matters most. Now touch gets the same
   scan layer the front face shows, and the full text lives on seguridad.html,
   reachable by the link under the list rather than by an impossible gesture.

   The front carries aria-hidden, so the back stays in the accessibility tree
   (visually hidden, not display:none) and assistive tech still gets every
   word plus the real h3 outline.

   A tap on a touch device still lands :hover and :focus-visible, so those
   have to be neutralised too — otherwise the container rotates while the
   face no longer counter-rotates, and the card renders mirrored. */
@media (hover: none) {
  .guard {
    min-height: 0;
  }

  .guard__flip,
  .guard:hover .guard__flip,
  .guard:focus .guard__flip,
  .guard:focus-visible .guard__flip,
  .guard:active .guard__flip {
    transform: none;
  }

  /* No flip here, so the front stops being a centred card face and becomes a
     scan row: icon left, capability and category stacked beside it. Same
     shape as the entries on seguridad.html, and roughly half the height of
     the centred version, which is the point of the layer. */
  .guard__face--front {
    transform: none;
    backface-visibility: visible;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    justify-content: start;
    column-gap: 16px;
    text-align: left;
    padding: 20px 22px;
  }

  .guard__face--front .guard__icon {
    grid-row: 1 / 3;
    grid-column: 1;
    margin-bottom: 0;
  }

  .guard__front-title {
    grid-column: 2;
    max-width: none;
    text-wrap: pretty;
  }

  .guard__front-sub {
    grid-column: 2;
    margin-top: 4px;
  }

  .guard__face--back {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  /* nothing flips, so the card is not a control and does not need a stop */
  .guard {
    pointer-events: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .guard__flip {
    transition-duration: 0.01ms;
  }
}

@media (max-width: 1100px) {
  .guard-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 760px) {
  .route-list {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }

  .guard-list {
    grid-template-columns: minmax(0, 1fr);
  }

  .route {
    padding-bottom: var(--row-pad);
  }

  .route:last-child {
    padding-bottom: 0;
  }

}

/* ---------- Para quiénes ---------------------------------------------------
   Segments as pills — this system's geometry. The four figures are run-in
   sentences, not a metric row: big number over small label with an accent is
   the template the craft floor refuses, and these numbers read as prose
   anyway ("1 bandeja para todos tus bancos").
   -------------------------------------------------------------------------- */

.who {
  background: var(--surface-mist);
  padding: var(--section-pad) 0;
}

.who__title,
.faq__title,
.close__title {
  font-size: clamp(29px, 3.8vw, 48px);
  line-height: 1.1;
  letter-spacing: -0.015em;
  font-weight: 400;
  color: var(--ink-900);
  text-wrap: balance;
}

.who__lede {
  font-size: clamp(17px, 1.6vw, 21px);
  line-height: 1.6;
  color: var(--ink-700);
  margin-top: 20px;
  max-width: 72ch;
  text-wrap: pretty;
}

.signal {
  font-weight: 400;
  color: var(--ink-900);
  background-image: linear-gradient(
    var(--state-done-fill),
    var(--state-done-fill)
  );
  background-repeat: no-repeat;
  background-position: 0 88%;
  background-size: 100% 0.5em;
}

/* The two conditions draw themselves as the section arrives, the second a
   beat behind the first. Resting state above is the finished one, so nothing
   depends on the timeline existing. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Anchored to the paragraph, not to each highlight: a short inline element
       enters and exits in a few score pixels, which is too fast to read. */
    .who__lede {
      view-timeline: --lede block;
    }

    .signal {
      animation: signal-wipe linear both;
      animation-timeline: --lede;
      animation-range: entry 30% cover 26%;
    }

    .who__lede .signal:last-of-type {
      animation-range: entry 55% cover 40%;
    }
  }

  @keyframes signal-wipe {
    from {
      background-size: 0% 0.5em;
    }
  }
}

/* ---------- Rubros carousel ------------------------------------------------
   Seven segments, five in view. Auto-advance is a courtesy, not a demand: it
   pauses on hover and on focus, and never runs under reduced-motion.
   -------------------------------------------------------------------------- */

.seg-slider {
  --per: 5;
  --seg-gap: 14px;
  margin-top: clamp(30px, 3.4vw, 44px);
}

.seg-nav {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-bottom: 14px;
}

.seg-btn {
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  border-radius: var(--r-pill);
  background: var(--white);
  color: var(--ink-700);
  box-shadow: var(--shadow-strip);
  transition: background-color 0.25s var(--ease-out), color 0.25s var(--ease-out),
    transform 0.25s var(--ease-out);
}

.seg-btn svg {
  width: 18px;
  height: 18px;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.seg-btn:hover {
  background: var(--navy-900);
  color: var(--white);
}

.seg-btn:active {
  transform: scale(0.94);
}

.seg-track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: calc(
    (100% - (var(--per) - 1) * var(--seg-gap)) / var(--per)
  );
  gap: var(--seg-gap);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  padding-bottom: 4px;
}

.seg-track::-webkit-scrollbar {
  display: none;
}

.seg-track:focus-visible {
  outline: 3px solid var(--blue-100);
  outline-offset: 4px;
  border-radius: var(--r-md);
}

@media (prefers-reduced-motion: reduce) {
  .seg-track {
    scroll-behavior: auto;
  }
}

.seg {
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  min-height: 132px;
  padding: 20px;
  background: var(--white);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-strip);
  transition: transform 0.35s var(--ease-out), box-shadow 0.35s var(--ease-out);
}

.seg:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-card);
}

.seg__icon {
  width: 26px;
  height: 26px;
  margin-bottom: 14px;
  flex-shrink: 0;
  color: var(--blue-500);
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: height 0.42s var(--ease-out), margin-bottom 0.42s var(--ease-out),
    opacity 0.26s var(--ease-out), transform 0.42s var(--ease-out);
}

/* flex-grow interpolates, so the label glides to the middle instead of
   jumping there when the alignment changes. */
.seg__gap {
  flex: 1 0 0;
}

.seg__pad {
  flex: 0 0 0;
  transition: flex-grow 0.42s var(--ease-out);
}

.seg__name {
  width: 100%;
  font-size: 16.5px;
  line-height: 1.35;
  color: var(--ink-900);
  text-wrap: pretty;
  transition: font-size 0.42s var(--ease-out), text-align 0.42s var(--ease-out);
}

.seg:hover .seg__icon,
.seg:focus-within .seg__icon {
  height: 0;
  margin-bottom: 0;
  opacity: 0;
  transform: scale(0.72);
}

.seg:hover .seg__pad,
.seg:focus-within .seg__pad {
  flex-grow: 1;
}

.seg:hover .seg__name,
.seg:focus-within .seg__name {
  font-size: 20px;
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .seg__icon,
  .seg__pad,
  .seg__name {
    transition-duration: 0.01ms;
  }
}

@media (max-width: 1180px) {
  .seg-slider {
    --per: 4;
  }
}

@media (max-width: 900px) {
  .seg-slider {
    --per: 3;
  }
}

@media (max-width: 640px) {
  .seg-slider {
    --per: 2;
  }
}

@media (max-width: 430px) {
  .seg-slider {
    --per: 1.25;
  }
}

.stat-list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: clamp(26px, 3.6vw, 60px);
  margin-top: clamp(34px, 4vw, 56px);
}

.stat {
  display: grid;
  grid-template-columns: 4.2ch minmax(0, 1fr);
  align-items: baseline;
  gap: 18px;
  padding: var(--row-pad) 0;
  border-top: 1px solid var(--slate-200);
}

.stat {
  border-bottom: 1px solid var(--slate-200);
}

.stat__n {
  justify-self: end;
  font-size: clamp(23px, 2.6vw, 31px);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.02em;
  /* DESIGN.md §2: blue is a data colour, for highlighted figures. These are
     the only real figures on the page, so they are where it belongs. */
  color: var(--blue-500);
}

.stat__what {
  font-size: 17px;
  line-height: 1.5;
  color: var(--ink-700);
  text-wrap: pretty;
}

/* ---------- Preguntas ------------------------------------------------------ */

.faq {
  background: var(--white);
  padding: var(--section-pad) 0;
}

.qa-list {
  margin-top: clamp(34px, 3.8vw, 52px);
  max-width: 96ch;
}

.qa {
  margin-inline: -18px;
  padding-inline: 18px;
  border-top: 1px solid var(--surface-subtle);
}

.qa:last-child {
  border-bottom: 1px solid var(--surface-subtle);
}

.qa__q {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: var(--row-pad) 18px;
  margin-inline: -18px;
  border-radius: var(--r-md);
  cursor: pointer;
  list-style: none;
  font-size: clamp(17px, 1.5vw, 20px);
  line-height: 1.3;
  color: var(--ink-900);
  transition: background-color 0.3s var(--ease-out), color 0.3s var(--ease-out);
}

.qa__q::-webkit-details-marker {
  display: none;
}

/* The negative margin lets the hover field reach past the text without the
   text itself moving when it appears. */
.qa__q:hover {
  background-color: var(--surface-mist);
  color: var(--state-done-ink);
}

.qa__q:focus-visible {
  background-color: var(--surface-mist);
}

.qa__mark {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: var(--ink-muted);
  transition: transform 0.4s var(--ease-out), color 0.3s var(--ease-out);
}

.qa__q:hover .qa__mark {
  color: var(--state-done-ink);
  transform: scale(1.22);
}

.qa[open] .qa__mark {
  transform: rotate(45deg);
  color: var(--state-done-ink);
}

.qa[open] .qa__q:hover .qa__mark {
  transform: rotate(45deg) scale(1.22);
}

.qa__a {
  font-size: 17px;
  line-height: 1.65;
  color: var(--ink-muted);
  max-width: 78ch;
  padding-bottom: var(--row-pad);
  margin-top: -6px;
  text-wrap: pretty;
}

/* The answer arrives rather than appearing. This runs everywhere. */
@media (prefers-reduced-motion: no-preference) {
  .qa[open] .qa__a {
    animation: qa-reveal 0.5s var(--ease-out) both;
  }

  @keyframes qa-reveal {
    from {
      opacity: 0;
      transform: translateY(-7px);
    }
  }
}

/* Where the browser can interpolate to auto, the panel's height animates both
   ways, so closing is as soft as opening instead of a cut. */
@supports (interpolate-size: allow-keywords) {
  :root {
    interpolate-size: allow-keywords;
  }

  @media (prefers-reduced-motion: no-preference) {
    .qa::details-content {
      block-size: 0;
      overflow: hidden;
      transition: block-size 0.45s var(--ease-out),
        content-visibility 0.45s allow-discrete;
    }

    .qa[open]::details-content {
      block-size: auto;
    }
  }
}

/* ---------- Demostración --------------------------------------------------- */

.close {
  background: var(--surface-mist);
  padding: var(--section-pad) 0;
}

.close__inner {
  text-align: center;
}

.close__title {
  max-width: 40ch;
  margin-inline: auto;
}

.close__body {
  font-size: clamp(17px, 1.6vw, 21px);
  line-height: 1.6;
  color: var(--ink-700);
  max-width: 74ch;
  margin: 20px auto 0;
  text-wrap: pretty;
}

.close__cta {
  margin-top: 34px;
}

@media (max-width: 760px) {
  .stat-list {
    grid-template-columns: minmax(0, 1fr);
  }

  .stat:not(:last-child) {
    border-bottom: 0;
  }

  .close__cta {
    width: 100%;
  }
}

/* ---------- Cómo funciona: hover ------------------------------------------
   One grammar, four readings: the card lifts and its shadow deepens, and the
   icon performs the thing its own step does. Not four unrelated effects —
   the same curve and duration throughout, and all of it reversible.
   -------------------------------------------------------------------------- */

.step {
  transition: transform 0.45s var(--ease-out), box-shadow 0.45s var(--ease-out),
    background-color 0.45s var(--ease-out);
}

.step:hover {
  background-color: var(--navy-900);
  transform: translateY(-5px);
  box-shadow: var(--shadow-card-lift);
}

.step__icon * {
  transition: transform 0.5s var(--ease-out), opacity 0.5s var(--ease-out);
  transform-box: fill-box;
  transform-origin: center;
}

/* 01 — the accounts travel the last of the distance into the hub. */
.step:hover .i-dot {
  transform: translateX(1.6px);
}

.step:hover .i-hub {
  transform: scale(1.14);
}

/* 02 — the movement drops into the tray on its own. */
.step:hover .i-fall {
  transform: translateY(2.2px);
}

/* 03 — the match is proposed: the pointer advances, the invoice meets it. */
.step:hover .i-chev {
  transform: translateX(1.4px);
}

.step:hover .i-doc {
  transform: translateX(-1.4px);
}

/* 04 — confirmation goes out, the nearer wave first. */
.step:hover .i-wave {
  transform: translateX(1.4px);
}

.step:hover .i-wave--2 {
  transition-delay: 0.07s;
}

@media (prefers-reduced-motion: reduce) {
  .step,
  .step__icon *,
  .step::before {
    transition: box-shadow 0.2s linear, opacity 0.2s linear,
      color 0.2s linear, border-bottom-color 0.2s linear;
  }

  .step:hover {
    transform: none;
  }

  .step:hover .i-dot,
  .step:hover .i-hub,
  .step:hover .i-fall,
  .step:hover .i-chev,
  .step:hover .i-doc,
  .step:hover .i-wave {
    transform: none;
  }
}

/* ---------- Arrivals, driven by the scroll --------------------------------
   On this surface scroll position is time — that is the argument the whole
   page makes — and two moments here already said so before this block
   existed: the dead weekend draws itself while the reader scrolls through it
   (.gap::before), and the two qualifying conditions underline themselves as
   they are read (.signal). Everything below extends that one grammar rather
   than introducing a second. Each section's arrival performs what that
   section argues, and the authored moment is La espera, where the timeline
   draws itself at the reader's own pace: the node lights, the rail runs down
   to the next beat, and the confirmed track ticks at its last beat while the
   other stalls at a gap that is already drawing.

   Four rules hold throughout, and the first is the reason there is no
   JavaScript in here at all.

   · The resting state is the finished one. Where `animation-timeline` does
     not exist the page is simply the page, every element present and at rest.
     Nothing is hidden waiting for a script, because there is no script: a
     reveal system that hides content first is one failed request away from a
     blank page.
   · Entrances move `translate`, `scale` and `clip-path` — never `transform`.
     `transform` belongs to the hover states (the step card's lift, the
     segment's rise, the button's press), and a scroll-driven animation is
     filled at every scroll offset, so it would own that property for good and
     those states would silently stop working. The individual transform
     properties compose with it instead.
   · Progress timelines ignore `animation-delay`; a stagger has to come from
     `animation-range`. Where several children must read as one event they
     share a named `view-timeline` on their row, so a 9px dot is not timing
     itself off its own 9px box.
   · Every arrival finishes while its element is still in the lower third of
     the viewport. Type that is being read is never type that is still moving.
   -------------------------------------------------------------------------- */

@supports (animation-timeline: view()) {
  @keyframes enter {
    from {
      opacity: 0;
      translate: 0 14px;
    }
  }

  @keyframes enter-sm {
    from {
      opacity: 0;
      translate: 0 8px;
    }
  }

  /* The 28×2px rule under a heading draws before the line it introduces.
     Both ends are explicit because the rest state carries a translate the
     animation must not drop. */
  @keyframes rule-wipe {
    from {
      transform: translateY(-6px) scaleX(0);
    }
    to {
      transform: translateY(-6px) scaleX(1);
    }
  }

  @keyframes rail-draw {
    from {
      transform: scaleY(0);
    }
  }

  @keyframes node-in {
    from {
      opacity: 0;
      scale: 0.3;
    }
  }

  @keyframes badge-in {
    from {
      opacity: 0;
      scale: 0.72;
    }
  }

  @keyframes tick-in {
    from {
      opacity: 0;
      scale: 0.4;
    }
  }

  /* A figure is a readout, so it fills rather than fades. The end state insets
     outward on every side: the animation is filled forever, and a clip at the
     exact box would shave the overshoot off a glyph. */
  @keyframes readout {
    from {
      clip-path: inset(-20% 100% -20% -20%);
    }
    to {
      clip-path: inset(-20% -20% -20% -20%);
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    /* -- Section leads ----------------------------------------------------
       Every section opens on the same two beats: the heading settles, then
       the line that carries the argument follows it. The hero is absent from
       all of this — it has its authored load entrance already, and a second
       one on scroll would be two answers to one question. */
    .wait__title,
    .signs__title,
    .how__title,
    .feats__title,
    .integra__title,
    .secure__title,
    .who__title,
    .faq__title,
    .close__title {
      animation: enter linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 22%;
    }

    .wait__claim,
    .wait__lede,
    .signs__cue,
    .feats__lede,
    .integra__lede,
    .secure__lede,
    .who__lede,
    .close__body {
      animation: enter linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 26%;
    }

    .wait__case,
    .wait__close,
    .feats__more,
    .integra__more,
    .secure__more {
      animation: enter linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 26%;
    }

    /* The rule keeps drawing for a beat after its line has settled. */
    .wait__claim,
    .signs__cue {
      view-timeline: --cue block;
    }

    .wait__claim::before,
    .signs__cue::before {
      transform-origin: left;
      animation: rule-wipe linear both;
      animation-timeline: --cue;
      animation-range: cover 20% cover 36%;
    }

    /* -- La espera: the clock the reader turns ----------------------------
       The tracks themselves do not animate. The empty stage arrives, and
       then it fills in with beats as the reader descends it — which is the
       whole point of the section, and a card that faded in first would only
       delay it. */
    .track__head,
    .day__name {
      animation: enter-sm linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 24%;
    }

    .beat {
      view-timeline: --beat block;
    }

    .beat__at,
    .beat__what {
      animation: enter-sm linear both;
      animation-timeline: --beat;
      animation-range: entry 0% cover 26%;
    }

    /* the node lights first */
    .beat::after {
      animation: node-in linear both;
      animation-timeline: --beat;
      animation-range: entry 0% cover 22%;
    }

    /* then the rail runs down from it toward the next beat */
    .beat::before {
      transform-origin: top;
      animation: rail-draw linear both;
      animation-timeline: --beat;
      animation-range: cover 20% cover 34%;
    }

    /* and the confirmed track ticks at its last beat: the resolution the
       whole comparison is driving at, so it lands last and on its own */
    .beat__done {
      animation: tick-in linear both;
      animation-timeline: --beat;
      animation-range: cover 26% cover 38%;
    }

    .gap__note {
      animation: enter linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 26%;
    }

    /* -- Si esto pasa -----------------------------------------------------
       The symptom reads, then its badge flags it. Both columns of a row sit
       at the same height, so they arrive together: a sweep across a rule
       that is level would be a lie about where the reader's eye is. */
    .sign {
      view-timeline: --sign block;
    }

    /* the badge is also a direct span child, and it has its own arrival */
    .sign__title > span:not(.sign__badge) {
      animation: enter-sm linear both;
      animation-timeline: --sign;
      animation-range: entry 0% cover 24%;
    }

    .sign__badge {
      animation: badge-in linear both;
      animation-timeline: --sign;
      animation-range: cover 18% cover 32%;
    }

    /* -- Cómo funciona ----------------------------------------------------
       Here the order is information — it is the only list on the page that is
       actually a sequence — so the four cards arrive in their own order. The
       offset is ~30px of scroll per card: read as life, not as a queue. */
    .step {
      animation: enter linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 24%;
    }

    .step:nth-child(2) {
      animation-range: entry 0% cover 27%;
    }

    .step:nth-child(3) {
      animation-range: entry 0% cover 30%;
    }

    .step:nth-child(4) {
      animation-range: entry 0% cover 33%;
    }

    /* -- Funcionalidades --------------------------------------------------
       The capability name, then what it gives you — the same reading order
       the section was built around, now in time as well as in space. Ten rows
       on funcionalidades.html run the identical rule. */
    .feat {
      view-timeline: --feat block;
    }

    .feat__title {
      animation: enter-sm linear both;
      animation-timeline: --feat;
      animation-range: entry 0% cover 24%;
    }

    .feat__benefit,
    .feat__body {
      animation: enter-sm linear both;
      animation-timeline: --feat;
      animation-range: cover 16% cover 30%;
    }

    /* -- Integración ------------------------------------------------------
       Three routes, enumerated left to right. The rule above each one keeps
       its hover wipe: that is a different sentence, and it stays on hover. */
    .route {
      view-timeline: --route block;
    }

    .route__title,
    .route__kind,
    .route__body {
      animation: enter linear both;
      animation-timeline: --route;
      animation-range: entry 0% cover 24%;
    }

    .route:nth-child(2) .route__title,
    .route:nth-child(2) .route__kind,
    .route:nth-child(2) .route__body {
      animation-range: entry 0% cover 27%;
    }

    .route:nth-child(3) .route__title,
    .route:nth-child(3) .route__kind,
    .route:nth-child(3) .route__body {
      animation-range: entry 0% cover 30%;
    }

    /* -- Seguridad --------------------------------------------------------
       The card, not the flip: `.guard__flip` keeps `transform` for its own
       rotation, so the arrival rides the perspective wrapper above it. */
    .guard,
    .guard-entry {
      animation: enter linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 24%;
    }

    .guard:nth-child(3n + 2) {
      animation-range: entry 0% cover 27%;
    }

    .guard:nth-child(3n) {
      animation-range: entry 0% cover 30%;
    }

    .guard-entry:nth-child(even) {
      animation-range: entry 0% cover 27%;
    }

    /* -- Para quiénes -----------------------------------------------------
       The slider arrives as one object; splitting a rail that scrolls
       sideways into seven arrivals would fight its own auto-advance. */
    .seg-slider {
      animation: enter linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 26%;
    }

    /* The three figures are the only real numbers on the page. They fill like
       a readout, left to right, and the sentence each one is the subject of
       follows it in. The fill is deliberately short — around 120px of scroll,
       low in the viewport — because two of these figures are a single glyph,
       and a glyph held half-clipped for a whole screen reads as a broken
       character rather than as a counter settling. */
    .stat {
      view-timeline: --stat block;
    }

    .stat__n {
      animation: readout linear both;
      animation-timeline: --stat;
      animation-range: cover 10% cover 22%;
    }

    .stat__what {
      animation: enter-sm linear both;
      animation-timeline: --stat;
      animation-range: cover 18% cover 30%;
    }

    .stat:nth-child(2) .stat__n {
      animation-range: cover 13% cover 25%;
    }

    .stat:nth-child(2) .stat__what {
      animation-range: cover 21% cover 33%;
    }

    .stat:nth-child(3) .stat__n {
      animation-range: cover 16% cover 28%;
    }

    .stat:nth-child(3) .stat__what {
      animation-range: cover 24% cover 36%;
    }

    /* -- Preguntas --------------------------------------------------------
       The row, not the answer: the panel has its own opening animation and
       two of them on one element would read as a stutter. */
    .qa {
      animation: enter-sm linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 24%;
    }

    /* -- The close --------------------------------------------------------
       The one place the sequence is allowed to land rather than settle: the
       heading, the line, and last the ask. */
    .close__cta {
      animation: enter linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 31%;
    }
  }
}
