/* ============================================================
   base.css — design tokens, reset, global typography
   Loaded first. Every other stylesheet (layout.css,
   components.css) relies on the custom properties defined here.

   Breakpoints (documented, not enforced by CSS — custom
   properties can't be used inside @media conditions):
     - mobile:  0 – 47.99em   (base styles, no media query needed)
     - tablet:  48em  (768px) → @media (min-width: 48em)
     - desktop: 64em  (1024px) → @media (min-width: 64em)
   Use these exact values everywhere so breakpoints stay in sync.

   Color system (Sundayness brand guidelines): exactly three
   brand primitives, used at roughly 60% crema / 30% carbon /
   10% lavanda. Components never reference --color-*, --lavanda-*,
   or --neutro-* directly — they read the semantic tokens below:
   --bg/--text/--text-secondary/--text-muted/--border for surfaces,
   --acento-grafico/--acento-texto for the two accent roles, and
   --link-hover/--btn-primary-bg/--btn-primary-text for interactive
   states. base.css re-points every one of the section-relative
   tokens inside the header, footer, and marquee.
   That's the single place the light/dark split happens; nothing
   else needs to know which context it's in — and that includes
   :hover/:focus/:active states, not just rest styling. A hardcoded
   --color-* (or raw hex) inside any interactive-state rule is a bug:
   it was exactly how link hover text went invisible against the
   mobile menu's charcoal panel (hardcoded to --color-carbon, which
   reads fine on crema and vanishes on carbon) — see the fix note on
   --link-hover below and the audit list in components.css.

   Two accent roles, not one — this is deliberate, not decoration:
   raw lavanda (#959EB5, ~chroma 0.035) is only ~2.7:1 on crema,
   under AA for text. --acento-grafico stays that raw value for
   rules/icons/large display figures, where size carries the
   contrast. --acento-texto is the darker lavanda-600 for anything
   that IS text at body/label scale (kickers, links) — swapped to
   lavanda-300 (not raw lavanda) inside charcoal sections below: a
   link needs body-text-legible contrast (10.17:1), not just the
   weaker bar raw lavanda clears for rules/icons at size.
   ============================================================ */

:root {
  /* ---- Brand primitives — do not use these directly in components ---- */
  --color-crema: #FEFEF8;
  --color-lavanda: #959EB5;
  --color-carbon: #0C1013;
  --crema-rgb: 254, 254, 248;
  --carbon-rgb: 12, 16, 19;

  --lavanda-300: #B5BDD2;
  --lavanda-600: #576280;
  --neutro-100: #E5E8ED;
  --neutro-300: #BABEC6;
  --neutro-500: #646971;
  --neutro-700: #292E34;

  /* ---- Semantic tokens: default context is a crema section (~60% of the page) ---- */
  --bg: var(--color-crema);
  --text: var(--color-carbon);
  --text-secondary: rgba(var(--carbon-rgb), 0.7);  /* body-secondary copy and footnotes (the brand spec's "70% opacity") */
  --text-muted: rgba(var(--carbon-rgb), 0.5);       /* least-emphasis labels (years, counts) */
  --border: rgba(var(--carbon-rgb), 0.12);          /* thin 1px editorial rules */
  --acento-grafico: var(--color-lavanda);           /* rules, icons, large display figures — constant on both backgrounds */
  --acento-texto: var(--lavanda-600);               /* link/kicker REST color on crema — never raw lavanda for text here */
  --link-hover: var(--color-carbon);                /* link HOVER color on crema — a real shift away from --acento-texto, not the same value */
  --btn-primary-bg: var(--color-carbon);            /* primary-CTA face, rest — inverted from the surface so it reads on either background */
  --btn-primary-text: var(--color-crema);

  /* ---- Typography ---- */
  --font-heading: "Playfair Display", Georgia, serif;
  --font-body: "Inter", Arial, sans-serif;

  /* Type scale, mobile-first via clamp(). Deliberately large jumps
     between tiers so hierarchy is felt, not just read — --text-display
     (stat/case figures) now outranks every heading, including H1. */
  --text-display:    clamp(3rem, 2rem + 4vw, 5.5rem);         /* stat strip figures — the boldest text on the page */
  --text-display-sm: clamp(2.5rem, 1.5rem + 3vw, 3.75rem);    /* case-card metrics — stat-strip scale, sized to fit a 3-col card */
  --text-hero:    clamp(2.5rem, 1.5rem + 6vw, 5rem);        /* H1 */
  --text-h2:      clamp(2rem, 1rem + 3.5vw, 3.5rem);        /* section headings */
  --text-h3:      clamp(1.5rem, 0.75rem + 2.5vw, 2.25rem);  /* subheadings, pull-quote */
  --text-h4:      clamp(1.125rem, 0.75rem + 1.5vw, 1.5rem); /* item/card titles within a list */
  --text-body:    1rem;                            /* 16px min, per brand spec */
  --text-small:   0.875rem;
  --text-xs:      0.75rem;
  --text-kicker:  0.8125rem;                       /* Inter 500, uppercase, letter-spacing .28em */

  /* ---- Layout tokens ---- */
  --container-max: 77.5rem; /* 1240px */

  /* Rendered height of the sticky .site-header (padding + its tallest
     row content, --tap-min on both mobile/desktop, + 1px border) — see
     .header-inner in layout.css, keep both in sync if either changes.
     Used as a scroll offset so a direct-linked target never lands
     underneath the fixed header. */
  --header-height: calc(2 * 0.875rem + var(--tap-min) + 1px);

  /* Fluid gutter/section rhythm: smaller on phones, caps at the
     original desktop value so large screens look unchanged. */
  --gutter:      clamp(1.25rem, 5vw, 2.5rem);   /* 20px  → 40px */
  --space-96:    clamp(3rem, 7.5vw, 6rem);      /* 48px  → 96px */
  --space-88:    clamp(2.75rem, 6.9vw, 5.5rem); /* 44px  → 88px */
  --space-80:    clamp(2.5rem, 6.25vw, 5rem);   /* 40px  → 80px */
  --space-72:    clamp(2.25rem, 5.6vw, 4.5rem); /* 36px  → 72px */

  /* Component-level spacing scale (card padding, small gaps). */
  --space-0-5: 0.25rem;  /* 4px  — micro (icon-to-text) */
  --space-1:   0.5rem;   /* 8px */
  --space-1-5: 0.75rem;  /* 12px */
  --space-2:   1rem;     /* 16px */
  --space-2-5: 1.375rem; /* 22px */
  --space-3:   1.5rem;   /* 24px */
  --space-4:   2rem;     /* 32px */
  --space-5:   2.5rem;   /* 40px */
  --space-6:   3rem;     /* 48px */

  /* ---- Shape ----
     One sober, small radius everywhere — flat and editorial, not
     the pill-shaped buttons/chips of the previous theme. */
  --radius-sm: 0.25rem; /* 4px */

  /* ---- Minimum touch target (WCAG 2.5.5 / mobile UX baseline) ---- */
  --tap-min: 2.75rem; /* 44px */

  /* ---- Motion ----
     Precise and fast, never exuberant: plain ease-out, no spring/
     bounce. Press is near-instant (physical feedback); reveals and
     hover states stay inside 150-400ms. All motion is gated behind
     prefers-reduced-motion at the point of use (see components.css
     and js/components/scrollReveal.js). */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --duration-press:  80ms;   /* :active feedback */
  --duration-exit:   120ms;  /* hover/label leaving */
  --duration-enter:  220ms;  /* hover/label arriving */
  --duration-reveal: 350ms;  /* scroll-reveal fade+translate */
}

/* Charcoal-context sections (~30% of the page): re-point the same
   semantic tokens components already read, so nothing outside this
   block needs to know it's sitting on a dark background.
   --acento-grafico is intentionally left alone — the brief fixes it
   to raw lavanda on both backgrounds; only the text-scale accent
   swaps, to the lighter value that reads on dark. */
.site-header,
.site-footer,
.marquee {
  --bg: var(--color-carbon);
  --text: var(--color-crema);
  --text-secondary: rgba(var(--crema-rgb), 0.7);
  --text-muted: rgba(var(--crema-rgb), 0.5);
  --border: rgba(var(--crema-rgb), 0.14);
  /* lavanda-300, not raw lavanda: a link needs to be legible as body
     text (10.17:1 on carbon), not just carry enough weight to read
     as a rule/icon — see the audit note in components.css for the
     bug this was covering for (link hover hardcoded to carbon,
     invisible against this same background). */
  --acento-texto: var(--lavanda-300);
  --link-hover: var(--color-crema); /* 18.87:1 on carbon */
  --btn-primary-bg: var(--color-crema);
  --btn-primary-text: var(--color-carbon);
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* Universal reduced-motion safety net: every transition/animation
   sitewide collapses to effectively instant, so every interactive
   state (button letterpress, link underline, hover color shifts...)
   still changes correctly under reduced motion — it just snaps
   instead of easing. Simpler and harder to miss a spot than wrapping
   every individual transition declaration in its own no-preference
   media query. Component-specific rules that change a *value* (not
   just speed) under reduced motion, like .whatsapp-float's resting
   transform, still need their own override — this only kills timing. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    transition-delay: 0s !important;
  }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--text-body); /* 16px baseline, respects user zoom/text-size settings */
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden; /* guards against any incidental horizontal bleed */
}

a { color: inherit; text-decoration: none; cursor: pointer; }
@media (hover: hover) {
  a:hover { color: var(--link-hover); }
}

::selection { background: var(--color-lavanda); color: var(--color-carbon); }

/* Visible keyboard focus ring for all interactive elements
   (hover-only affordances fail keyboard/touch users). */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
.service-row:focus-visible {
  outline: 2px solid var(--acento-grafico);
  outline-offset: 3px;
}

input, textarea, button, select { font: inherit; color: inherit; }

/* Universal press/hover physics for every clickable element site-wide.
   Transform gets its own fast duration so :active reads as instant
   physical feedback regardless of how any color transition is tuned. */
a, button {
  transition: background-color var(--duration-enter) var(--ease-out),
              border-color var(--duration-enter) var(--ease-out),
              color var(--duration-enter) var(--ease-out),
              transform var(--duration-press) var(--ease-out);
}
a:active, button:active { transform: scale(0.97); }

@keyframes slide {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* No overflow here: an ancestor with overflow != visible breaks
   position: sticky on .site-header (it stops sticking to the
   viewport entirely). */
.page { min-height: 100vh; }

/* ---------- Scroll reveal ----------
   [data-reveal] elements are the reveal targets js/components/
   scrollReveal.js observes. They render fully visible by default
   (this rule intentionally does nothing on its own) — JS adds
   .reveal-ready right before it starts observing, and ONLY when
   IntersectionObserver exists and the user allows motion. That
   ordering means content is never parked at opacity:0 waiting for
   an observer that might not fire: no JS, or reduced motion, and
   the page is simply, fully visible at rest. */
[data-reveal].reveal-ready {
  opacity: 0;
  transform: translateY(0.75rem);
  transition: opacity var(--duration-reveal) var(--ease-out),
              transform var(--duration-reveal) var(--ease-out);
}
[data-reveal].reveal-in {
  opacity: 1;
  transform: translateY(0);
}
