/* Cichocki Advisory — rotating hero metaphors
   Ten frames, one every 10s, crossfaded. Each frame argues the same claim a
   different way, so a visitor who stays a moment gets the point from a second
   angle rather than the same picture louder.

   WHY TWO LAYERS AND NOT TEN
   A ten-layer CSS keyframe carousel needs every image decoded before the first
   frame is right, which is ~645 KB of hero artwork on a marketing page. Two
   layers ping-pong instead: one holds what you see, the other loads what is
   next and fades in over it. Exactly one image is fetched per turn.

   WHY THE <img> STAYS
   The existing hero <img> carries srcset + fetchpriority=high and is the LCP
   element. It is untouched, so first paint is byte-identical to before the
   rotator existed. These layers sit ON TOP of it and only ever fade in after
   load. With JS off, or reduced-motion set, the page is exactly what it was.

   WHY NOTHING SHOWS THROUGH
   The incoming layer is moved to the end of the container and fades in while
   the outgoing layer stays fully opaque beneath it. An earlier version faded
   both at once, so mid-transition the base <img> was briefly visible through
   the pair — Jan caught it. Ordering is done with appendChild rather than
   z-index precisely because the scrim below must keep painting last.

   WHY THE SCRIM STILL WINS
   The scrim lives on .sov-hero-art::after. A pseudo-element generates as the
   last child, so it paints above these layers no matter how many there are —
   text protection cannot be undercut by adding frames. Every frame is
   nonetheless contrast-tested individually; see the deploy gate. */

/* The frame image arrives as a custom property rather than as background-image
   directly, so CSS can compose a wash OVER it per breakpoint. Setting
   background-image from the script would leave no room for that.

   Declared on the PARENT, used on the child: a custom property set in the same
   rule that consumes it cannot be overridden by a more specific ancestor rule
   (this is exactly what silently disabled the keystone scrim in the section
   work). Declaring it here keeps per-breakpoint and future per-frame overrides
   working. */
.sov-hero-art{
  /* DESKTOP. The static hero was tuned for ONE image; ten frames include
     brighter ones. Measured against the lead paragraph, the three brightest
     (controls-engaged, fixed-reference, designed-model) landed at 4.09-4.38
     against a 4.5 floor -- a real failure, not an artefact.

     Every frame is composed right-of-centre by design, so damping the copy
     column and letting the right side run clear costs almost no artwork. */
  --rot-wash:linear-gradient(90deg,
    rgba(10,11,15,.48) 0%,
    rgba(10,11,15,.43) 55%,
    rgba(10,11,15,.16) 80%,
    rgba(10,11,15,0)   100%);
  --rot-pos:76% 42%;
}

.sov-hero-art .ca-rot{
  position:absolute;
  inset:0;
  z-index:0;
  opacity:0;
  pointer-events:none;
  background-image:var(--rot-wash), var(--rot-src, none);
  background-repeat:no-repeat,no-repeat;
  background-size:cover,cover;
  /* matches the <img> object-position exactly, so a crossfade does not shift
     the subject sideways mid-transition */
  background-position:var(--rot-pos),var(--rot-pos);
  transition:opacity 1400ms ease-in-out;
  will-change:opacity;
}

.sov-hero-art .ca-rot.is-on{opacity:1}

/* MOBILE. The copy runs full width and the headline sits at the top, so a
   frame with a bright subject near the top centre lands directly behind it.
   Measured: the balance-scale frame put its brass beam under the h1 at 2.63:1
   against a 3.0 floor, and the attestation-seal frame at 2.30:1 -- its gold
   highlight is effectively blown out, so the background under the headline
   sits at ~0.41 relative luminance and needs roughly 0.82 wash to clear.
   Hence .86 at the very top, tapering to nothing by 88%: the band behind the
   headline is crushed, the lower two thirds of the frame still read.
   A wash on the rotating layer fixes every frame rather than one image. */
@media (max-width:720px){
  .sov-hero-art{
    --rot-pos:68% 45%;
    --rot-wash:linear-gradient(180deg,
      rgba(10,11,15,.86) 0%,
      rgba(10,11,15,.64) 30%,
      rgba(10,11,15,.28) 56%,
      rgba(10,11,15,0)   88%);
  }
}

/* A rotating background is motion. Someone who has asked for less of it gets
   the single static hero.

   The layers ARE created (the script must keep its matchMedia listener live so
   that turning reduce off mid-session still works), but they are display:none
   here and the script never assigns them an image — no turn is ever scheduled.
   So: mounted, hidden, and zero bytes fetched. The script enforces the same
   thing; this rule is the belt to its braces. */
@media (prefers-reduced-motion:reduce){
  .sov-hero-art .ca-rot{
    display:none !important;
    transition:none !important;
  }
}

/* Printing a fading background is meaningless and costs ink. */
@media print{
  .sov-hero-art .ca-rot{display:none !important}
}

/* ── pause control (WCAG 2.2.2) ─────────────────────────────────────────
   The first draft had no control and argued the rotator was decorative and
   therefore exempt. Codex rejected that reasoning (consult 20260802T183439Z):
   the background auto-updates indefinitely alongside page content, and
   prefers-reduced-motion only covers people who have set it. So: a real
   button, reachable by keyboard, labelled by state, remembered across visits.

   It is injected by the script, so it exists only when something is actually
   moving — a pause button next to a static image would be a lie.

   Placed bottom-LEFT, not bottom-right. The site's chat launcher is
   position:fixed at the bottom-right of the VIEWPORT, so anything anchored to
   the hero's bottom-right corner collides with it at every window size — the
   first attempt did exactly that and the control ended up underneath the
   launcher. The left gutter is the darkest part of the hero and is free. */
.sov-hero{position:relative}

.ca-rot-toggle{
  position:absolute;
  left:24px;
  bottom:22px;
  z-index:3;                      /* above the artwork AND the scrim */
  width:36px;
  height:36px;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  padding:0;
  border-radius:999px;
  cursor:pointer;
  color:rgba(232,234,240,.72);
  background:rgba(10,11,15,.62);
  border:1px solid rgba(216,190,134,.28);
  -webkit-backdrop-filter:blur(6px);
  backdrop-filter:blur(6px);
  transition:color .18s ease, border-color .18s ease, background-color .18s ease;
}
.ca-rot-toggle svg{width:15px;height:15px;fill:currentColor;display:block}

.ca-rot-toggle:hover{
  color:#D8BE86;
  border-color:rgba(216,190,134,.62);
  background:rgba(10,11,15,.80);
}

/* Never rely on hover alone: the control must be obvious to a keyboard user. */
.ca-rot-toggle:focus-visible{
  outline:2px solid #D8BE86;
  outline-offset:3px;
  color:#D8BE86;
}

@media (max-width:720px){
  .ca-rot-toggle{left:16px;bottom:16px;width:40px;height:40px}   /* 40px touch target */
}

@media print{ .ca-rot-toggle{display:none !important} }

/* ── HERO CAPTION ────────────────────────────────────────────────────────
   Names the metaphor and states the claim it makes. Kept deliberately as a
   quiet plate, not a card: it must read as a museum label beside the work,
   never as a callout competing with the headline.

   Upper right, above scrim and artwork. It carries its own ground rather than
   trusting whatever frame happens to be behind it — 31 images rotate under it
   and the caption has to be legible on all of them. */
.ca-rot-badge{
  position:absolute;
  top:18px;
  right:18px;
  z-index:3;
  width:min(288px, 34vw);
  padding:11px 14px 12px;
  border-radius:12px;
  color:#E8EAF0;
  background:rgba(10,11,15,.80);
  border:1px solid rgba(216,190,134,.34);
  -webkit-backdrop-filter:blur(9px) saturate(115%);
  backdrop-filter:blur(9px) saturate(115%);
  pointer-events:none;
  box-shadow:0 10px 30px rgba(0,0,0,.34);
}

.ca-rot-badge__head{
  display:flex;
  align-items:baseline;
  gap:3px;
  flex-wrap:wrap;
}
.ca-rot-badge b{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:17px;font-weight:700;line-height:1;
  color:#D8BE86;letter-spacing:.02em;
}
.ca-rot-badge span{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:11px;line-height:1;color:rgba(232,234,240,.45);
  margin-right:7px;
}
.ca-rot-badge strong{
  font-size:12px;font-weight:600;line-height:1.2;
  letter-spacing:.07em;text-transform:uppercase;
  color:#E8EAF0;
}
/* the claim — the part that is actually doing the marketing */
.ca-rot-badge p{
  margin:6px 0 0;
  font-size:12.5px;
  line-height:1.42;
  color:rgba(232,234,240,.80);
}

@media (max-width:900px){
  /* the hero copy needs the width more than the caption does */
  .ca-rot-badge{width:auto;max-width:none;left:auto;right:12px;top:12px;padding:8px 11px}
  .ca-rot-badge p{display:none}
  .ca-rot-badge strong{font-size:11px}
}

/* No caption when nothing is rotating. */
@media (prefers-reduced-motion:reduce){ .ca-rot-badge{display:none !important} }
@media print{ .ca-rot-badge{display:none !important} }

/* ── WIDE SCREENS — crop the artwork's own black edge ───────────────────
   Every frame is deliberately near-black across its LEFT THIRD so the copy
   always has dark ground. The hero is far wider in aspect than the image, so
   `cover` shows the ENTIRE image width — including that black edge, which on a
   2560 monitor reads as 760px of dead space.

   Two rules had to be beaten, found by enumerating what actually matches the
   element rather than assuming:
     - an INLINE <style> in the page head sets `.sov-hero-art img{width:100%}`
       and loads AFTER this sheet, so equal specificity loses. Hence the extra
       `.sov-hero` in the selector.
     - tailwind sets `img{max-width:100%}`.
   A previous attempt set only margin-left and lost the width, which shifted the
   image without widening it and left the RIGHT side empty instead.

   Anchored with right:0 so the overflow goes off the LEFT edge, which is the
   part being cropped. .sov-hero is overflow:hidden, so nothing spills. */
@media (min-width:1600px){
  .sov-hero .sov-hero-art img{
    position:absolute;
    top:0;
    right:0;
    width:128%;
    height:100%;
    max-width:none;
  }
  .sov-hero .sov-hero-art .ca-rot{
    background-size:128% auto, 128% auto;
    background-position:right center, right center;
  }
}
@media (min-width:2200px){
  .sov-hero .sov-hero-art img{width:150%}
  .sov-hero .sov-hero-art .ca-rot{background-size:150% auto, 150% auto}
}

/* ── ?v2 PREVIEW — proposed edge-to-edge hero (not the live design) ──────
   Visible only at /?v2 so Jan can judge it in a real browser. Delete this
   block and the matching JS toggle once the direction is settled. */
.ca-v2 .sov-hero-art img{
  content:url("/assets/img/hero-v2c-2560w.webp");
  position:absolute; inset:0;
  width:100%; height:100%; max-width:none;
  object-fit:cover; object-position:center;
}
.ca-v2 .sov-hero-art .ca-rot{display:none !important}
.ca-v2 .sov-hero-art::after{
  background:
    linear-gradient(90deg,
      rgba(10,11,15,.18) 0%,
      rgba(10,11,15,.55) 13%,
      rgba(10,11,15,.93) 21%,
      rgba(10,11,15,.93) 66%,
      rgba(10,11,15,.46) 82%,
      rgba(10,11,15,.14) 100%),
    linear-gradient(180deg, rgba(10,11,15,.42) 0%, rgba(10,11,15,0) 42%, rgba(10,11,15,.88) 100%) !important;
}

.ca-v2a .sov-hero-art img{content:url("/assets/img/hero-v2a-2560w.webp") !important}
.ca-v2b .sov-hero-art img{content:url("/assets/img/hero-v2b-2560w.webp") !important}
.ca-v2c .sov-hero-art img{content:url("/assets/img/hero-v2c-2560w.webp") !important}
