/* hb-companion.css — creative-designer-template child-theme companion sheet.
 *
 * Version 0.1.0. ONE canonical home (plan §10-D2) for everything Elementor V4
 * natively lacks a field for: group-9 motion values, ticker keyframes, the
 * skip-link off-screen behavior, focus-ring reinforcement, and the
 * reduced-motion / no-script guarantees (doctrine §5 — the a11y layer stays
 * ours on every build, verified alive after Elementor renders).
 *
 * Sourcing law: every value below is mirrored verbatim from the cited
 * in-repo source (file:line per §). Where this sheet and `:root` disagree,
 * `:root` wins (brand-book §1) — file the drift, never hand-edit around it.
 * Colors/fonts/sizes are referenced as var(--hb-*) — never hardcoded —
 * except the 8 motion values, which live HERE as their canonical home
 * (no motion variable type exists in V4; plan §3 group 9).
 *
 * Explicitly NOT in this file (scope fence):
 * - Responsive size/width steps (container-max / section-pad / rail steps).
 *   F-CH4-C native-first attempt lives at Chunk 6; a companion fallback
 *   lands only if native proves unable — nothing responsive ships here.
 * - E-slot darkening. E3/E5 gated treatments (--hb-coral-text) already live
 *   in variables/classes; E1/E2/E4 production closure is Chunk 10's. No
 *   exception-slot pair is altered by any rule below.
 */

/* ── §1 Motion values: the 8 group-9 tokens, canonical home ────────────────
 * Mirrored verbatim from assets/css/hb-tokens.css :root. If the child theme
 * also enqueues a tokens layer carrying these names, later-:root-wins keeps
 * this file authoritative on equal specificity (same promotion rule as
 * hb-tokens.css:296-299 TUNABLE note).
 */
:root {
  --hb-motion-duration: 0.6s; /* hb-tokens.css:252 — reveal transition duration */
  --hb-motion-ease: cubic-bezier(0.22, 1, 0.36, 1); /* hb-tokens.css:253 — reveal easing */
  --hb-motion-shift: 20px; /* hb-tokens.css:254 — reveal travel */
  --hb-motion-fast: 200ms; /* hb-tokens.css:292 — nav panel fade, bars, link tilt, CONTACT/CTA fades */
  --hb-motion-card: 0.3s; /* hb-tokens.css:293 — card image zoom transition */
  --hb-motion-section: 0.5s; /* hb-tokens.css:294 — section-scoped reveal duration override */
  --hb-motion-ease-section: cubic-bezier(0.44, 0, 0.56, 1); /* hb-tokens.css:295 — pairs with --hb-motion-section */
  --hb-card-zoom: 1.05; /* hb-tokens.css:299 — TUNABLE card image hover zoom scale */
}

/* ── §1b hb-reveal bridge notes (read before attaching hb-reveal) ──────────
 * Native first: V4 Entrance Interactions (animation + delay per widget,
 * d1–d3 stagger) own reveals — plan §5 "Reveal motion" row, doctrine §4.
 * The .hb-reveal rules below are a JUSTIFIED narrow bridge, attachable ONLY
 * if Entrance Interactions cannot express the exact 0.6s +
 * cubic-bezier(0.22,1,0.36,1) + 20px travel (plan §4 hb-reveal row). If
 * native expresses it, attach delays only and leave this bridge off.
 * Hidden pre-reveal state applies ONLY under html.hb-js (set by the theme
 * script swapping documentElement class); without JS everything paints
 * visible — see §5 no-script rule, which must never be removed.
 */
.hb-reveal {
  transition:
    opacity var(--hb-motion-duration) var(--hb-motion-ease) var(--hb-delay, 0s),
    transform var(--hb-motion-duration) var(--hb-motion-ease) var(--hb-delay, 0s);
  /* hb-base.css:547-551 — reveal contract, var() colors/sizes untouched */
}

html.hb-js .hb-reveal {
  opacity: 0;
  transform: translateY(var(--hb-motion-shift));
  /* hb-base.css:553-556 — pre-reveal hidden state, JS-gated only */
}

html.hb-js .hb-reveal.is-visible {
  opacity: 1;
  transform: none;
  /* hb-base.css:558-561 — end state */
}

.hb-reveal--d1 { --hb-delay: 0.1s; } /* hb-base.css:568-570 */
.hb-reveal--d2 { --hb-delay: 0.2s; } /* hb-base.css:572-574 */
.hb-reveal--d3 { --hb-delay: 0.3s; } /* hb-base.css:576-578 */

/* Section-scoped reveal override: duration+easing stay paired (brand-book
 * §8.5 — a new duration with a mismatched easing reads as a different
 * system). Attach .hb-reveal--section only where the section spec calls it.
 */
.hb-reveal--section {
  transition-duration: var(--hb-motion-section);
  transition-timing-function: var(--hb-motion-ease-section);
  /* hb-tokens.css:294-295, contract brand-book §4.3 */
}

/* Card image zoom: JUSTIFIED narrow custom (plan §5 Cards row — image-zoom
 * hover tuning only where the widget exposes no equivalent; native image
 * controls attempted first at Chunk 6-7). No consumer in hb-base.css ships
 * this rule, so the companion owns it; duration + scale resolve to §1 vars.
 */
.hb-card__media .hb-card__img {
  transition: transform var(--hb-motion-card) var(--hb-motion-ease);
}

.hb-card__media:hover .hb-card__img,
.hb-card__media:focus-within .hb-card__img {
  transform: scale(var(--hb-card-zoom));
}

/* ── §2 Ticker loop ─────────────────────────────────────────────────────────
 * JUSTIFIED custom: Elementor ships no marquee loop control (doctrine §3
 * "Ticker marquee loop" row: native home = none; plan §5 Ticker row).
 * Structure per doctrine ticker recipe: containers + one text widget per
 * editable pill (Q5 — copy stays client-editable); the track is duplicated
 * in markup with the second copy aria-hidden="true" (markup side lands at
 * Chunks 6-9; CSS side only here).
 */
.hb-ticker {
  overflow: hidden;
  white-space: nowrap;
  /* hb-base.css:351-354 — paint only */
}

.hb-ticker__track {
  display: inline-flex;
  align-items: center;
  gap: var(--hb-space-5);
  animation: hb-ticker-scroll 22s linear infinite;
  /* hb-base.css:356-361 — gap tokenized; duration below */
}

/* Duration tuning law (doctrine ticker recipe): duration is tuned from the
 * 75px/s reference velocity (hb-motion.js:14 source tickerEffectVelocity:75;
 * hb-motion.js:148 default `|| 75`). Longer strips run longer loops at
 * constant speed: duration ≈ single-copy track width in px / 75, rounded up
 * to whole seconds. 22s is the carried default (hb-base.css:360); re-time
 * per strip at Chunks 6-9 if a strip's measured width demands it, and record
 * the new duration in that chunk's spec row.
 */
@keyframes hb-ticker-scroll {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
  /* hb-base.css:371-378 — 0 → -50% on the duplicated track; the -50% lands
     exactly on the duplicate's start, so the loop has no gap */
}

/* Hover + focus-within pause via animation-play-state — pausing needs no
 * script and no extra control (doctrine ticker recipe). Keyboard users
 * holding focus inside the strip get the same pause as hover users.
 */
.hb-ticker:hover .hb-ticker__track,
.hb-ticker:focus-within .hb-ticker__track {
  animation-play-state: paused;
}

/* No-script note: the loop is pure CSS keyframes, so it runs with or without
 * script — but meaning never depends on the duplicate: the first copy is the
 * complete strip, static and visible by default; the duplicate (aria-hidden)
 * exists only to feed the -50% loop. If keyframes are unsupported, the track
 * simply rests with the full first copy visible (same static fallback as the
 * reduced-motion rule in §5).
 */

/* ── §3 Skip link ───────────────────────────────────────────────────────────
 * ALWAYS custom (plan §5 Accessibility row; doctrine §3 Skip-link row:
 * native home = HTML widget for markup, custom home = CSS for
 * off-screen-until-focused behavior). hb-base.css ships NO skip-link rule
 * (grep "skip" hits only a motion.js comment), so this is the single home.
 * Pairs with the HTML-widget skip link placed first in tab order targeting
 * the main landmark at Chunks 6-9 — CSS side only here.
 */
.hb-skip-link {
  position: absolute;
  top: -999px;
  left: var(--hb-space-2);
  z-index: 100;
  padding: var(--hb-space-2) var(--hb-space-4);
  background-color: var(--hb-cream);
  color: var(--hb-ink);
  /* ink on cream 15.99:1 PASS AAA — brand-book §3.1 script L138 */
  font-family: var(--hb-font-body);
  font-size: var(--hb-size-body);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.hb-skip-link:focus {
  top: var(--hb-space-2);
  /* :focus (not :focus-visible) — skip links must appear for every keyboard
     arrival, including browsers that would not flag them focus-visible */
  outline: 3px solid var(--hb-ink);
  outline-offset: 3px;
}

/* ── §4 Focus-ring reinforcement ────────────────────────────────────────────
 * ALWAYS custom (plan §5 Accessibility row; doctrine §3 Focus-ring row:
 * native home = none — Elementor does not own our ring spec; doctrine §5:
 * rings never removed, Elementor defaults never override silently).
 * Law per brand-book §3.3 "Focus-ring law" (hb-base.css:185-188 prose):
 * default outline --hb-ink (15.99:1 on cream, 6.16:1 on coral — both pass
 * non-text 3:1); on dark grounds restore --hb-coral (coral on ink 6.16:1,
 * passes); nav CONTACT pill keeps its own coral ring on the ink header
 * (6.16:1, passes). Selectors mirror hb-base.css:580-605 so the reinforced
 * set and the reference set cannot drift apart silently.
 */
a.hb-btn:focus-visible,
button.hb-btn:focus-visible,
a.hb-nav__link:focus-visible,
button.hb-nav__menu:focus-visible,
a.hb-footer__link:focus-visible,
a.hb-card:focus-visible,
button.hb-testimonials__btn:focus-visible {
  outline: 3px solid var(--hb-ink);
  outline-offset: 3px;
  /* hb-base.css:580-589 — default: ink. 15.99:1 on cream / 6.16:1 on coral */
}

.hb-section--dark a.hb-btn:focus-visible,
.hb-section--dark button.hb-btn:focus-visible,
.hb-section--dark a.hb-footer__link:focus-visible,
.hb-section--dark a.hb-card:focus-visible,
.hb-section--dark button.hb-testimonials__btn:focus-visible,
.hb-nav a.hb-nav__link:focus-visible,
.hb-nav button.hb-nav__menu:focus-visible {
  outline-color: var(--hb-coral);
  /* hb-base.css:591-599 — dark grounds: coral on ink 6.16:1 PASS */
}

.hb-section--coral a.hb-btn:focus-visible,
.hb-section--coral a.hb-footer__link:focus-visible,
.hb-section--coral button.hb-testimonials__btn:focus-visible {
  outline-color: var(--hb-ink);
  /* hb-base.css:601-605 — coral grounds: ink on coral 6.16:1 PASS */
}

/* CONTACT pill on the ink header keeps its own coral ring (brand-book §3.3;
 * hb-base.css:216-219). Redundant with the .hb-nav override above by value,
 * kept explicit so a future reorder cannot silently drop the pill's ring.
 */
.hb-nav__contact:focus-visible {
  outline: 3px solid var(--hb-coral);
  outline-offset: 3px;
  /* hb-base.css:216-219 — coral on ink 6.16:1 PASS non-text 3:1 */
}

/* ── §5 Reduced-motion kill + no-script visible fallback ────────────────────
 * ALWAYS custom (plan §5 Reveal row: prefers-reduced-motion kill block +
 * no-script visible fallback — Motion Effects has no global kill switch,
 * doctrine §3 "Reduced-motion kills" row: native home = none).
 * Law per brand-book §4.3 "Reduced-motion law" + §8.5 do-NOT-touch: this
 * block kills every motion in §1 and must keep killing them — retiming work
 * never edits here. Mirrors hb-base.css:676-707.
 */
@media (prefers-reduced-motion: reduce) {
  .hb-ticker__track {
    animation: none;
    /* hb-base.css:677-679 — strip rests static, full first copy visible */
  }

  .hb-nav__panel,
  .hb-nav__bar,
  a.hb-nav__link,
  .hb-nav__contact {
    transition: none;
    /* hb-base.css:681-686 */
  }

  .hb-card__media .hb-card__img {
    transition: none;
    /* companion-owned §1b zoom joins the kill set — same law, new member */
  }

  .hb-reveal {
    transition: none;
    /* hb-base.css:688-690 */
  }

  html.hb-js .hb-reveal {
    opacity: 1;
    transform: none;
    /* hb-base.css:692-695 — reveals forced to end states, no travel */
  }

  a.hb-btn:hover,
  button.hb-btn:hover,
  a.hb-nav__link:hover,
  a.hb-footer__link:hover {
    text-decoration: none;
    /* hb-base.css:697-702 — hover-decoration shifts removed */
  }

  .hb-testimonials__track {
    scroll-behavior: auto;
    scroll-snap-type: none;
    /* hb-base.css:704-707 — carousel stepping falls back to instant */
  }
}

/* No-script visible fallback: content must never strand hidden without JS
 * (hb-base.css:12-17 wiring; doctrine §4 "Reduced-motion and no-script
 * guarantees → custom CSS regardless"). The theme swaps
 * documentElement class no-js → hb-js when script runs; without script the
 * .hb-reveal rule below (and the §1b gate above) keeps every reveal painted.
 * Chunks 6-9 MUST ship <html class="no-js"> + the swap script + this sheet,
 * else reveals strand hidden — carried obligation, not waived here.
 */
html.no-js .hb-reveal {
  opacity: 1;
  transform: none;
  /* hb-base.css:563-566 — escape hatch; never gate on .is-visible */
}

.hb-reveal {
  /* Bare .hb-reveal with NEITHER html.hb-js NOR html.no-js (class-swap
     snippet missing): fail visible, never hidden. This selector loses to
     `html.hb-js .hb-reveal` on specificity when the swap is present, so it
     changes nothing on a correctly wired page and rescues a miswired one. */
  opacity: 1;
}
