/* ============================================================================
   Nahui — public presentation page
   Tokens: company/brand/brand-guide.md. Every colour below is either a value
   named in that file, or one of exactly two additions, each named, documented
   and measured against the surface it actually renders on.

   MEASURED CONTRAST (sRGB, WCAG 2.x relative luminance). Nothing on this page
   is placed on a surface it has not been measured against.

     ink      #2D2D2D  on white 13.77:1 · on shell 12.52:1 · AAA
     ink-mut  #6B6259  on white  5.97:1 · on shell  5.43:1 · AA, with margin
     coral-aa #C13F26  on white  5.24:1 · never on shell (4.77:1 is too thin,
                                          business-decisions.md Q12 rejected
                                          4.54:1 for exactly that reason)
     tez-dark #A72C2C  on white  6.91:1 · on shell  6.28:1 · the coral-family
                                          ink used wherever the surface is not
                                          white, and the primary-button hover
                                          (white on it = 6.91:1; brand-guide
                                          names Tezontle #D94C3A for hover, but
                                          white on Tezontle is 4.16:1 and fails
                                          AA — so hover goes darker, the same
                                          accessibility rebound brand-guide
                                          itself already applied to the bottom
                                          navigation)
     white    #FFFFFF  on coral-aa 5.24:1 · on ink 13.77:1 · on tez-dark 6.91:1
     on-dark-mut #D5CFCA on ink 8.92:1 · ADDITION 1. Needed because ink-mut is
                                          2.31:1 on the dark band, i.e. unusable
     line     #8C8681  on white 3.59:1 · on shell 3.27:1 · on obsidian 3.82:1
                                          · ADDITION 2. Every boundary token on
                                          the page, including every interactive
                                          boundary and the case card on the dark
                                          band, clears DESIGN-SYSTEM.md §10's
                                          3:1 floor
     success  #2E7D46  on white 5.07:1 · mark-signal only
     warning  #C8811A  on white 3.18:1 · mark-signal only, on white only
                                          (2.89:1 on shell — never placed there)
     coral    #E86248  brand mark only. Non-text-critical, per brand-guide.
     blush    #F2887C  on ink 5.63:1 · dark-band accent only, never on light.

   There are exactly three background surfaces: white, shell (#F4F4F4) and
   obsidian (#2D2D2D). No invented light tints.
   ========================================================================== */

/* ---------- fonts (self-hosted, subset to Latin; no third-party request) --- */
@font-face {
  font-family: 'Fredoka';
  src: url('assets/fonts/fredoka-var.woff2') format('woff2');
  font-weight: 300 700;
  font-stretch: 75% 125%;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'InterLocal';
  src: url('assets/fonts/inter-400.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'InterLocal';
  src: url('assets/fonts/inter-500.woff2') format('woff2');
  font-weight: 500; font-style: normal; font-display: swap;
}

/* ---------- tokens -------------------------------------------------------- */
:root {
  color-scheme: light;

  --ink:          #2D2D2D;
  --ink-muted:    #6B6259;
  --surface:      #FFFFFF;
  --shell:        #F4F4F4;
  --coral:        #E86248;
  --coral-aa:     #C13F26;
  --tezontle-dark:#A72C2C;
  --blush:        #F2887C;
  --success:      #2E7D46;
  --warning:      #C8811A;
  --on-dark:      #FFFFFF;
  --on-dark-muted:#D5CFCA;
  --line:         #8C8681;

  --mark-ink:  var(--coral);
  --mark-halo: var(--surface);

  /* brand-guide spacing scale, plus one doubling of 48 for section rhythm */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px;

  /* brand-guide radius scale */
  --r-sm: 8px; --r-md: 12px; --r-lg: 16px; --r-xl: 24px; --r-pill: 999px;

  --display: 'Fredoka', system-ui, sans-serif;
  --body: 'InterLocal', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --tap: 48px;             /* brand-guide states a ~46–48px floor twice */
  --measure: 66ch;
}

/* ---------- language switching -------------------------------------------
   data-l marks which language VERSION an element belongs to. Spanish is the
   static content; the English sits beside it in the same document. The script
   never writes text — it only sets data-lang on <html>. With no data-lang at
   all (JavaScript disabled) the <noscript> block in index.html reveals both.
   -------------------------------------------------------------------------- */
.t[data-l="en"] { display: none; }
:root[data-lang="en"] .t[data-l="es"] { display: none; }
:root[data-lang="en"] .t[data-l="en"] { display: revert; }

/* ---------- publication gates --------------------------------------------- */
:root[data-repo-link="off"]    [data-gated="repo"]    { display: none; }
:root[data-privacy-link="off"] [data-gated="privacy"] { display: none; }

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.65;
  font-feature-settings: 'tnum' 1, 'kern' 1;
  overflow-x: hidden;
}

h1, h2, h3 { font-family: var(--display); font-weight: 600; margin: 0; line-height: 1.2; }
p  { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg, picture { max-width: 100%; }
img { height: auto; display: block; }
i  { font-style: italic; }

:focus-visible {
  outline: 3px solid var(--coral-aa);   /* 5.24:1 on white, 4.77:1 on shell */
  outline-offset: 3px;
  border-radius: var(--r-sm);
}
.dark :focus-visible { outline-color: var(--on-dark); }  /* 13.77:1 on obsidian */

.skip {
  position: absolute; left: var(--s4); top: -100px; z-index: 50;
  background: var(--ink); color: var(--on-dark);
  padding: var(--s3) var(--s5); border-radius: var(--r-md);
  font-size: 15px; font-weight: 500; text-decoration: none;
  min-height: var(--tap); display: inline-flex; align-items: center;
  transition: top .15s ease;
}
.skip:focus { top: var(--s4); }

.wrap { width: 100%; max-width: 1120px; margin-inline: auto; padding-inline: var(--s5); }
.wrap--narrow { max-width: 760px; }

/* ---------- type roles ---------------------------------------------------- */
h2 {
  font-size: clamp(1.5rem, 1.18rem + 1.45vw, 2rem);      /* 24 → 32 */
  letter-spacing: -0.005em;
  margin-bottom: var(--s5);
  max-width: 24ch;
}
h3 { font-size: 1.25rem; line-height: 1.3; }

.lead {
  font-size: clamp(1.0625rem, 1.01rem + 0.28vw, 1.1875rem);  /* 17 → 19 */
  line-height: 1.6;
  max-width: 62ch;
}
.sec > .wrap > .lead { margin-bottom: var(--s6); }

.gloss { color: var(--ink-muted); font-family: var(--body); font-weight: 400; font-size: 0.875rem; }
.dark .gloss { color: var(--on-dark-muted); }

.caption { font-size: 0.875rem; color: var(--ink-muted); max-width: 52ch; }

/* an aside paragraph: quieter than body, still AA with margin */
.aside {
  font-size: 0.9375rem;
  color: var(--ink-muted);
  max-width: 62ch;
  margin-top: var(--s6);
  padding-left: var(--s4);
  border-left: 2px solid var(--line);
}

/* a bordered note — used for the two passages that qualify a claim */
.note {
  margin-top: var(--s6);
  padding: var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  font-size: 0.9375rem;
  max-width: 68ch;
}
.sec--shell .note { background: var(--surface); }
.note p + p { margin-top: var(--s3); }

/* ---------- header -------------------------------------------------------- */
.header { background: var(--surface); border-bottom: 1px solid var(--line); }

.bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
  min-height: 72px; padding-block: var(--s2);
}

.lockup {
  display: inline-flex; align-items: center; gap: var(--s3);
  text-decoration: none; color: var(--ink);
  min-height: var(--tap); min-width: var(--tap);
}
.lockup__mark { width: 32px; height: auto; display: block; flex: none; }

/* language control — one per region, never two adjacent. 48px minimum. */
.lang {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: var(--tap); min-width: var(--tap);
  padding: 0 var(--s4);
  background: var(--surface);
  color: var(--ink);
  border: 2px solid var(--ink);            /* boundary 13.77:1 on white */
  border-radius: var(--r-pill);
  font-family: var(--body); font-size: 0.9375rem; font-weight: 500;
  cursor: pointer; flex: none;
  transition: background-color .15s ease, color .15s ease;
}
.lang:hover { background: var(--ink); color: var(--on-dark); }
.lang__icon { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.6; flex: none; }

/* contents strip — a table of contents, visible at every width */
.toc { border-top: 1px solid var(--line); background: var(--shell); }
.toc ul {
  display: grid; grid-template-columns: 1fr 1fr;      /* an even contents table */
  column-gap: var(--s5); row-gap: var(--s2);          /* never 0: adjacent
     controls that swap the page must not touch */
  padding-block: var(--s2);
}
.toc a {
  display: inline-flex; align-items: center;
  min-height: var(--tap);
  color: var(--ink); text-decoration: none;
  font-size: 0.875rem; font-weight: 500;
  border-bottom: 2px solid transparent;
}
.toc a:hover { border-bottom-color: var(--tezontle-dark); }  /* 6.28:1 on shell */

/* ---------- hero ---------------------------------------------------------- */
.hero { background: var(--surface); padding-block: var(--s7) var(--s7); }
.hero__grid { display: grid; gap: var(--s7); align-items: start; }

.idlock { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s5); }
.idlock__mark { width: 42px; height: auto; flex: none; }
.idlock__name { font-size: 2rem; letter-spacing: -0.015em; line-height: 1.05; }
.idlock__tagline {
  font-family: var(--body);
  font-size: 0.6875rem; font-weight: 500;
  letter-spacing: 0.17em; text-transform: uppercase;
  color: var(--ink-muted);                 /* 5.97:1 on white */
}

.badge {
  display: inline-flex; align-items: center; gap: var(--s2);
  padding: var(--s1) var(--s3) var(--s1) var(--s2);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font-size: 0.8125rem; font-weight: 500;
  color: var(--ink);
  margin-bottom: var(--s5);
}
.badge__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--coral-aa); flex: none; }

.statement {
  font-family: var(--display); font-weight: 600;
  font-size: clamp(1.75rem, 1.2rem + 2.4vw, 2.5rem);      /* 28 → 40 */
  line-height: 1.17; letter-spacing: -0.015em;
  max-width: 19ch;
  margin-bottom: var(--s5);
}
.hero .lead { margin-bottom: var(--s6); }
.hero .caption { margin-top: var(--s3); }

/* ---------- buttons ------------------------------------------------------- */
.actions { display: flex; flex-wrap: wrap; gap: var(--s3); }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: var(--tap);
  padding: var(--s3) var(--s5);
  border-radius: var(--r-pill);
  font-family: var(--body); font-size: 0.9375rem; font-weight: 500;
  text-decoration: none; cursor: pointer;
  border: 2px solid transparent;
  overflow-wrap: anywhere;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.btn--primary {
  background: var(--coral-aa); color: var(--on-dark); border-color: var(--coral-aa);
}                                                   /* text 5.24:1 */
.btn--primary:hover, .btn--primary:active {
  background: var(--tezontle-dark); border-color: var(--tezontle-dark);
}                                                   /* text 6.91:1 */
.btn--secondary {
  background: var(--surface); color: var(--ink); border-color: var(--ink);
}                                                   /* boundary 13.77:1 */
.btn--secondary:hover { background: var(--shell); }
.ico { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; flex: none; }

.link {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: var(--tap);
  color: var(--coral-aa);                  /* 5.24:1 — white surfaces only */
  font-weight: 500; text-decoration: underline; text-underline-offset: 3px;
}
.link:hover { color: var(--tezontle-dark); }
.sec--shell .link, .foot .link { color: var(--tezontle-dark); }  /* 6.28:1 on shell */

/* ---------- hero screenshot ----------------------------------------------- */
.shot { margin: 0; display: flex; flex-direction: column; align-items: center; gap: var(--s4); }
.phone {
  width: min(258px, 70vw);
  padding: 9px;
  background: var(--ink);
  border-radius: 34px;
  box-shadow: 0 18px 40px -22px rgba(45,45,45,.55);
}
.phone img, .phone video { border-radius: 25px; width: 100%; display: block; }

/* The loop that stands where the screenshot stood. The frame, radius and
   shadow above are untouched — this is a swap inside the same box. */
.phone video {
  height: auto;
  aspect-ratio: 540 / 1024;    /* stated here, NOT left to the width/height
                                  attributes: those resolve to `auto 540/1024`,
                                  and whatever natural ratio the poster has
                                  would win until the video loads — the box
                                  would then resize under the reader. This
                                  declaration locks it from first paint, and it
                                  is the one line to change if the loop is ever
                                  recut to another height. */
  object-fit: cover;           /* The poster is a frame of the loop, so this is
                                  an exact match and cover does nothing today.
                                  It stays as the failure mode: if a future
                                  recut ever leaves the two at different sizes,
                                  the still gets cropped, never stretched. No
                                  object-position: there is nothing to crop. */
  pointer-events: none;        /* evidence, not media: nothing to click */

  /* ---- the box is painted by CSS, not by the `poster` attribute ----------
     The hero went blank on a real phone, and the cause was measured, not
     guessed: on a 200 kbps cold load the poster PNG is the LAST thing the
     page finishes (76 KB, behind the CSS, the document and three fonts), so
     the box stayed pure white for ~6 s and then painted top-down for ~6 s
     more. Nothing about `poster` was wrong — Chrome and WebKit both paint it
     at readyState 0 — it simply had not arrived yet. Moving the same file to
     a `background-image` alone would have changed nothing, because it is the
     same file arriving at the same time. So the box is painted in two
     layers, and the bottom one costs no request at all:

       layer 1 (top)    the real still, 540×1024 — the same URL the `poster`
                        attribute keeps, so it is already in cache by the
                        time CSS asks for it and is never fetched twice;
       layer 2 (bottom) THE SAME FRAME at 20×38, 551 bytes, inline. It
                        arrives inside this stylesheet, so it is painted at
                        first style resolution: before the gate runs, before
                        any image request completes, with JavaScript off, and
                        forever for a reader who asked for less motion. It is
                        a real downscale of the real frame — the header band,
                        the two scan buttons, the four product cards and the
                        coral action button are all where they will be — so
                        the handoff is a sharpening, never a substitution.

     The `poster` attribute stays as well: it is what a browser uses when it
     decides to, and it costs nothing to keep both. With all three present
     there is no state, and no instant, in which this box can be empty. */
  background-color: var(--surface);          /* a decoding video is never black */
  background-image:
    url("assets/app-loop-poster.png"),
    url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAmCAMAAADz5UuZAAAAYFBMVEX+/v70+/35+fn49vT09PT78u/08vHy8fH78Oz68Oz67+v58Oz47ejx8O/v7e306ebu6ufx5eLw3tjn4t/j29br2Mvz0cri1M3mycHhwbnVxb3Vu7HEq6LBdmjDTzmpPSizPPhzAAABgklEQVR42mWR2bakIAwAM4ggNrLTrLb//5c3uPQ9M1MPkZQJJ0YQ88nri5hfIENwIdhlvVl8ECCdtd4NKX7lKrTTi5SrkO5GgvapRYP9wsYUUwop6tGuHV6BnXpgrV7haZqXeRngJCvoWlNNqdZQ3Lwu+v1+S1hfUmqNQWqJV+BZS7C91uowJHnOJIT4Sz7zS1jI+Mpp3P8rtwHjnLMBH5mCjSCccbph4BSAkA04ID4EH9KII+OwMUrpRG4mTBi/2jFjlKn7HUo44cH7QO8zcMUIXMUzkAmhHKa294JlnoZ9D8oYozjovu+oWzH1OAqu4+0U+N5bLkiOvb9PzJC9XbKUdkoLvqFstyz5kaWdZId5vtp5qMkGxJiS7MkGpPa2B0IZDb0nHM3jSFPtqdmJMZp7z3FgYKNsgmlsJeacbsmVN6/haMQ5nkqmlJmnqzKf/Rblg4s3+DvUzfY94er+/AcH0vZ/aATicRyfi/HE7IhgPl95vTk+GojGQcq5u3yGrMkPvBQtaqF7ECwAAAAASUVORK5CYII=");
  background-size: cover;      /* one value, both layers — they are the same
                                  frame at two sizes, so they crop alike. */
  background-position: center;
  background-repeat: no-repeat;
}
/* With JavaScript disabled, Chrome and Safari force their own media controls
   onto a <video> that carries none — a play button, a mute toggle and a scrub
   bar land across "Finalizar Venta" and the hero reads as broken. There is no
   attribute that refuses them; this pseudo-element is the only lever. Inert in
   every other case, because without `controls` nothing is drawn anyway. */
.phone video::-webkit-media-controls,
.phone video::-webkit-media-controls-enclosure,
.phone video::-webkit-media-controls-panel { display: none !important; }
.shot figcaption {
  font-size: 0.8125rem; color: var(--ink-muted); text-align: center; max-width: 30ch; line-height: 1.5;
}

/* ---------- sections ------------------------------------------------------ */
.sec { padding-block: var(--s7); scroll-margin-top: var(--s4); }
.sec--shell { background: var(--shell); }
.dark { background: var(--ink); color: var(--on-dark); }

/* ---------- el problema --------------------------------------------------- */
/* A pull statement, not a testimonial: no quotation marks, no portrait. */
.pull { border-left: 4px solid var(--coral-aa); padding-left: var(--s5); margin-bottom: var(--s6); }
.pull__text {
  font-family: var(--display); font-weight: 500;
  font-size: clamp(1.25rem, 1.08rem + 0.75vw, 1.5rem);    /* 20 → 24 */
  line-height: 1.35; max-width: 32ch;
}
.pull__src { margin-top: var(--s3); font-size: 0.875rem; color: var(--ink-muted); max-width: 44ch; }

.cols { display: grid; gap: var(--s5); }
.cols p + p { margin-top: var(--s4); }

/* ---------- qué existe hoy ------------------------------------------------ */
.caps { display: grid; gap: var(--s3); grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.cap {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s5);
}
.cap h3 { margin-bottom: var(--s2); }
.cap p { font-size: 0.9375rem; color: var(--ink-muted); }   /* 5.97:1 on white */

/* today.notYet — the plain closing LINE of the capability list, deliberately
   not a block: no fill, no border, no left rule, nothing that makes it a panel.
   It states two scope decisions that were taken, so it must not read as the
   first of two stacked "not yet" boxes (CONTENT.md §6.11b). Full ink, one step
   below body size: a confident closer on the list, not small print. */
.capsnote {
  margin-top: var(--s5);
  max-width: 70ch;
  font-size: 0.9375rem;
  color: var(--ink);                       /* 13.77:1 — not greyed down */
}

/* today.bar — the section's closing argument, as its own card.
   CONTENT.md §6.11a, binding: this carries NO status signal. No alert fill, no
   icon, no state colour, no heavier border than its siblings. The subject of
   the passage is the standard Nahui set itself before building anything, which
   is a strength, so its hierarchy is built only from width, type size and air:
   it is the one full-width card in a section of narrow ones, it sits a full
   --s8 below the list, and its text runs at body size in full ink while the
   capability cards run smaller and muted. */
.standard {
  margin-top: var(--s8);
  /* Phone only: widen the measure by stepping outside the wrap's gutter. The
     closing argument must not end up the narrowest column on the page, which
     is what card padding inside an already-narrow gutter would make it. */
  margin-inline: calc(var(--s3) * -1);
  padding: var(--s5) var(--s4);
  border: 1px solid var(--line);           /* same weight as every card here */
  border-radius: var(--r-xl);              /* one step up: a bigger sibling */
  background: var(--surface);
  max-width: 72ch;
}
.standard p {
  font-size: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
  line-height: 1.62;
  color: var(--ink);                       /* 13.77:1 */
}
/* The sentence that names the standard, given its own block and one size step.
   A typographic device over an unmodified string — not a copy edit: no word,
   no order and no punctuation changes, the paragraph is still one paragraph in
   the markup and one string in CONTENT.md. It exists because the card was calm
   but had no entry point, so the fact that Nahui wrote a measurable bar before
   building anything was arriving flat. Body font, not display: this is a
   standard being stated, not a headline being made of it. */
.standard .lede {
  display: block;
  margin-bottom: var(--s3);
  font-size: clamp(1.125rem, 1.08rem + 0.2vw, 1.1875rem);
  line-height: 1.45;
  letter-spacing: -0.005em;
}

/* ---------- the dated record --------------------------------------------- */
.record { display: grid; gap: var(--s3); }
.record__row {
  display: grid; gap: var(--s3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s5);
}
.record__stamp {
  font-size: 0.8125rem; font-weight: 500; letter-spacing: 0.08em;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
.record__body h3 { margin-bottom: var(--s2); }
.record__body p { font-size: 0.9375rem; }

/* ---------- cómo va (dark) ------------------------------------------------ */
.dark h2 { color: var(--on-dark); }
.dark p  { color: var(--on-dark-muted); }               /* 8.92:1 */
.status {
  font-family: var(--display); font-weight: 600;
  font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2rem);
  color: var(--blush) !important;                        /* 5.63:1 on obsidian */
  margin-bottom: var(--s6);
  line-height: 1.2;
}
.dark .cols { margin-bottom: var(--s6); }
/* status.blocked — a dated case, never a disclaimer (CONTENT.md §4.5 rule 1,
   §6.11d). Built from .record's grammar — a date stamp beside a body — because
   that is already this page's established form for "this happened, it is
   dated, it is in the public log", and the reader met it one section earlier.
   Deliberately NOT .declare's grammar (§4.5 rule 3): that one is a rule-left
   declaration, this one is a bordered case, so the two can never stack as
   matching panels of bad news. No alert fill, no icon, no state colour, and
   the body runs at full white — one step STRONGER than the muted paragraphs
   around it, because it is the concrete evidence in the section, not a
   qualification of it. */
.case {
  display: grid; gap: var(--s3);
  margin-bottom: var(--s7);
  /* Phone only: step outside the wrap gutter, for the same reason .standard
     does — a case card's own padding would otherwise make the evidence the
     narrowest column in the section. */
  margin-inline: calc(var(--s3) * -1);
  padding: var(--s5) var(--s4);
  border: 1px solid var(--line);           /* 3.82:1 on obsidian */
  border-radius: var(--r-lg);
  max-width: 76ch;
}
.dark .case__stamp {
  font-size: 0.8125rem; font-weight: 500; letter-spacing: 0.08em;
  color: var(--on-dark-muted);             /* 8.92:1 */
  font-variant-numeric: tabular-nums;
}
.case__body p { color: var(--on-dark); font-size: 1rem; }   /* 13.77:1 */

/* status.body3 + status.body4 are ONE block, always, in this order
   (CONTENT.md §6.11c). The rule down the left belongs to the wrapper, not to
   body3, which is what stops body3 from ever standing alone or closing the
   section: the block ends on "lo que sí hay". Inside it, the declaration is
   full white and the follow-through is the dark band's muted ink, so the
   sequence reads as one movement rather than two statements. */
.declare {
  max-width: 52ch;
  padding-left: var(--s5);
  border-left: 3px solid var(--blush);     /* 5.63:1 on obsidian */
}
.plain {
  color: var(--on-dark) !important;                      /* 13.77:1 */
  font-size: clamp(1.0625rem, 1.0rem + 0.4vw, 1.25rem);
  line-height: 1.5;
}
.declare__then { margin-top: var(--s4); }                /* 8.92:1, via .dark p */

/* ---------- cómo está construido ------------------------------------------ */
.built { display: grid; gap: 0; margin-top: var(--s2); }
.built > li { padding-block: var(--s5); border-top: 1px solid var(--line); }
.built > li:last-child { border-bottom: 1px solid var(--line); }
.built h3 { margin-bottom: var(--s2); }
.built p { max-width: 74ch; font-size: 0.9375rem; }
.built .gate { margin-top: var(--s2); }

@media (min-width: 760px) {
  .built > li { display: grid; grid-template-columns: 15rem 1fr; gap: var(--s6); align-items: start; }
}

/* ---------- lo que sigue -------------------------------------------------- */
.next { display: grid; gap: var(--s3); }
.next__row {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s5);
  display: grid; gap: var(--s4);
}
.tag {
  display: inline-flex; align-items: center; gap: var(--s2);
  align-self: start; justify-self: start;
  padding: var(--s1) var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font-size: 0.75rem; font-weight: 500;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink);                       /* the label states the status: */
  white-space: nowrap;                     /* colour is never the only cue */
}
.tag__dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.tag--built    .tag__dot { background: var(--success); }  /* 5.07:1 on white */
.tag--progress .tag__dot { background: var(--warning); }  /* 3.18:1 on white */
.tag--hold     .tag__dot { background: var(--ink-muted); }/* 5.97:1 on white */
.next__body h3 { margin-bottom: var(--s2); }
.next__body p { font-size: 0.9375rem; max-width: 70ch; }

@media (min-width: 760px) {
  .next__row { grid-template-columns: 13rem 1fr; gap: var(--s6); align-items: start; }
}

/* ---------- contacto ------------------------------------------------------ */
.sec--contact { background: var(--surface); }
.sec--contact .lead { margin-bottom: var(--s6); }
.sec--contact .caption { margin-top: var(--s4); }

/* ---------- footer -------------------------------------------------------- */
.foot { background: var(--shell); border-top: 1px solid var(--line); padding-block: var(--s6); }
.foot__grid { display: grid; gap: var(--s6); }
.foot__made { font-size: 0.875rem; font-weight: 500; }
.foot__stage { font-size: 0.875rem; color: var(--ink-muted); max-width: 46ch; margin-top: var(--s2); }
.foot__side { display: flex; flex-direction: column; gap: var(--s4); align-items: flex-start; }
.foot__links { display: flex; flex-wrap: wrap; gap: var(--s3) var(--s6); }
.foot__links .link { font-size: 0.875rem; }

/* ---------- breakpoints --------------------------------------------------- */
@media (min-width: 680px) {
  .wrap { padding-inline: var(--s6); }
  .standard { margin-inline: 0; padding: var(--s6); }
  .case { grid-template-columns: 7.5rem 1fr; gap: var(--s5); align-items: start;
          margin-inline: 0; padding: var(--s6); }
  .dark .case__stamp { padding-top: 3px; }
  .cols { grid-template-columns: 1fr 1fr; gap: var(--s6); }
  .toc ul { display: flex; flex-wrap: wrap; column-gap: var(--s6); row-gap: var(--s2); }
  .record__row { grid-template-columns: 7.5rem 1fr; gap: var(--s5); align-items: start; }
  .record__stamp { padding-top: 3px; }
  .foot__grid { grid-template-columns: 1fr auto; align-items: start; }
  .foot__side { align-items: flex-end; }
}

@media (min-width: 900px) {
  .sec, .hero { padding-block: var(--s9); }
  .hero__grid { grid-template-columns: minmax(0, 1.08fr) auto; gap: var(--s8); align-items: center; }
  .phone { width: 282px; }
  .shot figcaption { max-width: 26ch; }
  .toc ul { display: flex; flex-wrap: wrap; column-gap: var(--s7); row-gap: var(--s2); }
}

/* ---------- motion: one orchestrated entrance, nothing else --------------- */
/* One orchestrated entrance: the words, then the product. Nothing else moves.
   Deliberately not a per-child stagger — the hidden language siblings are
   still children, so nth-child delays would land on the wrong elements. */
@media (prefers-reduced-motion: no-preference) {
  .idlock, .badge, .statement, .hero .lead, .hero .actions, .hero .caption, .shot {
    animation: rise .5s cubic-bezier(.22,.7,.3,1) backwards;
  }
  .badge         { animation-delay: .04s; }
  .statement     { animation-delay: .08s; }
  .hero .lead    { animation-delay: .12s; }
  .hero .actions { animation-delay: .16s; }
  .hero .caption { animation-delay: .18s; }
  .shot          { animation-delay: .14s; }
}
@keyframes rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* ---------- print --------------------------------------------------------- */
@media print {
  .toc, .lang, .skip { display: none; }
  .dark { background: #FFF; color: #000; }
  .dark p, .status, .plain, .case__body p, .dark .case__stamp { color: #000 !important; }
  .case { border-color: #000; }
  .sec, .hero { padding-block: var(--s5); }
}
