/* ── Cubiq Process — scroll-pinned process dial ────────────────────────────
   The section holds still while the page scrolls past it: a brand-gradient
   stroke sweeps clockwise around the orb like a progress bar, and each stage
   arrives as the stroke reaches its position. When the circle closes the pin
   releases and the page carries on. */

/* This theme sets `html, body { overflow-x: hidden }`, which turns the page
   into a scroll container and breaks position:sticky. `overflow-x: clip`
   contains the same overflow without creating a scroll container, so the pin
   works. The theme's rule is unlayered and loads after this file, so the
   !important is needed to win. */
html, body { overflow-x: clip !important; }

.cqp,
.cqp *,
.cqp *::before,
.cqp *::after { box-sizing: border-box; }

.cqp {
  /* Brand palette — same triple as the hero */
  --c1: 20, 171, 218;   /* #14ABDA cyan   */
  --c2: 3, 139, 241;    /* #038BF1 blue   */
  --c3: 114, 89, 191;   /* #7259BF purple */

  /* 0…1 scroll progress through the pin. JS writes it; the fallback of 1
     means a no-JS visitor gets the finished state rather than an empty dial. */
  --p: 1;

  --ring-d: clamp(220px, 40vh, 420px);          /* dial diameter */
  --ring-r: calc(var(--ring-d) / 2);
  --stage-r: calc(var(--ring-r) * 1.5);         /* where the stage blocks anchor */

  position: relative;
  background: #020b31;
  font-family: 'Open Sauce One', system-ui, -apple-system, "Segoe UI", sans-serif;
  color: #eaf1f8;
}

/* The tall element that donates the scroll distance. --pin is how many extra
   screens of scrolling the section holds for. */
.cqp-track { height: calc((var(--pin, 3) + 1) * 100vh); }

.cqp-pin {
  position: sticky;
  top: 0;
  height: 100vh;
  padding: clamp(12px, 1.6vh, 26px) clamp(12px, 1.4vw, 28px);
}

/* The inset panel everything happens inside. It has to separate itself from
   the page navy, so it's lifted on three counts: a base a few steps lighter
   than #020b31, a defined rim light around the frame, and an outer shadow to
   sit it above the page rather than flush with it. */
.cqp-canvas {
  position: relative;
  height: 100%;
  overflow: hidden;
  isolation: isolate;
  border-radius: clamp(16px, 1.6vw, 30px);
  /* A touch deeper and less saturated than it was — the panel still has to
     separate from the page navy, but it was reading as a bright blue block
     rather than a lit dark one. */
  background: radial-gradient(125% 115% at 50% 0%,
    #091747 0%, #061231 44%, #030e2b 100%);
  box-shadow:
    /* No rim line and no top highlight. Both drew a visible lighter stroke
       around the panel; with the glow and the drop shadow already defining
       the frame, the stroke was the thing making the edge look hard. The
       separation now comes from tone and depth alone. */
    inset 0 0 70px -12px rgba(var(--c2), .12),
    /* lifts the whole panel off the page */
    0 34px 80px -34px rgba(0, 0, 3, .85);
}

/* ── Inner glow ────────────────────────────────────────────────────────────
   Built the same way as the hero's Orb: brand-gradient conics turning slowly
   behind a mask, rather than blobs animated on keyframes. Two of them at
   different speeds and opposite directions, so the colour that travels round
   the inside of the frame never repeats the same pass — that continuous drift
   is what reads as expensive, where keyframed blobs read as a loop.

   The mask lives on the parent and the rotation on the children: the colour
   turns underneath a fixed edge-weighted window, so the glow always hugs the
   frame instead of sweeping across the middle. */

.cqp-aura {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  -webkit-mask-image: radial-gradient(118% 118% at 50% 50%,
    transparent 26%, rgba(0, 0, 0, .5) 58%, #000 100%);
          mask-image: radial-gradient(118% 118% at 50% 50%,
    transparent 26%, rgba(0, 0, 0, .5) 58%, #000 100%);
}

/* Square and oversized, so no rotation angle can pull the colour off an edge.
   The centring uses the standalone `translate` property, which leaves
   `transform` free for the spin. */
.cqp-glow {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 165%;
  aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  will-change: transform;
  backface-visibility: hidden;
}

.cqp-glow--a {
  background: conic-gradient(from 200deg,
    rgb(var(--c3)), rgb(var(--c2)), rgb(var(--c1)), rgb(var(--c2)), rgb(var(--c3)));
  filter: blur(70px) saturate(108%);
  opacity: .5;
  animation: cqp-orbit 46s linear infinite;
}

.cqp-glow--b {
  background: conic-gradient(from 20deg,
    rgb(var(--c1)), rgb(var(--c3)), rgb(var(--c2)), rgb(var(--c1)));
  filter: blur(100px) saturate(102%);
  opacity: .34;
  animation: cqp-orbit 71s linear infinite reverse;
}

@keyframes cqp-orbit {
  to { transform: rotate(360deg); }
}

/* Keeps the middle of the panel deep so the colour stays edge light and the
   dial has something to read against. */
.cqp-canvas::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(66% 70% at 50% 50%,
    rgba(4, 16, 47, .96) 0%, rgba(4, 16, 47, .8) 40%, rgba(4, 16, 47, 0) 82%);
}

.cqp-grain {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: .05;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ── Panel background: cursor discs ────────────────────────────────────────
   The hero's Discs background, brought inside the panel. Tall sheets are
   layered across the canvas, invisible at rest, and light up only where the
   pointer is — so the panel keeps the deep middle and lit frame it needs for
   contrast, and the layering is something the visitor uncovers.

   The turning edge glow stays on underneath, dialled back: it's what gives
   the panel its presence before anyone touches it. */

.cqp--bg-discs .cqp-glow--a { opacity: .28; }
.cqp--bg-discs .cqp-glow--b { opacity: .19; }

.cqp-sheets {
  /* Mirrors the hero exactly. The radius has to comfortably exceed the panel's
     height or the pool's own circular falloff clips the sheets vertically —
     they'd light in a band across the middle instead of running top to bottom.
     At 34vw the pool is about 1.2x the panel's height, same as in the hero. */
  --hr: clamp(340px, 34vw, 700px);
  --hx: 50%;
  --hy: 50%;
  --hov: 0;

  position: absolute;
  inset: 0;
  /* Above the canvas's dark centre wash, which is a pseudo-element and so
     paints after the element's children. At z-index 0 the sheets sat under it
     and were smothered in the middle — exactly where the cursor is. The dial
     (2) and copy (3) still sit above these. */
  z-index: 1;
  border-radius: inherit;
  pointer-events: none;

  /* Keeps the background out of the dial's own space. The cursor pool is
     masked onto the child layer, so putting this one on the parent intersects
     the two without needing mask-composite — nested elements with one mask
     each is the same result and works everywhere.

     Sized off --ring-d so it tracks the dial at every breakpoint rather than
     being a fixed hole that drifts out of register.

     Pulled in from 1.05 to 0.62 when the sheets became rings. At the old size
     the clearance ran to twice the dial's radius, which was right for sheets
     cutting across the panel — they had no business near the middle. Rings are
     supposed to start at the dial and travel out, so the same clearance
     swallowed the first two of them and the effect looked like it began from
     nowhere. At 0.62 the hole covers the dial and is fully open just outside
     it, so the innermost ring reads as leaving the orb's edge. */
  -webkit-mask-image: radial-gradient(circle calc(var(--ring-d) * .62) at 50% 50%,
    transparent 0%, rgba(0, 0, 0, .18) 42%, rgba(0, 0, 0, .62) 72%, #000 100%);
          mask-image: radial-gradient(circle calc(var(--ring-d) * .62) at 50% 50%,
    transparent 0%, rgba(0, 0, 0, .18) 42%, rgba(0, 0, 0, .62) 72%, #000 100%);
}

/* The pointer reads as the source rather than the sheets glowing by themselves. */
.cqp-spot {
  position: absolute;
  left: var(--hx);
  top: var(--hy);
  width: calc(var(--hr) * 2);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  opacity: calc(var(--hov) * .38);
  background: radial-gradient(circle at 50% 50%,
    rgba(var(--c1), .13) 0%, rgba(var(--c2), .09) 34%, transparent 68%);
  filter: blur(30px);
}

/* Nothing is painted at rest: opacity rides the pointer, and the rings are
   masked down to a soft pool around it.

   Held at a fraction of the hero's strength on purpose. The hero has a
   headline and two buttons over it and can carry a bold background; this panel
   has a dial, six stage blocks and body copy, and at full strength the
   background competed with them. One multiplier keeps the character of the
   effect and just turns its presence down — raise or lower this single number
   to taste.

   The ring geometry is one repeating gradient rather than a stack of elements:
   concentric circles are exactly what a radial gradient already draws, so
   twelve positioned spans would be doing by hand what one background does
   natively — and the pitch stays perfectly even at any size, which hand-placed
   rings would not.

   --rband  distance from one ring's edge to the next
   --rstep  width of the hard edge itself; a hairline rather than a true 0,
            because a zero-length stop aliases into a ragged edge on a circle
            this large, where a sub-pixel one resolves clean. */
.cqp-rings {
  --rband: calc(var(--ring-r) * .46);
  --rstep: 1px;

  position: absolute;
  inset: 0;
  opacity: calc(var(--hov) * .55);
  will-change: opacity;

  /* Each band builds tone outwards and then drops dead at its edge, so what
     you see is a step between two tones rather than a drawn line — the same
     construction as the hero's sheets, wrapped around the dial instead of
     laid across the panel. The repeat runs two bands deep and alternates the
     orb's first two ramp colours, so the light varies ring to ring instead of
     reading as one flat tone.

     Starting the repeat at --ring-r puts the first edge exactly on the dial,
     so the rings look like they are spreading from it rather than passing
     behind it at an arbitrary phase. */
  background: repeating-radial-gradient(circle at 50% 50%,
    rgba(var(--c1), .03) var(--ring-r),
    rgba(var(--c1), .24) calc(var(--ring-r) + var(--rband)),
    rgba(var(--c1), .03) calc(var(--ring-r) + var(--rband) + var(--rstep)),
    rgba(var(--c2), .21) calc(var(--ring-r) + var(--rband) * 2),
    rgba(var(--c2), .03) calc(var(--ring-r) + var(--rband) * 2 + var(--rstep)));

  -webkit-mask-image: radial-gradient(circle var(--hr) at var(--hx) var(--hy),
    #000 0%, rgba(0, 0, 0, .62) 42%, transparent 76%);
          mask-image: radial-gradient(circle var(--hr) at var(--hx) var(--hy),
    #000 0%, rgba(0, 0, 0, .62) 42%, transparent 76%);
}

/* No pointer to follow, so the rings would never be seen — JS sweeps the
   light across on its own instead (see the script). The pool shrinks with the
   viewport; the band tightens with it so a phone still shows several rings
   rather than one wall of tone. */
@media (max-width: 900px) {
  .cqp-sheets { --hr: clamp(200px, 52vw, 380px); }
  .cqp-rings  { --rband: calc(var(--ring-r) * .38); }
}

/* ── Heading ───────────────────────────────────────────────────────────────
   Sits top-centre, which stays clear of the stage blocks — with an even
   number of stages they land on the diagonals, not directly above the orb. */

.cqp-head {
  position: absolute;
  z-index: 3;
  top: clamp(24px, 5vh, 60px);
  left: 50%;
  transform: translateX(-50%);
  width: min(92%, 46rem);
  text-align: center;
}

.cqp-eyebrow {
  display: inline-block;
  font-size: 12px;
  letter-spacing: .3em;
  text-transform: uppercase;
  font-weight: 600;
  color: rgba(120, 200, 240, .85);
  margin-bottom: 14px;
}

.cqp-heading {
  margin: 0;
  font-size: clamp(26px, 3.4vw, 48px);
  background: linear-gradient(180deg, #ffffff 0%, #c4d2e6 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  /* Shared display-heading treatment — matches .cqh-title in cubiq-hero-glow.
     Grep "display-heading treatment" to find every module using it. */
  font-weight: 300;
  line-height: 1.06;
  letter-spacing: -.032em;
  /* background-clip:text cuts the gradient at the box edge, and 1.06 leading
     leaves the last line's descenders outside it. */
  padding-bottom: .13em;
}

.cqp-heading .cqp-hl {
  background: linear-gradient(100deg, rgb(var(--c1)) 0%, rgb(var(--c2)) 52%, rgb(var(--c3)) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

/* ── The dial ──────────────────────────────────────────────────────────────*/

.cqp-ring {
  position: absolute;
  z-index: 2;
  top: 50%;
  left: 50%;
  width: var(--ring-d);
  height: var(--ring-d);
  transform: translate(-50%, -50%);
}

.cqp-dial {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  /* Start the sweep at 12 o'clock and run it clockwise */
  transform: rotate(-90deg);
}

/* Stroke widths are in the viewBox's own units, so the dial keeps its
   proportions at every size rather than thinning out as the ring grows.
   Widths are set per-circle: a shared `.cqp-dial circle` rule would be the
   more specific selector and would win over the single-class ones below. */
.cqp-dial circle { fill: none; }

.cqp-dial-track {
  stroke: rgba(190, 220, 245, .14);
  stroke-width: .5;
}

.cqp-dial-prog {
  stroke-width: 1.15;
  stroke-linecap: round;
  stroke-dasharray: var(--circ);
  /* --p drives the sweep directly, so the stroke is locked to the scroll
     position rather than animating on its own. */
  stroke-dashoffset: calc(var(--circ) * (1 - var(--p)));
  filter: drop-shadow(0 0 6px rgba(var(--c2), .55));
}

/* The orb at the centre — same construction as the hero's Orb background, at
   module scale: a brand conic halo behind a deep navy sphere. */
.cqp-core {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 62%;
  height: 62%;
  transform: translate(-50%, -50%);
}

.cqp-core-glow,
.cqp-core-body {
  position: absolute;
  border-radius: 50%;
}

.cqp-core-body { inset: 0; }

/* Sits slightly proud of the sphere so the brand ramp shows as a rim light
   around it rather than being hidden behind the body. */
.cqp-core-glow {
  inset: -9%;
  background: conic-gradient(from 200deg,
    rgb(var(--c3)), rgb(var(--c2)), rgb(var(--c1)), rgb(var(--c2)), rgb(var(--c3)));
  filter: blur(14px);
  opacity: .9;
  /* Turns with the scroll, so the orb feels driven by the same input as the
     stroke rather than idling on a timer of its own. */
  transform: rotate(calc(var(--p) * 200deg)) scale(calc(.9 + var(--p) * .12));
}

.cqp-core-body {
  background: radial-gradient(circle at 38% 30%,
    #16295c 0%, #0a1741 46%, #04102f 74%, #030d33 100%);
  box-shadow:
    inset 0 -2px 30px rgba(255, 255, 255, .06),
    inset 0 2px 18px rgba(var(--c1), .10);
}

/* ── Stage icons ───────────────────────────────────────────────────────────
   Stacked in the middle of the orb, one per stage. The current stage's icon is
   lifted in while the previous one settles back and softens, so the swap reads
   as a change of focus rather than a slideshow. */

.cqp-icons {
  position: absolute;
  inset: 0;
  z-index: 2;                  /* over the orb body */
  pointer-events: none;
}

.cqp-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 30%;
  aspect-ratio: 1;
  /* Centring uses `translate` so `transform` stays free for the reveal. */
  translate: -50% -50%;
  transition:
    opacity .5s ease,
    transform .65s cubic-bezier(.2, .7, .2, 1),
    filter .5s ease;
}

.cqp-icon img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

/* Without JS there's no current stage, so the first icon stands in and the
   rest stay out of the way — never a stack of icons on top of each other. */
.cqp-icon:not(:first-child) { opacity: 0; }

/* Once JS is driving, the stage the visitor is on is the one on show. */
.cqp-js .cqp-icon {
  opacity: 0;
  transform: scale(.84) translateY(8px);
  filter: blur(7px);
}

.cqp-js .cqp-icon.is-now {
  opacity: 1;
  transform: none;
  filter: none;
}

/* The stacked-layout copy: hidden here, shown in the fallback block below. */
.cqp-stage-icon { display: none; }

/* ── Stages ────────────────────────────────────────────────────────────────
   Anchored around the dial by the sine/cosine PHP worked out for each step.
   Right-hand stages read outwards from their anchor, left-hand ones mirror. */

/* Spans the whole canvas rather than sitting inside the dial. The dial is
   centred on the canvas, so 50% here is the same origin the stage angles were
   worked out from — and in the stacked fallback the list can simply flow
   underneath the ring instead of being boxed in by it. */
.cqp-stages {
  position: absolute;
  inset: 0;
  z-index: 3;
  margin: 0;
  padding: 0;
  list-style: none;
}

.cqp-stage {
  position: absolute;
  top: calc(50% + var(--sy) * var(--stage-r));
  left: calc(50% + var(--sx) * var(--stage-r));
  /* Wider than it was (21vw / 330px cap), so the descriptions run over fewer,
     longer lines rather than a narrow column. The ceiling is set by the worst
     case: six stages puts a block at the ring's horizontal extreme, where the
     anchor already sits 1.5 ring-radii from centre — much past 400px and that
     block runs out of canvas. */
  width: clamp(220px, 26vw, 400px);
}

.cqp-stage--right { transform: translateY(-50%); }
.cqp-stage--left  { transform: translate(-100%, -50%); text-align: right; }

/* The reveal transform lives on the inner wrapper so it can't fight with the
   positioning transform above. Visible by default — the hidden state is only
   applied once JS has taken over (see .cqp-js), so a JS failure leaves every
   stage on screen instead of an empty panel. */
.cqp-stage-inner {
  transition: opacity .55s ease, transform .55s cubic-bezier(.2, .7, .2, 1), filter .55s ease;
}

.cqp-js .cqp-stage-inner {
  opacity: 0;
  filter: blur(3px);
  transform: translateY(14px);
}

.cqp-js .cqp-stage.is-in .cqp-stage-inner {
  opacity: 1;
  filter: none;
  transform: none;
}

.cqp-stage-label {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  font-size: clamp(15px, 1.3vw, 18px);
  line-height: 1.3;
}

.cqp-stage--left .cqp-stage-label { flex-direction: row-reverse; }

.cqp-stage-dot {
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(var(--c1), .9);
  box-shadow: 0 0 10px rgba(var(--c1), .8);
}

.cqp-stage-num {
  font-weight: 400;
  color: rgba(206, 224, 245, .55);
  font-variant-numeric: tabular-nums;
}

.cqp-stage-title {
  font-weight: 600;
  color: #f2f7fc;
}

/* Pushes the description clear of the dot so it lines up with the title */
.cqp-stage-text {
  margin: 0;
  padding-left: 14px;
  font-size: clamp(13px, 1.05vw, 15px);
  line-height: 1.55;
  color: rgba(206, 224, 245, .66);
}

.cqp-stage--left .cqp-stage-text { padding-left: 0; padding-right: 14px; }

/* ── Fallbacks: no pin ─────────────────────────────────────────────────────
   Narrow screens and reduced-motion visitors get the same content as an
   ordinary stacked list. Nothing is pinned, nothing is hidden, and the dial
   sits complete above the stages. JS skips its scroll wiring in both cases. */

@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  .cqp-track { height: auto; }

  .cqp-pin {
    position: static;
    height: auto;
    padding: clamp(14px, 3vw, 24px);
  }

  .cqp-canvas {
    height: auto;
    padding: clamp(40px, 9vw, 72px) clamp(20px, 6vw, 48px);
  }

  .cqp-head {
    position: static;
    transform: none;
    width: auto;
    margin: 0 auto clamp(36px, 7vw, 56px);
  }

  .cqp-ring {
    position: relative;
    top: auto;
    left: auto;
    transform: none;
    margin: 0 auto clamp(40px, 8vw, 64px);
    width: clamp(180px, 52vw, 260px);
    height: clamp(180px, 52vw, 260px);
  }

  .cqp-stages {
    position: static;
    display: grid;
    gap: clamp(26px, 6vw, 38px);
  }

  .cqp-stage,
  .cqp-stage--right,
  .cqp-stage--left {
    position: static;
    width: auto;
    transform: none;
    text-align: left;
  }

  .cqp-stage--left .cqp-stage-label { flex-direction: row; }
  .cqp-stage--left .cqp-stage-text { padding-left: 14px; padding-right: 0; }

  /* No single centre to swap icons in on this layout — each stage carries its
     own instead, so the centre stack stands down. */
  .cqp-icons { display: none; }

  .cqp-stage-icon {
    display: block;
    width: clamp(30px, 8vw, 40px);
    margin: 0 0 14px;
  }
  .cqp-stage-icon img { width: 100%; height: auto; display: block; }

  /* The panel is taller than it is wide here, so the glow has to be sized off
     its height instead — a width-derived square wouldn't reach top and bottom,
     and the rotation would pull the colour away from those edges. */
  .cqp-glow { width: auto; height: 165%; }

  /* The colour fills a much smaller panel, so it comes down to keep the stage
     copy readable against it. */
  .cqp-glow--a { opacity: .32; }
  .cqp-glow--b { opacity: .2; }

  /* Even with JS loaded, nothing hides on this layout. */
  .cqp-js .cqp-stage-inner {
    opacity: 1;
    filter: none;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cqp-glow { animation: none; }
  .cqp-stage-inner { transition: none; }
}

/* Frozen while the page is hidden (set by JS), so the turning glow can't run
   out of step with the paused scroll loop. */
.cqp-paused .cqp-glow { animation-play-state: paused; }

/* Frozen while the panel is scrolled away (set by JS). These are the most
   expensive things the module paints — two ~8-megapixel surfaces under a 70px
   and a 100px blur — and there is no reason to keep turning them while the
   visitor is somewhere else on the page. */
.cqp-offscreen .cqp-glow { animation-play-state: paused; }
