/* ============================================================
   Falemos Finanzen - the stylesheet (copied from its Spanish sibling site, palette swapped).
   Palette hex values are fixed by the brand strategy.
   Contrast pairings are VERIFIED - run tools/contrast.py before changing any value.
   ============================================================ */

:root {
  /* Palette - exact values from the brand strategy, do not invent variants */
  --green: #1a6b4a;  /* authority ground (.on-dark). carries cream (5.70). gold FAILS (2.72) */
  --wine:  #7a1a2e;  /* accent on cream: big numbers, claim, errors, focus ring (9.19) */
  --gold:  #d4a017;  /* the protagonist. carries INK only (7.17). FAILS as text on cream (2.09) */
  --cream: #f5f0e8;  /* default page ground. carries ink (15.02) */
  --ink:   #1c1c1c;  /* all body text */
  /* Type scale */
  --step--1: clamp(0.89rem, 0.85rem + 0.2vw, 1rem);
  --step-0:  clamp(1rem, 0.95rem + 0.25vw, 1.125rem);   /* body, >=16px always */
  --step-1:  clamp(1.33rem, 1.2rem + 0.6vw, 1.6rem);
  --step-2:  clamp(1.78rem, 1.5rem + 1.4vw, 2.6rem);
  --step-3:  clamp(2.37rem, 1.8rem + 2.8vw, 4.2rem);
  --step-4:  clamp(3.16rem, 2rem + 5.5vw, 7rem);        /* display + oversized figures */
  /* Vidaloka display floor - never below 28px (hairline Didone strokes break up smaller).
     Ceiling stays below --step-2 so anything using it reads as subordinate to a page h1/h2.
     Shared by .article h2 and .legal h2 - do not give either of them their own
     sub-28px clamp again, that defect has already been reintroduced once. */
  --step-1-display: clamp(1.75rem, 1.5rem + 0.8vw, 2.1rem);

  /* Spacing - 8px rhythm */
  --s1: 0.5rem; --s2: 1rem; --s3: 1.5rem; --s4: 2rem;
  --s5: 3rem;   --s6: 4rem; --s7: 6rem;   --s8: 8rem;

  --measure: 68ch;       /* desktop line length 60-75 */
  /* The gutter grows past 76rem viewport width so content never exceeds 68rem (1088px)
     and sits centered - without it every band starts at a 4rem gutter and the right
     half of a laptop screen is dead space. Colour grounds still bleed full-width
     because the cap lives in the padding, not in a wrapper.
     --gutter-min is the ungrown gutter: use it inside already-centered max-width
     boxes (.article, .legal), where the grown gutter would eat the text measure. */
  --gutter-min: clamp(1.25rem, 5vw, 4rem);
  --gutter: max(var(--gutter-min), calc((100vw - 68rem) / 2));
}

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

/* The nav scrolls to sections on this same page, so every landing point has to clear the
   sticky header. scroll-padding on the scroll container does it for every anchor at once -
   one line instead of a scroll-margin on each target. The value is the header's real height
   (var(--s3) padding x2 + the 3.25rem logo) plus a little air. Smooth scrolling is turned
   off again by the prefers-reduced-motion block below. */
html { scroll-padding-top: 7rem; scroll-behavior: smooth; }
@media (max-width: 860px) { html { scroll-padding-top: 5.5rem; } }

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: "Lato", system-ui, sans-serif;
  font-size: var(--step-0);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: "Vidaloka", Georgia, serif;  /* display ONLY - never below 28px */
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -0.01em;
}
h1 { font-size: var(--step-3); }
h2 { font-size: var(--step-2); }
/* --step-1-display, not --step-1: same 28px Vidaloka floor as .article h2 (see :root). */
h3 { font-size: var(--step-1-display); }

/* Anton: eyebrows, labels, oversized figures. NEVER paragraphs. */
.eyebrow {
  font-family: "Anton", Impact, sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: var(--step--1);
}
.figure {
  font-family: "Anton", Impact, sans-serif;
  font-size: var(--step-4);
  line-height: 0.85;
  font-feature-settings: "tnum";  /* tabular figures - no shift */
}

/* German financial terms in Spanish copy stay wrapped in <span class="de" lang="de">
   so screen readers switch pronunciation - but they render as plain text. German nouns
   are already capitalized, and the strategy reserves Anton for the logo, titles and
   figures. (An earlier Anton-uppercase + gold-underline inline treatment was not in the
   brand strategy and read as highlighter noise - removed on user call, 2026-07-15.) */

/* The surfaces nobody draws. Selection, the caret and the scrollbar ship with browser
   defaults that belong to no design system - a stock blue highlight is the loudest tell
   that a page was assembled rather than built. Gold carries ink at 7.17, and because a
   selection paints its own ground it reads the same on cream, green and black. */
::selection { background: var(--gold); color: var(--ink); }
/* Standard property only. The -webkit-scrollbar pseudo-elements would need a second, much
   longer implementation of the same two colours. */
html { scrollbar-color: rgba(28,28,28,0.4) transparent; }
.lead input, .lead textarea { caret-color: var(--wine); }

/* Focus: visible, always. Never remove. */
:focus-visible { outline: 3px solid var(--wine); outline-offset: 3px; }
/* --wine measures 9.19 on cream but only 1.61 against --green, under the 3.0 non-text floor -
   so on every dark band the ring was effectively invisible, including the header nav, which is
   the most-used keyboard path on the site. The stylesheet already records that same problem for
   the button's border and solves it with cream; the ring gets the same treatment. Cream clears
   the dark ground (5.70 on green) and would vanish against cream itself, which is why the
   override stays scoped to .on-dark. */
.on-dark :focus-visible { outline-color: var(--cream); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Self-hosted. NEVER load these from Google's CDN - see LG Muenchen I (2022). */
@font-face {
  font-family: "Vidaloka"; src: url("../fonts/vidaloka-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Anton"; src: url("../fonts/anton-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Lato"; src: url("../fonts/lato-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Lato"; src: url("../fonts/lato-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}

/* ---------- shell ---------- */
.skip {
  position: absolute; left: -9999px;
  background: var(--ink); color: var(--cream); padding: var(--s2);
}
.skip:focus { left: var(--s2); top: var(--s2); z-index: 100; }

.on-dark { background: var(--green); color: var(--cream); }

/* Sticky ("barra fija" - client, 2026-08-27). One page now, so the nav is the only way
   back out of a section; it has to stay reachable. sticky still establishes the containing
   block the mobile dropdown needs, so `relative` was not lost, it was upgraded. */
.site-header {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--gutter);
}
/* touch target floor (44px) on the hit area only - the logo itself stays 3.25rem/52px */
.site-header .brand {
  margin-right: auto;
  display: inline-flex; align-items: center; min-height: 44px;
}
/* logo.svg ships cropped to the wordmark (LOGO_VIEWBOX in tools/build-assets.py), so this
   height IS the wordmark height - not a box with invisible padding around it. The green
   ground it keeps blends into the header. Box lands ~130x52. */
.site-header img { height: 3.25rem; width: auto; }
.site-header nav { display: flex; gap: var(--s3); }
.site-header a {
  color: var(--cream); text-decoration: none;
  display: inline-flex; align-items: center; min-height: 44px;   /* touch target floor */
}
.site-header a:hover { text-decoration: underline; text-underline-offset: 0.3em; }
.nav-toggle {
  display: none; min-height: 44px; min-width: 44px;   /* touch target floor */
  background: none; border: 1px solid var(--cream); color: var(--cream);
  font-family: "Anton", sans-serif; text-transform: uppercase; cursor: pointer;
}

/* ---------- hero: the frame runs full-bleed, the type sits on it ----------
   Two numbers drive this whole block, and both are read off the photograph rather
   than guessed: Julio's outline starts at 50% of the frame's width, his face at
   58%. The frame is scaled to the hero's width, so those land at 50vw and 58vw at
   every viewport - which is why the caps below are derived from vw. Swapping the
   photograph invalidates them; re-measure before you do. */
.hero {
  position: relative; isolation: isolate; overflow: hidden;
  min-height: min(86vh, 820px);
  display: grid; align-items: end;
  /* --gutter-min, not --gutter: the grown gutter centres the reading bands at 68rem,
     which on a wide screen pushes this copy ~400px inward - straight into him. A
     full-bleed hero has no reason to obey it, so the type hangs at the viewport edge. */
  padding: var(--s6) var(--gutter) var(--s5) var(--gutter-min);
}
.hero__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; object-position: center 46%;
  /* No negative z-index: .on-dark's ground would paint over it. Instead .hero
     isolates, and paint order (image, scrim, then positioned text) does the work -
     which also leaves the green ground as the fallback until the frame loads. */
}
/* Legibility scrim: deep green (half of --green), heaviest at the left where the type sits.
   The 26% stop is 0.72 because the left third of this frame is sunlit sandstone: at the
   0.42 it had, gold "de amanha." measured 1.87 worst-case at 1440 and the cream eyebrow
   3.75. 0.72 is the lightest stop that holds the gold line over 3.0 at 1101, 1440 and 1920
   (3.15 / 3.19 / 3.29; 0.67 gave 2.89).
   The 46% stop carries the right ends of the lede and of "a liberdade financeira", which
   reach 45-47% of the width over the bright square. Without it the cream lede measured
   2.38 / 2.24 (4.5 floor) and the h1 2.41 / 2.10 (3.0 floor) at 1440 / 1101; with it the
   lede is 5.15 / 4.92 and the h1 4.80 / 4.59 (6.64 and 6.54 at 1920). The ramp still reaches
   0 at 52%, and his outline starts at 50%: the stop only touches the anti-aliased edge of his
   left arm (mean 6 / max 24 per channel across the 50-52% strip at 1440), never his face or
   shirt. */
.hero__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(90deg, rgba(13,53,37,0.78) 0%, rgba(13,53,37,0.72) 26%, rgba(13,53,37,0.6) 46%, rgba(13,53,37,0) 52%),
    linear-gradient(0deg,  rgba(13,53,37,0.55) 0%, rgba(13,53,37,0.14) 38%, rgba(13,53,37,0) 60%);
}
.hero-text { position: relative; }        /* positioned, so it paints over the scrim */
/* Cream, not gold: small text needs 4.5 and gold over this scrim tops out at 3.87 (1440) /
   3.36 (1101) even at a 0.9 stop. Cream measures 6.48 / 5.45 / 6.69 at 1440 / 1101 / 1920. */
.hero .eyebrow { color: var(--cream); }
/* The headline is set as three written lines, so each has to fit as written. Measured
   as rendered, the longest ("a liberdade financeira") is 9.15em at every width from 390 to
   1920 - hence the 10em cap, in em so it tracks the headline rather than the body text.
   The hero also takes a steeper scale than --step-3: same 4.2rem ceiling, smaller start,
   because below ~1500px the site scale cannot fit that line in the strip of frame left
   beside him. */
.hero h1 {
  font-size: clamp(2.37rem, 0.4rem + 3.5vw, 4.2rem);
  max-width: 10em; margin: var(--s2) 0 var(--s3);
}
.lede { font-size: var(--step-1); max-width: 40ch; }
/* The lede is the one block long enough to reach his jacket, so it is capped against
   that 50vw line and not by measure alone. */
.hero .lede {
  max-width: min(40ch, calc(50vw - 1.5rem - var(--gutter-min)));
  margin: 0 0 var(--s4);
}
/* The hero h1 carries the slogan; its closing line takes the gold "goal" accent. It is large
   text (3.0 floor) and, worst case over the scrim, measures 3.15 / 3.19 / 3.29 at 1101 / 1440 /
   1920. On flat cream it is 2.09 and on flat green 2.72, so the rule is scoped to .hero h1,
   not .on-dark - and the stacked hero below, where the type sits on flat green, turns it cream. */
.hero h1 .goal { color: var(--gold); }

/* CTA: gold ground carries INK only (7.17). Cream fails at 2.09.
   .wa is the WhatsApp button; .cta is the same object pointing somewhere on this page.
   Two names because the destination differs, one treatment because the job is identical. */
.wa, .cta {
  display: inline-block; background: var(--gold); color: var(--ink);
  font-family: "Anton", sans-serif; text-transform: uppercase;
  letter-spacing: 0.04em; text-decoration: none;
  padding: var(--s2) var(--s4); min-height: 44px;
  border: 2px solid var(--ink);
  transition: transform 180ms ease-out, background-color 180ms ease-out;
}
.wa:hover, .cta:hover { transform: translateY(-2px); background: color-mix(in srgb, var(--gold) 85%, var(--ink)); }
.wa:active, .cta:active { transform: translateY(0); }
/* gold's own edge is 2.09 on cream and 2.72 on green, under the 3.0 non-text floor - ink
   border on cream, cream border on green (5.70). */
.on-dark .wa, .on-dark .cta { border-color: var(--cream); }

/* Below 1100px the strip of frame beside him is too narrow to hold the headline at any
   readable size, so the hero stops overlaying and stacks: the photo becomes a band and
   the type takes .on-dark's ground underneath it. The breakpoint is where the geometry
   runs out, which is why it is not the site's usual 860. */
@media (max-width: 1100px) {
  .hero { min-height: 0; padding: 46svh var(--gutter) var(--s5); }
  /* 22%, not 44%: the square crop puts his hair ~150px below its top edge, and a short
     landscape band (1024x768: 353px) at 44% showed only his mouth. At 22% his whole head
     is in at 390x844, 768x1024, 860x1180, 1024x768 and 1100x800. */
  .hero__img { height: 46svh; bottom: auto; object-position: center 22%; }
  .hero__scrim { display: none; }
  /* The type no longer sits on the frame, so there is no face to stop short of. The
     50vw cap has to go with it - left on, it collapses the lede on a phone. */
  .hero .lede { max-width: 40ch; }
  /* On flat green gold measures 2.72, under the 3.0 floor; cream is 5.70. */
  .hero h1 .goal { color: var(--cream); }
}

@media (max-width: 860px) {
  .nav-toggle { display: block; }
  .site-header nav { display: none; }
  .site-header nav:not([hidden]) {
    display: flex; flex-direction: column;
    /* anchored to .site-header (position: relative above), not a hardcoded offset - the
       header's real height changes with its padding/logo/button, a fixed rem value would drift */
    position: absolute; top: 100%; left: 0; right: 0;
    background: var(--green); padding: var(--s3) var(--gutter);
    /* Same rule as the footer's top border: the dropdown ground matches the hero
       ground, so without an edge it reads as links floating over the headline. */
    border-bottom: 1px solid rgba(245,240,232,0.2);
    z-index: 10;
  }
}

/* ---------- bands ---------- */
.band { padding: var(--s7) var(--gutter); }
.band > h2 { max-width: 20ch; margin: var(--s2) 0 var(--s4); }
.band .note { max-width: var(--measure); font-size: var(--step-1); }
.band .wa, .band .cta { margin-top: var(--s2); }

/* Oversized figures. Every one needs a .source next to it - no invented numbers. */
.figure-row {
  display: grid; gap: var(--s5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  margin: var(--s5) 0;
}
.figure-row > div { border-top: 3px solid var(--wine); padding-top: var(--s2); }
.figure-row .figure { color: var(--wine); margin: 0 0 var(--s2); }
.figure-row p { max-width: 30ch; }
.source { font-size: var(--step--1); opacity: 0.7; margin-top: var(--s1); }
/* Same small-print link treatment as .reviews-score: the default blue is not in the palette. */
.source a { color: inherit; text-decoration-color: var(--gold); text-underline-offset: 0.2em; }

/* Process: numbered ONLY because these steps are a real sequence. */
/* padding: 0 como .articles y .qualifiers - sin el, el <ol> conserva sus 40px de sangria
   por defecto y la lista queda descuadrada respecto al h2 y al .note de su banda.
   Se notaba poco mientras no habia filetes; con los de .proceso salta a la vista. */
.process { counter-reset: step; display: grid; gap: var(--s4);
  margin-top: var(--s5); padding: 0; }
.process > li { counter-increment: step; list-style: none; display: grid;
  grid-template-columns: auto 1fr; gap: var(--s3); align-items: start; }
.process > li::before {
  content: counter(step, decimal-leading-zero);
  font-family: "Anton", sans-serif; font-size: var(--step-2);
  color: var(--wine); line-height: 1;
}
.process p { margin: 0; max-width: var(--measure); }

/* #proceso - "Tu plan individual". Iba sobre --black; la clienta lo rechazo (2026-08-30)
   porque no combinaba con el resto del sitio, y era cierto: --black era un color inventado
   para este unico bloque. Ahora la banda es hueso y quien marca el modulo es la tipografia.
   Las cifras suben a --step-3 y se pintan en vino - el idiom del "48 %". --wine sobre hueso
   mide 9.19, AAA: --step-3 pisa 37.9px, muy por encima del limite,
   igual que lo esta .figure-row .figure.
   Comparte fondo con #servicios pero no se pega a el: entre los dos hay una fila de dos
   fotos que ya rompe la mancha, y de paso desaparece el roce rojo-negro-rojo con
   #para-quien que quedaba anotado en /variantes.
   Los filetes son los de .articles (1px tinta al 15 %), NO los 3px dorados de
   .qualifiers: #para-quien viene justo despues y los dos bloques no pueden leerse igual -
   alli son cuatro condiciones en paralelo, aqui cuatro pasos en secuencia. */
.proceso .process { gap: 0; border-top: 1px solid rgba(28,28,28,0.15); }
.proceso .process > li {
  padding: var(--s3) 0; border-bottom: 1px solid rgba(28,28,28,0.15);
}
/* Ancho fijo, no `auto`: cada <li> es su propia rejilla, asi que con `auto` la columna la
   fija el ancho de SU cifra - y en Anton "01" es mas estrecho que "04", con lo que el texto
   arrancaba en una x distinta en cada paso. 1.1em sobre el propio tamano de la cifra cubre
   el avance de dos digitos (~0.96em) y escala con el clamp. */
.proceso .process > li::before {
  font-size: var(--step-3); color: var(--wine); width: 1.1em;
}
/* A --step-3 la cifra se lleva ~65px de la columna izquierda: en un movil de 320px al
   texto le quedan 27ch, por debajo de lo legible. Debajo de 560px la cifra pasa a ser un
   rotulo sobre el paso, no una columna al lado. */
@media (max-width: 560px) {
  .proceso .process > li { grid-template-columns: 1fr; gap: var(--s1); }
}

/* Editorial article list - rules and type, no icon boxes. */
.articles {
  list-style: none; margin: var(--s5) 0 0; padding: 0;
  border-top: 1px solid rgba(28,28,28,0.15);
}
.articles li { border-bottom: 1px solid rgba(28,28,28,0.15); }
.articles a {
  display: block; padding: var(--s3) 0; text-decoration: none; color: inherit;
  min-height: 44px;
}
.articles .title {
  display: block; font-weight: 700; font-size: var(--step-1);
  max-width: 40ch; margin-bottom: var(--s1);
}
.articles .dek { display: block; font-size: var(--step--1); opacity: 0.7; max-width: 40ch; }
.more {
  display: inline-flex; align-items: center; min-height: 44px;   /* touch target floor */
  margin-top: var(--s4); color: var(--ink); font-weight: 700;
  text-decoration-color: var(--gold); text-underline-offset: 0.25em;
}

/* ---------- servicios: six areas as cards, each opening its .area block as a popup ----------
   Boxed, unlike the rest of the site - the client's own material presents these six as tiles.
   Kept to type and a rule: cream ground, ink border, no shadow, no icons. */
.services {
  list-style: none; padding: 0; margin: var(--s5) 0 0;
  display: grid; gap: var(--s3);
  /* 18rem lands on 3 columns at the 68rem measure (4 would need <16rem tracks), so the six
     cards read as the client's 3x2 slide, then 2 and 1 as the viewport narrows. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
}
.service-card {
  position: relative; display: flex; flex-direction: column; gap: var(--s2);
  height: 100%; padding: var(--s3) var(--s3) var(--s5);
  align-items: center; text-align: center;
  border: 2px solid var(--ink); color: inherit; text-decoration: none;
  transition: background 0.2s ease-out;
}
/* currentColor by default; .services overrides it with the client's green/wine further
   down - decorative only, which is exactly what these are. */
.card-icon { width: 2.25rem; height: 2.25rem; flex: none; align-self: center; }
/* Anton, not Vidaloka: a card title lands under the 28px Vidaloka floor (see :root). */
.service-card .title {
  font-family: "Anton", Impact, sans-serif; text-transform: uppercase;
  letter-spacing: 0.04em; font-size: var(--step-1); line-height: 1.1;
}
.service-card .dek { font-size: var(--step--1); }
/* --step--1 lands at 14.4px on a phone. That is fine for a label and under the floor for
   three lines of body copy, which is what this is once the cards are one per row. */
@media (max-width: 560px) { .service-card .dek { font-size: var(--step-0); } }
/* "There is more behind this" affordance. Decorative, so a pseudo-element - never announced. */
.service-card::after {
  content: "+"; position: absolute; right: var(--s3); bottom: var(--s2);
  font-family: "Anton", Impact, sans-serif; font-size: var(--step-1);
  color: inherit; opacity: 0.55; line-height: 1;
}
/* An 8% wash of --green over cream, replacing the ink invert the clients turned down
   (2026-08-27: "no les gusta el fondo oscuro"). The card stays light and the gesture is
   unchanged - only the value. Ink text on the washed ground measures 13.44, AAA. Alpha
   over cream rather than a new hex, the same technique as the hairlines. Elegida entre
   seis maquetas; las otras cinco vivian en /hover-models/, que se borro con la decision. */
.service-card:hover, .service-card:focus-visible { background: rgba(26, 107, 74, 0.08); }
@media (prefers-reduced-motion: reduce) { .service-card { transition: none; } }

/* Popup. Padding lives on .area so a click on the dialog element itself is unambiguously
   a backdrop click (see modal.js). */
.area-dialog {
  padding: 0; border: 2px solid var(--ink); background: var(--cream); color: var(--ink);
  width: min(38rem, calc(100vw - 2 * var(--s3)));
  max-width: none; max-height: min(84vh, 44rem);
}
.area-dialog[open] { display: flex; flex-direction: column; }
.area-dialog::backdrop { background: rgba(13, 53, 37, 0.72); }
.area-dialog .area { overflow-y: auto; padding: 0 var(--s4) var(--s4); }
.area-dialog h3 { margin: 0 0 var(--s3); }
.area-close {
  align-self: flex-end; flex: none; min-width: 44px; min-height: 44px;
  background: none; border: 0; color: var(--ink); cursor: pointer;
  font-size: var(--step-1); line-height: 1;
}
.area-close:hover { color: var(--wine); }
/* Without JS the dialogs never exist and these are ordinary sections below the cards. */
.area { margin-top: var(--s5); }
.area h3 { margin: 0 0 var(--s2); }
.area p { margin: 0 0 var(--s3); max-width: var(--measure); }
.area p:last-child { margin-bottom: 0; }
/* showModal() makes the page inert, not unscrollable. */
.dialog-open { overflow: hidden; }
@media (max-width: 560px) {
  .area-dialog { width: calc(100vw - var(--s2)); max-height: 90vh; }
  .area-dialog .area { padding: 0 var(--s3) var(--s3); }
}

/* Qualifiers: the four conditions of "Este servicio es exclusivo para ti si:".
   A grid, not a list. They are not a sequence (no order to carry) and not an index of
   links, which is what the hairline .articles idiom they used to borrow announces - they
   are four conditions that hold at the same time, and a grid says "in parallel" where a
   stack says "one after another". Two columns at the measure, one on a phone.
   The rule above each is the same device as the "48 %" figures - which the client pointed
   at and called "este estilo". Note that #riesgo is hidden right now, so on the rendered
   page that precedent is carried only by .manifesto .claim; it comes back when the figures
   do. Gold, because these sit on the green ground where wine measures 1.61.
   Two explicit columns, not auto-fit. Four items want 2x2; auto-fit gave three columns at
   1440 (a ragged 3+1) and, at a 26rem floor, dropped to one column for everything between
   700 and 940 - where a 34ch cap then held the text at ~490px and stranded up to 403px of
   empty green beside it, worse than the stack this replaced. A stated breakpoint says what
   the layout actually is and cannot surprise at either end. Below it, one column, and the
   track sets the measure so there is no cap left to strand anything. */
.qualifiers {
  list-style: none; margin: var(--s5) 0 0; padding: 0;
  display: grid; gap: var(--s5) var(--s4);
  grid-template-columns: 1fr;
}
@media (min-width: 44rem) { .qualifiers { grid-template-columns: 1fr 1fr; } }
.qualifiers > li {
  border-top: 3px solid var(--gold); padding-top: var(--s2);
  /* body leading (1.6) at --step-1 is 41px, which exaggerates the height difference between
     a two-line and a four-line condition. The site steps type up but never steps leading
     down; at this size it should. balance evens the last lines - `pretty` is scoped to
     p/.lede/.note and never reached these. */
  font-size: var(--step-1); line-height: 1.35; text-wrap: balance;
}
/* The opening clause of each condition, so the eye has somewhere to land. These are the
   client's own words - the mark is typographic, nothing is reworded. */
.qualifiers strong { font-weight: 700; }
/* Without this the closing line sits a paragraph gap under the last condition and
   reads as part of it - most visible on a phone, where the grid is one column. */
.qualifiers + .note { margin-top: var(--s5); }

/* ---------- article ---------- */
.article { max-width: var(--measure); margin: 0 auto; padding: var(--s6) var(--gutter-min); }
.article h1 { margin-bottom: var(--s3); }
.article p { margin: 0 0 var(--s3); }
/* --step-1-display, not --step-1: --step-1's ceiling is 1.6rem = 25.6px, below Vidaloka's
   28px floor (see :root). */
.article h2 { font-size: var(--step-1-display); margin: var(--s5) 0 var(--s2); }
.article .lede { font-size: var(--step-1); }
.article-cta { margin-top: var(--s6); padding-top: var(--s4); border-top: 2px solid var(--ink); }

/* ---------- contenido: editorial index, rules and type only ---------- */
.article-index {
  list-style: none; margin: var(--s5) 0 0; padding: 0;
  border-top: 1px solid rgba(28,28,28,0.15);
}
.article-index li { border-bottom: 1px solid rgba(28,28,28,0.15); }
.article-index a { display: block; padding: var(--s5) 0; text-decoration: none; color: inherit; }
.article-index h2 { margin: var(--s1) 0 var(--s2); }
.article-index p { max-width: var(--measure); margin: 0; }

/* Internal review notes are NEVER visible page text (they look real and would ship) -
   they live in HTML comments carrying a [PLACEHOLDER] gate that check-site.py enforces.
   The old .notice callout existed only for such notes and was removed with them. */

/* ---------- contacto: the lead form, posting to Web3Forms (see index.html) ---------- */
/* Two-column form: short fields pair up, full-width ones and the button span both. */
.lead { margin: 0; display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3); }
.lead .field { display: grid; gap: var(--s1); }
.lead .field--full { grid-column: 1 / -1; }
.lead label { font-weight: 700; }
.lead input, .lead textarea {
  min-height: 44px; padding: var(--s1) var(--s2); border: 2px solid var(--ink);
  background: var(--cream); color: var(--ink); font: inherit; width: 100%;
}
.lead textarea { min-height: 8rem; resize: vertical; }
.lead button {
  grid-column: 1 / -1; justify-self: start; min-height: 44px; padding: var(--s2) var(--s4);
  background: none; border: 2px solid var(--ink); color: var(--ink);
  font-family: "Anton", sans-serif; text-transform: uppercase; letter-spacing: 0.04em;
  cursor: pointer;
}
@media (max-width: 560px) { .lead { grid-template-columns: 1fr; } }
.lead button:hover { background: var(--ink); color: var(--cream); }
/* Consent gate. The label is the whole row so the text is part of the hit target, and the
   box has to opt out of the 44px/full-width sizing every other .lead input gets. Normal
   weight: .lead label is bold because it titles a field, and this one is a sentence. */
.lead .consent label { display: flex; gap: var(--s1); align-items: flex-start; font-weight: 400; }
.lead .consent input { width: auto; min-height: 0; margin-top: 0.25em; flex: none; }
/* Same link idiom as .legal and .more - the browser default blue is not in the palette. */
.lead .consent a { color: inherit; text-decoration-color: var(--gold); text-underline-offset: 0.25em; }
/* What form.js writes into after a submit. Spans both columns like .field--full; hidden
   until there is something to say. --wine on cream measures 9.19 (AAA), well above the
   body-size floor. */
.form-status { grid-column: 1 / -1; margin: 0; font-weight: 700; }
.form-status.is-error { color: var(--wine); }

/* ---------- contact segment (home #contato): contact photo + WhatsApp + form ---------- */
/* Photo fills the left column full-height (cover), WhatsApp + form fill the right - both
   sides stay full so a tall form never leaves dead cream beside a short photo. */
.contact-split {
  display: grid; grid-template-columns: 42fr 58fr; gap: var(--s5);
  align-items: stretch; margin-top: var(--s4);
}
/* absolute-fill so the photo always spans the stretched column height (a percentage-height
   chain silently fails when the ancestor height is grid-derived rather than declared). */
.contact-photo { position: relative; overflow: hidden; min-height: 22rem; }
/* .contact-split prefix raises specificity over the generic `.photo-frame img { height:auto }`
   defined later in the file, which otherwise wins the tie and collapses the fill. */
.contact-split .contact-photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.contact-body .wa { margin-top: var(--s2); }
.contact-form h3 { margin: 0 0 var(--s3); }
/* The form leads; WhatsApp is the alternative below it, separated by the same rule idiom
   as .article-cta. */
.contact-wa { margin-top: var(--s5); padding-top: var(--s4); border-top: 2px solid var(--ink); }
@media (max-width: 860px) {
  .contact-split { grid-template-columns: 1fr; gap: var(--s4); align-items: start; }
  /* stacked: portrait back in normal flow with a sane fixed ratio, not stretched */
  .contact-photo { min-height: 0; }
  .contact-split .contact-photo img { position: static; height: auto; aspect-ratio: 4 / 5; }
}

/* ---------- impressum / datenschutz: dense legal prose, same measure as .article ---------- */
.legal { max-width: var(--measure); margin: 0 auto; padding: var(--s6) var(--gutter-min); }
.legal h1 { margin-bottom: var(--s3); }
/* --step-1-display, not --step-1: same 28px Vidaloka floor as .article h2 (see :root). */
.legal h2 { font-size: var(--step-1-display); margin: var(--s5) 0 var(--s2); }
.legal p { margin: 0 0 var(--s3); }
/* Same link idiom as .more: ink text, gold underline - the default browser blue
   is the only unstyled colour that can appear on these pages. */
.legal a { color: inherit; text-decoration-color: var(--gold); text-underline-offset: 0.25em; }

/* ---------- motion: subtle, opt-out-safe ---------- */
/* Scroll-reveal. reveal.js adds .reveal-ready to <html> (only when JS runs AND motion is
   allowed), then toggles .is-visible per element via IntersectionObserver. Without JS, or
   under reduced motion, .reveal-ready is never set so the hidden state never applies and
   content is always shown. IntersectionObserver is chosen over CSS animation-timeline because
   the latter isn't supported in Firefox yet - this must work in every browser. */
@media (prefers-reduced-motion: no-preference) {
  /* 0.55s over 1rem on an exponential ease-out, not 0.8s over 2rem on ease-out. The long
     slow rise from far below is the stock scroll entrance; at this distance and curve the
     movement settles almost as the eye arrives and reads as the page catching up with the
     reader rather than as an effect being played at her. */
  .reveal-ready .reveal {
    opacity: 0; transform: translateY(1rem);
    transition: opacity 0.55s cubic-bezier(0.16, 1, 0.3, 1),
                transform 0.55s cubic-bezier(0.16, 1, 0.3, 1);
  }
  .reveal-ready .reveal.is-visible { opacity: 1; transform: none; }
}

/* Shared image-hover zoom: the frame clips, the image scales a touch. Only when motion is
   welcome. Used by the on-location photos. */
.photo-frame { overflow: hidden; margin: 0; }
.photo-frame img { display: block; width: 100%; height: auto; }
@media (prefers-reduced-motion: no-preference) {
  .photo-frame img { transition: transform 0.6s ease; }
  .photo-frame:hover img { transform: scale(1.04); }
}

/* ---------- brand band: full-bleed on-location photo, one line over it ---------- */
.brand-band {
  position: relative; overflow: hidden;
  min-height: min(78vh, 660px);
  display: grid; align-items: end;
  /* --gutter-min on the left, as in the hero: the grown gutter pushed the text to 416px at
     1920, onto his jacket. Full-bleed, so the type hangs at the viewport edge. */
  padding: var(--s7) var(--gutter) var(--s7) var(--gutter-min);
}
.brand-band__img {
  position: absolute; inset: 0; z-index: -2;
  width: 100%; height: 100%; object-fit: cover;
  /* y: his hair sits ~23% down the frame. At 62% a 1920-wide band (685px of a 1280px-tall
     frame) cut the top of his head; 38% keeps the head in at 1920 and the cut below the
     hands at 1440.
     x: below ~1100px the text wraps longer, the band grows taller than the frame's 3:2 and
     the frame fills by height with spare width. Centred, that put his sleeve under the text
     at 861 (sleeve at 219px, text to 237px). 25% spends the spare width on the left and moves
     him right (sleeve at 301px at 861); at 1440 and 1920 the frame fills by width and the x
     value does nothing. */
  object-position: 25% 38%;
}
/* Legibility scrim: deep green, heavier on the left where the text sits (cream on the darkest
   stop clears AA). Uses the palette's authority colour, not a neutral black. The 40% stop is
   0.67, not 0.62: with the column widened toward him the gold headline reaches 30% of the
   width at 1920, where 0.62 measured 2.97. */
.brand-band__scrim {
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(90deg,
    rgba(13,53,37,0.85) 0%, rgba(13,53,37,0.67) 40%, rgba(13,53,37,0.12) 100%);
}
/* His sleeve is his leftmost point, at 32.5% of the frame's width (his face starts at 39%).
   Where the frame fills by width that is 32.5vw, so the column is capped there less 1.5rem of
   clearance; --measure only bounds it on very wide screens. Measured glyph right edge vs
   sleeve: 1920 589 / 624px, 1440 386 / 468, 1101 330 / 358, 990 296 / 331, 861 250 / 301
   (with the 25% x above). At the full Ken Burns zoom the sleeve comes in to ~597px at 1920
   and ~343px at 1101, still clear. */
.brand-band__text { color: var(--cream); max-width: min(var(--measure), calc(32.5vw - 1.5rem - var(--gutter-min))); }
.brand-band__text .eyebrow { color: var(--gold); }
.brand-band__text h2 { margin: var(--s2) 0 0; }
.brand-band__text p { margin: var(--s2) 0 0; font-size: var(--step-1); }
/* On a phone the frame crops to portrait, which pulls him into the text column - the 90deg
   left-weighted scrim above is built for a landscape crop and leaves the type on his white
   shirt and the white colonnade. Below 860 the gradient turns bottom-up and heavier, which
   is where the type sits. Measured worst case behind the headline with this scrim: 2.53
   gold / 5.29 cream at 390, 1.79 / 3.76 at 860 - gold stays under the 3.0 floor even with
   the 42% stop at 0.9 (2.89 / 1.94), so the headline goes cream here (see below). Desktop
   keeps the ranged-left scrim and gold (numbers at .brand-band__text h2).
   The 25% x of .brand-band__img pulls him right of the headline at 560-860 (centred, at 768
   it sat on his face) and still keeps his whole figure in the frame at 390. */
@media (max-width: 860px) {
  .brand-band__scrim {
    background: linear-gradient(0deg,
      rgba(13,53,37,0.94) 0%, rgba(13,53,37,0.82) 42%, rgba(13,53,37,0.34) 100%);
  }
  .brand-band__text { max-width: 30ch; }
}

/* Slow Ken Burns, motion-gated. Scales within overflow:hidden so no layout shift. */
@media (prefers-reduced-motion: no-preference) {
  .brand-band__img { animation: kenburns 26s ease-in-out infinite alternate; }
}
@keyframes kenburns { from { transform: scale(1); } to { transform: scale(1.08); } }

/* ---------- band-split: portrait beside the founder's bio ----------
   One person, so the portrait sits beside his bio rather than beside the section intro,
   which had left the bio alone in a narrow column with empty space to its right. Top-aligned:
   the bio is far taller than the photo, and centring it floated the photo mid-text. */
.band-split {
  display: grid; grid-template-columns: 38fr 62fr; gap: var(--s5);
  align-items: start; margin-top: var(--s6);
}
@media (max-width: 860px) {
  .band-split { grid-template-columns: 1fr; gap: var(--s3); }
}

/* "Online o en persona" is a part of the services module now, not a section of its own
   (client, 2026-08-27: dropping its eyebrow "kinda merges all as a Services module"). The
   child combinator keeps this off the six .area h3s nested deeper in the same section. The
   top margin does the work the band padding used to do when they were two sections. */
#servicos > h3 { margin: var(--s7) 0 var(--s2); text-wrap: balance; }

/* ---------- mode-photos: two on-location shots side by side (online / in person) ---------- */
/* A sangre (full-bleed): margin negativo del gutter para tocar los dos bordes de la
   pantalla, como .brand-band y el hero. Es lo que convierte estas dos fotos en una franja
   que corta la pagina, en vez de dos imagenes posadas sobre el hueso.
   Sin esto, #servicios (1711px) y #proceso (931px) suman 2642px de hueso seguidos - el
   doble que cualquier otro tramo de un mismo fondo en la pagina - y #proceso se lee como
   una tercera parte de #servicios en vez de como un bloque propio.
   gap: 0 a proposito: las dos fotos se tocan y leen como UNA franja. Con separacion
   vuelven a leerse como dos imagenes, que es justo lo que hay que evitar aqui. */
.mode-photos {
  display: grid; grid-template-columns: 1fr 1fr; gap: 0;
  margin-top: var(--s5); margin-inline: calc(var(--gutter) * -1);
}
/* the two sources differ slightly in ratio (16:9 online vs 3:2 office); a shared aspect-ratio
   box + object-fit cover keeps the pair the same height. 21:9 en vez de 3:2: a sangre, cada
   foto es el doble de ancha, y a 3:2 la franja se comia ~700px de alto. */
.mode-photos .photo-frame img { aspect-ratio: 21 / 9; object-fit: cover; }
/* The second frame has him high: centred, the 21:9 cut touched his hair at 1440 and 1920. */
.mode-photos .photo-frame:nth-child(2) img { object-position: center 25%; }
/* Apiladas, cada foto ocupa el ancho entero, asi que 21:9 recortaria las caras. 16:9 las
   deja respirar y las dos juntas siguen cabiendo en una pantalla de movil. */
@media (max-width: 860px) {
  .mode-photos { grid-template-columns: 1fr; }
  .mode-photos .photo-frame img { aspect-ratio: 16 / 9; }
}

/* ---------- reviews: real ProvenExpert quotes, editorial not carded ---------- */
.reviews-score { font-size: var(--step--1); opacity: 0.85; margin: 0 0 var(--s4); max-width: var(--measure); }
.reviews-score a { color: inherit; text-decoration-color: var(--gold); text-underline-offset: 0.2em; }

/* Marquee: two identical tracks sit side by side in a flex row; both translate -100% of
   their own width in sync, so when track 1 scrolls off the left, track 2 is exactly where
   it started - a seamless loop with no seam arithmetic. Spacing lives on .review's right
   margin (not a flex gap) so the wrap-around gap matches the inter-item gap. */
.marquee {
  display: flex; overflow: hidden;
  /* full-bleed: cancel the band's side padding so quotes scroll edge to edge */
  margin: 0 calc(-1 * var(--gutter));
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.marquee-track {
  display: flex; flex: 0 0 auto; margin: 0; padding: 0; list-style: none;
  animation: marquee-scroll 70s linear infinite;
}
.marquee:hover .marquee-track,
.marquee:focus-within .marquee-track { animation-play-state: paused; }
@keyframes marquee-scroll { to { transform: translateX(-100%); } }

.review {
  flex: 0 0 auto; width: 32ch; margin-right: var(--s5);
  border-top: 3px solid var(--gold); padding-top: var(--s2);
}
.review:first-child { margin-left: var(--gutter); }  /* first quote clears the mask edge */
.review blockquote { margin: 0 0 var(--s2); font-size: var(--step-0); }
.review cite { font-style: normal; font-size: var(--step--1); opacity: 0.7; }
.review cite b {
  font-family: "Anton", Impact, sans-serif; font-weight: 400;
  text-transform: uppercase; letter-spacing: 0.04em; opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  /* no motion: one track only, swipe/scroll horizontally instead. !important beats the
     global reduced-motion animation-duration override, which would otherwise snap the
     track to its -100% end state and hide it. */
  .marquee { overflow-x: auto; margin: 0 calc(-1 * var(--gutter)); -webkit-mask-image: none; mask-image: none; }
  .marquee-track { animation: none !important; transform: none !important; }
  .marquee-track[aria-hidden="true"] { display: none; }
  .review:first-child { margin-left: var(--gutter); }
}

/* ---------- footer: slim - brand left, one row of links right ---------- */
.site-footer {
  display: flex; justify-content: space-between; align-items: center;
  flex-wrap: wrap; gap: var(--s1) var(--s4);
  padding: var(--s4) var(--gutter);
  border-top: 1px solid rgba(245,240,232,0.2);
  font-size: var(--step--1);
}
.site-footer .footer-brand { font-weight: 700; font-size: var(--step-0); margin: 0; }
.footer-nav { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s4); }
.footer-nav a, .footer-copy {
  color: var(--cream);
  display: inline-flex; align-items: center; min-height: 44px;   /* touch target floor */
}
.footer-copy { opacity: 0.7; }
.footer-ig { min-width: 44px; justify-content: center; }
.footer-ig svg { width: 20px; height: 20px; }

/* ============================================================
   Polish pass (2026-07-30): typography, view transitions,
   hyphenation, drawn underline. No JS, no third-party.
   ============================================================ */

/* Balance headlines (no orphan words); let paragraphs avoid runt last lines.
   Unsupported browsers fall back to normal wrapping. */
h1, h2 { text-wrap: balance; }
p, .lede, .note { text-wrap: pretty; }

/* Hyphenate the narrow-measure columns so long Portuguese/German words don't rag.
   lang is set on <html lang="pt-BR"> and on .de spans, so dictionaries resolve.
   :not(.eyebrow) porque la linea de creditos del asesor tambien es un <p> dentro de
   .team, y ahi partia "SENIOR CONSUL-TANT". Un cargo no se parte: es un nombre, no prosa.
   Sin guion la linea rompe por el espacio y sigue cayendo en dos lineas igual. */
.figure-row p, .review blockquote, .team p:not(.eyebrow) { hyphens: auto; }

/* Cross-document view transitions: content crossfades on navigation; the header
   and footer are named so they persist (do not fade) across the transition.
   Chrome/Safari honor this; Firefox ignores it and navigates normally. */
@view-transition { navigation: auto; }

/* Motion opt-out: disable the crossfade under reduced-motion (web.dev pattern). */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}

.site-header { view-transition-name: site-header; }
.site-footer { view-transition-name: site-footer; }

/* Drawn underline: a gradient "rule" under editorial link titles that grows
   left-to-right on hover/focus, instead of snapping on. Gold, 2px. */
.articles .title, .article-index a h2 {
  background-image: linear-gradient(var(--gold), var(--gold));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 2px;
  transition: background-size 220ms ease;
}
.articles a:hover .title, .articles a:focus-visible .title,
.article-index a:hover h2, .article-index a:focus-visible h2 {
  background-size: 100% 2px;
}
@media (prefers-reduced-motion: reduce) {
  .articles .title, .article-index a h2 { transition: none; }
}

/* Staggered reveal: for these containers, the container itself does not fade;
   its direct children rise in sequence. Cancels the generic container hide from
   the ".reveal-ready .reveal" rule above for exactly these selectors. Motion-gated. */
@media (prefers-reduced-motion: no-preference) {
  .reveal-ready .figure-row.reveal,
  .reveal-ready .process.reveal,
  .reveal-ready .articles.reveal { opacity: 1; transform: none; }

  .reveal-ready .figure-row.reveal > *,
  .reveal-ready .process.reveal > li,
  .reveal-ready .articles.reveal > li {
    opacity: 0; transform: translateY(0.75rem);
    transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1),
                transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
  }
  .reveal-ready .figure-row.reveal.is-visible > *,
  .reveal-ready .process.reveal.is-visible > li,
  .reveal-ready .articles.reveal.is-visible > li { opacity: 1; transform: none; }

  /* Per-position delay. Containers here hold 2-5 children; cap at 320ms. */
  .reveal-ready .reveal.is-visible > *:nth-child(2) { transition-delay: 80ms; }
  .reveal-ready .reveal.is-visible > *:nth-child(3) { transition-delay: 160ms; }
  .reveal-ready .reveal.is-visible > *:nth-child(4) { transition-delay: 240ms; }
  .reveal-ready .reveal.is-visible > *:nth-child(5) { transition-delay: 320ms; }
}

/* ============================================================
   Landing page (2026-08-27): the site collapsed from five pages into one, so the
   grounds now have to do the work the page boundaries used to do. Cream is the
   reading ground and --green marks the moments that carry weight (film, riesgo,
   para-quien, resenas). No two reading bands of the same ground ever touch - the photo
   bands break the runs, and #servicios -> #proceso, the one place two cream bands meet,
   is cut by the full-bleed two-photo stripe that closes #servicios (see .mode-photos).
   Hubo un tercer fondo, --black, para #proceso; la clienta lo rechazo (no combinaba) y se
   retiro con /beta y /variantes. La paleta de fondos es hueso, --green y foto.
   ============================================================ */

/* Grounds swapped under blocks that were built for cream. Each of these is a colour
   swap only - no geometry changes, so the block reads identically on either ground. */
/* Oversized figures: --wine is the accent on cream (9.19) but fails on green (1.61) as
   text. Gold fails as text on green too (2.72), so the figure text goes cream (5.70) and
   gold keeps the border rule. */
.on-dark .figure-row > div { border-top-color: var(--gold); }
.on-dark .figure-row .figure { color: var(--cream); }
/* Hairline rules are ink-tinted by default and vanish on a dark ground. */
.on-dark .articles,
.on-dark .articles li { border-color: rgba(245,240,232,0.25); }

/* ---------- manifiesto: the opening block, centred, no photo ----------
   The client pointed at the "48 %" card and wrote "este estilo!" - that card is a wine
   rule over the type and nothing else. Here the rule sits over the closing claim, which
   is the line she wants set "tipo titulo, un poco mas grande" than an .eyebrow. */
.manifesto { text-align: center; }
.manifesto > .eyebrow { margin: 0 auto var(--s3); }
.manifesto > h2 { max-width: 24ch; margin: 0 auto var(--s4); }
.manifesto > p { max-width: 62ch; margin-inline: auto; }
/* Wider than the generic .lede's 40ch: centred, a 40ch column in the middle of a full
   band is what made this block read as empty. */
.manifesto > .lede { max-width: 56ch; }
/* This is the "mismo estilo que el 48%" the client asked for, transplanted from a number
   onto a sentence: wine hairline, oversized Anton, wine fill, short measure. --wine on cream
   is 9.19 - AAA at any size, though it is only ever used at display size here,
   exactly as .figure-row .figure already uses it. --step-2 floors at 28.5px; a 68-character
   sentence cannot take .figure's --step-4 without becoming a tower - and 34ch is the measure
   that lands it on four lines instead of six. */
/* On the green ground the whole 48% idiom has to change hand: --wine measures 1.61 on
   --green, and gold fails as text there too (2.72), so the text goes cream (5.70) and gold
   keeps the rule. Scoped to .on-dark so the cream version keeps the wine the client actually
   pointed at. */
.manifesto.on-dark .claim { color: var(--cream); border-top-color: var(--gold); }
.manifesto.on-dark .eyebrow { color: var(--cream); }

/* line-height 1.15, not the 1.05 the headings use: this line is set in Anton and forced
   to uppercase, and Anton's accented capitals are drawn far above its cap height - the
   tilde of N-tilde reaches 1.07em over the baseline and the acute of I/U/A 1.10em, while a
   flat cap stops at 0.86em. At 1.05 the accent therefore lands INSIDE the line above: on a
   phone the sentence wraps to three lines and the tilde of ACOMPANARTE touched the letters
   of ESTAMOS (reported 2026-09-02). 1.15 is the tightest value that clears every accented
   capital in the face, and it is still far tighter than the 1.6 body default. Do not lower
   it back to match the headings - Vidaloka's accents only reach 0.90em, so 1.05 is safe
   there and is not safe here. */
.manifesto .claim {
  font-family: "Anton", Impact, sans-serif;
  text-transform: uppercase; letter-spacing: 0.01em;
  font-size: var(--step-2); line-height: 1.15; color: var(--wine);
  max-width: 34ch; margin: var(--s6) auto var(--s5);
  border-top: 3px solid var(--wine); padding-top: var(--s3);
}

/* The client's docx asked for this block's type centred; centred on this frame it lands on
   the two of them, so it moved above their heads. Reverted on 2026-08-27 to the ranged-left
   treatment main still carries - "as it was" - which is the base .brand-band, so the whole
   --center modifier goes rather than being overridden. What does NOT revert: the eyebrow
   stays deleted and the body line stays rewritten, both explicit client edits nobody asked
   to undo. The gold headline is the third of those edits, so it needs its own rule now
   that it is no longer riding on the modifier. It is large text (3.0 floor). Worst case over
   the desktop scrim, taken as the minimum across the Ken Burns zoom at scale 1, 1.04 and 1.08
   (the image scales, the scrim does not): 3.23 at 1920, 3.31 at 1440, 3.36 at 1101, 3.42 at
   990 and 3.50 at 861. The cream paragraph under it stays at 6.45 or more (4.5 floor).
   Over the phone scrim gold cannot reach 3.0 (see .brand-band above), so there it is cream.
   Never on cream: 2.09. */
.brand-band__text h2 { color: var(--gold); }
@media (max-width: 860px) { .brand-band__text h2 { color: var(--cream); } }

/* ---------- service card icons: alternating green and wine ----------
   .card-icon is normally currentColor. These six are aria-hidden and every card repeats
   its label as text, so the mark is pure decoration - which is exactly the case WCAG
   1.4.11 exempts from the 3:1 non-text floor (both clear it regardless: green measures 5.70
   on cream, wine 9.19). Alternating the two gives the grid the two-colour rhythm she asked
   for without adding a third idea. */
.services .card-icon { color: var(--green); }
.services > li:nth-child(2n) .card-icon { color: var(--wine); }

/* ---------- team: credential block, as type ----------
   Same rule-over-type idiom as .bio, but no portrait. h3, not h2: these sit under the
   section's own h2, and --step-1-display keeps them above Vidaloka's 28px floor.
   One bio today (Julio's); the layout below was built for parallel bios and still holds
   for a single one. */
.team {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--s5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}
.team > li { border-top: 3px solid var(--gold); padding-top: var(--s2); }
.team h3 { margin: 0 0 var(--s1); }
.team .eyebrow { margin: 0 0 var(--s3); }
/* The cap goes on ALL paragraphs, not just the last. With a single sentence per bio it
   made no difference - it was the last paragraph and the only one. With full biographies,
   leaving it only on the last gave two paragraphs at the column's full width (~64ch) and
   the third at 40ch, with the right edge jumping. 40ch was sized for parallel bios; beside
   the portrait the single bio takes the site measure, or it strands a strip of the column. */
.team p:not(.eyebrow) { max-width: var(--measure); }
.team p:last-child { margin-bottom: 0; }
/* The credential always sits on its own line, regardless of width - the break comes from
   markup, not from the column being wide enough to wrap it there itself. Built when there
   were two credential lines of different lengths that had to break at the same point to
   read alike; the rule holds just as well for a single line. */
.team .eyebrow .cert { display: block; }

/* ---------- sticky header on small screens ----------
   A 6.25rem bar pinned to the top of a phone eats a tenth of the viewport for the whole
   scroll. Below 860 the logo and the padding both come down, to ~4.4rem. */
@media (max-width: 860px) {
  .site-header { padding: var(--s2) var(--gutter); }
  .site-header img { height: 2.5rem; }
}

/* ---------- 404: the dog from the Dresden shoot, under the way back ---------- */
.not-found-photo { max-width: min(100%, 22rem); margin-top: var(--s5); }
