/* ── Cubiq Logo Grid — 3-D cube-flip animated client logos ─────────────── */

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

.cql {
  --c1: 20, 171, 218;
  --c2: 3, 139, 241;
  --c3: 114, 89, 191;

  /* Aura anchor — orbits gently on its own (JS-driven ambient drift) */
  --mx: 10%;
  --my: 50%;
  /* Parallax offset — cursor position as -1…1. JS eases these;
     each depth layer translates by a different fraction of them. */
  --px: 0;
  --py: 0;

  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding-top: clamp(64px, 9vh, 120px);
  padding-bottom: clamp(64px, 9vh, 120px);
  font-family: 'Open Sauce One', system-ui, -apple-system, "Segoe UI", sans-serif;

  background: #020b31;
}

/* ── Three-colour parallax aura ─────────────────────────────────────────── */
/* Overscanned (-15%) so the soft blob edges never reveal a hard clip.
   Promoted to a composited layer (translateZ) to prevent repaint flicker. */
.cql-aura {
  position: absolute;
  inset: -15%;
  z-index: 0;
  filter: blur(30px) saturate(130%);
  transform: translateZ(0);
  backface-visibility: hidden;
  pointer-events: none;
}

.cql-layer {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
}

/* Cyan — wide foreground bloom anchored left, fading out across the logos */
.cql-l1 {
  background: radial-gradient(75% 65% at var(--mx) var(--my),
    rgba(var(--c1), .20), transparent 72%);
  transform: translate(calc(var(--px) * -5%), calc(var(--py) * -5%));
}

/* Blue — slightly lower/larger, centred just left of the anchor */
.cql-l2 {
  background: radial-gradient(65% 70% at calc(var(--mx) + 8%) calc(var(--my) + 12%),
    rgba(var(--c2), .16), transparent 68%);
  transform: translate(calc(var(--px) * -3%), calc(var(--py) * -3%));
}

/* Purple — background blob, hugs the far-left edge and spreads upward */
.cql-l3 {
  background: radial-gradient(55% 60% at calc(var(--mx) - 5%) calc(var(--my) - 10%),
    rgba(var(--c3), .13), transparent 70%);
  transform: translate(calc(var(--px) * -1.5%), calc(var(--py) * -1.5%));
}

/* Ambient colour wheel — runs whether or not the mouse moves */
.cql-aura::before {
  content: '';
  position: absolute;
  inset: -30%;
  background: conic-gradient(
    from 0deg,
    rgba(var(--c1), 0) 0%,
    rgba(var(--c2), .13) 28%,
    rgba(var(--c3), .11) 55%,
    rgba(var(--c1), .09) 78%,
    rgba(var(--c1), 0) 100%
  );
  filter: blur(55px);
  opacity: .30;
  animation: cql-spin 38s linear infinite;
}

@keyframes cql-spin { to { transform: rotate(360deg); } }

/* Freeze while page is hidden (set by JS to keep rAF loop in sync) */
.cql-paused .cql-aura::before { animation-play-state: paused; }

/* Edge fades — rendered above the aura (z-index: 1 > aura's 0) so the glow
   always dissolves cleanly into the site navy at every edge, never hard-clipping. */
.cql::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(to bottom, #020b31 0%, transparent 30%),
    linear-gradient(to top,    #020b31 0%, transparent 30%);
}

.cql-inner {
  position: relative;
  z-index: 2;   /* above edge-fade overlay (z-index: 1) */
  width: 100%;
  max-width: 92rem;
  margin: 0 auto;
  padding: 0 20px;
}
@media (min-width: 1536px) {
  .cql-inner { padding: 0; }
}

/* ── Optional header ───────────────────────────────────────────────────── */
.cql-header {
  text-align: center;
  margin-bottom: clamp(32px, 5vw, 56px);
}

.cql-heading {
  margin: 0;
  font-size: clamp(11px, 0.9vw, 13px);
  font-weight: 500;
  letter-spacing: .25em;
  text-transform: uppercase;
  color: rgba(160, 200, 235, .5);
}

.cql-sub {
  margin: 10px 0 0;
  font-size: clamp(22px, 3vw, 38px);
  color: #eaf1f8;
  /* Shared display-heading treatment — matches .cqh-title in cubiq-hero-glow.
     Grep "display-heading treatment" to find every module using it.
     Note: .cql-heading above is deliberately left alone — it's an 11-13px
     uppercase eyebrow with +.25em tracking, and light weight with negative
     tracking would make it weak and cramped. Micro-labels want the opposite
     treatment to display type. */
  font-weight: 300;
  line-height: 1.06;
  letter-spacing: -.032em;
}

/* ── Asterisk-wrapped gradient accent words ─────────────────────────── */
.cql-accent {
  background: linear-gradient(90deg, #14ABDA 0%, #038BF1 50%, #7259BF 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  font-style: normal;
}

/* ── Grid ─────────────────────────────────────────────────────────────── */
/* Perspective lives here, not per-cell. Per-cell perspective makes each face
   appear ~10% larger than its grid slot (translateZ pushes the face toward the
   viewer, scaling it up under the short focal length). A shared grid-level
   perspective at 1400px keeps the 3D depth effect while keeping apparent face
   size within the cell boundary. */
.cql-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 10px;
  perspective: 1400px;
  perspective-origin: center 50%;
}

@media (max-width: 1023px) {
  .cql-grid { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 767px) {
  .cql-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 479px) {
  .cql-grid { grid-template-columns: repeat(2, 1fr); }
}

/* ── Cell ─────────────────────────────────────────────────────────────── */
.cql-cell {
  /* transform-style: preserve-3d passes the grid's perspective context down
     to the flipper and faces inside. Without it the 3D is flattened at
     the cell boundary and the cube effect disappears. */
  transform-style: preserve-3d;
  aspect-ratio: 5 / 3;
  /* Staggered entrance fade-in.
     fill-mode: backwards (not both) so after the animation the cell returns
     to transform:none — a non-none transform on a preserve-3d element can
     flatten the 3D context in some browsers. */
  animation: cql-in .5s cubic-bezier(.2,.6,.2,1) backwards;
  animation-delay: calc(var(--i, 0) * 55ms);
}

@keyframes cql-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* The element that physically rotates */
.cql-flipper {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform .55s cubic-bezier(.4, 0, .2, 1);
}

.cql-flipper.is-flipped {
  transform: rotateX(-90deg);
}

/* ── Both faces ───────────────────────────────────────────────────────── */
.cql-face {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(18px, 2.2vw, 32px);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  overflow: hidden;
}

/* Front — sits on the front face of the cube, offset toward the viewer by
   half the cell height (--cql-d). JS measures and sets this variable so the
   offset is always exactly half the rendered cell height. */
.cql-face--front  { transform: translateZ(var(--cql-d, 50px)); }

/* Back — sits on the top face of the cube: rotated 90° then pushed outward
   by the same depth. When the parent flipper rotates -90° around X, this
   face descends from above and lands flush facing the viewer.

   Hidden by default: backface-visibility only hides faces whose normal points
   AWAY from the viewer. At rest the back face points UPWARD (not away), so on
   mobile GPUs it bleeds as a thin strip at the cell edge. We hide it with
   visibility and JS reveals it just before each flip. */
.cql-face--back {
  transform: rotateX(90deg) translateZ(var(--cql-d, 50px));
  visibility: hidden;
}

/* ── Logo images ──────────────────────────────────────────────────────── */
.cql-face img {
  display: block;
  max-width: 75%;
  max-height: 60%;
  width: auto;
  height: auto;
  object-fit: contain;
  user-select: none;
}

/* White treatment — invert to white, full opacity */
.cql--white .cql-face img {
  filter: brightness(0) invert(1);
}

/* Original colour treatment — full opacity */

/* ── Bottom padding size toggle ────────────────────────────────────────── */
.cql--pad-less   { padding-bottom: clamp(24px, 3vh, 40px); }
.cql--pad-medium { padding-bottom: clamp(64px, 9vh, 120px); }
.cql--pad-more   { padding-bottom: clamp(100px, 14vh, 180px); }

/* ── Marquee mode ──────────────────────────────────────────────────────── */

/* Hide the grid inner wrapper when in marquee mode — the marquee-wrap takes
   over. The .cql-inner stays so the optional header renders correctly. */
.cql--marquee .cql-grid { display: none; }

/* The scrolling viewport. overflow:hidden clips the track; the mask fades
   logos in from the left and out to the right so nothing hard-clips. */
.cql-marquee-wrap {
  position: relative;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(
    to right,
    transparent 0%,
    black 13%,
    black 87%,
    transparent 100%
  );
  mask-image: linear-gradient(
    to right,
    transparent 0%,
    black 13%,
    black 87%,
    transparent 100%
  );
  padding: clamp(8px, 1.5vh, 20px) 0;
}

/* Pause scroll on hover so the user can read a specific logo */
.cql-marquee-wrap:hover .cql-track {
  animation-play-state: paused;
}

/* The two-copy inner strip — translateX(-50%) produces a seamless loop
   because both sets are identical width. */
.cql-track {
  display: flex;
  width: max-content;
  animation: cql-marquee var(--cql-duration, 35s) linear infinite;
}

/* Each identical half. padding-right = gap so the join between set 1
   and set 2 has the same spacing as any two adjacent logos within a set. */
.cql-track-set {
  display: flex;
  align-items: center;
  gap: clamp(48px, 5.5vw, 96px);
  padding-right: clamp(48px, 5.5vw, 96px);
}

/* Individual logo wrapper. will-change promotes it to its own layer so
   the JS scale transforms are compositor-only and never trigger layout. */
.cql-logo-item {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  height: clamp(40px, 4vh, 60px);
  will-change: transform;
  transform-origin: center;
}

.cql-logo-item img {
  display: block;
  max-height: 100%;
  max-width: clamp(90px, 10vw, 150px);
  width: auto;
  height: auto;
  object-fit: contain;
  pointer-events: none;
  user-select: none;
}

/* White treatment in marquee mode */
.cql--marquee.cql--white .cql-logo-item img {
  filter: brightness(0) invert(1);
}

@keyframes cql-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* ── Reduced motion ────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .cql-cell         { animation: none; opacity: 1; }
  .cql-flipper      { transition: none; }
  .cql-aura::before { animation: none; }
  .cql-track        { animation: none; }
}
