/* Reticle, sheen and glint for the Services and Testimonials panels.

   Each .quadrant and .quote carries an inline <svg class="ghost"> in its
   bottom-right corner (cropped by the panel) and a <span class="ghost
   gh-edge"> on its top edge. The reticle draws in once when the grid scrolls
   into view (ghost.js adds .is-in), then runs a few continuous loops; the
   panel's ::after is a slow sheen and .gh-edge carries a red glint. Panels
   pause when off screen (.gh-off) and brighten on mouse hover.

   Transform and opacity only; the panels themselves never move. Every animated
   rule sits inside a prefers-reduced-motion: no-preference query, so with
   reduced motion the reticle rests drawn and static. */

.quadrant,
.quote {
  /* Width and height of the reticle box. Bigger = more of it shows. */
  --ghost-size: 20rem;
  /* Overhang past the panel's right and bottom edge (cropped by the panel).
     More negative = less visible. */
  --ghost-offset: -5.5rem;
  /* Opacity of the white parts. The committed ghost was about 0.1. */
  --ghost-alpha: 0.62;
  /* Opacity of the red scan wedge. */
  --ghost-sweep-alpha: 0.32;
  /* Red top bar, 0 to 1 of its width. 1 = drawn. A and C drive this on scroll. */
  --ghost-bar: 1;

  /* ---- Continuous life. Everything loops seamlessly (rotations are linear
     full turns, breaths are symmetric), so there is no restart point. ------ */

  /* Reticle. Seconds per cycle; lower = faster. */
  /* Crosshair, one full turn. */
  --live-spin-dur: 20s;
  /* Tick ring, one full turn the other way. */
  --live-tick-dur: 32s;
  /* Red scan, one full turn: how often it passes. */
  --live-sweep-dur: 6s;
  /* Ring breathing, one full breath. */
  --live-pulse-dur: 4s;
  /* Ring size at the peak of a breath. 1 = no scale. */
  --live-pulse-scale: 1.05;
  /* Red dot beat size. */
  --live-dot-scale: 1.7;

  /* Panel. */
  /* Seconds for the sheen to cross the panel once. Slower = more expensive. */
  --live-sheen-dur: 16s;
  /* Sheen brightness, as the alpha of white at its centre. 0 = off. */
  --live-sheen-alpha: 0.055;
  /* Seconds for the red glint to travel the panel's top edge once. */
  --live-glow-dur: 8s;
  /* Red glint brightness. 0 = off, 1 = full. */
  --live-glow: 0.9;

  /* Offset between neighbouring panels, so the section shimmers rather than
     pulses. 0 = all in step. Sheen and glint use different multiples so they
     never line up. */
  --live-desync: 1.9s;

  --ghost-i: 0;
}
.quadrant:nth-of-type(2), .quote:nth-of-type(2) { --ghost-i: 1; }
.quadrant:nth-of-type(3), .quote:nth-of-type(3) { --ghost-i: 2; }
.quadrant:nth-of-type(4), .quote:nth-of-type(4) { --ghost-i: 3; }
.quote:nth-of-type(5) { --ghost-i: 4; }

@media (max-width: 820px) {
  .quadrant,
  .quote { --ghost-size: 17rem; --ghost-offset: -5.5rem; --ghost-alpha: 0.4; }
}

.quadrant::before,
.quote::before { transform-origin: left center; transform: scaleX(var(--ghost-bar)); }

.js .quadrant > svg.ghost,
.js .quote > svg.ghost,
.quadrant > svg.ghost,
.quote > svg.ghost {
  position: absolute;
  right: var(--ghost-offset);
  bottom: var(--ghost-offset);
  z-index: 0;
  width: var(--ghost-size);
  height: var(--ghost-size);
  overflow: hidden;
  pointer-events: none;
  clip-path: none;
  color: rgb(242, 242, 240);
}
.ghost circle,
.ghost line,
.ghost path { fill: none; stroke: currentColor; stroke-opacity: var(--ghost-alpha); stroke-width: 1; vector-effect: non-scaling-stroke; }
.ghost .gh-rings circle { stroke-width: 1.25; }
.ghost .gh-tickring { stroke-width: 6; stroke-dasharray: 1.2 8.1; vector-effect: none; }
.ghost .gh-notch    { stroke: var(--red); stroke-opacity: 1; stroke-width: 2.5; }
.ghost .gh-wedge    { fill: url(#gh-fade); stroke: none; }
.ghost stop:first-child { stop-opacity: var(--ghost-sweep-alpha); }
.ghost .gh-lead     { stroke: var(--red); stroke-opacity: 1; stroke-width: 1.5; }
.ghost .gh-dot      { fill: var(--red); stroke: none; }
.ghost g,
.ghost .gh-dot { transform-box: view-box; transform-origin: 100px 100px; }
.ghost .gh-rings, .ghost .gh-ticks, .ghost .gh-cross, .ghost .gh-sweep, .ghost .gh-dot-in { transform-origin: 0 0; }


@keyframes gh-spin     { to { transform: rotate(360deg); } }
@keyframes gh-spin-rev { to { transform: rotate(-360deg); } }
@keyframes gh-breathe  { 0%, 100% { transform: scale(1); opacity: 0.55; } 50% { transform: scale(var(--live-pulse-scale)); opacity: 1; } }
@keyframes gh-dot-beat { 0%, 100% { transform: scale(1); } 50% { transform: scale(var(--live-dot-scale)); } }
/* Sheen and glint sit offscreen at both ends of the loop, so the restart is invisible. */
@keyframes gh-sheen    { 0% { transform: translateX(-110%); } 62%, 100% { transform: translateX(210%); } }
@keyframes gh-glint    { 0% { transform: translateX(-120%); } 70%, 100% { transform: translateX(420%); } }

/* Sheen: a wide soft diagonal band, behind the content. */
.quadrant::after,
.quote::after {
  content: '';
  display: block;
  position: absolute;
  top: 0; bottom: 0; left: 0; right: auto;
  width: 55%;
  height: auto;
  opacity: 1;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(105deg, transparent 0%, rgba(242, 242, 240, var(--live-sheen-alpha)) 50%, transparent 100%);
  transform: translateX(-110%);
}

/* Red glint along the panel's top edge. */
.js .quadrant > .gh-edge,
.js .quote > .gh-edge,
.quadrant > .gh-edge,
.quote > .gh-edge {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  z-index: 2;
  overflow: hidden;
  pointer-events: none;
  clip-path: none;
}
.gh-edge i {
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: 22%;
  background:
    linear-gradient(90deg, transparent, rgba(214, 32, 43, var(--live-glow)), transparent) top / 100% 1px no-repeat,
    linear-gradient(90deg, transparent, rgba(214, 32, 43, calc(var(--live-glow) * 0.35)), transparent);
  transform: translateX(-120%);
}

@media (prefers-reduced-motion: no-preference) {
  .ghost .gh-cross-spin  { animation: gh-spin     var(--live-spin-dur)  linear      infinite; }
  .ghost .gh-sweep-spin  { animation: gh-spin     var(--live-sweep-dur) linear      infinite; }
  /* Dropped to lighten the load: tick-ring counter-spin, two ring breaths,
     dot beat. Their keyframes and --live-* knobs are left in place. */
  .ghost .gh-cross-spin,
  .ghost .gh-sweep-spin { animation-delay: calc(var(--ghost-i) * var(--live-desync) * -1); }

  .quadrant::after,
  .quote::after { animation: gh-sheen var(--live-sheen-dur) ease-in-out infinite; animation-delay: calc(var(--ghost-i) * var(--live-desync) * -2.3); }
  .gh-edge i    { animation: gh-glint var(--live-glow-dur)  ease-in-out infinite; animation-delay: calc(var(--ghost-i) * var(--live-desync) * -1.4); }
}

/* Reduced motion: no sheen, no glint; the reticle rests drawn. */
@media (prefers-reduced-motion: reduce) {
  .quadrant::after, .quote::after, .gh-edge { display: none; }
}

/* Off-screen panels stop animating (ghost.js toggles .gh-off). Nothing
   visible changes; a panel resumes exactly where it paused. */
.gh-off .ghost *,
.gh-off.quadrant::after,
.gh-off.quote::after,
.gh-off .gh-edge i { animation-play-state: paused; }

/* ==========================================================================
   Fast reveal-once (replaces the scrubbed entrance). When a grid scrolls in,
   ghost.js adds .is-in; each panel's red bar and reticle then draw in over
   --ghost-enter-dur, a --ghost-enter-stagger apart, matching the content wipe
   in main.js (0.5s, 0.06s). They stay drawn afterwards.
   ========================================================================== */
.quadrant,
.quote {
  /* Seconds for each piece of the reticle and the red bar to draw in. */
  --ghost-enter-dur: 0.5s;
  /* Delay before the first panel starts, after the trigger. */
  --ghost-enter-delay: 0s;
  /* Extra delay per panel, left to right and top to bottom. */
  --ghost-enter-stagger: 0.06s;
  --ghost-t0: calc(var(--ghost-enter-delay) + var(--ghost-i) * var(--ghost-enter-stagger));
}

/* The scrub drove these with GSAP and needed a 0 0 origin; CSS wants the centre. */
.ghost .gh-rings, .ghost .gh-ticks, .ghost .gh-cross, .ghost .gh-sweep, .ghost .gh-dot-in { transform-origin: 100px 100px; }

@keyframes gh-in-scale { from { opacity: 0; transform: scale(0.25); } to { opacity: 1; transform: scale(1); } }
@keyframes gh-in-spin  { from { opacity: 0; transform: rotate(-150deg) scale(0.55); } to { opacity: 1; transform: rotate(0) scale(1); } }
@keyframes gh-in-fade  { from { opacity: 0; } to { opacity: 1; } }
@keyframes gh-in-dot   { 0% { opacity: 0; transform: scale(0); } 65% { opacity: 1; transform: scale(1.8); } 100% { opacity: 1; transform: scale(1); } }
@keyframes gh-bar      { from { transform: scaleX(0); } to { transform: scaleX(1); } }

@media (prefers-reduced-motion: no-preference) {
  .js .quadrants:not(.is-in) > .quadrant .ghost > *,
  .js .quotes:not(.is-in)    > .quote    .ghost > * { opacity: 0; }
  .js .quadrants:not(.is-in) > .quadrant::before,
  .js .quotes:not(.is-in)    > .quote::before { transform: scaleX(0); }

  .quadrants.is-in > .quadrant::before,
  .quotes.is-in    > .quote::before { animation: gh-bar var(--ghost-enter-dur) var(--ease-out) both; animation-delay: var(--ghost-t0); }

  .is-in > .quadrant .gh-cross,  .is-in > .quote .gh-cross  { animation: gh-in-scale var(--ghost-enter-dur) var(--ease-out) both; animation-delay: var(--ghost-t0); }
  .is-in > .quadrant .gh-rings,  .is-in > .quote .gh-rings  { animation: gh-in-scale var(--ghost-enter-dur) var(--ease-out) both; animation-delay: calc(var(--ghost-t0) + 0.05s); }
  .is-in > .quadrant .gh-ticks,  .is-in > .quote .gh-ticks  { animation: gh-in-spin  var(--ghost-enter-dur) var(--ease-out) both; animation-delay: calc(var(--ghost-t0) + 0.08s); }
  .is-in > .quadrant .gh-sweep,  .is-in > .quote .gh-sweep  { animation: gh-in-fade  var(--ghost-enter-dur) ease-out both;       animation-delay: calc(var(--ghost-t0) + 0.15s); }
  .is-in > .quadrant .gh-dot-in, .is-in > .quote .gh-dot-in { animation: gh-in-dot   var(--ghost-enter-dur) var(--ease-out) both; animation-delay: calc(var(--ghost-t0) + 0.15s); }
}

/* ==========================================================================
   Hover. The panel under the cursor lights up: the reticle's white parts and
   the red scan wedge get brighter, and the scan turns faster (ghost.js eases
   the speed). Everything eases back on mouse-out. Mouse only; never touch,
   never reduced motion. The committed link lift and background step on
   .quadrant:hover are untouched.
   ========================================================================== */
.quadrant,
.quote {
  /* Opacity of the reticle's white parts while hovered (rest: --ghost-alpha). */
  --live-hover-alpha: 0.95;
  /* Opacity of the red scan wedge while hovered (rest: --ghost-sweep-alpha). */
  --live-hover-sweep-alpha: 0.65;
  /* How many times faster the scan turns while hovered. 1 = no change. */
  --live-hover-sweep-speed: 3;
  /* Seconds to ease in, and back out. Plain seconds with the s unit, e.g. 0.45s. */
  --live-hover-dur: 0.45s;
}

/* Registered so the two alphas can transition. */
@property --ghost-alpha       { syntax: '<number>'; inherits: true; initial-value: 0.62; }
@property --ghost-sweep-alpha { syntax: '<number>'; inherits: true; initial-value: 0.32; }

@media (prefers-reduced-motion: no-preference) {
  /* .quadrant keeps its committed background-color transition. */
  .quadrant {
    transition: background-color var(--t-mid) ease,
                --ghost-alpha var(--live-hover-dur) ease,
                --ghost-sweep-alpha var(--live-hover-dur) ease;
  }
  .quote {
    transition: --ghost-alpha var(--live-hover-dur) ease,
                --ghost-sweep-alpha var(--live-hover-dur) ease;
  }
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .quadrant:hover,
  .quote:hover {
    --ghost-alpha: var(--live-hover-alpha);
    --ghost-sweep-alpha: var(--live-hover-sweep-alpha);
  }
}
