/*
Theme Name:   DebAnanda — Hello Elementor Child
Theme URI:    https://lyvek.com
Description:  Child theme for DebAnanda. Holds the four things Elementor cannot own: the design
              tokens (single source of truth for recolouring), the skip link, the no-JS guard,
              and self-hosted typefaces. Everything here is tracked in git — never per-widget
              Custom CSS, which lives in the database and is neither reviewable nor reproducible.
Author:       Lyvek
Template:     hello-elementor
Version:      0.1.0
Text Domain:  debananda
*/

/* ─────────────────────────────────────────────────────────────────────────────
   DESIGN TOKENS

   Ported from assets/html/assets/css/styles.css :root, with two corrections that
   had to be made BEFORE the Elementor Kit import. A broken indirection imported
   into a Kit lives in the database, not in a file — it becomes permanent.
   ───────────────────────────────────────────────────────────────────────────── */

:root {
  /* Palette — derived from the @ananda.174 logo and photo set. Unchanged. */
  --cream:        #F1EAE0;
  --cream-deep:   #E9DFCF;
  --paper:        #FBF8F2;
  --bone:         #E3D5BE;

  --sage:         #87977A;
  --sage-soft:    #C6CFB7;
  --sage-deep:    #41543A;
  --sage-mist:    #ECEFE2;

  --clay:         #AC7A4C;
  --clay-soft:    #D9BC93;
  --clay-deep:    #7A5230;

  --ink:          #26201A;
  --ink-soft:     #4C4237;
  --ink-mute:     #695947;
  --ink-line:     rgba(38, 32, 26, 0.12);

  /* Semantic layer */
  --bg:           var(--cream);
  --bg-alt:       var(--paper);
  --bg-deep:      var(--ink);
  --bg-mist:      var(--sage-mist);
  --fg:           var(--ink);
  --fg-soft:      var(--ink-soft);
  --fg-mute:      var(--ink-mute);
  --rule:         var(--ink-line);
  --primary:      var(--sage-deep);

  /* CORRECTION 1 (F29) — --accent was --clay, which fails WCAG AA as text on every
     light surface: 3.12:1 on cream, 3.49:1 on paper, 2.57:1 on bone, 2.21:1 on
     sage-deep. --clay-deep clears AA normal text on all five: bone 4.72, cream-deep
     5.38, sage-mist 5.66, cream 5.71, paper 6.39. One swap closes 7 of 8 measured
     failures and the --bone band survives. --clay is kept below for decorative,
     non-text use only. */
  --accent:       var(--clay-deep);
  --accent-text:  var(--clay-deep);   /* anything that is read */
  --accent-deco:  var(--clay);        /* rules, dots, ornament — never text */

  /* CORRECTION 2 (F29) — --rule-strong measured 1.79:1, below 1.4.11's 3:1 for a
     form control border. The contact form does not exist yet, so this is fixed
     before it inherits the defect rather than after. */
  --rule-strong:  rgba(38, 32, 26, 0.55);

  /* Focus ring: --clay measured 2.57:1 on bone and 2.82:1 on cream-deep, under the
     3:1 that 1.4.11 requires. Prior analysis only ever checked it on --cream. */
  --focus-ring:   var(--clay-deep);

  --font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
  --font-body:    'Manrope', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
}

/* CORRECTION 3 (F28) — the reference declared --bg-alt / --bg-deep / --bg-mist and
   then referenced them ZERO times, using raw --paper (23x), --cream (15x) and --ink
   (13x) instead. DESIGN.md §1 and §9.2 state the opposite rule. The aliases above
   are now the ones components consume, so recolouring stays a single edit — which
   is the property the Kit import is supposed to preserve. */

/* ─────────────────────────────────────────────────────────────────────────────
   SKIP LINK — a theme concern. Verified: `grep -rl "skip-link"` returns 0 files in
   Elementor core and 0 in Elementor Pro. Nothing in the builder emits one, so if the
   theme does not, the site does not have one.
   ───────────────────────────────────────────────────────────────────────────── */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100000;
  padding: var(--space-3, 0.75rem) var(--space-5, 1.5rem);
  background: var(--sage-deep);
  color: var(--paper);
  font: 600 1rem/1.2 var(--font-body);
  text-decoration: none;
  border-radius: 0 0 6px 0;
}
.skip-link:focus { left: 0; outline: 3px solid var(--focus-ring); outline-offset: 2px; }

/* COLOUR CORRECTION, 2026-09-03 (B1) — MEASURED, and it was invisible.
   The Kit emits `.elementor-kit-5 a { color: var(--e-global-color-accent) }` (specificity
   0,2,0) which outranks `.skip-link` (0,1,0) above, so the skip link rendered --clay-deep
   #7A5230 on its own --sage-deep #41543A ground: a contrast ratio of 1.20:1. Not "low
   contrast" — unreadable, on the single control that exists for keyboard users, on every
   page. `a.skip-link:focus` is 0,2,1 and outranks the Kit without reaching for !important.
   Measured after the fix: --paper on --sage-deep = 7.7:1. */
a.skip-link,
a.skip-link:focus {
  color: var(--paper);
}

/* Visible focus everywhere. Elementor does not guarantee one and the reference's dead
   .field CSS actively set `outline: none`. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}

/* ─────────────────────────────────────────────────────────────────────────────
   NO-JS GUARD (F40) — Elementor sets `.elementor-invisible { visibility: hidden }`
   in frontend.css:184 and removes it ONLY from JavaScript (frontend.js:639,650).
   Core ships zero `no-js`/`noscript` escapes. So with JS off, every entrance-animated
   element is permanently INVISIBLE — not merely unanimated. The reference had the same
   defect via 26 `.reveal` elements at opacity:0; moving to Elementor reproduces it
   rather than fixing it.
   ───────────────────────────────────────────────────────────────────────────── */
.no-js .elementor-invisible { visibility: visible !important; }
.no-js .elementor-invisible[class*="animated"] { animation: none !important; }

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

/* Tap targets — the reference's burger measured 38 x 18px, failing WCAG 2.2 2.5.8
   (24 x 24) on height. Nothing interactive ships under 44px here. */
.elementor-widget-button .elementor-button,
.menu-toggle, .skip-link { min-height: 44px; }

/* ═════════════════════════════════════════════════════════════════════════════
   B1 · THEME BUILDER HEADER + FOOTER — custom CSS ledger

   Five rules. Every one is here because a native Elementor control cannot express
   it — not because it was quicker. Each is tracked in git rather than in
   `_elementor_data` or Kit Custom CSS, for the three reasons in
   docs/analysis/elementor-build-strategy.md §7 (not diffable, licence-gated,
   not reproducible on a clean install).

   Templates that consume these classes: tools/elementor/build-header-footer.php
   ═════════════════════════════════════════════════════════════════════════════ */

/* CSS-02 (extended) — the header ground.
   `backdrop-filter` exists ONLY in the atomic style schema
   (core/modules/atomic-widgets/styles/style-schema.php:312); the classic container
   exposes `css_filters`, which emits `filter:` — a different property with a
   different result. Verified: grep "backdrop-filter" over core/includes + core/core
   returns 0.

   The container therefore carries NO native background: one property, one owner, no
   specificity race against Elementor's 0,3,0 post CSS. The first declaration is the
   fallback for engines without color-mix() — they get the opaque token, which is the
   correct colour, just not translucent. */
.deb-header {
  background-color: var(--cream);
  background-color: color-mix(in srgb, var(--cream) 86%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  /* 280ms = DESIGN.md §7 --dur-base. Killed by the reduced-motion block above. */
  transition: background-color 280ms, box-shadow 280ms;
}

/* Sticky scrolled state. Elementor Pro's Sticky module adds
   `.elementor-sticky--effects` past the effects offset but exposes no style controls
   for that state, so there is no native path to a distinguishable scrolled header.
   COLOUR AND SHADOW ONLY — never padding, height or border-width — so the header box
   is byte-identical in both states and the transition contributes 0 to CLS. */
.deb-header.elementor-sticky--effects {
  background-color: var(--paper);
  background-color: color-mix(in srgb, var(--paper) 94%, transparent);
  box-shadow: 0 4px 14px -4px rgba(38, 32, 26, 0.08); /* DESIGN.md §6 --shadow */
}

/* Header brand hit area. MEASURED: the lockup link's box was 36x178px — over WCAG 2.2
   2.5.8's 24x24 floor, but under the 44px this project holds interactive controls to.
   Visual size and target size are allowed to differ, and this is the standard way to
   separate them: the logo keeps its designed 36px height, the link gets a 44px box.
   Elementor's Image widget has no padding control that reaches the <a> it generates. */
.deb-header .elementor-widget-image a,
.deb-footer-logo a {
  display: flex;
  align-items: center;
  min-height: 44px;
}

/* The desktop nav must never wrap to a second line.
   MEASURED: it did, and the header grew from 72px to 113px at 1440. Elementor gives every
   child container `--width: 100%`, so the three header columns each ask for the full row
   and flexbox shrinks them proportionally. A flex item cannot shrink below its own
   min-content — which is what holds the row together here — but `.elementor-nav-menu` is
   `flex-wrap: wrap`, so the menu's min-content is ONE LINK WIDE and the floor collapsed.
   Disabling the wrap restores the floor. There is no native control for it: the <ul> is
   inside the widget, not a container. */
.deb-header .elementor-nav-menu {
  flex-wrap: nowrap;
}

/* The open drawer must not depend on a keyframe having run.
   Elementor renders the off-canvas panel visible via an ANIMATION even when the entrance
   animation is set to "none": `.e-off-canvas__main.animated.none` gets
   `animation: e-off-canvas-without-animation 1ms`, whose 0% keyframe is
   `opacity: 0; visibility: hidden`. On any real device that 1ms elapses instantly. But the
   resting state of an OPEN dialog is then "invisible", and only the animation makes it
   appear — the same shape as F40 above, where `.elementor-invisible` is only ever undone by
   JavaScript. Measured: wherever the animation timeline does not advance, the drawer reports
   itself open, with focus trapped inside it, while rendering nothing.

   `!important` is deliberate and is the only mechanism that works: a running CSS animation
   outranks normal declarations but not important ones. Scoped to `aria-hidden="false"` so
   the closing state is untouched. */
.e-off-canvas[aria-hidden="false"] .e-off-canvas__main {
  opacity: 1 !important;
  visibility: visible !important;
}

/* `visibility` must never be TRANSITIONED inside the drawer.
   Elementor's buttons and menu items carry `transition: all`, which picks up `visibility`
   when the drawer opens. A transition that has not progressed holds its FROM value —
   `visibility: hidden` — and an element computing `visibility: hidden` cannot take focus:
   `.focus()` fails silently on it. Measured with `document.timeline.currentTime === 0` and a
   `CSSTransition` on `visibility` reported `running` at `currentTime: 0`: the drawer was
   open and correct, the close button measured 108x44, and focus could not be placed in it.

   And `!important` CANNOT rescue this, which is the part worth knowing. Per CSS Cascading 4
   the transition origin sits ABOVE important author declarations, while animations sit below
   them — so the same `visibility: visible !important` that successfully overrides the
   panel's keyframe animation above is powerless against a transition. The property has to
   leave the transition list instead. */
.e-off-canvas .e-off-canvas__content *,
.e-off-canvas .e-off-canvas__content *::before,
.e-off-canvas .e-off-canvas__content *::after {
  /* `!important` on transition-property, not on visibility. transition-property is an
     ordinary property, so important-author applies normally to it — whereas an in-flight
     transition's OUTPUT outranks important-author and cannot be overridden at all. Without
     it the nav items kept `transition: all` from Elementor's own 0,5,0 widget rule and stayed
     unfocusable; specificity arithmetic against generated selectors is not worth maintaining
     here. */
  transition-property: background-color, color, border-color, box-shadow, opacity, transform !important;
}


/* The `nav-cta` menu item, styled as the WhatsApp CTA without leaving the menu.
   The operator's instruction: the CTA is item 6 of `Principal` and carries the class
   `nav-cta`, so the site owner can retitle, relink or remove it from Appearance -> Menus.
   Elementor's nav-menu has no per-item styling, so the class is the only handle — which is
   exactly why the item was given one.

   NOTE the two-element hop: `elementor-nav-menu--main` sits on the <nav>, `elementor-nav-menu`
   on the <ul> inside it. Chaining them as `.elementor-nav-menu.elementor-nav-menu--main`
   matched nothing at all — measured, the pill silently stopped rendering. They must be written
   as ancestor + descendant.

   SPECIFICITY, measured rather than assumed. Elementor emits
   `.elementor-61 .elementor-element.elementor-element-hdnav .elementor-nav-menu--main .elementor-item`
   — five classes, 0,5,0. A first attempt at `.deb-header .elementor-nav-menu > li.nav-cta >
   a.elementor-item` is 0,4,2 and LOST: the pill got its --clay-deep ground while the label
   kept Elementor's --ink-soft, which measured 1.44:1. Chaining `.elementor-nav-menu` with
   `.elementor-nav-menu--main` brings these to five classes plus two elements (0,5,2), which
   wins on the class count without reaching for !important.
   Measured after the fix: --paper on --clay-deep = 6.44:1. */
.deb-header .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item,
.deb-cta-only .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item {
  background-color: var(--accent);
  color: var(--paper);
  fill: var(--paper);
  border-radius: 999px;
  padding-inline: 18px;
  min-height: 44px;
  margin-inline-start: 8px;
  transition: background-color 180ms; /* DESIGN.md §7 --dur-fast */
}
.deb-header .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item:hover,
.deb-header .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item:focus,
.deb-cta-only .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item:hover,
.deb-cta-only .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item:focus {
  background-color: var(--ink);
  color: var(--paper);
  fill: var(--paper);
}
/* The pointer underline belongs to navigation, not to a button. */
.deb-header .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item::before,
.deb-header .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item::after {
  content: none !important;
}
/* Inside the drawer the same item is a full-width pill on the sage ground. */
.e-off-canvas .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item {
  background-color: var(--primary);
  color: var(--paper);
  fill: var(--paper);
  border-radius: 999px;
  justify-content: center;
  min-height: 48px;
  margin-block-start: 14px;
}
.e-off-canvas .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item:hover,
.e-off-canvas .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item:focus {
  background-color: var(--ink);
  color: var(--paper);
}

/* Mobile CTA: the same `Principal` menu rendered a second time, showing only its `nav-cta`
   item. Elementor's nav-menu cannot be filtered to one item, and that is precisely why this
   is done in CSS rather than by hand-writing the link — the item's label, URL, target and
   existence stay in the CMS. Also drops the aria-hidden dropdown copy Elementor emits beside
   every nav-menu, so the CTA is not present twice in the DOM. */
.deb-cta-only .elementor-nav-menu > li:not(.nav-cta),
.deb-cta-only .elementor-nav-menu--dropdown,
.deb-cta-only .elementor-menu-toggle {
  display: none !important;
}
.deb-cta-only .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item {
  padding-inline: 15px;
  margin-inline-start: 0;
}

/* Footer nav links must not be distinguished by colour alone (WCAG 1.4.1). Elementor's
   nav-menu strips text-decoration and offers no control to put it back, so hover and
   keyboard focus get an underline in addition to the colour change. */
.deb-footer-nav .elementor-nav-menu > li > a.elementor-item:hover,
.deb-footer-nav .elementor-nav-menu > li > a.elementor-item:focus-visible {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Footer link tap targets. Elementor's Icon List separates items with margin, so each
   <a> is only as tall as its line box (~20px) — under WCAG 2.2 2.5.8's 24x24 and well
   under the 44px this build holds everything else to. `space_between` cannot fix it:
   it adds margin outside the link, not padding inside it. A vocabulary rule for any
   footer link column, not a per-element patch. */
.deb-footer-links .elementor-icon-list-item > a {
  min-height: 44px;
  display: flex;
  align-items: center;
}

/* Display face for footer display type (the CTA headline and the ornamental wordmark). Elementor's typography group can set
   the family, but writing "Fraunces" into `_elementor_data` hard-codes a brand decision
   into the database; `var(--font-display)` keeps it in the token block above, where a
   rebrand is one edit. Size, weight and tracking stay native controls. */
.deb-display {
  font-family: var(--font-display);
}

/* The giant footer wordmark is the ONE display-scale element that is NOT the display
   face. Measured on the reference: `.footer-giant` computes to Manrope, not Fraunces —
   a heavy geometric sans, which is what makes it read as a wordmark rather than as an
   oversized headline. An earlier pass here grouped it with `.deb-display` and put
   Fraunces on it; that was a divergence from the reference, not a decision. */
/* `body` prefix on purpose. The Kit emits
   `.elementor-widget-heading .elementor-heading-title { font-family: <primary> }`
   at (0,2,0); a bare `.deb-giant` is (0,1,0) and silently loses — the same
   specificity collision that once let the Kit override the skip link. Measured:
   the wordmark computed to Fraunces while this file said otherwise. */
body .deb-giant .elementor-heading-title {
  /* La DISPLAY, no la body. La referencia usa Manrope aquí y así estuvo, pero el operador
     pidió uniformidad con el logo — cuyo lettering es la serif — y esa razón manda sobre
     la fidelidad al HTML de referencia: el wordmark del pie y el de la cabecera son la
     misma marca vista dos veces.
     CONSECUENCIA MEDIBLE: la constante de llenado de abajo se calculó PARA MANROPE. Al
     cambiar de familia deja de valer y hay que volver a medirla; se hizo. */
  font-family: var(--font-display);
}

/* The footer logo is the same lockup as the header's, on --ink instead of --cream. The
   SVG carries color="#AC7A4C" on its root, which `currentColor` then resolves against;
   as an <img> it cannot inherit the surrounding colour, so the clay ink stays as the
   client drew it. Recorded, not silently changed — see F56 in docs/PLAN.md. */
.deb-footer-logo img {
  /* no colour override — F56 is the client's decision, not ours */
  display: block;
}

/* ═════════════════════════════════════════════════════════════════════════════
   B2 · HOME PAGE — custom CSS ledger

   Three rules, and the count is the point: everything else on the home page is a
   native Elementor container/widget setting, visible and editable in the builder.
   Each rule below exists because a native control cannot reach INSIDE a string —
   Elementor styles a whole widget, never a span within it — and the reference's
   design line puts two recolours and one sub-scale inside running text.

   Consumed by: docs/build/b2-home-build.php (the page is rebuildable from that file)
   ═════════════════════════════════════════════════════════════════════════════ */

/* CSS-B2-01 — the accent recolour inside a heading.
   `.hero-headline em` and `.text-clay` in the reference. The Heading widget has one
   colour control for the whole string, so the two-tone headline ("Cuatro pilares,
   un mismo camino.") is not expressible natively. The alternative — TinyMCE's colour
   picker — emits a hard-coded hex into post content, which is worse on every axis.

   Reads the KIT variable first so the client changing Accent in the builder UI still
   works, and falls back to the git token if the Kit is ever rebuilt without it.
   Both resolve to #7A5230 clay-deep, never #AC7A4C: F29/IMP-01. */
.deb-accent {
  color: var(--e-global-color-accent, var(--accent));
  font-style: normal;          /* the <em> is a recolour, not an italic */
}

/* CSS-B2-02 — the qualifier inside a price slot.
   "Grupal · presencial", "75 min". The reference used <sub>, whose UA default is
   vertical-align: sub + font-size: smaller — a subscript, which is wrong here.
   currentColor + opacity rather than a fixed colour so the one rule is correct on
   BOTH the paper cards and the ink card, instead of needing a dark variant.
   MEASURED on the built page, opacity folded into the ratio: 6.19:1 on the paper cards,
   7.62:1 on the ink card. A fixed --fg-mute would not have cleared AA on the dark one.

   The size is clamped, not a bare `em`: this qualifier appears in two places whose parents
   are 48px and 16px, and a plain 0.42em rendered the hero card's "min" at 6.72px — smaller
   than the 10px functional text IMP-25 exists to retire. The floor is --text-xs (12px), the
   ceiling 18px, which is where the reference's own --text-base lands. */
.deb-sub {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: clamp(0.75rem, 0.38em, 1.125rem);
  letter-spacing: 0;
  vertical-align: baseline;
  color: currentColor;
  opacity: 0.72;
  /* Wraps as a UNIT, but can still wrap internally rather than overflow. Without any of this,
     "A demanda · en tu empresa" broke as "A demanda · en tu" / "empresa" — an orphan that also
     made the third plan card's price slot two lines tall while its neighbours were one.

     This was `white-space: nowrap` for one build, and that was a defect: nowrap makes the box
     un-shrinkable, so at the narrowest 3-column desktop width the qualifier pushed past the card.
     MEASURED by B1: 6px of horizontal page overflow between 1025 and 1028px, right edge 1034.1
     in a 1028 viewport — the only overflow left on the site. 1025 is the first pixel after
     Elementor's tablet breakpoint, where the plans grid goes to 3 columns and the card content
     box is at its narrowest (~238px) while the price is still scaling up with the viewport.

     `inline-block` keeps the orphan fix — the qualifier moves to the next line whole rather than
     splitting — while leaving internal wrapping available as the fallback instead of overflow. */
  display: inline-block;
}

/* CSS-B2-03 — list markers inside the plans' feature lists.
   The lists are Text Editor widgets holding a real <ul>, which is the semantic and
   client-editable choice; the widget has typography and colour controls but no
   list-marker control. Hello Elementor ships no <ul> styling either, so without this
   the markers are UA discs at UA indent. Tokens only. */
.elementor-widget-text-editor ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.elementor-widget-text-editor ul li {
  position: relative;
  padding-left: 1.25rem;
}
.elementor-widget-text-editor ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: currentColor;
  opacity: 0.55;
}

/* ─────────────────────────────────────────────────────────────────────────────
   HOVER LEGIBILITY — outline buttons.  WITHDRAWN 2026-09-03 (C), and the
   withdrawal is the finding.

   What stood here was a hand-kept list of five element IDs on post 33, forcing
   `color: var(--e-global-color-paper)` on their :hover. It was correct about the
   symptom — five home buttons went to an ink background on hover and kept ink text,
   1.00:1 — and wrong about the cause, so it fixed exactly those five.

   The cause is a key that does not exist. Elementor's Button widget names its hover
   text-colour control `hover_color`
       includes/widgets/traits/button-trait.php:395
       '{{WRAPPER}} .elementor-button:hover, {{WRAPPER}} .elementor-button:focus' => 'color: {{VALUE}};'
   and every button produced by the PAGE build scripts set `button_hover_color`
   instead. Elementor stores an unknown key silently and emits nothing for it, so
   14 buttons across 5 posts changed their hover background and never their text.
   The header and footer templates used the real key and were always right — which is
   why this survived review: the two surfaces everyone checks were fine.

   MEASURED, /contacto/ before the correction: two ghost buttons at 13.49:1 at rest
   and **1.00:1** under the pointer. Fixed at source in docs/build/c-refinement.php;
   after it, Elementor emits the `color` rule itself and also an SVG `fill` rule the
   ID list never had. Re-measured across 5 pages x 4 states: 0 failures.

   NOTE for whoever adds the next button. There is no CSS safety net here and there
   cannot be one: CSS cannot ask whether a hover background is dark. The net is the
   audit — a button that sets `button_background_hover_color` must also set
   `hover_color`, and the query that proves it is in docs/build/C-refinamiento.md §4.
   A comment that used to sit here claimed a net existed; the rule under it only set a
   transition. Do not restore that claim.
   ───────────────────────────────────────────────────────────────────────────── */
body .elementor-button:hover { transition: color .18s ease, background-color .18s ease, border-color .18s ease; }

/* ─────────────────────────────────────────────────────────────────────────────
   PILL VERTICAL CENTERING
   Reportado con capturas: el texto de los tags ("Yoga", "1 a 1", "Empresas",
   "Acompañamiento", "Individual") queda alto dentro de la píldora.

   Causa medida: Elementor emite el wrapper con `padding: 5px 12px` y fondo, pero
   SIN `display` ni `align-items`. Un padding simétrico no centra ópticamente —
   la posición del texto la fija la caja de línea y la métrica de la fuente, y
   con line-height ajustado los glifos se apoyan arriba.

   La referencia lo resuelve así (styles.css:336-338):
       .pill { display: inline-flex; align-items: center; gap: var(--space-2); }
   Esto replica exactamente eso. No es una corrección de padding a ojo.
   ───────────────────────────────────────────────────────────────────────────── */
body .elementor-widget.deb-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
/* La causa REAL, medida — no era la métrica de la fuente.
   El widget no es un heading: es un <p>, y arrastra el margin-bottom por defecto.
       píldora 43.2px · padding 5/5 · flex + center
       hijo <p> 16.8px · margin-bottom 14.4px
   Al centrar en flex el ítem mide 16.8+14.4 = 31.2px, así que el TEXTO queda alto
   por la mitad del margen: 14.4/2 = 7.2px. El desvío medido era 7.7px.
   Anular el margen es el arreglo; tocar line-height habría sido adivinar. */
body .elementor-widget.deb-pill > .elementor-widget-container,
body .elementor-widget.deb-pill .elementor-widget-container > *,
body .elementor-widget.deb-pill p,
body .elementor-widget.deb-pill .elementor-heading-title {
  margin: 0;
  display: inline-flex;
  align-items: center;
}

/* ═════════════════════════════════════════════════════════════════════════════
   D3 · CONTACT PAGE — custom CSS ledger

   ONE rule. Everything else on /contacto/ is a native Elementor container or
   widget setting, visible and editable in the builder: the eyebrow pill, the
   card grids, the DOM reversal, the sentence-case reassurance line, the card
   head, the section rhythm.

   Consumed by: docs/build/build-contacto.php (the page is rebuildable from that
   file; the database is its output, not its authority).
   ═════════════════════════════════════════════════════════════════════════════ */

/* CSS-D3-01 — the underline on inline links inside running text.

   MEASURED, and it is a site-wide defect that this page is simply the first to
   need fixed. Two stylesheets combine into it:

     hello-elementor/assets/css/reset.css   a { text-decoration: none }
     Kit post-5.css                         .elementor-kit-5 a { color: accent }

   So an inline link inside a paragraph is distinguished from the surrounding
   copy by COLOUR ALONE, and the colour difference is
       accent  #7A5230  vs  ink-soft #4C4237  =  1.44:1
   against the 3:1 that WCAG 1.4.1's colour-only technique requires. Not close.

   The reference had the underline — set inline on all five of these links
   (`style="color:var(--accent); text-decoration:underline"`). Restoring it is
   fidelity, not a redesign.

   Only the decoration is declared here. The Kit already supplies the right
   colours — accent at rest (6.44:1 on paper, 4.72:1 on bone) and primary
   #41543A on hover (7.76:1 / 5.69:1) — and `.elementor-kit-5 a` is (0,2,0),
   which a `.deb-prose a` rule at (0,1,1) would lose to anyway. Declaring a
   colour we cannot win with would be theatre; `text-decoration` is undeclared
   by the Kit, so (0,1,1) beats reset.css's (0,0,1) and this rule takes effect.

   SCOPED, deliberately. An unscoped `a` rule would change the home page and the
   B1 footer, which are not this build's to touch. The consequence — that every
   other inline link on the site is still colour-only — is reported, not fixed
   here.

   Applied by build-contacto.php via `_css_classes` on the CON-02 channel list
   and the three CON-05 card bodies. */
.deb-prose a {
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

/* CSS-D3-02 — an inline link inside an Icon List row must stay inline.

   MEASURED: the four channel rows in CON-02 rendered 45px tall (two lines) with the
   link on its own line, instead of 22px with "WhatsApp: Escríbeme por WhatsApp" on
   one. Cause, read out of the cascade rather than guessed —
       widget-icon-list.min.css
       .elementor-widget .elementor-icon-list-item,
       .elementor-widget .elementor-icon-list-item a { display: flex }
   That selector is written for the widget's own "link the whole row" mode and catches
   ANY <a> inside a row, including one sitting in the middle of a sentence.

   The Icon List is still the right widget here — it emits a real <ul>/<li>, passes item
   text through wp_kses_post so the <strong> labels survive, and is not hit by CSS-B2-03's
   bullet the way a Text Editor <ul> would be. Only this one inherited rule has to go.

   Specificity is deliberate: Elementor's rule is (0,2,1). Chaining the opt-in class onto
   Elementor's own widget class gives (0,3,1), which wins on class count rather than on
   source order, so a future reorder of this file cannot silently resurrect the break. */
.elementor-widget.deb-prose .elementor-icon-list-text a {
  display: inline;
}

/* COLLISION, found by measuring rather than by reading (2026-09-03, D3).
   `CSS-C2-02` further down this file declares `.deb-prose a` again — the same class
   name, from the same measurement, reached independently by the agent building
   /sobre-debananda/. Two consequences, one of them a live bug:

     1. Their block is LATER and uses the `text-decoration` SHORTHAND, which resets
        `text-decoration-thickness` to `auto`. This rule originally set
        `text-decoration-thickness: 1px` at rest and `2px` on hover; computed style
        on the built page came back `auto`, so BOTH declarations were dead. They are
        removed rather than left in the ledger looking effective.
     2. `text-underline-offset` is theirs, not this one's: 3px wins over 0.18em
        (2.5px at 14px). Visually indistinguishable, so it is not worth fighting.

   `.deb-prose` is now a shared site-wide utility that two pages declare separately.
   That is a ledger defect and it needs ONE owner and ONE declaration — merged
   deliberately, not by whichever agent edits last. Reported, not resolved here:
   deleting another build's lines while it may still be running is a lost-update
   race, and the duplicate is inert, not harmful. */


/* ═════════════════════════════════════════════════════════════════════════════
   B4 · SOBRE DEBANANDA — custom CSS ledger

   Three rules. Each exists because a native Elementor control cannot express it,
   and each is tracked in git rather than in `_elementor_data` or Kit Custom CSS —
   see docs/analysis/elementor-build-strategy.md §7 (not diffable, licence-gated,
   not reproducible on a clean install).

   Consumed by: docs/build/b4-sobre-build.php
   ═════════════════════════════════════════════════════════════════════════════ */

/* CSS-B4-01 — the 21:9 photo band (`.photo-panel`, styles.css:735-742).
   `.photo-panel` is a component DEFINED by its aspect ratio, and Elementor exposes no
   aspect-ratio control on a container (component-inventory.md §352). Without this a
   4:5 source renders as a 1248x1560 full-bleed portrait instead of a band.

   The ratio goes on the IMG, not the container: `height: 100%` on the image would have
   to resolve through `.elementor-widget-image > .elementor-widget-container`, neither of
   which has a resolved height, so the percentage would compute to auto. Sizing the image
   directly needs no height chain at all.

   Specificity: Elementor's frontend.css sets `.elementor img { height: auto }` at (0,1,1);
   this is (0,2,1) and wins regardless of stylesheet order. */
.deb-ratio-21x9 .elementor-widget-image img {
  width: 100%;
  height: auto;
  aspect-ratio: 21 / 9;
  object-fit: cover;
}

/* CSS-B4-02 — inline prose links must not be distinguished by colour alone. WCAG 1.4.1.
   The Kit binds `link_normal_color -> accent` (#7A5230) and hello-elementor's reset.css
   sets `a { text-decoration: none }`. `theme.css` restores an underline only under
   `.page-content` / `.comments-area`, which the `elementor_header_footer` template never
   emits — so a link inside running text arrives coloured and unmarked.
   MEASURED at rest: accent on `paper` = 6.39:1, on the current white ground 6.82:1.
   Scoped to prose widgets so buttons, nav menus and icon lists are untouched.
   Also closes IMP-11 / R-08 for any future prose link anywhere on the site. */
.elementor-widget-text-editor a {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}

/* CSS-B4-03 — uppercase on the mono caps (`.mono-cap`, `.specialty`, `.duration`).

   MEASURED on the shipped home page, 2026-09-03: binding a typography group to a Kit
   global makes Elementor emit `var(--e-global-typography-<id>-<prop>)` for the properties
   the global defines and DROP every local value in the same group. The Kit's `accent`
   typography sets no text-transform, so `typography_text_transform => 'uppercase'` written
   next to the binding is silently discarded. Verified in post-33.css: fourteen mono labels
   bound to the global, zero `text-transform` among them — the home page's eyebrows and
   facet specialty lines are rendering sentence case where the reference is uppercase.

   Two ways out. Set the mono type locally on every label — which hard-codes "JetBrains
   Mono" into `_elementor_data` and makes a Kit type change stop propagating. Or keep the
   Kit as the single owner of the type and add the one property it cannot carry. This is
   the second. The home page can be brought back to the reference by adding the same class
   to `eyebrow()` in docs/build/b2-home-build.php — no font values duplicated.

   Better still, and out of scope here: add `text-transform: uppercase` to the Kit's
   `accent` typography global, after which this rule and the class both come out. */
.deb-eyebrow .elementor-heading-title {
  text-transform: uppercase;
}

/* ═════════════════════════════════════════════════════════════════════════════
   C2 · CLASSES PAGE (/clases/, page #35) — custom CSS ledger

   Two rules. Each exists because a native Elementor control cannot express it —
   not because it was quicker. Both are tracked in git rather than in
   `_elementor_data` or Kit Custom CSS (not diffable, licence-gated, not
   reproducible on a clean install).

   Consumed by: docs/build/build-clases.php
   ═════════════════════════════════════════════════════════════════════════════ */

/* CSS-C2-01 — the six service cards' meta list has NO marker.
   CSS-B2-03 above is GLOBAL: `.elementor-widget-text-editor ul li::before` gives every
   Text Editor <ul> on the site a 6px dot at 1.25rem indent, which is right for the home
   page's feature lists and wrong here — the reference's meta rows are
   `list-style:none; padding:0` with no marker at all (classes.html:78, 98, 118, 138,
   158, 178).

   An opt-out class is the smallest change that leaves B2's rule untouched. The
   alternative — three separate Text Editor widgets per card — is native and needs no
   CSS, but costs 18 extra widgets and throws away the list semantics a screen reader
   uses to announce "list, 3 items".

   Specificity is deliberate, not incidental: B2's rule is (0,1,3). Chaining the opt-out
   class onto Elementor's own widget class gives (0,2,3), which wins on class count
   rather than on source order — so a future reorder of this file cannot silently
   resurrect the dots. */
.elementor-widget-text-editor.deb-meta ul {
  display: grid;
  gap: 8px;
}
.elementor-widget-text-editor.deb-meta ul li {
  padding-left: 0;
}
.elementor-widget-text-editor.deb-meta ul li::before {
  content: none;
}

/* CSS-C2-02 — links inside running prose must not be distinguished by colour alone.
   The Kit already emits `.elementor-kit-5 a { color: var(--e-global-color-accent) }`
   = #7A5230 clay-deep, which measures 6.44:1 on --paper and 5.71:1 on --cream, so the
   contrast half is solved. The decoration half is not: hello-elementor's reset sets
   `a { text-decoration: none }`, leaving colour as the ONLY signal — a WCAG 1.4.1
   failure on the FAQ's three inline links.

   The Text Editor widget's typography controls style the whole widget and never reach
   an <a> inside its string, which is the same class of reason CSS-B2-01 exists for. A
   global `a { text-decoration: underline }` would underline the nav, the footer and
   every button, so the rule is scoped to a class the build applies deliberately. */
.deb-prose a {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ═════════════════════════════════════════════════════════════════════════════
   B3 · SCHEDULE PAGE (/horarios/) — custom CSS ledger

   ONE rule, and the second is recorded as withdrawn rather than deleted, because
   why it was not needed is worth more than the rule would have been.

   PREFIXED `CSS-SCH-`, not `CSS-B3-`: a concurrent build claimed CSS-B3-01..03 further
   down this file for the footer wordmark. Two rules answering to one id in a shared
   ledger is how a ledger stops being one, so these are renamed to the surface they
   serve rather than to a phase letter.

   The one that ships exists because a native Elementor control cannot express it —
   not because it was quicker — and is tracked in git rather than in
   `_elementor_data` or Kit Custom CSS.

   Consumed by: docs/build/build-horarios.php (the page is rebuildable from it)
   ═════════════════════════════════════════════════════════════════════════════ */

/* CSS-SCH-01 — uppercase for mono labels bound to the Kit's Eyebrow typography.

   MEASURED THIS SESSION, on the home page, and it is a live defect there too:
   a local `typography_*` sub-control set ALONGSIDE a global typography is
   SILENTLY DROPPED by Elementor's CSS generator. Element 64935af asks for
   `typography_text_transform: uppercase`; post-33.css emits only the five
   `--e-global-typography-accent-*` custom properties and no text-transform, so
   every eyebrow on the home renders in sentence case rather than caps.

   The alternatives were both worse. Setting typography wholly locally would
   hard-code "JetBrains Mono" into `_elementor_data` on eight more widgets, so a
   rebrand would stop being one Kit edit. Uppercasing the strings in content is
   wrong at the source: the day comes from the `dia` taxonomy, whose term name is
   "Lunes", not "LUNES".

   So the face, weight, size, line-height and tracking stay bound to the Kit's
   Eyebrow slot (JetBrains Mono 500 / 0.75rem / 1.4em / 1.2px) and only the one
   property Elementor drops lives here. The Kit's 1.2px at 12px is 0.1em, against
   the reference's 0.08em on `.level` and 0.06em on `.duration` — a difference of
   0.24-0.48px per character, deliberately not overridden so the whole ramp stays
   on one token. */
.deb-mono-cap .elementor-heading-title {
  text-transform: uppercase;
}

/* CSS-SCH-02 — WITHDRAWN, and the withdrawal is the finding.

   SCH-03's WhatsApp link needs a persistent underline (WCAG 1.4.1: `schedule.html:98`
   has no class, no colour and no underline, and the Kit emits `.elementor-kit-5 a`
   with a colour and NO text-decoration, so colour alone was the only signal at
   accent-on-sagemist 5.85:1 — the contrast passes, 1.4.1 does not).

   It is already covered. VERIFIED in the browser on /horarios/: the callout link
   computes `text-decoration-line: underline` from CSS-B4-02's
   `.elementor-widget-text-editor a` above. A B3-specific rule would have been the
   FOURTH rule in this file for one property — CSS-B4-02 plus two separate `.deb-prose a`
   blocks from the Contacto and Clases builds — so it is not added.

   THE REAL DEFECT IS THE KIT, and it should be fixed once rather than patched per page:
       Kit #5 -> Typography -> Link -> Text Decoration: underline
   That closes IMP-11 / R-08 site-wide and retires all three surviving rules. The Kit is
   outside this phase's scope, so this is reported, not done.

   One consequence to hand to whoever owns CSS-B4-02: it is UNSCOPED, so it also
   underlines the LYVEK credit in B1's footer on all five pages. Measured, not changed —
   the footer is not this build's to touch. */

/* CSS-B4-04 — the display measure (`.page-header h1 { max-width: 18ch }`, styles.css:829).

   Elementor's widget Width control emits its value on the WIDGET WRAPPER, and the Heading
   widget applies its typography one level deeper (`{{WRAPPER}} .elementor-heading-title`).
   So the wrapper keeps the UA font — 16px system — and a font-relative unit written into that
   control silently measures against the wrong typeface at the wrong size.

   MEASURED at 375px, both stylesheets side by side:
     reference  `max-width: 18ch` on the h1, Fraunces 48px  ->  573px cap, 3 lines, 150px tall
     Elementor  `width: min(100%, 18ch)` on the wrapper     ->  160px cap, 5 lines, 250px tall
   A 100px regression, from a control that reads as if it says the same thing.

   The Text Editor widget does NOT share the defect — its typography selector IS the wrapper —
   which is why STU-01's lead keeps the native `min(100%, 56ch)` control and measures 615px
   against the reference's 614.88px. Native where native is correct; this rule only where it
   is not. */
.deb-measure-18ch .elementor-heading-title {
  max-width: 18ch;
}

/* CSS-C2-03 — the FAQ disclosure is ONE card, not a header box plus a body box.
   MEASURED, with a screenshot: Elementor's Nested Accordion is architecturally two boxes.
   `widget-nested-accordion.min.css` puts border-radius and border-width on
   `.e-n-accordion-item-title` alone, hands the answer its own `> .e-con` with the same border
   variables and no radius, and separates them with
   `[open] > .e-n-accordion-item-title { margin-block-end: var(--n-accordion-item-title-distance-from-content) }`.
   Opened, that rendered a pill-rounded question floating above a square-cornered answer wider
   than it — the answer detached from the question it belongs to. The reference is one card:
   `.faq details { background: paper; border: 1px solid rule; border-radius: 28px }`.

   Three of the four halves ARE native controls and are set in the build file (content
   background, content border, content radius `0 0 28 28`, distance 0). The fourth is not
   expressible: `accordion_border_radius` is a SINGLE global control, not per-state, so a
   header whose bottom corners square off only when open cannot be built in the panel —
   setting `28 28 0 0` there would square the bottom of all six CLOSED items too.

   The open header's bottom BORDER is not fought here — `accordion_border_active_width` is a
   native per-state control and is set to `1 1 0 1` in the build file. Measured first the other
   way round: Elementor emits that width as a direct declaration at specificity (0,7,0), which a
   class-only selector cannot outrank without writing the generated element id into this
   stylesheet. The radius is different — it is emitted as `--n-accordion-border-radius` and
   consumed by a (0,1,0) rule, so a class selector wins cleanly and no id appears below.

   The second rule carries the reference's `details[open] { border-color: var(--accent) }`
   across the joint: the native active-state control reaches the header only, so without it the
   open card would change colour on three sides and not on the other three. Elementor's own
   content rule is wrapped in `:where()`, i.e. specificity zero, so this wins without a fight. */
.deb-faq .e-n-accordion-item[open] > .e-n-accordion-item-title {
  border-end-end-radius: 0;
  border-end-start-radius: 0;
}
.deb-faq .e-n-accordion-item[open] > .e-con {
  border-color: var(--e-global-color-accent, var(--accent));
}

/* ═════════════════════════════════════════════════════════════════════════════
   B3 · FOOTER — ornamental sunburst + full-bleed wordmark

   Four rules. Each exists because a native Elementor control cannot reach the
   property, not because the builder was inconvenient:

     1. `container-type` — Elementor has no layout-containment control at all.
     2. The sun — a decorative pseudo-element; adding it as a widget would put an
        ornament in the DOM and in the accessibility tree for no reason.
     3. `font-size` as a FILL COMPUTATION — the builder's size control takes a
        value, and what this needs is "however large makes nine glyphs span the
        container exactly". That is a different kind of thing.
     4. The per-letter stagger — styles a span the builder never created.

   Measured against the reference before writing any of it: the static site's
   wordmark fills only 66.7% of its container at 2560px (880px of 1248), because
   clamp() caps it at 168px. Full-bleed is therefore an IMPROVEMENT on the
   reference, not a copy of it — recorded so nobody later "fixes" it back.
   ═════════════════════════════════════════════════════════════════════════════ */

.deb-giantw {
  position: relative;
  overflow: hidden;            /* crops the sun, and the letters as they rise */
  container-type: inline-size; /* 100cqw now means "the width of this container" */
}

/* CSS-B3-01 — the sun from the brand mark, rising behind the whole footer.
   Delivered as a MASK rather than a coloured image so the clay is read from the
   Elementor Kit at use site: the client changing Accent in the builder moves this
   ornament too, and no brand hex is baked into an asset file. The git token is the
   fallback only if the Kit is ever rebuilt without that global.

   ON ::after, NOT ::before. Elementor already owns `.e-con::before` for its container
   background overlay, and that rule carries its own width/height/inset. A first pass
   here used ::before and the sun rendered 680x212 instead of 680x680, because an
   explicit height beats aspect-ratio and Elementor's was the only one declared.
   `.e-con::after` is unused — checked across every loaded sheet — so it is free.

   POSITIONING. Anchored to the TOP-RIGHT corner of the FULL-WIDTH footer, and bled off
   both edges by a fraction of its OWN size, so it reads as an ornament running off the
   page rather than a sticker placed on it. The offsets are percentages on `transform`,
   which resolve against the element and not the container, so the crop keeps its
   proportion at every viewport instead of drifting with the footer's width.

   Anchored to the full-width footer deliberately, not to the boxed content: on a wide
   viewport most of it lands in the margin beside the CTA card, and on a narrow one the
   card covers the centre while the corner still reads. At z-index -1 the CTA card always
   wins where the two meet, so the ornament never competes with the content.

   OPACITY IS A MEASURED CHOICE, not a taste call, and the measurement moved with the
   ornament. In the top-right corner the sun no longer passes behind the link columns at
   all; the only text it crosses is the marquee rail, and the CTA card carries its own
   opaque gradient so a z-index:-1 ornament never shows through it.

   Worst case is the ornament read as a solid clay fill under that rail (it is thin
   strokes, so this over-states it). Rail text rgb(198,207,183), 12px:
       on --ink ......... 9.98:1
       under the sun 0.20 . 7.62:1   <- past AA (4.5:1) and past AAA (7:1)
       under the sun 0.26 . 7.00:1
   Overlap is real at every ordinary width, so this had to be checked rather than
   assumed: the sun's left edge lands at 1286px against content ending at 1560 (1920px
   viewport), 806 vs 1320 (1440), 646 vs 1240 (1280), 373 vs 744 (768). Only the widest
   viewports clear it entirely. */
.deb-footer {
  isolation: isolate;   /* keeps the z-index:-1 ornament inside this footer */
  /* `clip`, NOT `hidden`. `overflow: hidden` makes the box programmatically
     scrollable, and the sun's lower half — 484px of absolutely positioned overflow —
     becomes scrollable content inside the footer. Measured consequence: any
     scrollIntoView (or a keyboard focus landing on a footer link) scrolls that inner
     box, and the whole footer content jumped up 356px, putting the fine rail at 727px
     instead of 1083. `overflow: clip` crops identically and creates no scroll
     container: verified inner offset 0, and f.scrollTop = 999 stayed at 0. */
  overflow: clip;
}

.deb-footer::after {
  content: "";
  position: absolute;
  z-index: -1;          /* above the footer background, below every child */
  top: 0;
  right: 0;
  bottom: auto;
  left: auto;
  /* width AND height both declared: an explicit height beats aspect-ratio, and
     Elementor declares one on its own container pseudo-element. */
  width: min(117vw, 1440px);
  height: min(117vw, 1440px);
  transform: translate(34%, -40%);
  background-color: var(--e-global-color-accent, #AC7A4C);
  -webkit-mask: url("assets/img/sun-ornament.svg") center / contain no-repeat;
          mask: url("assets/img/sun-ornament.svg") center / contain no-repeat;
  opacity: 0.20;
  pointer-events: none;
}

/* CSS-B3-02 — the wordmark, sized to fill.
   The ratio is measured, not guessed: at any font-size, the rendered advance width
   of "DebAnanda" in Fraunces 700 at -0.05em tracking is 4.4822x that font-size. It was
   5.2384 while the face was Manrope; changing the family invalidated the constant and it
   was re-measured rather than carried over — which is exactly the trap this note exists
   to name. The
   ratio is scale-invariant because the tracking is in em, so one constant holds at
   every viewport. 100cqw / 5.2384 is therefore the size that fills the container
   exactly. Recompute it if the string, the family or the tracking ever change.

   letter-spacing lives here rather than in the builder because it MUST stay in em:
   a px value would stop scaling the moment the font-size does, and the fill would
   drift. Size and tracking are one decision, so they are one rule. */
/* `body` delante, y por una razón medida. Elementor reemite
   `.elementor-widget-heading .elementor-heading-title { font-size: ... }` en CADA hoja de
   página, incluida la INLINE que /horarios/ carga por su plantilla de fila (post 157).
   Esa regla es (0,2,0), igual que un `.deb-giant .elementor-heading-title` pelado, y al
   ir más tarde en el documento GANA el desempate por orden.
   Síntoma medido en staging: en /horarios/ el wordmark del pie caía a 48px y llenaba el
   21,5%, mientras en las demás páginas eran 278px al 100% — el mismo footer, distinto
   según la página que lo alojara. La familia sí sobrevivía porque su regla ya llevaba
   este prefijo; el tamaño no lo llevaba. Cuarta vez que esta colisión muerde en este
   proyecto. */
body .deb-giant .elementor-heading-title {
  position: relative;
  z-index: 1;
  display: block;
  overflow: hidden;              /* the letters rise into view from under this edge */
  font-size: clamp(48px, 11vw, 168px);  /* pre-container-query fallback */
  letter-spacing: -0.05em;
  /* F67 — nowrap is STRUCTURAL here, not cosmetic.
     CSS-B3-03 wraps every glyph in its own inline-block span so it can stagger them,
     and an inline-block is a legal line-break opportunity. Combined with a fill
     constant that sizes the text to EXACTLY 100% of the container, any sub-pixel
     rounding pushes the last glyph onto a second line. Measured on staging at a
     500px viewport: container 453.00px, text advance 453.00px, and the wordmark
     rendered as "DebAnand" / "a" on two lines. Desktop only escaped because its
     numbers happened to round the other way — the defect was always latent at
     every width. The nine letters are one word and must break nowhere. */
  white-space: nowrap;
  /* F68 — and the fill constant was NOT scale-invariant, which nowrap then exposed.
     CSS-B3-02 argues the 4.4822 ratio holds at every size because the tracking is in
     em. That reasoning is sound and the conclusion was still wrong: Fraunces is a
     variable font with an OPTICAL SIZE axis, and `font-optical-sizing: auto` widens
     the letterforms as the size drops — which is exactly what an opsz axis is for.
     Measured advance ratio for "DebAnanda"'s nine glyphs at weight 800:
         48px .. 5.2448     100px .. 4.5291     180px .. 4.4820
         64px .. 5.0796     140px .. 4.4836     278px .. 4.4820
     So 4.4822 is the LARGE-SIZE ASYMPTOTE, not a constant. Below ~140px the text
     runs wider than the container it was supposed to fill: 113.3% at 288px,
     110.7% at 328px, 106.4% at 358px — clipped by the overflow:hidden above.

     Pinning opsz to the display end restores the invariant: ratio 4.4820 at every
     size, verified 64px through 321px. Desktop renders identically because a
     font-size past ~180px already saturates this axis, so nothing above tablet
     moves a pixel. The design argument agrees with the arithmetic: a wordmark is a
     LOGOTYPE, and its letterforms must not change shape between a phone and a
     laptop. Only the axes named here are overridden — `font-weight` still drives
     wght — so this pins the optical size and nothing else. */
  font-variation-settings: "opsz" 144;
  -webkit-mask-image: linear-gradient(180deg, #000 30%, rgba(0, 0, 0, 0.22));
          mask-image: linear-gradient(180deg, #000 30%, rgba(0, 0, 0, 0.22));
}

@supports (container-type: inline-size) {
  body .deb-giant .elementor-heading-title { font-size: calc(100cqw / 4.4822); }
}

/* CSS-B3-03 — the per-letter stagger.
   85ms apart, matching the reference. Transform and opacity only: both are
   compositor properties, so nine simultaneous transitions cost no layout. */
.deb-giant .deb-fg-ch {
  display: inline-block;
  transition:
    transform 1s cubic-bezier(0.16, 1, 0.3, 1) calc(var(--d, 0) * 85ms),
    opacity 0.7s ease-out calc(var(--d, 0) * 85ms);
}
body .deb-giant .elementor-heading-title.is-armed:not(.is-in) .deb-fg-ch {
  opacity: 0;
  transform: translateY(0.45em);
}
/* Flipped while the tab was hidden: land settled rather than mid-fade. */
body .deb-giant .elementor-heading-title.is-settled .deb-fg-ch { transition: none; }

@media (prefers-reduced-motion: reduce) {
  .deb-giant .deb-fg-ch { transition: none; }
}

/* ═════════════════════════════════════════════════════════════════════════════
   C · REFINEMENT PASS — home, header, footer
   2026-09-03. Six operator observations, closed against
   https://debananda.ddev.site/_ref/index.html. Every number below was measured in
   this session with puppeteer-core + the compositing contrast helper in
   scratchpad/lib.js, never read off a design file.

   Consumed by: docs/build/c-refinement.php · reported in docs/build/C-refinamiento.md
   ═════════════════════════════════════════════════════════════════════════════ */

/* CSS-C-01 — THE PAGE GROUND, and the reason the site stopped looking like itself.
   MEASURED: the built page ground is #FFFFFF. The reference's is --cream #F1EAE0
   (ref styles.css `body { background: var(--bg) }`), and DESIGN.md:20 names
   `--cream` "Page background (--bg)". Nothing in the Elementor Kit or hello-elementor
   binds it, so every section without its own background sat on white:
       build  hero/cards/plans bands -> #ffffff
       ref    same bands             -> #f1eae0
   Consequence beyond the look: B2-home.md:61 reports `.deb-accent` at "6.82:1 on
   cream". 6.82 is its ratio on WHITE; on cream the same pair is 5.71:1. The column
   named a background that was not being painted.

   TYPE, same rule, same cause. `body` computed -apple-system 16px/24px — the UA font
   — because a Kit typography ROLE is not a Kit BODY setting. Anything not explicitly
   typed by a widget fell out of the type system entirely. Bound here to the Kit's
   `text` role so the client changing Body in the builder still governs. */
body {
  background-color: var(--e-global-color-cream, var(--bg));
  color: var(--e-global-color-text, var(--fg));
  font-family: var(--e-global-typography-text-font-family, 'Manrope'), var(--font-body);
  font-size: var(--e-global-typography-text-font-size, 1.0625rem);
  font-weight: var(--e-global-typography-text-font-weight, 400);
  line-height: 1.55;
}

/* CSS-C-02 — the skip link on HOVER. The same specificity collision that once left it
   at 1.20:1, one pseudo-class further along.
   MEASURED before: rest 7.76:1, focus 7.76:1, **hover 1.00:1** — --sage-deep text on
   its own --sage-deep ground, i.e. the control is invisible while the pointer is on it.
   Cause: the Kit emits `.elementor-kit-5 a:hover { color: var(--e-global-color-primary) }`
   at (0,2,1), which outranks the existing `a.skip-link, a.skip-link:focus` at (0,2,0).
   `a.skip-link:hover` is (0,2,1) and wins on source order without !important.
   The pair :focus + :hover is the real state: a keyboard user tabs to it, then moves
   the mouse. Measured after: 7.76:1 in all four states. */
a.skip-link:hover,
a.skip-link:focus-visible,
a.skip-link:active {
  color: var(--paper);
}

/* CSS-C-03 — the display accent, returned to the reference clay.
   The global `accent` was moved to clay-deep #7A5230 to fix BODY-size accents, and it
   over-reached: it also darkened the hero, which the reference sets in clay #AC7A4C at
   104px. At 104px WCAG's threshold is 3:1, not 4.5:1 — measured clay on cream 3.12:1,
   PASS. Reverting the whole global would re-break the 12px eyebrows (clay on sage-mist
   measured 3.19:1 against a 4.5 requirement, a real failure in the reference).

   So the split is by TYPE SIZE, which is what the threshold is keyed on. Audited every
   .deb-accent on all five pages: 20 of 20 are 56-104px, i.e. all "large text". This
   modifier is opt-in per occurrence rather than automatic because one of them —
   "colaboraciones." on --bone, /contacto/ — measures clay 2.58:1 on that ground and
   must stay clay-deep (4.72:1). Ground, not just size, decides.
   Measured with this class: cream 3.12:1 · paper 3.51:1 · sage-mist 3.16:1 — all pass. */
.deb-accent--lg {
  color: var(--e-global-color-accentbig, var(--clay));
}

/* CSS-C-04 — "Powered by Lyvek".
   Replaces the `v1.0` chip AND the plain "Engineered by LYVEK" line: two Lyvek bylines
   in a three-slot rail is two thirds of a client's footer talking about us. One credit,
   carrying the treatment.

   Built only from the site's own language — no new family, no new colour:
     face      the Kit's `accent` role (JetBrains Mono 500), the eyebrow face already
               used on every section label
     colour    "Powered by" at cream 60% over ink, which is the reference fine-rail
               colour #A09991 reproduced as a color-mix of two Kit globals instead of a
               literal rgba; "Lyvek" in --claysoft, the exact colour the reference's own
               .pg-credit uses
     caret     the block caret from the reference's .pg-credit__caret (0.5em x 1.02em,
               currentColor), resting at 0.28 opacity
     motion    the reference's --ease-out cubic-bezier(0.16, 1, 0.3, 1)

   The caret does NOT blink at rest. An ambient terminal cursor in the footer of a yoga
   and psychology practice is a costume; here it wakes on hover/focus, which is where a
   terminal reference is a reward rather than a claim. Reduced motion stops it.
   MEASURED: "Powered by" 5.73:1, "Lyvek" 8.87:1, both on --ink. */
.deb-credit {
  font-family: var(--e-global-typography-accent-font-family, 'JetBrains Mono'), var(--font-mono);
  /* 12px and 1.55, matched to the copyright on the other side of the rail rather than
     to the reference's own 0.86em. Two labels on one hairline rail that differ by a
     single pixel read as a mistake, not as hierarchy. Measured before: copyright 12px /
     18.6px against credit 11px / 18.15px. Equal size AND equal line-height is what puts
     the baselines on the same line — the grid centres the boxes, and boxes of unequal
     line-height centre to different baselines. */
  font-size: 12px;
  line-height: 1.55;
  font-weight: var(--e-global-typography-accent-font-weight, 500);
  letter-spacing: 0.1em;
  color: var(--e-global-color-sagesoft);
  color: color-mix(in srgb, var(--e-global-color-cream) 60%, var(--e-global-color-text));
}
/* THIRD SIGHTING OF ONE ROOT CAUSE, so it is worth naming rather than patching again.
   An Elementor Text widget emits a <p>, and that <p> carries the theme's bottom margin.
   Any time such a widget is vertically centred, the glyphs land HIGH by half that
   margin, and the widget box is that much taller than it looks:
     · the card pills          43.2px box, text 8.09px high   (fixed via .deb-pill)
     · this copyright          33.0px box, text 7.19px high   (14.4 / 2 = 7.2)
   The general remedy is zeroing the trailing margin on the last paragraph inside every
   text widget. It is NOT applied globally here on purpose: four pages were verified
   against their gates with those margins in place, and silently tightening every text
   block site-wide after that verification would invalidate it. Recorded in PLAN.md as a
   pattern to sweep deliberately, with the gates re-run. */
.deb-fine p { margin: 0; }
.deb-credit p { margin: 0; }
.deb-credit,
.deb-credit p { white-space: nowrap; }
.deb-credit a {
  position: relative;
  padding-right: 0.72em;
  color: var(--e-global-color-claysoft, var(--clay-soft));
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
/* The colour must be PINNED across states, not just set at rest. MEASURED: without
   this, hovering turned the link --sage-deep on ink — the Kit emits
   `.elementor-kit-5 a:hover { color: var(--e-global-color-primary) }` at (0,2,1),
   which ties `.deb-credit a:hover` and wins on load order. `body` makes it (0,2,2).
   Sage-deep on ink measures 1.62:1; clay-soft holds 8.87:1 in every state. The
   reference does the same thing — `.pg-credit__link:hover` changes only the
   underline — so the hover reward is the rule appearing, not a recolour. */
body .deb-credit a,
body .deb-credit a:hover,
body .deb-credit a:focus-visible,
body .deb-credit a:active {
  color: var(--e-global-color-claysoft, var(--clay-soft));
}
.deb-credit a:hover,
.deb-credit a:focus-visible { border-bottom-color: currentColor; }
/* The caret is now a REAL span, not `::after` on the link — the typing effect slides it
   from the start of the line to the end, and a pseudo-element cannot be moved
   independently of the link it hangs off. Kept absolutely positioned against the whole
   credit so `right: 0` is the finish line and every stop is a negative translateX. */
.deb-credit { position: relative; padding-right: 0.72em; }

.deb-credit__caret {
  position: absolute;
  right: 0;
  top: 50%;
  width: 0.5em;
  height: 1.02em;
  margin-top: -0.51em;
  background: currentColor;
  color: var(--e-global-color-claysoft, var(--clay-soft));
  opacity: 0.32;
  pointer-events: none;
  transform: translateX(0);
}

/* While typing: the characters are hidden and the caret is bright and moving. */
.deb-credit.is-armed .deb-credit__caret { opacity: 0.85; will-change: transform; }
.deb-credit.is-armed .deb-credit__ch    { opacity: 0; }
.deb-credit .deb-credit__ch             { transition: opacity 90ms linear; }

/* Finished: the caret blinks, which is the terminal idiom the whole credit borrows. */
.deb-credit.is-done .deb-credit__caret {
  opacity: 0.85;
  animation: deb-caret-blink 0.6s ease-in-out infinite alternate;
}
@keyframes deb-caret-blink { from { opacity: 0.85; } to { opacity: 0.2; } }

/* No JS, or reduced motion: the script never arms, so nothing is hidden and the caret
   simply rests where CSS put it. The credit reads identically either way. */
@media (prefers-reduced-motion: reduce) {
  .deb-credit__caret { animation: none; }
  .deb-credit .deb-credit__ch { transition: none; }
}

/* The old pseudo-element caret, neutralised: leaving it would draw a second block. */
/* The old pseudo-element caret is GONE, not disabled. A first attempt set
   `content: none` at the top of the rule, which the rule's own `content: ""` two
   lines below promptly overrode — the page would have drawn TWO carets, one static
   after "Lyvek" and one travelling. Deleting the rule is the fix; neutralising a rule
   from inside itself is not a thing. */
@media (prefers-reduced-motion: reduce) {
  .deb-credit a:hover::after,
  .deb-credit a:focus-visible::after { animation: none; opacity: 0.85; }
}

/* CSS-C-05 — the fine rail, back on the reference's scale.
   MEASURED: the whole rail rendered Manrope 17px — body size — because the widgets
   carry `__globals__: { typography: globals/typography?id=accent }` and THAT EMITS NO
   CSS. Verified by probe: adding the same global to a second widget and regenerating
   post-62.css produced colour rules and not one font declaration. Every typography
   global in these templates is a silent no-op; the widgets were falling through to
   Elementor's own `.elementor-widget-text-editor` default, which binds the `text` role.
   Reference rail: Manrope 12px. */
.deb-fine {
  font-size: 0.75rem;
  line-height: 1.55;
  /* One colour for the whole rail. The reference paints it rgba(241,234,224,0.6)
     over ink; this is that value reproduced from Kit globals, and it is the same
     mix the credit uses, so the two slots read as one line. MEASURED 5.73:1 —
     identical to the reference. */
  color: var(--e-global-color-sagesoft);
  color: color-mix(in srgb, var(--e-global-color-cream) 60%, var(--e-global-color-text));
}

/* CSS-C-06 — the UI label face.
   MEASURED root cause of "a league of fonts", straight out of Elementor's generated CSS:
       .elementor-widget-nav-menu .elementor-nav-menu .elementor-item
           { font-family: var(--e-global-typography-primary-font-family) }   -> Fraunces
       .elementor-widget-button .elementor-button
           { font-family: var(--e-global-typography-accent-font-family) }    -> JetBrains Mono
   Those are Elementor's DEFAULT bindings, not a choice anyone made here. The header nav,
   the header CTA and all four footer link lists rendered in Fraunces (a display serif
   doing UI-label duty, at 400 — a weight the Kit's Fraunces role does not even have),
   and the two footer CTA buttons rendered monospace while the identical hero buttons
   rendered Manrope. Same component, two families, one page.

   Rebound to the Kit's `text` role FAMILY only — size and weight stay native Elementor
   settings, because they are per-component decisions (nav 14/600, footer links 14/400,
   buttons 17/600) and not Kit roles. Scoped by an explicit class so this is greppable
   and cannot silently capture a future widget.
   NOT applied to: the off-canvas drawer nav (Fraunces 26 is the reference's own
   `.mobile-drawer a { font-family: var(--font-display) }`) or the drawer close button
   (mono is the reference's `.drawer-close { font-family: var(--font-mono) }`). */
.deb-ui-face .elementor-nav-menu .elementor-item,
.deb-ui-face.elementor-widget-button .elementor-button,
.deb-ui-face .elementor-button {
  font-family: var(--e-global-typography-text-font-family, 'Manrope'), var(--font-body);
}

/* CSS-C-07 — the footer logo, given the hover the rest of the footer has.
   MEASURED: rest and hover were byte-identical — box 178x36 in both, translate none in
   both, opacity 1 in both, filter none in both. The `<a>`'s colour did change
   (clay-deep -> sage-deep, from the Kit's `a:hover`) but its only child is an <img>, so
   that colour paints nothing. It was the one link in the footer with no feedback.
   The reference gives its footer brand the same 6px slide as every other footer link
   (`.footer-grid a:hover { color: var(--clay-soft); translate: 6px 0 }` — measured
   translate none -> 6px, colour rgb(241,234,224) -> rgb(217,188,147)). A recolour is
   not available on an SVG <img>, so the slide carries it, with a small opacity step
   standing in for the colour change.
   Expressed here rather than as Elementor's Transform-on-hover because that control
   emits `transform`, which would fight the wordmark stagger's own transform if the two
   ever meet; `translate` is the independent property the reference itself uses. */
.deb-footer-logo a {
  display: inline-block;
  transition: translate 0.35s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
}
.deb-footer-logo a:hover,
.deb-footer-logo a:focus-visible {
  translate: 6px 0;
  opacity: 0.82;
}

/* CSS-C-08 — the footer brand pill, restored.
   The reference carries `<span class="pill pill--sage">…Medellín · Aquí y ahora</span>`
   inside `.footer-brand`; the build dropped it. Rebuilt as an Icon List widget (the
   same construct as the hero eyebrow) so it is a builder object the client can hide.
   The ground is the one thing a native control cannot express: the reference uses a
   20% sage wash over ink, and Elementor's colour control takes solid globals only.
   color-mix of two Kit globals reproduces it without a literal.
   MEASURED: --sagesoft on that blend = 7.23:1 (reference: 7.33:1 on its own blend). */
.deb-pill-onink {
  background-color: color-mix(in srgb, var(--e-global-color-secondary) 20%, var(--e-global-color-text));
  border-color: color-mix(in srgb, var(--e-global-color-secondary) 35%, var(--e-global-color-text));
  /* The widget wrapper inherits `color` from body — ink, i.e. 1.36:1 on its own
     ground. The list item sets its own colour so nothing is wrong today, but a
     second line of text added in the builder tomorrow would arrive invisible. */
  color: var(--e-global-color-sagesoft);
}

/* CSS-C-09 — the page-title band on the inner pages.
   MEASURED, reference vs build, /clases/ at 1440:
       ref    eyebrow -> h1  31.6px   h1 -> lede  16px   h1 line-height 83.2px (1.04)
       build  eyebrow -> h1  20px     h1 -> lede  20px   h1 line-height 80px   (1.00)
   And the four inner pages disagreed with each other: gaps of 20 / 16 / 24 / 16+8.
   The reference's rhythm is asymmetric on purpose — the title gets room above and holds
   its lede close below, so it reads as the anchor of the band instead of the middle row
   of three evenly spaced things. The gap is set natively (container flex-gap 16 + a 16px
   top margin on the heading); only the leading is here, because 1.0 crops Fraunces'
   descenders against the lede at 80px. */
/* Nothing here: the leading is a native `typography_line_height` on each h1 widget,
   because the builder's control reaches it and a rule here would only shadow a
   setting the client can see. Recorded so the class name in the data is not read as
   a missing rule — `.deb-page-title` remains as a hook and a grep target. */

/* CSS-B3-04 — no flash before the split.
   REPORTED FROM THE LIVE SITE, and a real defect of this build: on reload the
   wordmark "se ve, luego desaparece y luego se anima". The reveal script is
   deferred, so the browser paints the intact word first; the script then splits it
   into nine spans and arms them at opacity 0, which reads as a disappearance.

   The element therefore starts hidden WHENEVER JavaScript is present. Whether it is
   present is decided before the first paint by the theme's own no-js guard
   (functions.php:267): the class is added server-side and removed by an inline head
   script at priority 0, so this rule matches or does not match before anything is
   painted — no second frame, no flash.

   `visibility`, not `opacity`: it reserves the layout box, so the footer height
   never changes between the hidden and revealed states.

   The safety is in footer-wordmark.js, not here: EVERY exit path in that script adds
   `is-ready`, including the prefers-reduced-motion bail and the missing
   IntersectionObserver bail. Residual risk, stated rather than hidden: if the script
   itself never loads at all, this decorative wordmark stays invisible. That is the
   one case the no-js guard cannot see, and it costs an ornament, not content. */
html:not(.no-js) body .deb-giant .elementor-heading-title:not(.is-ready) {
  visibility: hidden;
}

/* CSS-B3-05 — the focus ring has to change colour on the dark footer.
   WCAG 2.2 SC 1.4.11 wants a focus indicator at >=3:1 against what surrounds it. The
   theme's one global rule (line 117) paints `3px solid var(--focus-ring)`, and
   --focus-ring is --clay-deep. Measured:
       clay-deep on --paper (the whole light site) ... 6.44:1  PASSES
       clay-deep on --ink   (this footer) .......... 2.36:1  FAILS
       paper     on --ink   (this fix) ............ 15.19:1
   So the ring was never wrong — it was wrong on ONE surface, and only there.

   Redefining the custom property rather than the outline: --focus-ring inherits, the
   global rule already reads it, and every focusable inside the footer picks the new
   value up with no selector to maintain and no specificity to win. Adding a second
   outline rule would have had to out-specify Elementor's per-widget selectors for
   every widget type in the footer, and would have gone stale the moment one was added.

   Found by the page-build workflow as F-SITE-01, which reported it as two links with
   no focus style falling back to the browser default. That was the wrong diagnosis:
   the links do have a style, it is the theme's own, and the count is every focusable
   in the footer rather than two. Same defect, wider than reported. */
.deb-footer {
  --focus-ring: var(--paper);
}

/* ═════════════════════════════════════════════════════════════════════════════
   B3 · FOOTER — the animated rail and the CTA box highlight

   Both were flagged by the operator as present in the reference and missing here.
   Both are genuinely absent rather than approximated: the rail's own `_title` in the
   builder read "Phrase rail (replaces the reference marquee)", and the CTA box carried
   a radial background overlay standing in for an ellipse it does not resemble.
   ═════════════════════════════════════════════════════════════════════════════ */

/* CSS-B3-06 — the marquee.
   FULL-BLEED out of a boxed container. The reference's `.footer-marquee` is a direct
   child of <footer>, outside `.container`, so it runs edge to edge; here the rail lives
   inside Elementor's boxed inner, so it is pushed back out with the standard
   `50% - 50vw` trick. Safe specifically because `.deb-footer` is `overflow: clip`:
   the 100vw box is wider than the footer once a scrollbar exists, and the footer crops
   it rather than the document growing a horizontal scrollbar. */
.deb-mq {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  overflow: clip;         /* never `hidden`: see the note on .deb-footer */
  user-select: none;
}

.deb-mq__track {
  display: flex;
  width: max-content;
  animation: deb-mq-scroll 192s linear infinite;
  will-change: transform;
}

/* The reference pauses on hover, which is also the accessible behaviour: WCAG 2.2
   SC 2.2.2 asks for a mechanism to pause anything that moves for more than five
   seconds, and this moves for 192. */
.deb-mq:hover .deb-mq__track { animation-play-state: paused; }

.deb-mq__group {
  display: flex;
  align-items: center;
  gap: 2.6em;
  padding-right: 2.6em;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  white-space: nowrap;
  /* 0.62, where the reference uses 0.55 — because the reference has no sun behind this
     rail and we do. Composited and measured at 12px:
         on clean ink ......... 0.55 -> 5.04:1
         under the sun (0.20) . 0.55 -> 4.30:1  FAILS AA
                                0.62 -> 5.02:1  passes
     The alternative was dimming the sun to 0.14, which is the exact moment after being
     asked to make it 50% bigger. A seven-point alpha bump on decorative micro-text costs
     nothing and reads slightly better everywhere else too. The sage dot is non-text
     (SC 1.4.11, 3:1) and sits at 3.97:1 under the sun, so it needed nothing. */
  color: rgba(241, 234, 224, 0.62);
}

.deb-mq__dot {
  width: 5px;
  height: 5px;
  flex: none;
  border-radius: 50%;
  background: var(--e-global-color-sage, #7C9C5C);
  animation: deb-mq-breathe 4.5s ease-in-out infinite alternate;
}

/* -50%, and the markup ships EIGHT groups for that reason: half of eight is four, so
   the frame at the loop point is identical to the frame at the start. A -100% loop over
   four groups would jump every cycle. */
@keyframes deb-mq-scroll  { to { transform: translateX(-50%); } }
@keyframes deb-mq-breathe { from { transform: scale(1); opacity: 0.55; }
                              to { transform: scale(1.7); opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .deb-mq__track, .deb-mq__dot { animation: none; }
}

/* CSS-B3-07 — the highlight on the CTA box.
   `.footer-cta::before` in the reference: a 60% x 200% white ellipse at 8% opacity,
   inset -50% -10% auto auto, bleeding off the top-right corner of the clay gradient.

   On ::after, not ::before — Elementor owns `.e-con::before` for its own background
   overlay, and it declares width and height there, which is what silently flattened
   the sun ornament earlier in this build. Same reason, same remedy.

   `overflow: clip` rather than `hidden` for the same reason as the footer root: hidden
   would make the CTA card a scroll container, and the ellipse hangs 50% above it. */
.deb-cta-box {
  position: relative;
  overflow: clip;
  isolation: isolate;
}

.deb-cta-box::after {
  content: "";
  position: absolute;
  z-index: -1;            /* over the gradient, under the copy and the buttons */
  top: -50%;
  right: -10%;
  bottom: auto;
  left: auto;
  width: 60%;
  height: 200%;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  pointer-events: none;
}

/* CSS-B3-08 — the active nav item is a DOT, as in the reference.
   `.nav-links a[aria-current="page"]::after` there: 4x4px, centred, 4px below the link.
   The build had Elementor's "underline" pointer — a 2px rule the full 43px width of the
   link, which is a different mark. Elementor's pointer control has no dot option
   (none | underline | overline | double-line | framed | background | text), so the
   pointer is off and this draws it.

   Keyed on `aria-current`, which WordPress already writes on the current menu item —
   no extra class to maintain and no JavaScript. It also means the visual mark and the
   programmatic one cannot drift apart: they are the same attribute.

   COLOUR. The reference's own token is `--accent: var(--clay)` = #AC7A4C, and the first
   instinct was to swap it for clay-deep the way F29 did for accent TEXT. Measured
   instead of assumed: #AC7A4C on --paper is 3.51:1, and a dot is a non-text state
   indicator, whose bar is 3:1 (SC 1.4.11) — so the reference's own colour passes and is
   kept. The Kit global that holds it is `accentbig`, whose title already says as much. */
.deb-header .elementor-nav-menu a.elementor-item[aria-current="page"] { position: relative; }

.deb-header .elementor-nav-menu a.elementor-item[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -4px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  transform: translateX(-50%);
  background: var(--e-global-color-accentbig, #AC7A4C);
}

/* The off-canvas menu is a vertical list: a dot centred under a full-width row would
   float in the middle of nowhere, so there it sits before the label instead. */
.e-off-canvas .elementor-nav-menu a.elementor-item[aria-current="page"]::after {
  left: -12px;
  bottom: auto;
  top: 50%;
  transform: translateY(-50%);
}

/* ═════════════════════════════════════════════════════════════════════════════
   B6 · HEADER — el CTA al extremo, y con icono de WhatsApp
   ═════════════════════════════════════════════════════════════════════════════ */

/* CSS-B6-01 — un solo CTA, y en el borde.
   El contenedor `hdacts` siempre llegó al borde del contenido (1897px), pero su menú
   solo-CTA estaba oculto en escritorio, así que el botón caía como último ítem del nav
   principal y terminaba en 1534 — a 363px del extremo. Con `hdctam` ya visible, este
   ítem sobra dentro del nav: se oculta AQUÍ y no en el CMS, para que el ítem siga
   existiendo una sola vez en Apariencia → Menús y la clienta pueda retitularlo o
   quitarlo desde un único sitio. */
@media (min-width: 1025px) {
  .deb-nav-nocta .elementor-nav-menu > li.nav-cta { display: none; }
}

/* CSS-B6-02 — el icono de WhatsApp.
   Como MÁSCARA, no como imagen: hereda `currentColor`, así que sigue al texto en reposo
   y en hover y conserva el contraste ya medido (6.44:1 en reposo, --paper sobre --ink al
   pasar el cursor). Una imagen coloreada se habría quedado fija y habría desaparecido en
   uno de los dos estados.

   `content` se re-declara porque la regla del pointer apaga `::before` y `::after` con
   `!important` para matar el subrayado de navegación — legítimo entonces, pero también
   cierra la puerta al icono. Se reabre solo para `::before`; `::after` sigue apagado.

   Decorativo a propósito: la etiqueta dice "Escríbeme" y el icono solo confirma el canal.
   Un `aria-label` que dijera "WhatsApp" duplicaría el nombre accesible del enlace. */
.deb-header .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item::before,
.deb-cta-only .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item::before {
  content: "" !important;
  /* `position: static` a propósito. Elementor posiciona `::before` en ABSOLUTO para el
     subrayado de navegación, y ese absoluto sobrevivía: medido, el icono salía a
     `left:15px top:14.6px` — encima de la primera letra de "Escríbeme". Devolverlo al
     flujo es lo que hace que el margen y el flex del enlace lo coloquen antes del texto. */
  position: static;
  /* `opacity: 1` explícito. El nav-menu lleva `animation_line: fade`, que deja `::before`
     en opacidad 0 y lo sube a 1 solo en hover — es el subrayado que se desvanece. El
     icono heredaba ese 0: estaba presente, dimensionado (el botón creció de 95 a 117px),
     coloreado y enmascarado, y era invisible. Un fallo que el ancho delataba y la vista no. */
  opacity: 1;
  display: inline-block;
  /* 1.45em. A 1.05em (14.7px) los trazos finos del glifo caían por debajo de un píxel
     completo y el antialiasing lo hacía ver sucio — no era pixelado por resolución, que
     en una máscara SVG no ocurre, sino por detalle bajo el umbral de rasterizado. */
  width: 1.45em;
  height: 1.45em;
  margin-inline-end: 0.55em;
  vertical-align: -0.28em;
  background-color: currentColor;
  -webkit-mask: url("assets/img/whatsapp.svg") center / contain no-repeat;
          mask: url("assets/img/whatsapp.svg") center / contain no-repeat;
  flex: none;
}

/* El enlace pasa a flex para que icono y etiqueta compartan línea base y el botón
   conserve sus 44px de alto táctil. */
.deb-header .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item,
.deb-cta-only .elementor-nav-menu--main .elementor-nav-menu li.nav-cta > a.elementor-item {
  display: inline-flex;
  align-items: center;
}

/* ═════════════════════════════════════════════════════════════════════════════
   B7 · EL SOL COMO ORNAMENTO DE SECCIÓN

   El mismo sunburst de la marca que ya vive en el footer, ahora repartido por el
   contenido. La instrucción del operador fue explícita: que NUNCA quede en la misma
   posición que en el footer. De ahí que esto sea un SISTEMA DE VARIANTES y no una regla
   única — cada sitio recibe una esquina, un tamaño y una opacidad distintos, y el footer
   (arriba-derecha, 1440px, 0.20) queda reservado como su propia variante.

   Reglas que hereda de lo ya aprendido en este mismo archivo, para no repetir errores:
     · `::after`, nunca `::before` — Elementor usa `::before` en TODO contenedor para su
       superposición de fondo, y declara ahí width/height, que es lo que aplastó el sol
       del footer a 680x212 la primera vez.
     · `overflow: clip`, nunca `hidden` — `hidden` convierte la caja en contenedor
       desplazable y un `scrollIntoView` desplaza su contenido; medido, movió el footer
       356px.
     · width Y height declarados: una altura explícita gana a `aspect-ratio`, y Elementor
       declara una.
     · `z-index: -1` con `isolation: isolate` — sobre el fondo, bajo todo el contenido.

   CONTRASTE: sobre fondos claros el ornamento OSCURECE la base, y el texto del sitio es
   tinta sobre claro, así que oscurecer sube el contraste en vez de bajarlo. Es lo
   contrario del caso del footer, donde el fondo ya era oscuro y hubo que medir el riel.
   Aun así se mide después, porque «se deduce» no es «se midió». */

.deb-sun {
  position: relative;
  overflow: clip;
  isolation: isolate;
}

.deb-sun::after {
  content: "";
  position: absolute;
  z-index: -1;
  background-color: var(--e-global-color-accentbig, #AC7A4C);
  -webkit-mask: url("assets/img/sun-ornament.svg") center / contain no-repeat;
          mask: url("assets/img/sun-ornament.svg") center / contain no-repeat;
  pointer-events: none;
  /* Valores por defecto que cada variante sobrescribe. Sin variante no se dibuja nada
     fuera de sitio: la variante es obligatoria. */
  inset: auto;
  width: 0;
  height: 0;
  opacity: 0;
}

/* ── Variantes. Cada una difiere del footer y de las demás en esquina, escala y peso ── */

/* Arriba-derecha, para el héroe del home. Comparte esquina con el footer a petición del
   operador, pero NO comparte escala ni peso: 760px contra 1440, y 0.09 contra 0.20 — el
   del footer manda sobre tinta y este solo insinúa sobre papel. Menos sangrado que la
   variante anterior, que se leía cortada en vez de continuar fuera de cuadro. */
.deb-sun--tr::after {
  right: 0; top: 0; left: auto; bottom: auto;
  width: min(58vw, 760px); height: min(58vw, 760px);
  /* La entrada. Los textos del héroe usan las animaciones de Elementor con retardos de
     0, 70, 140 y 210ms y duración `fast` (~800ms), así que el último aterriza cerca de
     los 1010ms — medido en el DOM, no supuesto. El sol arranca en 1050ms: entra cuando
     la lectura ya ocurrió, que es lo que se pidió, en vez de competir con ella.

     Se anima TRANSFORM y OPACITY, las dos propiedades de compositor, así que la entrada
     no provoca ni un reflow. Sube desplazándose un poco hacia su sitio en lugar de solo
     aparecer: un fundido puro sobre un fondo claro casi no se percibe.

     `animation-fill-mode: both` es lo que sostiene el estado final; sin él volvería a la
     opacidad declarada al terminar. Y la opacidad de reposo es 0 SOLO porque esta
     animación es CSS y siempre corre — no depende de JavaScript ni de un observador,
     que es justo la fragilidad que dejó el wordmark del pie invisible en pestaña oculta. */
  transform: translate(26%, -30%);
  opacity: 0.09;
  animation: deb-sun-in 900ms cubic-bezier(0.16, 1, 0.3, 1) 1050ms both;
}

@keyframes deb-sun-in {
  from { opacity: 0; transform: translate(30%, -36%) scale(0.94); }
  to   { opacity: 0.09; transform: translate(26%, -30%) scale(1); }
}

/* Sin movimiento: el sol simplemente está. No se oculta ni se retrasa — un ornamento que
   nunca aparece es peor que uno que no se anima. */
@media (prefers-reduced-motion: reduce) {
  .deb-sun--tr::after { animation: none; opacity: 0.09; }
}

/* Abajo-izquierda, grande y muy sangrada. Sin uso hoy; se conserva como variante. */
.deb-sun--bl::after {
  left: 0; bottom: 0; right: auto; top: auto;
  width: min(72vw, 900px); height: min(72vw, 900px);
  transform: translate(-42%, 46%);
  opacity: 0.13;
}

/* Media altura a la izquierda, mediano: para bandas de contenido donde el encabezado
   ocupa la izquierda y el ornamento pasa por detrás sin tocar la rejilla. */
.deb-sun--ml::after {
  left: 0; top: 50%; right: auto; bottom: auto;
  width: min(56vw, 660px); height: min(56vw, 660px);
  transform: translate(-46%, -50%);
  opacity: 0.11;
}

/* Arriba-izquierda, contenido: para el bloque del formulario, donde el ornamento debe
   acompañar sin robarle atención a los campos. Es el más discreto de los cuatro. */
.deb-sun--tl::after {
  left: 0; top: 0; right: auto; bottom: auto;
  width: min(48vw, 560px); height: min(48vw, 560px);
  transform: translate(-40%, -44%);
  opacity: 0.10;
}

/* Abajo-derecha, pequeño: para los héroes de páginas interiores. Comparte el lado
   derecho con el footer pero NO la esquina ni la escala, que es lo que se pidió evitar. */
.deb-sun--br::after {
  right: 0; bottom: 0; left: auto; top: auto;
  width: min(40vw, 460px); height: min(40vw, 460px);
  transform: translate(38%, 40%);
  opacity: 0.12;
}

/* Bajo `prefers-reduced-motion` no hay nada que desactivar: el ornamento es estático a
   propósito. Se deja dicho para que nadie le añada rotación sin pensarlo. */

/* CSS-B7-02 — el footer se centra en móvil y tablet, no en escritorio.
   En escritorio la rejilla es `1.4fr repeat(4, 1fr)` y todo alinea a la izquierda, que es
   lo que hace la referencia. Por debajo de 1025px la rejilla colapsa a dos columnas y
   luego a una, y ahí una columna estrecha alineada a la izquierda deja el bloque flotando
   contra el borde: se centra la marca, el título de cada columna y sus enlaces.

   Se centra el CONTENIDO, no solo el texto: los menús de Elementor son listas cuyos
   enlaces son `display: flex`, así que `text-align` por sí solo no los mueve — hace falta
   `justify-content` en el propio enlace. Medido: sin esa línea los títulos se centraban y
   los enlaces se quedaban a la izquierda, que se ve peor que no centrar nada. */
/* En ESCRITORIO la marca va a la izquierda, como el resto de su columna. El widget de
   imagen de Elementor centra por defecto, y medido daba 48px de sangría — la imagen
   quedaba centrada en sus 274px mientras el párrafo y la píldora de debajo empezaban en
   el borde. Alinearlos es lo que hace que la columna lea como una columna. */
@media (min-width: 768px) {
  .deb-footer .deb-footer-logo,
  .deb-footer .deb-footer-logo img { margin-inline-start: 0; margin-inline-end: auto; }
  .deb-footer .deb-footer-logo { text-align: left; }
}

/* Solo MÓVIL. Antes esto llegaba hasta 1024px y centraba también la tablet; a petición
   del operador la tablet se comporta como el escritorio, que es donde la rejilla aún
   tiene dos columnas y el centrado dejaba los bloques flotando sin eje común. */
@media (max-width: 767px) {
  .deb-footer .elementor-element-ftbrand,
  .deb-footer .elementor-element-ftc1,
  .deb-footer .elementor-element-ftc2,
  .deb-footer .elementor-element-ftc3,
  .deb-footer .elementor-element-ftc4 {
    align-items: center;
    text-align: center;
  }

  /* La marca: el <img> es un elemento de bloque dentro de su widget. */
  .deb-footer .deb-footer-logo,
  .deb-footer .deb-footer-logo img { margin-inline: auto; }

  /* Los enlaces del menú nativo. `justify-content` es la línea que de verdad los mueve. */
  .deb-footer .deb-footer-nav .elementor-nav-menu > li > a.elementor-item {
    justify-content: center;
    text-align: center;
  }

  /* El párrafo de la marca deja de estar limitado a la izquierda. */
  .deb-footer .elementor-element-ftbrandp { text-align: center; }
}

/* ═════════════════════════════════════════════════════════════════════════════
   DB-01 · HERO CAROUSEL — the four pillars in the hero frame (client request, docx
   2026-09-07 item 9, then "que se pueda editar el contenido de cada imagen y los overlays").

   History: CSS-only rotor (cut, not crossfade) → core Image Carousel (fade, but the two
   overlay cards were frame-level and could not change per photo) → Elementor Pro Nested
   Carousel: every slide is a container holding its photo and its own two cards, so the
   client edits any of it in the builder. Slide effect (the widget has no fade), 6 s per
   slide, 1.4 s transition, loop, no arrows / dots, pause on hover; functions.php adds
   the pause button, focus pause and the reduced-motion guard.
   ═════════════════════════════════════════════════════════════════════════════ */
/* The photo is an ordinary Image widget; pin the 4:5 frame so every slide has the same
   height whatever photo the client drops in — 0 CLS by construction. */
.deb-hero-carousel .swiper-slide .deb-hero-photo img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}
/* Slide containers must not add the widget's default gutters: the cards position against them. */
.deb-hero-carousel .swiper-slide > .e-con { padding: 0; }

/* Swiper ships `.swiper-wrapper { z-index: 1 }`; the pause button and the focus ring sit above. */
.deb-hero-carousel .swiper-wrapper { z-index: 0; }

/* The region is focusable (it pauses on focus) and its box is the hero frame itself, which
   clips with overflow:hidden — an outline outside the box is never seen (WCAG 2.4.7), and an
   inset one is painted under the positioned `.swiper-wrapper`. The ring is therefore a
   positioned pseudo-element above the slides. */
.elementor-widget-n-carousel.deb-hero-carousel .e-n-carousel:focus-visible {
  outline: none;
  box-shadow: none;
}
.elementor-widget-n-carousel.deb-hero-carousel .e-n-carousel:focus-visible::after {
  content: "";
  position: absolute;
  inset: 6px;
  z-index: 3;
  border: 3px solid var(--focus-ring);
  border-radius: 42px;
  pointer-events: none;
}

/* Pause / play. The one persistent, discoverable control WCAG 2.2.2 asks for; hover and focus
   pauses are transient. Top-left because the two overlay cards own the top-right and the bottom.
   44px hit area, positioned against the carousel box, which is the frame. */
.deb-carousel-toggle {
  position: absolute;
  top: 20px;
  left: 20px;
  z-index: 2;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--cream);
  color: var(--ink);
  box-shadow: 0 6px 18px rgba(38, 32, 26, 0.18);
  display: grid;
  place-items: center;
  cursor: pointer;
}
.deb-carousel-toggle:hover { background: var(--paper); }
.deb-carousel-toggle:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.deb-carousel-toggle svg { width: 16px; height: 16px; fill: currentColor; }
.deb-carousel-toggle .deb-ic-play,
.is-paused .deb-carousel-toggle .deb-ic-pause { display: none; }
.is-paused .deb-carousel-toggle .deb-ic-play { display: block; }
