/* ═══════════════════════════════════════════════════════════════════
   SSO design classes — every value derived, none declared
   ────────────────────────────────────────────────────────────────
   The four --e-global-* properties below are published by the Elementor
   kit on <body class="elementor-kit-NN">. They are what the client edits
   in Site Settings → Global Colors / Global Fonts. This file adds NO brand
   value of its own; it only derives the working set from them.

   That is the whole point. Change the accent in Elementor and the native
   widgets, these classes and the motion layer move together.
═══════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════
   `body`, NOT `:root`. This is the entire file's load-bearing detail.
   ────────────────────────────────────────────────────────────────
   Elementor publishes the kit's custom properties on
   `<body class="elementor-kit-NN">` — not on `<html>`. A derivation
   written at `:root` therefore evaluates where NONE of the
   --e-global-* properties exist, silently takes the fallback after the
   comma, and hands that inherited value down to the whole document.

   It shipped like that, and it was invisible for a day, because the
   fallbacks had been chosen to match this client's brand: --sso-accent
   fell back to #d42a1c, the kit also said #D42A1C, and every check I
   ran agreed. The only token whose fallback did NOT match was the
   typeface — the kit said Space Grotesk, `:root` resolved Inter, and
   Inter is self-hosted nowhere, so every heading on the site rendered
   in the browser's default sans. "The fonts look generic" was the one
   symptom this bug was able to produce.

   The lesson is not "use body". It is that a fallback which equals the
   real value cannot fail visibly, so it cannot be tested. Hence the
   `:root` block below: deliberately NEUTRAL values, nothing brand-ish.
   If the kit is ever unreachable again the page turns plainly grey and
   system-font, and anyone looking at it knows within a second.
═══════════════════════════════════════════════════════════════════ */

:root {
  --sso-ink: #101010;   --sso-accent: #666;   --sso-paper: #ededed;   --sso-text: #333;
  --sso-display: system-ui, sans-serif;
  --sso-body:    system-ui, sans-serif;
  --sso-mono:    ui-monospace, monospace;
}

body {
  --sso-ink:    var(--e-global-color-primary, #000);
  --sso-accent: var(--e-global-color-accent, #d42a1c);
  --sso-paper:  var(--e-global-color-secondary, #f4f2ee);
  --sso-text:   var(--e-global-color-text, #111);

  --sso-display: var(--e-global-typography-primary-font-family, system-ui), system-ui, sans-serif;
  --sso-body:    var(--e-global-typography-text-font-family, system-ui), system-ui, sans-serif;
  --sso-mono:    var(--e-global-typography-accent-font-family, ui-monospace), ui-monospace, monospace;

  /* Derived, not declared: everything below is a transform of the above. */
  --sso-on-ink:   color-mix(in srgb, var(--sso-paper) 96%, transparent);
  --sso-dim:      color-mix(in srgb, var(--sso-paper) 58%, transparent);
  --sso-ghost:    color-mix(in srgb, var(--sso-paper) 22%, transparent);
  --sso-hair:     color-mix(in srgb, var(--sso-paper) 14%, transparent);
  --sso-hair-ink: color-mix(in srgb, var(--sso-ink) 16%, transparent);
  --sso-mute-ink: color-mix(in srgb, var(--sso-ink) 62%, transparent);

  --sso-ease: cubic-bezier(.2,.7,.3,1);
  --sso-gutter: clamp(20px, 5vw, 88px);
}

/* ── type ─────────────────────────────────────────────────────────── */

.sso-kicker {
  font-family: var(--sso-mono);
  font-size: 12px; letter-spacing: .22em; text-transform: uppercase;
  color: var(--sso-accent); margin: 0;
}
.sso-kicker--rule {
  padding-block-end: 8px;
  border-block-end: 1px solid var(--sso-hair);
  width: fit-content;
}

.sso-h1 { font-family: var(--sso-display); font-weight: 700;
  font-size: clamp(52px, 11vw, 164px); line-height: .86; letter-spacing: -.04em; margin: 0; }
.sso-h2 { font-family: var(--sso-display); font-weight: 700;
  font-size: clamp(34px, 7vw, 104px); line-height: .9; letter-spacing: -.035em; margin: 0; }
.sso-h3 { font-family: var(--sso-display); font-weight: 700;
  font-size: clamp(23px, 3vw, 40px); line-height: 1.08; letter-spacing: -.02em; margin: 0; }

.sso-lead { font-family: var(--sso-body); font-size: clamp(16px, 1.3vw, 20px);
  line-height: 1.55; max-width: 56ch; margin: 0; }
.sso-body { font-family: var(--sso-body); font-size: 16px; line-height: 1.6;
  max-width: 62ch; margin: 0; }

/* Tone, on either ground. */
.sso-on-dark  { color: var(--sso-on-ink); }
.sso-on-dark  .sso-lead, .sso-lead.sso-muted { color: var(--sso-dim); }
.sso-ghost    { color: var(--sso-ghost); }
.sso-on-light { color: var(--sso-ink); }
.sso-on-light .sso-body, .sso-body.sso-muted { color: var(--sso-mute-ink); }

/* ── surfaces ─────────────────────────────────────────────────────── */

.sso-ink   { background-color: var(--sso-ink); }
.sso-paper { background-color: var(--sso-paper); }

/* Two gradients, not one. The vertical pass sets the mood; the horizontal
   pass is what actually buys legibility, because the type is left-aligned and
   a photograph's bright area is wherever it happens to be. On the first live
   hero the kicker sat over a lit wall at 30% scrim and was unreadable — the
   fix is not "more black everywhere", which flattens the picture, but black
   where the words are. */
.sso-scrim { position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(90deg,
      color-mix(in srgb, var(--sso-ink) 74%, transparent) 0%,
      color-mix(in srgb, var(--sso-ink) 34%, transparent) 46%,
      transparent 72%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--sso-ink) 56%, transparent) 0%,
      color-mix(in srgb, var(--sso-ink) 26%, transparent) 34%,
      color-mix(in srgb, var(--sso-ink) 96%, transparent) 100%); }

/* ── the ladder row: the hairline that opens on hover ─────────────── */

.sso-row { border-block-start: 1px solid var(--sso-hair-ink);
  transition: padding-inline-start .5s var(--sso-ease), background-color .5s var(--sso-ease); }
.sso-row:hover { padding-inline-start: 22px;
  background-color: color-mix(in srgb, var(--sso-ink) 5%, transparent); }
.sso-row__n { font-family: var(--sso-mono); font-size: 12px; letter-spacing: .14em;
  color: var(--sso-accent); min-width: 34px; margin: 7px 0 0 0; }

/* ── figures ──────────────────────────────────────────────────────── */

.sso-figure { border-block-start: 2px solid var(--sso-accent); padding-block-start: 22px; }
.sso-figure__n { font-family: var(--sso-display); font-weight: 700;
  font-size: clamp(52px, 8vw, 110px); line-height: .9; letter-spacing: -.045em;
  color: var(--sso-on-ink); margin: 0; }
.sso-figure__l { font-family: var(--sso-mono); font-size: 11px; letter-spacing: .2em;
  text-transform: uppercase; color: var(--sso-dim); margin: 0; }

/* ── media ────────────────────────────────────────────────────────── */

.sso-frame { overflow: hidden; border-radius: 3px; border: 1px solid var(--sso-hair);
  transition: border-color .6s var(--sso-ease); }
.sso-frame:hover { border-color: var(--sso-accent); }
/* Note `img.sso-media` as well as `.sso-media img`.
   Elementor's V4 `e-image` renders the <img> ITSELF — there is no wrapper —
   so a class put on that element lands on the img. A descendant-only selector
   silently does nothing, which is how the hero shipped stretched
   (object-fit: fill) and in full colour while the two framed images beside it
   were correctly cropped and desaturated. Both forms, every time. */
.sso-frame img, img.sso-frame,
.sso-media img, img.sso-media { display: block; width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(.55) contrast(1.05);
  transition: filter .7s var(--sso-ease), transform .9s var(--sso-ease); }
.sso-frame:hover img, img.sso-frame:hover { filter: grayscale(0) contrast(1.02); transform: scale(1.05); }
/* A scrubbed image is driven by scroll, one frame at a time. A .9s transform
   transition would make every frame chase the last one and the parallax would
   lag a half-second behind the page. Keep the filter fade, drop the rest. */
img.sso-media.sso-fx-scrub, .sso-media.sso-fx-scrub { transition: filter .7s var(--sso-ease); }

/* ── buttons ──────────────────────────────────────────────────────── */

.sso-btn { display: inline-flex; align-items: center; gap: 14px;
  padding: 17px 30px; border-radius: 2px; cursor: pointer; text-decoration: none;
  border: 1px solid currentColor; background-color: transparent; width: fit-content;
  font-family: var(--sso-mono); font-size: 12px; letter-spacing: .16em; text-transform: uppercase;
  transition: background-color .45s var(--sso-ease), border-color .45s var(--sso-ease),
              color .45s var(--sso-ease), transform .45s var(--sso-ease),
              box-shadow .45s var(--sso-ease); }
.sso-btn--light { color: var(--sso-on-ink); }
.sso-btn--light:hover { background-color: var(--sso-accent); border-color: var(--sso-accent);
  color: var(--sso-paper); transform: translateY(-3px);
  box-shadow: 0 18px 40px color-mix(in srgb, var(--sso-accent) 34%, transparent); }
.sso-btn--dark { color: var(--sso-ink); }
.sso-btn--dark:hover { background-color: var(--sso-ink); color: var(--sso-paper);
  transform: translateY(-4px);
  box-shadow: 0 22px 46px color-mix(in srgb, var(--sso-ink) 30%, transparent); }

/* ── the marquee: one signature moment, CSS-only ──────────────────── */

.sso-marquee { overflow: hidden; border-block: 1px solid var(--sso-hair); padding-block: 18px; }
.sso-marquee__t { display: inline-block; white-space: nowrap; will-change: transform;
  font-family: var(--sso-display); font-weight: 700;
  font-size: clamp(28px, 5vw, 66px); letter-spacing: -.02em; color: var(--sso-ghost); }
.sso-fx-ready .sso-marquee__t { animation: sso-marquee 28s linear infinite; }
@keyframes sso-marquee { from { transform: translate3d(0,0,0); } to { transform: translate3d(-50%,0,0); } }

/* ═══════════════════════════════════════════════════════════════════
   MOTION — six moves, and every one of them is opt-in by class name
   ────────────────────────────────────────────────────────────────
   Note the .sso-fx-ready gate on EVERY hidden state. Without JavaScript
   that class is never added, nothing is ever hidden, and the page reads
   perfectly. The old runtime hid content first and revealed it second,
   so a failed script left a blank page.
═══════════════════════════════════════════════════════════════════ */

.sso-fx-ready .sso-fx-reveal,
.sso-fx-ready .sso-fx-stagger > * {
  opacity: 0; transform: translate3d(0, 26px, 0);
  transition: opacity .8s var(--sso-ease), transform .8s var(--sso-ease);
}
.sso-fx-ready .sso-fx-reveal--left  { transform: translate3d(-34px, 0, 0); }
.sso-fx-ready .sso-fx-reveal--right { transform: translate3d(34px, 0, 0); }
.sso-fx-ready .sso-fx-scale         { opacity: 0; transform: scale(.92);
  transition: opacity .65s var(--sso-ease), transform .65s var(--sso-ease); }

.sso-fx-ready .sso-fx-reveal.is-in,
.sso-fx-ready .sso-fx-stagger > .is-in,
.sso-fx-ready .sso-fx-scale.is-in { opacity: 1; transform: none; }

/* Scrub — the runtime only writes --sso-p (0→1). The look lives here. */
.sso-fx-ready .sso-fx-scrub { transform: translate3d(0, calc(var(--sso-p, 0) * -8%), 0) scale(calc(1 + var(--sso-p, 0) * .08)); }

/* Split — the runtime wraps words; the timing is CSS. */
.sso-fx-ready .sso-fx-split .sso-w { display: inline-block; opacity: 0;
  transform: translate3d(0, .5em, 0);
  transition: opacity .7s var(--sso-ease), transform .7s var(--sso-ease);
  transition-delay: calc(var(--sso-i, 0) * 55ms); }
.sso-fx-ready .sso-fx-split.is-in .sso-w { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .sso-fx-reveal, .sso-fx-stagger > *, .sso-fx-scale,
  .sso-fx-split .sso-w { opacity: 1 !important; transform: none !important; }
  .sso-marquee__t { animation: none !important; }
}

/* ═══════════════════════════════════════════════════════════════════
   PART TWO — the pieces that make it stop looking like a template
   ────────────────────────────────────────────────────────────────
   Same two rules as everything above. Every colour and typeface is
   derived from the Elementor kit; the runtime may touch transform,
   opacity, a class and a custom property, and nothing else.

   Every effect below is opt-in by a class name a client can type into
   Elementor's "CSS Classes" field, and every one of them degrades to
   something readable when its script does not run.
═══════════════════════════════════════════════════════════════════ */

/* ── 1 · IMAGE HOVER: the caption arrives ──────────────────────────
   Markup, all native Elementor elements:

     .sso-reveal            (a container, position:relative)
       └ img.sso-media      the photograph
       └ .sso-reveal__cap   a container
           ├ .sso-reveal__t   heading
           └ .sso-reveal__d   paragraph

   :focus-within is not decoration — without it the caption is
   unreachable by keyboard, and the caption is where the actual
   information lives. Anything a mouse can reveal, Tab must reveal. */

.sso-reveal { position: relative; overflow: hidden; isolation: isolate; }
.sso-reveal__cap {
  position: absolute; inset-inline: 0; inset-block-end: 0; z-index: 2;
  display: flex; flex-direction: column; gap: 8px;
  padding: clamp(18px, 2.4vw, 34px);
  background: linear-gradient(0deg,
    color-mix(in srgb, var(--sso-ink) 94%, transparent) 0%,
    color-mix(in srgb, var(--sso-ink) 72%, transparent) 58%,
    transparent 100%);
  transform: translate3d(0, 24px, 0); opacity: 0;
  transition: transform .62s var(--sso-ease), opacity .45s var(--sso-ease); }
.sso-reveal:hover .sso-reveal__cap,
.sso-reveal:focus-within .sso-reveal__cap { transform: none; opacity: 1; }

.sso-reveal__t { font-family: var(--sso-display); font-weight: 700;
  font-size: clamp(19px, 1.9vw, 30px); line-height: 1.06; letter-spacing: -.02em;
  color: var(--sso-on-ink); margin: 0; }
.sso-reveal__d { font-family: var(--sso-body); font-size: 14px; line-height: 1.5;
  color: var(--sso-dim); margin: 0; max-width: 46ch;
  transform: translate3d(0, 12px, 0); opacity: 0;
  transition: transform .6s var(--sso-ease) .08s, opacity .5s var(--sso-ease) .08s; }
.sso-reveal:hover .sso-reveal__d,
.sso-reveal:focus-within .sso-reveal__d { transform: none; opacity: 1; }

/* A hairline that draws itself across the top of the caption. */
.sso-reveal__cap::before { content: ''; position: absolute; inset-block-start: 0;
  inset-inline-start: clamp(18px, 2.4vw, 34px); inset-inline-end: clamp(18px, 2.4vw, 34px);
  height: 2px; background: var(--sso-accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform .7s var(--sso-ease); }
.sso-reveal:hover .sso-reveal__cap::before,
.sso-reveal:focus-within .sso-reveal__cap::before { transform: scaleX(1); }

.sso-reveal img { transition: transform .9s var(--sso-ease), filter .7s var(--sso-ease); }
.sso-reveal:hover img { transform: scale(1.045); }

/* ── 2 · THE CURSOR ────────────────────────────────────────────────
   A dot that tracks exactly and a ring that lags behind it. The ring
   is what reads as "expensive"; the dot is what keeps it usable.

   Gated on (hover: hover) and (pointer: fine) so it never appears on
   a touch device, and the real cursor is only hidden once the runtime
   has actually drawn a replacement — `sso-cursor-on` is set by JS. A
   page that hides the pointer and then fails to draw one is unusable,
   and that failure is invisible in a screenshot. */

.sso-cursor, .sso-cursor__ring { display: none; }

/* Scoped back, on report: "the mouse pointer sucks, should only change on
   certain sections or on hover over link."

   It was wrong in two ways. It replaced the system pointer EVERYWHERE, which
   is a toy rather than a detail — and `cursor: none` on the whole document
   means any failure in the runtime leaves a page with no pointer at all. Now
   the real pointer is never hidden and never touched; the ring is an accent
   that appears around it over links, buttons and elements the client has
   opted in with an sso-cursor-* class, and is invisible the rest of the
   time. */
@media (hover: hover) and (pointer: fine) {
  .sso-cursor, .sso-cursor__ring {
    display: block; position: fixed; inset-block-start: 0; inset-inline-start: 0;
    z-index: 2147483000; pointer-events: none;
    transform: translate3d(-50%, -50%, 0); will-change: transform; }

  .sso-cursor { width: 6px; height: 6px; border-radius: 50%;
    background: var(--sso-accent);
    transition: width .3s var(--sso-ease), height .3s var(--sso-ease),
                background-color .3s var(--sso-ease), opacity .3s var(--sso-ease); }

  .sso-cursor__ring { width: 34px; height: 34px; border-radius: 50%;
    border: 1px solid color-mix(in srgb, var(--sso-accent) 55%, transparent);
    display: grid; place-items: center;
    transition: width .38s var(--sso-ease), height .38s var(--sso-ease),
                border-color .38s var(--sso-ease), background-color .38s var(--sso-ease); }

  /* The label lives inside the ring and only exists when the ring is big. */
  .sso-cursor__label { font-family: var(--sso-mono); font-size: 9px;
    letter-spacing: .18em; text-transform: uppercase; color: var(--sso-paper);
    opacity: 0; transition: opacity .25s var(--sso-ease); white-space: nowrap; }

  /* Invisible by default. The system pointer is doing its job; this only
     joins in when there is something to say. */
  .sso-cursor      { opacity: 0; }
  .sso-cursor__ring { opacity: 0; width: 0; height: 0; }

  .sso-cursor-on.is-link  .sso-cursor__ring { opacity: 1; width: 46px; height: 46px;
    border-color: color-mix(in srgb, var(--sso-accent) 70%, transparent);
    background: color-mix(in srgb, var(--sso-accent) 10%, transparent); }

  .sso-cursor-on.is-label .sso-cursor__ring { opacity: 1; width: 78px; height: 78px;
    border-color: transparent; background: var(--sso-accent); }
  .sso-cursor-on.is-label .sso-cursor__label { opacity: 1; }

  .sso-cursor-on.is-down  .sso-cursor__ring { transform: translate3d(var(--sso-cx,0), var(--sso-cy,0), 0) translate(-50%,-50%) scale(.86); }
}

/* The opt-in hooks. `sso-cursor-view` / `sso-cursor-drag` put a word in
   the ring; the runtime reads the class, never an attribute, because a
   class is the one thing Elementor gives the client a field for. */
.sso-cursor-view, .sso-cursor-drag, .sso-cursor-open { /* marker classes */ }

/* ── 3 · THE SLIDER ────────────────────────────────────────────────
   Deliberately NOT a carousel library. It is a scroll-snap strip, so:

     · with no JavaScript at all it is a horizontally scrollable row of
       slides — every slide reachable, nothing hidden
     · touch and trackpad work natively, with the platform's own physics
     · Tab and arrow keys work natively
     · the runtime NEVER restructures the DOM, which matters because
       Elementor's editor renders the live front end in an iframe — a
       carousel that moves children into a track breaks editing

   Each slide is an ordinary Elementor container. The client adds one by
   duplicating it. */

.sso-slider { position: relative; }
.sso-slider__track {
  display: flex; overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scroll-behavior: smooth;
  scrollbar-width: none; -ms-overflow-style: none; }
.sso-slider__track::-webkit-scrollbar { display: none; }
/* `flex: 0 0 100%` needs to beat Elementor's own per-element class, which
   ties on specificity and wins on order. Two classes does it, and the
   selector is still something a human can read. */
.sso-slider .sso-slider__track > * { flex: 0 0 100%; scroll-snap-align: start; position: relative; }
.sso-slider.is-dragging .sso-slider__track { scroll-behavior: auto; cursor: grabbing; }
.sso-slider.is-dragging .sso-slider__track > * { pointer-events: none; }

/* The rail: a counter, a progress line, and nothing else. */
.sso-slider__rail { position: absolute; z-index: 3;
  inset-inline: var(--sso-gutter); inset-block-end: clamp(20px, 3.4vh, 40px);
  display: flex; align-items: center; gap: 20px; pointer-events: none; }
.sso-slider__n { font-family: var(--sso-mono); font-size: 12px; letter-spacing: .16em;
  color: var(--sso-on-ink); white-space: nowrap; }
.sso-slider__n b { color: var(--sso-accent); font-weight: 500; }
.sso-slider__bar { flex: 1; height: 1px; background: var(--sso-hair); position: relative; }
.sso-slider__bar i { position: absolute; inset-block: 0; inset-inline-start: 0;
  background: var(--sso-accent); width: var(--sso-w, 33%);
  transition: width .45s var(--sso-ease), transform .45s var(--sso-ease);
  transform: translate3d(calc(var(--sso-x, 0) * 100%), 0, 0); }

/* ── 4 · HEADER & MENU ─────────────────────────────────────────────
   `sso-nav` on the Elementor nav widget, `sso-header` on the header
   container. Both are classes typed into a normal field. */

.sso-nav a, a.sso-nav-link { position: relative; }
.sso-nav a::after, a.sso-nav-link::after {
  content: ''; position: absolute; inset-inline: 0; inset-block-end: -5px; height: 1px;
  background: var(--sso-accent);
  transform: scaleX(0); transform-origin: right;
  transition: transform .45s var(--sso-ease); }
.sso-nav a:hover::after, .sso-nav a:focus-visible::after,
a.sso-nav-link:hover::after { transform: scaleX(1); transform-origin: left; }

/* Hides going down, returns coming up. Transform only — a header that
   animates `top` reflows the whole document on every scroll frame. */
.sso-header { transition: transform .5s var(--sso-ease),
                          background-color .5s var(--sso-ease); }
.sso-fx-ready .sso-header.is-hidden { transform: translate3d(0, -102%, 0); }
.sso-header.is-stuck { background-color: color-mix(in srgb, var(--sso-ink) 92%, transparent);
  backdrop-filter: saturate(1.4) blur(9px); }

/* ── 5 · WEBGL SURFACES ────────────────────────────────────────────
   The canvas is inserted only AFTER a context is successfully created,
   and it sits on top of the image rather than replacing it. If WebGL is
   unavailable, blocked, or the context is lost, what remains on screen
   is the photograph — which is the correct fallback and needs no code.

   These two rules are the entire CSS cost of the effect. */

.sso-gl-canvas { position: absolute; inset: 0; width: 100%; height: 100%;
  display: block; pointer-events: none; opacity: 0;
  transition: opacity .6s var(--sso-ease); }
.sso-gl-canvas.is-on { opacity: 1; }
/* Once the shader is showing the picture, the <img> underneath is a
   duplicate. Keep it in the accessibility tree, take it out of the paint. */
.sso-gl-live > img { visibility: hidden; }
@media (prefers-reduced-motion: reduce) {
  .sso-gl-canvas { display: none !important; }
  .sso-gl-live > img { visibility: visible !important; }
}

/* ── 6 · SLIDE INTERIOR ────────────────────────────────────────────
   A hero slide is a full-bleed picture with type over it. Stated once
   here rather than as inline layout on every slide, because it is a
   rule of the design and not a per-instance decision. */

.sso-slide { position: relative; display: flex; flex-direction: column;
  justify-content: flex-end; overflow: hidden; isolation: isolate; }
.sso-slide > img { position: absolute; inset: 0; z-index: 0; }
.sso-slide > .sso-scrim { z-index: 1; }
.sso-slide__body { position: relative; z-index: 2; display: flex; flex-direction: column;
  gap: clamp(12px, 2vh, 22px); max-width: 1180px;
  padding: 0 var(--sso-gutter) clamp(96px, 13vh, 156px); }

/* The slide that is currently snapped gets its type; the others hold it
   back a little. One class, written by the runtime, no layout thrash. */
.sso-fx-ready .sso-slider__track > *:not(.is-current) .sso-slide__body {
  opacity: .12; transform: translate3d(0, 16px, 0); }
.sso-slider__track > * .sso-slide__body {
  transition: opacity .7s var(--sso-ease), transform .7s var(--sso-ease); }


/* ═══════════════════════════════════════════════════════════════════
   THE EDITOR IS A SECOND RENDERING ENVIRONMENT
   ────────────────────────────────────────────────────────────────
   Belt and braces beside the wp_head guard. If `sso-fx-ready` ever
   reaches the editor iframe by some route I have not thought of, this
   makes sure the client still sees their page: nothing hidden, nothing
   dimmed, no horizontal clipping, no shader over the photograph.

   Editing a page you cannot see is not a degraded experience, it is a
   broken product. It costs four rules to make that impossible.
═══════════════════════════════════════════════════════════════════ */

body.elementor-editor-active .sso-fx-reveal,
body.elementor-editor-active .sso-fx-stagger > *,
body.elementor-editor-active .sso-fx-scale,
body.elementor-editor-active .sso-fx-split .sso-w,
body.elementor-editor-active .sso-slide__body {
  opacity: 1 !important; transform: none !important; }

body.elementor-editor-active .sso-slider__track {
  overflow-x: visible !important; flex-wrap: wrap !important; }
body.elementor-editor-active .sso-slider .sso-slider__track > * {
  flex: 1 1 100% !important; }

body.elementor-editor-active .sso-reveal__cap,
body.elementor-editor-active .sso-reveal__d {
  opacity: 1 !important; transform: none !important; }
body.elementor-editor-active .sso-gl-canvas { display: none !important; }
body.elementor-editor-active .sso-gl-live > img { visibility: visible !important; }
body.elementor-editor-active .sso-cursor,
body.elementor-editor-active .sso-cursor__ring { display: none !important; }

/* ═══════════════════════════════════════════════════════════════════
   THE HORIZONTAL ACCORDION
   ────────────────────────────────────────────────────────────────
   Panels sit at equal width; the one under the pointer expands and its
   photograph comes up to full colour while the others narrow. Asked
   for as: "on hover the section widens from 25% to like 80% and the
   image is fully revealed as well as additional descriptions appear."

   Two things make this worth having rather than clever:

   · It is CSS-only. No runtime, no JS, no library. `:hover` on the
     panel, `:has(:hover)` on the row to narrow the siblings. With
     JavaScript off it is four equal panels with four legible titles —
     which is a perfectly good section, not a broken one.

   · `:focus-within` sits beside `:hover` on every rule. The
     description is the content; if only a mouse can reach it, the
     section does not exist for a keyboard or a screen reader.

   Layout (padding, flex, gap) is set per-instance on the elements, for
   the reason recorded at the top of atomic-showcase.js. What lives
   here is what recurs: the expansion curve, the colour lift, the
   swap between the vertical tag and the horizontal title.
═══════════════════════════════════════════════════════════════════ */

.sso-hacc__p {
  position: relative; overflow: hidden; min-width: 0;
  border-radius: 3px; cursor: pointer;
  transition: flex-grow .8s var(--sso-ease); }

/* The expansion. 1 → 4.6 against three siblings at .78 puts the open
   panel near 70% of the row, which is the ratio that reads as "opened"
   without the closed ones becoming unreadable slivers. */
.sso-hacc:hover .sso-hacc__p:not(:hover),
.sso-hacc:focus-within .sso-hacc__p:not(:focus-within) { flex-grow: .78; }
.sso-hacc__p:hover, .sso-hacc__p:focus-within { flex-grow: 4.6; }

.sso-hacc__p > img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(1) brightness(.42) contrast(1.1);
  transform: scale(1.08);
  transition: filter 1s var(--sso-ease), transform 1.2s var(--sso-ease); }
.sso-hacc__p:hover > img, .sso-hacc__p:focus-within > img {
  filter: grayscale(0) brightness(.66) contrast(1.02); transform: scale(1); }

/* A red rule that draws itself down the leading edge of the open panel. */
.sso-hacc__p::before { content: ''; position: absolute; z-index: 2;
  inset-block: 0; inset-inline-start: 0; width: 2px;
  background: var(--sso-accent); transform: scaleY(0); transform-origin: top;
  transition: transform .7s var(--sso-ease); }
.sso-hacc__p:hover::before, .sso-hacc__p:focus-within::before { transform: scaleY(1); }

/* Legibility floor, under the type, over the picture. */
.sso-hacc__p::after { content: ''; position: absolute; inset: 0; z-index: 1;
  pointer-events: none;
  background: linear-gradient(0deg,
    color-mix(in srgb, var(--sso-ink) 88%, transparent) 0%,
    color-mix(in srgb, var(--sso-ink) 34%, transparent) 52%,
    color-mix(in srgb, var(--sso-ink) 12%, transparent) 100%); }

/* The number: always there, in the corner, quiet. */
.sso-hacc__n { font-family: var(--sso-mono); font-size: 11px; letter-spacing: .2em;
  color: var(--sso-accent); margin: 0; }

/* Collapsed state shows a vertical tag; open state shows the real title.
   Two elements rather than one rotating element, because writing-mode does
   not animate and a client can see and edit both. */
.sso-hacc__tag { font-family: var(--sso-mono); font-size: 12px; letter-spacing: .22em;
  text-transform: uppercase; color: var(--sso-on-ink); margin: 0;
  writing-mode: vertical-rl; transform: rotate(180deg); white-space: nowrap;
  opacity: 1; transition: opacity .35s var(--sso-ease); }
.sso-hacc__p:hover .sso-hacc__tag,
.sso-hacc__p:focus-within .sso-hacc__tag { opacity: 0; }

.sso-hacc__t { font-family: var(--sso-display); font-weight: 700;
  font-size: clamp(24px, 2.6vw, 46px); line-height: 1.02; letter-spacing: -.025em;
  color: var(--sso-on-ink); margin: 0; white-space: nowrap;
  opacity: 0; transform: translate3d(0, 14px, 0);
  transition: opacity .5s var(--sso-ease) .12s, transform .6s var(--sso-ease) .12s; }
.sso-hacc__d { font-family: var(--sso-body); font-size: 15px; line-height: 1.55;
  color: var(--sso-dim); margin: 0; max-width: 46ch;
  opacity: 0; transform: translate3d(0, 14px, 0);
  transition: opacity .5s var(--sso-ease) .2s, transform .6s var(--sso-ease) .2s; }
.sso-hacc__p:hover .sso-hacc__t, .sso-hacc__p:focus-within .sso-hacc__t,
.sso-hacc__p:hover .sso-hacc__d, .sso-hacc__p:focus-within .sso-hacc__d {
  opacity: 1; transform: none; }

/* Narrow screens: stack. A 25%-wide panel on a phone is a sliver, and the
   expansion has nowhere to go. */
@media (max-width: 860px) {
  .sso-hacc { flex-direction: column !important; }
  .sso-hacc__p { flex: 1 1 auto !important; min-height: 190px; }
  .sso-hacc__tag { display: none; }
  .sso-hacc__t, .sso-hacc__d { opacity: 1; transform: none; white-space: normal; }
  .sso-hacc__p > img { filter: grayscale(.3) brightness(.6); transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════
   NARROW SCREENS — measured, not assumed
   ────────────────────────────────────────────────────────────────
   Reported as "many overlapping not aligned items" on a phone, and
   every item below was then measured in a real 412×860 viewport.

   Worth recording how nearly I missed it: my first audit script said
   zero overlaps, because it counted two words sitting side by side on
   one line as overlapping and I trusted the number over the report.
   The second harness rendered the page in a 4600px-tall iframe, which
   makes `94vh` mean 4324px — so the hero measured five times its real
   height and the numbers were confident nonsense. A viewport is not a
   parameter you can fake; the frame has to actually be 412×860.
═══════════════════════════════════════════════════════════════════ */

@media (max-width: 860px) {

  /* 1 · HORIZONTAL REVEALS OFF.
     `--left` / `--right` translate ±34px. On a 1600px desktop that is
     invisible; at 397px it pushed the media frames to x=-4 and x=379 and
     gave the document 4px of horizontal scroll — the page rocked
     sideways under a thumb. Vertical entry only down here. */
  .sso-fx-ready .sso-fx-reveal--left,
  .sso-fx-ready .sso-fx-reveal--right { transform: translate3d(0, 22px, 0); }

  /* 2 · SCRIM: VERTICAL ONLY.
     The horizontal pass exists to protect left-aligned type on a wide
     screen, where the photograph continues past the text. At 397px it
     covers the entire width and the image turns to mud. */
  .sso-scrim { background: linear-gradient(180deg,
      color-mix(in srgb, var(--sso-ink) 42%, transparent) 0%,
      color-mix(in srgb, var(--sso-ink) 22%, transparent) 34%,
      color-mix(in srgb, var(--sso-ink) 96%, transparent) 100%) !important; }

  /* 3 · CAPTIONS ARE CONTENT, AND A PHONE HAS NO HOVER.
     The duo captions carry the only description of each photograph. On
     a touch device :hover never fires, so on mobile they were simply
     absent — not degraded, gone. Always open below this width. */
  .sso-reveal__cap, .sso-reveal__d {
    opacity: 1 !important; transform: none !important; }
  .sso-reveal__cap::before { transform: scaleX(1) !important; }
  .sso-reveal img { transform: none !important; }

  /* 4 · FULL-WIDTH MEDIA.
     `flex: 2 / 1` with `min-width: 280px` in a 357px column produced
     315px frames inside a 397px screen: not full-bleed, not centred,
     just narrower than everything around them. That is the "not
     aligned" in the report. */
  .sso-frame { flex: 1 1 100% !important; min-width: 0 !important;
    height: clamp(230px, 38vh, 320px) !important; }

  /* 5 · THE HERO HAS TO FIT.
     94vh minus a 150px bottom pad left the copy 6px past the slide's
     own edge on an 860px screen. Shorter slide, tighter pad, and the
     headline steps down a size so it stops eating half the viewport. */
  .sso-slide { min-height: 82vh !important; }
  .sso-slide__body { padding-block-end: clamp(40px, 7vh, 64px) !important; }
  .sso-h1 { font-size: clamp(40px, 12vw, 62px); }
  .sso-h2 { font-size: clamp(28px, 8.5vw, 44px); }

  /* 6 · The marquee stops being a 66px shout. */
  .sso-marquee__t { font-size: clamp(22px, 7vw, 34px); }

  /* 7 · THE STACKED ACCORDION MUST GROW.
     The row carries an inline `height: clamp(360px,58vh,600px)` — correct
     while it is a row of four side-by-side panels. When rule §3 of the
     accordion block turns it into a column, four panels of at least 190px
     need ~800px and the row is still pinned at ~500px, so the last panel
     ran straight over the figures band underneath it. THIS is the
     overlap in the report, and it is the one my box-overlap detector
     could never have found: the boxes do not intersect, the fixed-height
     parent simply ends early and the next section starts on top.
     `!important` because the height is an inline per-instance style, and
     inline wins. */
  .sso-hacc { height: auto !important; }
  .sso-hacc__p { min-height: 200px; }

  /* 8 · Nothing may cause sideways scroll. A phone with a 4px
     horizontal overflow feels broken in a way no screenshot shows. */
  .sso-slider, .sso-hacc, .sso-marquee { max-width: 100%; }
}

/* The slider is the one place horizontal scroll is intended — but the
   page around it must never inherit it. */
body { overflow-x: clip; }
