/* =====================================================================
   COUPONTOOLS DESIGN SYSTEM — single source of truth (2026 overhaul)
   ---------------------------------------------------------------------
   - One token block (:root). Every new page/component consumes these.
     No one-off colors / radii / shadows anywhere else.
   - All classes are namespaced `ct-` so they never collide with the
     legacy Bootstrap / theme CSS already loaded site-wide.
   - Fonts stay on the existing Circular Std stack (decision: 2026-06-24,
     Tom). To swap to Sora+Jakarta / Geist+Inter later, change ONLY the
     two --ct-font-* tokens below — nothing else.
   - Motion: see ct-motion.js. Every animation here is disabled under
     @media (prefers-reduced-motion: reduce) at the bottom of this file.
   ===================================================================== */

:root {
  /* ---- Brand color -------------------------------------------------- */
  --ct-orange: #f39a22;          /* used sparingly: accents, single CTA  */
  --ct-orange-600: #e08714;
  --ct-orange-050: #fef4e6;

  /* Brand navy — the real Coupontools navy used across the legacy site.
     Base + a derived deeper shade and a lifted tail for the animated gradient
     backdrops (hero / USP / CTA). Do NOT invent new navies: derive from these. */
  --ct-navy:      #233D63;       /* brand navy                             */
  --ct-navy-deep: #16263f;       /* derived: navy darkened — gradient base  */
  --ct-navy-lift: #223a5e;       /* derived: navy lifted  — gradient tail   */

  /* Blue -> purple gradient = dominant accent (hero glows / highlights) */
  --ct-grad-from: #5b6cff;       /* blue   */
  --ct-grad-mid:  #7a5cff;       /* indigo */
  --ct-grad-to:   #b14bff;       /* purple */
  --ct-gradient:  linear-gradient(120deg, var(--ct-grad-from), var(--ct-grad-mid) 52%, var(--ct-grad-to));
  --ct-gradient-soft: linear-gradient(120deg, rgba(91,108,255,.16), rgba(177,75,255,.16));

  /* ---- Brand color codes (canonical — GLOBAL RULE C) -----------------
     Single source for the brand palette. The dark-section + on-dark tokens
     below are POINTED AT these, so there is no scattered raw hex anywhere.
     --ct-accent is the flat interactive accent (links / highlights / active
     states). NB: --ct-accent as text on --ct-brand-dark is only ~2.2:1 (fails
     WCAG AA) — do NOT use it for links/text on dark sections without a
     lightened on-dark variant (flagged to Tom, pending decision). */
  --ct-brand-dark:        #101028;   /* dark section background            */
  --ct-brand-on-dark:     #ffffff;   /* headings / important text on dark  */
  --ct-brand-on-dark-sub: #acaceb;   /* body / secondary text on dark      */
  --ct-accent:            #3c41e6;   /* flat interactive accent on LIGHT bg
                                        (links / active / button accents).
                                        Coexists with the decorative blue->purple
                                        gradient (--ct-gradient) — the gradient
                                        stays for glows / gradient-text / icon tints. */
  --ct-accent-on-dark:    #7c86ff;   /* lightened accent for use on --ct-brand-dark.
                                        #3c41e6 is only 2.74:1 on #101028 (fails AA);
                                        #7c86ff is ~5.9:1 on #101028 (passes WCAG AA). */

  /* ---- Generic link colour (kills Bootstrap's #007bff fallback) ----------
     Bootstrap's reboot sets `a{color:#007bff}` + `a:hover{color:#0056b3}`, so
     ANY link not covered by a more specific rule (legacy-orange links, ct-
     buttons, nav, a.ct-link …) falls through to browser-ish blue. This token
     is the resting/hover colour for those generic links; the low-specificity
     `a`/`a:hover` rule below consumes it so only the blue-fallback links are
     recoloured — everything with its own colour rule (≥0-1-0) still wins.
     Flips to the AA-safe on-dark accent inside dark contexts via the two
     re-points (.ct-section--dark and [data-theme=dark]). */
  --ct-link-fallback: var(--ct-accent);

  /* ---- Text selection (::selection) — brand purple, not browser blue -----
     Derived from the accent (#3c41e6 = rgb(60,65,230)), NOT a new colour.
     LIGHT default: soft accent tint so the highlight stays pale and the dark
     --ct-ink text on it reads clearly. The dark-section / dark-mode variants
     (below) switch to a stronger accent-on-dark wash with white text so the
     highlight is visible against #101028. Consumed by the single ::selection
     rule below; every context just re-points these two tokens. */
  --ct-selection-bg: rgba(60, 65, 230, .20);  /* accent @20% → pale lavender on white */
  --ct-selection-fg: var(--ct-ink);           /* dark ink → strong contrast on the tint */

  /* ---- Neutrals / surfaces (light-dominant base, dark accents) ------ */
  --ct-bg:        #ffffff;       /* page base                            */
  --ct-bg-subtle: #f7f8fb;       /* alternating section wash             */
  --ct-bg-dark:   var(--ct-brand-dark);   /* #101028 — brand dark sections */
  --ct-bg-dark-2: #171a2e;
  --ct-surface:   #ffffff;       /* card surface                         */

  /* ---- Ink / text --------------------------------------------------- */
  --ct-ink:       #14152b;       /* headings / strong text               */
  --ct-sub-ink:   #565a73;       /* body / secondary                     */
  --ct-faint-ink: #8a8ea6;       /* captions / meta                      */
  --ct-on-dark:        var(--ct-brand-on-dark);      /* #ffffff           */
  --ct-on-dark-sub:    var(--ct-brand-on-dark-sub);  /* #acaceb           */

  /* ---- Hairlines / borders ----------------------------------------- */
  --ct-line:        #e9ebf2;     /* 1px subtle card/section hairline     */
  --ct-line-dark:   rgba(255,255,255,.10);

  /* ---- Radius ------------------------------------------------------- */
  --ct-r-card: 18px;
  --ct-r-sm:   10px;
  --ct-r-lg:   26px;
  --ct-r-pill: 999px;

  /* ---- Shadow (soft, layered) -------------------------------------- */
  --ct-shadow-sm: 0 1px 2px rgba(0,0,0,.05);
  --ct-shadow:    0 1px 2px rgba(0,0,0,.05), 0 16px 40px -16px rgba(20,20,40,.18);
  --ct-shadow-lg: 0 1px 2px rgba(0,0,0,.05), 0 30px 70px -24px rgba(20,20,40,.30);
  --ct-glow:      0 24px 80px -20px rgba(122,92,255,.45);

  /* ---- Spacing (4px base scale) ------------------------------------ */
  --ct-1: 4px;   --ct-2: 8px;   --ct-3: 12px;  --ct-4: 16px;
  --ct-5: 20px;  --ct-6: 24px;  --ct-8: 32px;  --ct-10: 40px;
  --ct-12: 48px; --ct-16: 64px; --ct-20: 80px; --ct-24: 96px;
  --ct-section-y: clamp(4rem, 9vw, 8rem);       /* section top/bottom    */
  --ct-container: 1200px;

  /* ---- Typography --------------------------------------------------- */
  --ct-font-display: 'degular-display', 'Hanken Grotesk', system-ui, sans-serif;
  --ct-font-body:    'Inter', system-ui, -apple-system, sans-serif;

  --ct-fs-display: clamp(2.75rem, 6vw, 4.5rem);  /* H1 */
  --ct-fs-h2:      clamp(2rem, 4vw, 3rem);
  --ct-fs-h3:      1.5rem;
  --ct-fs-body:    1.0625rem;
  --ct-fs-body-lg: 1.125rem;
  --ct-fs-small:   0.875rem;
  --ct-lh-tight:   1.05;
  --ct-lh-snug:    1.2;
  --ct-lh-body:    1.65;
  --ct-tracking-tight: -0.02em;

  /* ---- Motion ------------------------------------------------------- */
  --ct-ease: cubic-bezier(.16,1,.3,1);
  --ct-dur-1: 180ms;
  --ct-dur-2: 240ms;
  --ct-dur-3: 320ms;
}

/* =====================================================================
   TEXT SELECTION — one global rule, brand purple everywhere
   ---------------------------------------------------------------------
   Site-wide ::selection so highlighting reads on-brand instead of the
   browser's default blue. Colours come ONLY from the --ct-selection-*
   tokens, so headings, body, links — every element — highlight the same.
   Context just re-points the two tokens:
     • LIGHT (default, :root)          → pale accent tint + dark ink
     • .ct-section--dark               → stronger accent-on-dark + white
     • [data-theme="dark"] (dark mode) → same treatment, page-wide
   text-shadow:none keeps the selected glyphs crisp under the wash. */
::selection      { background-color: var(--ct-selection-bg); color: var(--ct-selection-fg); text-shadow: none; }
::-moz-selection { background-color: var(--ct-selection-bg); color: var(--ct-selection-fg); text-shadow: none; }
/* Dark sections on an otherwise-light page: brighter wash + white text so the
   highlight is visible against #101028 (a 20% tint would nearly vanish there). */
.ct-section--dark, .ct-footer {
  --ct-selection-bg: rgba(124, 134, 255, .45);  /* accent-on-dark @45% on #101028 */
  --ct-selection-fg: #ffffff;
}

/* =====================================================================
   LAYOUT PRIMITIVES
   ===================================================================== */
.ct-scope { font-family: var(--ct-font-body); color: var(--ct-ink); }

.ct-section {
  position: relative;
  padding-top: var(--ct-section-y);
  padding-bottom: var(--ct-section-y);
  background: var(--ct-bg);
}
.ct-section--subtle { background: var(--ct-bg-subtle); }
.ct-section--dark   { background: var(--ct-bg-dark); color: var(--ct-on-dark); }
.ct-section--dark .ct-eyebrow { color: var(--ct-on-dark-sub); }
.ct-section--flush-top    { padding-top: 0; }
.ct-section--flush-bottom { padding-bottom: 0; }

.ct-container {
  width: 100%;
  max-width: var(--ct-container);
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 4vw, 2.5rem);
}
.ct-container--narrow { max-width: 820px; }

.ct-stack > * + * { margin-top: var(--ct-6); }
.ct-grid { display: grid; gap: var(--ct-6); }
.ct-grid--2 { grid-template-columns: repeat(2, 1fr); }
/* 3-up / 4-up use centered flex-wrap (NOT grid) so an incomplete last row —
   including DB-driven variable counts (press, integrations) and the
   campaign_types 3-in-4 case — is CENTERED instead of left-aligned.
   A full row fills 100%, so justify-content:center is a no-op there → zero
   change on already-even pages. Equal-height is preserved (flex line +
   align-items:stretch == grid row stretch). Safe swap: no .ct-grid--3/--4
   child uses grid-column spans (those live on namespaced grids:
   .ct-rt/md/tc/fan/r26-*, .ct-uc), and no page overrides its template. */
.ct-grid--3,
.ct-grid--4 { display: flex; flex-wrap: wrap; justify-content: center; }
/* Width is set as its own property (NOT calc() inside the `flex` shorthand —
   that basis is mis-parsed in some engines and silently falls back to
   content-size). The -1px trims sub-pixel rounding so an exact-fit row never
   wraps one card down (e.g. 3×384+2×24 == 1200 would otherwise wrap to 2). */
.ct-grid--3 > *,
.ct-grid--4 > * { box-sizing: border-box; }  /* width must include padding so N-up math holds regardless of any reset */
.ct-grid--3 > * { flex: 0 1 auto; width: calc((100% - 2 * var(--ct-6)) / 3 - 1px); }
.ct-grid--4 > * { flex: 0 1 auto; width: calc((100% - 3 * var(--ct-6)) / 4 - 1px); }
@media (max-width: 991px) {
  .ct-grid--3 > *,
  .ct-grid--4 > * { width: calc((100% - var(--ct-6)) / 2 - 1px); }
}
@media (max-width: 575px) {
  .ct-grid--2 { grid-template-columns: 1fr; }
  .ct-grid--3 > *,
  .ct-grid--4 > * { width: 100%; }
}

/* =====================================================================
   TYPOGRAPHY
   ===================================================================== */
.ct-eyebrow {
  display: inline-block;
  font-family: var(--ct-font-body);
  font-size: var(--ct-fs-small);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ct-sub-ink);
  margin-bottom: var(--ct-3);
}
.ct-h1 {
  font-family: var(--ct-font-display);
  font-weight: 700; /* Degular kit ships 400 + 700; pin to 700 so headings render bold */
  font-size: var(--ct-fs-display);
  line-height: var(--ct-lh-tight);
  letter-spacing: var(--ct-tracking-tight);
  margin: 0 0 var(--ct-5);
  color: inherit;
}
.ct-h2 {
  font-family: var(--ct-font-display);
  font-weight: 700;
  font-size: var(--ct-fs-h2);
  line-height: var(--ct-lh-snug);
  letter-spacing: var(--ct-tracking-tight);
  margin: 0 0 var(--ct-4);
  color: inherit;
}
.ct-h3 {
  font-family: var(--ct-font-display);
  font-weight: 700;
  font-size: var(--ct-fs-h3);
  line-height: 1.3;
  margin: 0 0 var(--ct-2);
  color: inherit;
}
.ct-lead {
  font-size: var(--ct-fs-body-lg);
  line-height: var(--ct-lh-body);
  color: var(--ct-sub-ink);
  margin: 0 0 var(--ct-6);
}
.ct-section--dark .ct-lead { color: var(--ct-on-dark-sub); }
.ct-body { font-size: var(--ct-fs-body); line-height: var(--ct-lh-body); color: var(--ct-sub-ink); }
.ct-small { font-size: var(--ct-fs-small); color: var(--ct-faint-ink); }

.ct-gradient-text {
  background: var(--ct-gradient);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

/* Flat interactive accent (GLOBAL RULE C). Use for inline links / highlighted
   interactive text. Auto-swaps to the AA-safe lightened accent on dark sections
   (and in dark mode). Decorative fills stay on --ct-gradient, never this. */
.ct-accent { color: var(--ct-accent); }
a.ct-link { color: var(--ct-accent); font-weight: 600; text-decoration: none; transition: color var(--ct-dur-2) var(--ct-ease); }
a.ct-link:hover { text-decoration: underline; }
.ct-section--dark .ct-accent, .ct-section--dark a.ct-link,
[data-theme="dark"] .ct-accent, [data-theme="dark"] a.ct-link { color: var(--ct-accent-on-dark); }
@media (prefers-reduced-motion: reduce) { a.ct-link { transition: none !important; } }

/* --- Generic link colour: blue → brand accent, site-wide -----------------
   Centralised replacement for Bootstrap's `a{color:#007bff}` / `a:hover{#0056b3}`.
   Loaded AFTER bootstrap (head.php), and deliberately kept at type-selector
   specificity (0-0-1) so it ONLY colours links that currently fall through to
   the blue fallback. Anything with its own colour — .ct-btn* buttons, nav
   links, a.ct-link, the legacy-orange link rules, footer links — has ≥0-1-0
   specificity and is unaffected. Body/paragraph text isn't an <a>, so it stays
   neutral. Only the colour changes: Bootstrap's underline-on-hover and every
   font-weight/decoration is preserved. The colour flips light→dark via the
   --ct-link-fallback token re-points just below (no per-selector dark rule, so
   no risk of clobbering buttons/nav inside dark surfaces). */
a         { color: var(--ct-link-fallback); }
a:hover   { color: var(--ct-link-fallback); }
/* On brand-dark bands and in dark mode, the same generic links use the AA-safe
   on-dark accent (#7c86ff). Re-pointing the TOKEN (not adding a higher-
   specificity `a` selector) keeps buttons/nav/component links intact. */
.ct-section--dark { --ct-link-fallback: var(--ct-accent-on-dark); }
[data-theme="dark"] { --ct-link-fallback: var(--ct-accent-on-dark); }

/* =====================================================================
   BUTTONS  (primary = single orange CTA, secondary = ghost/outline)
   ===================================================================== */
.ct-btn {
  --_btn-bg: var(--ct-orange);
  display: inline-flex;
  align-items: center;
  gap: var(--ct-2);
  font-family: var(--ct-font-body);
  font-weight: 600;
  font-size: var(--ct-fs-body);
  line-height: 1;
  padding: 14px 26px;
  border-radius: var(--ct-r-pill);
  border: 1px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--ct-dur-2) var(--ct-ease),
              box-shadow var(--ct-dur-2) var(--ct-ease),
              background-color var(--ct-dur-2) var(--ct-ease);
  will-change: transform;
}
.ct-btn--primary {
  background: var(--ct-orange);
  color: #fff;
  box-shadow: 0 10px 24px -10px rgba(243,154,34,.7);
}
.ct-btn--primary:hover { background: var(--ct-orange-600); color: #fff; transform: translateY(-2px); box-shadow: 0 16px 30px -10px rgba(243,154,34,.8); }
.ct-btn--secondary {
  background: transparent;
  color: var(--ct-ink);
  border-color: var(--ct-line);
}
.ct-btn--secondary:hover { transform: translateY(-2px); border-color: var(--ct-ink); color: var(--ct-ink); }
.ct-section--dark .ct-btn--secondary { color: var(--ct-on-dark); border-color: var(--ct-line-dark); }
.ct-section--dark .ct-btn--secondary:hover { border-color: var(--ct-on-dark); }
.ct-btn--ghost { background: rgba(255,255,255,.08); color: var(--ct-on-dark); border-color: var(--ct-line-dark); }
.ct-btn:focus-visible { outline: 3px solid var(--ct-grad-mid); outline-offset: 3px; }

.ct-btn-row { display: flex; flex-wrap: wrap; gap: var(--ct-3); align-items: center; }

/* =====================================================================
   CARDS  (crisp, soft depth, hairline — never heavy boxes)
   ===================================================================== */
.ct-card {
  position: relative;
  background: var(--ct-surface);
  border: 1px solid var(--ct-line);
  border-radius: var(--ct-r-card);
  padding: var(--ct-8);
  box-shadow: var(--ct-shadow-sm);
  transition: transform var(--ct-dur-3) var(--ct-ease),
              box-shadow var(--ct-dur-3) var(--ct-ease),
              border-color var(--ct-dur-3) var(--ct-ease);
}
.ct-card--link { display: block; text-decoration: none; color: inherit; }
/* One-big-anchor cards inherit the resting text colour in EVERY state. Without
   this, a legacy/Bootstrap `a:hover{color}` (0-1-1) beats `.ct-card--link`
   (0-1-0) and turns the whole card's text blue/purple on hover. 0-2-0 wins.
   (Card CTAs carry their own accent via .ct-pill / .ct-prod__cta, unaffected.) */
.ct-card--link:hover, .ct-card--link:focus { color: inherit; }
.ct-card--hover:hover,
a.ct-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--ct-shadow);
  border-color: #dfe2ee;
}
.ct-section--dark .ct-card {
  background: var(--ct-bg-dark-2);
  border-color: var(--ct-line-dark);
  color: var(--ct-on-dark);
}
.ct-card__icon {
  display: inline-grid;
  place-items: center;
  width: 52px; height: 52px;
  border-radius: 14px;
  background: var(--ct-gradient-soft);
  color: var(--ct-grad-mid);
  font-size: 22px;
  margin-bottom: var(--ct-4);
}
.ct-card__title { font-family: var(--ct-font-display); font-weight: 700; font-size: 1.25rem; margin: 0 0 var(--ct-2); color: inherit; }

/* Other display-font elements defined in header_v2.php / home_2026.php inline
   styles: pin them to 700 here too (centralised) since the Degular kit has no
   weight above 700. These have no font-weight of their own, so this applies
   cleanly regardless of stylesheet order. */
.ct-sol-block__title,
.ct-step__n,
.ct-price,
.ct-faq summary { font-weight: 700; }
.ct-card__text  { font-size: var(--ct-fs-body); line-height: var(--ct-lh-body); color: var(--ct-sub-ink); margin: 0; }
.ct-section--dark .ct-card__text { color: var(--ct-on-dark-sub); }

/* Pills / chips / hairline separators */
.ct-pill {
  display: inline-flex; align-items: center; gap: var(--ct-2);
  padding: 6px 14px;
  border-radius: var(--ct-r-pill);
  background: var(--ct-orange-050);
  color: var(--ct-orange-600);
  font-size: var(--ct-fs-small); font-weight: 600;
}
.ct-pill--accent { background: var(--ct-gradient-soft); color: var(--ct-grad-mid); }
.ct-hairline { border: 0; border-top: 1px solid var(--ct-line); margin: 0; }
.ct-section--dark .ct-hairline { border-top-color: var(--ct-line-dark); }

/* Decorative gradient glow blob for hero / USP backdrops.
   Slow drift + breathe via the individual `translate`/`scale` properties so it
   never fights a positioning `transform` (e.g. translate(-50%,-50%)) on the blob. */
.ct-glow-blob {
  position: absolute; z-index: 0; pointer-events: none;
  width: 46vw; max-width: 640px; aspect-ratio: 1;
  border-radius: 50%;
  background: var(--ct-gradient);
  filter: blur(90px); opacity: .35;
  animation: ct-glow-drift 20s ease-in-out infinite;
}
@keyframes ct-glow-drift {
  0%, 100% { translate: 0 0; scale: 1; }
  50%      { translate: 3% -4%; scale: 1.14; }
}

/* =====================================================================
   SMOOTH SCROLL (Lenis) — homepage foundation. These rules are INERT
   until Lenis adds `html.lenis` at runtime (ct-scroll.js, homepage only,
   skipped under reduced motion), so they are safe to ship globally.
   ===================================================================== */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: clip; }
.lenis.lenis-smooth iframe { pointer-events: none; }

/* =====================================================================
   CUSTOM CURSOR (homepage, desktop fine-pointer only)
   ---------------------------------------------------------------------
   Created at runtime by ct-cursor.js, which starts ONLY on
   (pointer:fine)+(hover:hover) and NOT under reduced motion. Two layers:
   a small dot (fast follow) and a trailing ring (lagged, grows on hover).
   Both are white with mix-blend-mode:difference so they stay visible over
   light AND dark sections. The native cursor is intentionally kept.
   ===================================================================== */
.ct-cursor-dot, .ct-cursor-ring {
  position: fixed; top: 0; left: 0; z-index: 10000;
  pointer-events: none; border-radius: var(--ct-r-pill);
  mix-blend-mode: difference; opacity: 0;
  will-change: transform;
}
.ct-cursor-dot  { width: 7px;  height: 7px;  background: #fff; }
.ct-cursor-ring {
  width: 34px; height: 34px; border: 1.5px solid #fff; background-color: transparent;
  transition: background-color .25s var(--ct-ease), border-color .25s var(--ct-ease);
}
/* hover-grow fill: the ring fills with the brand accent. Blend-mode is turned
   off so the accent reads true (instead of being inverted by difference). */
.ct-cursor-ring.is-accent {
  mix-blend-mode: normal;
  background-color: var(--ct-grad-mid);
  border-color: var(--ct-grad-mid);
}

/* Label pill (effect 3): expands out of the cursor over labelled targets
   (campaign cards -> "Explore", case studies -> "Read case", demo -> "Play").
   Brand-gradient fill, white label; transform/opacity only for the morph. */
.ct-cursor-label {
  position: fixed; top: 0; left: 0; z-index: 10000; pointer-events: none;
  display: inline-flex; align-items: center; white-space: nowrap;
  padding: 9px 18px; border-radius: var(--ct-r-pill);
  background-image: var(--ct-gradient); color: #fff;
  font-family: var(--ct-font-body); font-weight: 700; font-size: 13px; line-height: 1;
  letter-spacing: .01em; box-shadow: var(--ct-shadow);
  opacity: 0; will-change: transform, opacity;
}

/* Belt-and-suspenders: never paint on touch / coarse pointers or under
   reduced motion (ct-cursor.js also refuses to start in those cases). */
@media (hover: none), (pointer: coarse) {
  .ct-cursor-dot, .ct-cursor-ring, .ct-cursor-label { display: none !important; }
}
@media (prefers-reduced-motion: reduce) {
  .ct-cursor-dot, .ct-cursor-ring, .ct-cursor-label { display: none !important; }
}

/* Magnetic buttons (effect 4): on fine pointers GSAP owns the transform, so
   drop transform from the CSS transition to avoid double-smoothing (hover
   background/shadow still transition). On touch the default .ct-btn transition
   stays; under reduced motion the global rule below forces transition:none. */
@media (pointer: fine) and (hover: hover) {
  [data-ct-magnetic] {
    transition: box-shadow var(--ct-dur-2) var(--ct-ease),
                background-color var(--ct-dur-2) var(--ct-ease);
  }
}

/* =====================================================================
   MOTION UTILITIES  (canonical = .ct-reveal via ct-motion.js IO)
   ===================================================================== */
.ct-reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity var(--ct-dur-3) var(--ct-ease),
              transform var(--ct-dur-3) var(--ct-ease);
  will-change: opacity, transform;
}
.ct-reveal.is-visible { opacity: 1; transform: none; }
/* staggered children for grids: add .ct-stagger to the grid, .ct-reveal to items */
.ct-stagger .ct-reveal.is-visible { transition-delay: calc(var(--ct-reveal-i, 0) * 70ms); }

/* Horizontal scroll-parallax targets (effect 2). Inline elements ignore
   transform, so the hero's inner drift span must be inline-block. */
[data-ct-xdrift], .ct-ctexp__title { will-change: transform; }
.ct-h1__drift { display: inline-block; }

/* Oversized statement headline for the horizontal scroll-parallax (effect 2,
   driven by data-ct-xdrift in ct-scroll-effects.js). On wide viewports it goes
   big and single-line so it can bleed off-screen — the bleed is clipped by
   .ct-home { overflow-x: clip }, so it never creates a horizontal scrollbar.
   Opt-in per heading; below the breakpoint it stays a normal responsive head. */
@media (min-width: 992px) {
  .ct-headline-xl {
    font-size: clamp(3.25rem, 7.5vw, 6.5rem);
    line-height: var(--ct-lh-tight);
    letter-spacing: var(--ct-tracking-tight);
    white-space: nowrap;
  }
}

.ct-float { animation: ct-float 6s var(--ct-ease) infinite; }
@keyframes ct-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-14px); }
}

.ct-lift { transition: transform var(--ct-dur-2) var(--ct-ease), box-shadow var(--ct-dur-2) var(--ct-ease); }
.ct-lift:hover { transform: translateY(-3px); box-shadow: var(--ct-shadow); }

/* =====================================================================
   REDUCED MOTION — mandatory fallback for everything above
   ===================================================================== */
@media (prefers-reduced-motion: reduce) {
  .ct-reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
  .ct-float  { animation: none !important; }
  .ct-glow-blob { animation: none !important; }
  .ct-lift, .ct-btn, .ct-card, a.ct-card { transition: none !important; }
  .ct-lift:hover, .ct-card--hover:hover, a.ct-card:hover,
  .ct-btn--primary:hover, .ct-btn--secondary:hover { transform: none !important; }
}

/* =====================================================================
   DARK MODE — manual, default light. Flips ONLY the ct- tokens; every ct-
   component reads from these so it switches automatically. Legacy theme CSS
   doesn't use these tokens, so legacy pages/content stay light. Toggle sets
   [data-theme="dark"] on <html>.
   ===================================================================== */
[data-theme="dark"] {
  --ct-bg:        #0d1017;   /* deep, not pure black */
  --ct-bg-subtle: #141a26;   /* alternating section wash (slightly elevated) */
  --ct-bg-dark:   #08090f;   /* .ct-section--dark goes deeper to keep rhythm */
  --ct-bg-dark-2: #1b2130;   /* cards inside dark sections */
  --ct-surface:   #161b27;   /* card surface (elevated above --ct-bg) */

  --ct-ink:       #eef1f8;   /* AA contrast on the dark bg */
  --ct-sub-ink:   #a7adc4;
  --ct-faint-ink: #727991;
  --ct-on-dark:        #f4f5fb;
  --ct-on-dark-sub:    #b6bbd6;

  --ct-line:      rgba(255,255,255,.10);  /* hairline = subtle light edge */
  --ct-line-dark: rgba(255,255,255,.08);

  --ct-orange-050: rgba(243,154,34,.16);  /* active-pill / chip tint on dark */

  /* Selection: page-wide dark bg → the light-tint default would vanish, so use
     the same brighter accent-on-dark wash + white text as the dark sections. */
  --ct-selection-bg: rgba(124,134,255,.40);
  --ct-selection-fg: #ffffff;

  /* big shadows read poorly on dark — keep them deep but subtle */
  --ct-shadow-sm: 0 1px 2px rgba(0,0,0,.5);
  --ct-shadow:    0 1px 2px rgba(0,0,0,.5), 0 14px 36px -18px rgba(0,0,0,.7);
  --ct-shadow-lg: 0 1px 2px rgba(0,0,0,.5), 0 28px 60px -26px rgba(0,0,0,.85);

  --ct-gradient-soft: linear-gradient(120deg, rgba(91,108,255,.22), rgba(177,75,255,.22));
}
/* a few component touch-ups that can't come from a token alone */
[data-theme="dark"] .ct-card--hover:hover,
[data-theme="dark"] a.ct-card:hover { border-color: rgba(255,255,255,.20); }
[data-theme="dark"] .ct-section--dark { border-top: 1px solid rgba(255,255,255,.05); border-bottom: 1px solid rgba(255,255,255,.05); }
[data-theme="dark"] .ct-logos img { filter: grayscale(1) brightness(0) invert(1) opacity(.85); }
[data-theme="dark"] .ct-logos img:hover { filter: none; }

/* Soft colour transition ONLY while toggling (class added by JS), so normal
   interactions aren't affected. Disabled under reduced motion. */
html.ct-theming, html.ct-theming *, html.ct-theming *::before, html.ct-theming *::after {
  transition: background-color var(--ct-dur-3) var(--ct-ease),
              color var(--ct-dur-3) var(--ct-ease),
              border-color var(--ct-dur-3) var(--ct-ease),
              box-shadow var(--ct-dur-3) var(--ct-ease) !important;
}
@media (prefers-reduced-motion: reduce) {
  html.ct-theming, html.ct-theming *, html.ct-theming *::before, html.ct-theming *::after { transition: none !important; }
}

/* =====================================================================
   REUSABLE COMPONENTS  (extracted from home_2026.php — Phase 1)
   ---------------------------------------------------------------------
   These were proven on the 2026 homepage and lifted here VERBATIM so
   every other page consumes the SAME classes (no per-page style forks).
   All values come from the ct- tokens above. Homepage-specific decoration
   (hero collage, floating-logo constellation, logo strip, video modal)
   intentionally stays inline in home_2026.php — it is not part of the
   shared system.
   ===================================================================== */

/* ---- Display title (.ct-ctexp__title) ----------------------------
   The signature section heading. Scope with .ct-ctexp on a <section>; tag
   the heading .ct-ctexp__title (keep .ct-h2 on it too as the fallback).
   .ct-ctexp__head is the intro block above a section body.

   SCALE POLICY (Tom, 2026-07-06): title size follows title LENGTH, not the
   page. Choose ONE modifier per heading, explicitly in the markup:
     • .ct-ctexp--xl  -> short punchy titles (≈≤25 chars / 2-3 words). Big & bold.
     • (no modifier)  -> medium default, for in-between titles.
     • .ct-ctexp--sm  -> long, full-sentence titles, so they don't dominate/wrap.
   The homepage is unchanged: .ct-home forces the oversized scale on all its
   .ct-ctexp titles regardless of modifier (higher specificity). */
/* Contain the scroll-drift of .ct-ctexp__title (ct-scroll-effects.js) so the
   horizontal parallax can NEVER produce a scrollbar or push adjacent content,
   on any width. Mirrors the homepage's `.ct-home { overflow-x: clip }`, but
   scoped to the display-title section: the section is full-viewport-width, so
   clipping here == clipping at the viewport edge (no visual change to the
   homepage, which already clips at .ct-home). overflow-y stays visible. */
.ct-ctexp { overflow-x: clip; }
.ct-ctexp .ct-ctexp__head { max-width: none !important; }   /* let the big title breathe */
.ct-ctexp .ct-ctexp__title {                /* descendant on purpose: a .ct-ctexp__title
                                               WITHOUT a .ct-ctexp section stays .ct-h2 size
                                               (e.g. the homepage final-CTA heading) */
  font-family: var(--ct-font-display);
  font-weight: 700;                          /* Degular kit ships max 700 (no 800/900) */
  font-size: clamp(2.25rem, 5.5vw, 4rem);    /* medium default: 36px -> 64px */
  line-height: 1.06;
  letter-spacing: -0.02em;
  text-wrap: balance;                        /* avoids lonely/awkward last-line words */
  margin: 0 0 var(--ct-4);
}
/* --xl : big & impressive — short punchy titles + the homepage (auto via .ct-home). */
.ct-home .ct-ctexp .ct-ctexp__title,
.ct-ctexp--xl .ct-ctexp__title {
  font-size: clamp(2.75rem, 10vw, 9rem);     /* 44px mobile floor -> 144px desktop */
  line-height: 1.0;
}
/* --sm : reduced — long full-sentence titles so they read calmly and wrap well. */
.ct-ctexp--sm .ct-ctexp__title {
  font-size: clamp(1.75rem, 3.2vw, 2.75rem); /* 28px -> 44px */
  line-height: 1.15;
}

/* ---- Tile (compact left-aligned card body) ------------------------ */
.ct-tile { text-align: left; }
.ct-tile .ct-card__icon { font-size: 20px; }

/* ---- Card footer link (flat-accent "Read More" inside a card) -----
   Composes with a.ct-link for the accent colour / dark-mode swap. Sits
   at the bottom of a .ct-tile / .ct-card with a subtle arrow nudge. */
.ct-card__link { display: inline-flex; align-items: center; gap: .4em; margin-top: var(--ct-4); font-weight: 600; }
.ct-card__link i { font-size: .8em; transition: transform var(--ct-dur-2) var(--ct-ease); }
.ct-card__link:hover i { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) { .ct-card__link i { transition: none !important; } }

/* ---- Industry-solution hero (two-column: copy + image) -----------
   Shared across the solutions/industry pages. Top padding clears the
   fixed legacy navbar (220px desktop / 120px mobile). Optional subtle brand
   glow: drop a <span class="ct-glow-blob ct-sol-hero__glow"> inside (matches
   the retail hero glow). */
.ct-sol-hero { position: relative; overflow: hidden; padding-top: clamp(120px, 16vw, 220px); }
.ct-sol-hero > .ct-container { position: relative; z-index: 1; }
.ct-sol-hero__glow { left: -8%; top: -12%; opacity: .2; animation-duration: 23s; }
.ct-sol-hero__grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
.ct-sol-hero__media img { width: 100%; height: auto; border-radius: var(--ct-r-lg); display: block; box-shadow: var(--ct-shadow); }
@media (max-width: 991px) {
  .ct-sol-hero__grid { grid-template-columns: 1fr; }
  .ct-sol-hero__media { order: -1; }
}

/* ---- Star rating row ---------------------------------------------- */
.ct-stars { color: var(--ct-orange); font-size: 1.1rem; letter-spacing: 2px; }

/* ---- Compact price display ---------------------------------------- */
.ct-price { font-family: var(--ct-font-display); font-size: clamp(2.5rem, 5vw, 3.5rem); line-height: 1; }
.ct-price small { font-size: 1rem; color: var(--ct-sub-ink); font-family: var(--ct-font-body); font-weight: 400; }

/* ---- Alternating image + text split rows (.ct-split) --------------
   Two-column media/text row used for "reasons / solutions / examples"
   sections. Add .ct-split--rev to flip the media to the right. Stacks on
   mobile (media first). */
.ct-split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
.ct-split + .ct-split { margin-top: clamp(3rem, 7vw, 6rem); }
.ct-split__media img { width: 100%; height: auto; border-radius: var(--ct-r-lg); display: block; box-shadow: var(--ct-shadow); }
.ct-split--rev .ct-split__media { order: 2; }
.ct-split__point + .ct-split__point { margin-top: var(--ct-6); }
.ct-split__point h3 { margin-bottom: var(--ct-2); }
@media (max-width: 767px) {
  .ct-split { grid-template-columns: 1fr; }
  .ct-split--rev .ct-split__media { order: -1; }
}

/* ---- Video frame (ct- treatment for the legacy .videoWrapper embed) -------
   Wrap a legacy `.videoWrapper` in `.ct-video-frame` to swap its off-brand
   orange 1px border + orange glow for the design-system rounded + elevated
   card look, so the video reads like the hero / .ct-split media on the same
   page. Frame only — the embed / .youtube-video-place placeholder is untouched.
   Reusable on any converted page that keeps the legacy video markup. */
.ct-video-frame { max-width: 860px; margin-inline: auto; }
.ct-video-frame .videoWrapper { border: 0; border-radius: var(--ct-r-lg); box-shadow: var(--ct-shadow); overflow: hidden; }
.ct-video-frame .videoWrapper iframe { border-radius: var(--ct-r-lg); box-shadow: none; }

/* ---- Image-driven media card (.ct-prod) --------------------------
   Fixed aspect-ratio media so lazy-loading never shifts the grid. */
.ct-prod { padding: 0; overflow: hidden; }
.ct-prod__media {
  display: block; width: 100%; aspect-ratio: 786 / 730;
  background: var(--ct-bg-subtle); overflow: hidden; position: relative;
}
.ct-prod__media img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform var(--ct-dur-3) var(--ct-ease);
}
.ct-prod:hover .ct-prod__media img { transform: scale(1.05); }
.ct-prod__title { margin: 0; padding: var(--ct-5) var(--ct-6); font-size: 1.1rem; }
/* Optional description + CTA slots below the title, for product/method card
   grids that need more than a title (digital_coupons types, methods steps). */
.ct-prod__text { margin: 0; padding: 0 var(--ct-6) var(--ct-5); }
.ct-prod__cta { display: inline-flex; margin: 0 var(--ct-6) var(--ct-6); }
.ct-prod:has(.ct-prod__text) .ct-prod__title,
.ct-prod:has(.ct-prod__cta) .ct-prod__title { padding-bottom: var(--ct-2); }
.ct-prod__media--ph {
  display: grid; place-items: center; align-content: center; gap: var(--ct-3);
  background: var(--ct-gradient-soft); color: var(--ct-grad-mid); text-align: center;
}
.ct-prod__media--ph i { font-size: 2.6rem; }
.ct-prod__phlabel { font-family: var(--ct-font-display); font-weight: 700; color: var(--ct-ink); font-size: 1.05rem; }
.ct-prod__badge {
  position: absolute; top: 10px; left: 10px;
  background: rgba(13,16,23,.88); color: #fff; font-size: 10px; font-weight: 700; letter-spacing: .04em;
  padding: 4px 9px; border-radius: var(--ct-r-pill); text-transform: uppercase;
}

/* ---- .ct-prod-cell — shared Option-A glow wrapper for .ct-prod grids -------
   Wrap each .ct-prod card in <div class="ct-prod-cell"> to get the site-wide
   on-hover glow WITHOUT the rectangular clip bug. .ct-prod keeps its own
   overflow:hidden (media + rounded top); the soft brand-gradient glow lives on
   this NON-clipping, never-transformed wrapper (z-index:-1) so it feathers on
   every side. Generalises the proven .ct-intov-cell / .ct-prs-cell / .ct-gb
   pattern for the shared partials (campaign_types, coupontools-methods,
   othergames_v2, …) and the product/loyalty/gamification example grids.
   Also makes .ct-prod a flex column so a trailing .ct-prod__cta pins to the
   bottom (CTAs aligned across a row) at its natural width. */
.ct-prod-cell { position: relative; display: flex; }
.ct-prod-cell > .ct-prod { flex: 1; display: flex; flex-direction: column; }
.ct-prod-cell > .ct-prod > .ct-prod__cta { margin-top: auto; align-self: flex-start; }
/* Soft brand-gradient glow BEHIND/AROUND the card (wrapper, un-clipped). */
.ct-prod-cell::before {
  content: ""; position: absolute; z-index: -1; inset: -2px;
  border-radius: var(--ct-r-card);
  background: var(--ct-gradient); filter: blur(24px);
  opacity: 0; transform: scale(.96);
  transition: opacity var(--ct-dur-3) var(--ct-ease), transform var(--ct-dur-3) var(--ct-ease);
  pointer-events: none;
}
/* Gradient top-edge accent — ON the card, clipped to its rounded top. */
.ct-prod-cell > .ct-prod::after {
  content: ""; position: absolute; z-index: 2; top: 0; left: 0; right: 0; height: 4px;
  background: var(--ct-gradient);
  opacity: 0; transform: scaleX(.55); transform-origin: left center;
  transition: opacity var(--ct-dur-3) var(--ct-ease), transform var(--ct-dur-3) var(--ct-ease);
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  .ct-prod-cell:hover::before { opacity: .3; transform: scale(1); }
  .ct-prod-cell:hover > .ct-prod::after { opacity: 1; transform: scaleX(1); }
}
@media (prefers-reduced-motion: reduce) {
  .ct-prod-cell::before, .ct-prod-cell > .ct-prod::after { transition: none !important; }
  .ct-prod-cell:hover::before { opacity: 0 !important; }
}

/* ---- Integration use-case card (.ct-intuc) -----------------------
   Make / Pabbly "when X → then Y" template cards: an app-pair icon row +
   description + a "Try it now" pseudo-button. The WHOLE card is the external
   link (workflow-invite URL), so the button is a decorative <span>, not a
   nested <a>. Composes on .ct-card (use .ct-card ct-card--link ct-card--hover). */
.ct-intuc-grid { display: grid; gap: var(--ct-4); }
.ct-intuc { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: clamp(1rem, 3vw, 2rem); }
.ct-intuc__apps { display: inline-flex; align-items: center; gap: var(--ct-2); flex: none; }
.ct-intuc__apps img { width: 48px; height: 48px; border-radius: 12px; background: #fff; padding: 4px; border: 1px solid var(--ct-line); object-fit: contain; }
.ct-intuc__plus { color: var(--ct-faint-ink); font-size: 13px; }
.ct-intuc__body { min-width: 0; }
.ct-intuc__title { display: block; font-weight: 700; color: var(--ct-ink); margin: 0 0 4px; font-size: 1.05rem; line-height: 1.35; }
.ct-intuc__sub { display: block; color: var(--ct-sub-ink); font-size: var(--ct-fs-small); }
.ct-intuc__cta { flex: none; white-space: nowrap; }
@media (max-width: 767px) {
  .ct-intuc { grid-template-columns: 1fr; justify-items: center; text-align: center; gap: var(--ct-4); }
}

/* ---- Game product page (.ct-game) --------------------------------
   Shared layout for the 7 gamified-coupon detail pages (scratch, spin,
   slot, memory, match2win, slide, catch). Each page is markup-only and
   reuses these classes — no per-file <style>. Mirrors the digital_coupons
   hero/video treatment so the whole product set reads as one system. */
.ct-game { overflow-x: clip; background: var(--ct-bg); color: var(--ct-ink); }
.ct-game-hero { position: relative; overflow: hidden; padding-top: clamp(120px, 16vw, 220px); }
.ct-game-hero > .ct-container { position: relative; z-index: 1; }
.ct-game-hero__glow { left: -8%; top: -14%; opacity: .2; animation-duration: 23s; }
.ct-game-hero__grid { display: grid; grid-template-columns: 1.05fr .95fr; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
.ct-game-hero__media img { width: 100%; height: auto; border-radius: var(--ct-r-lg); display: block; }
@media (max-width: 991px) {
  .ct-game-hero__grid { grid-template-columns: 1fr; }
  .ct-game-hero__media { order: -1; }
}
.ct-game-video { max-width: 860px; margin-inline: auto; }
.ct-game-video .videoWrapper { border-radius: var(--ct-r-lg); overflow: hidden; box-shadow: var(--ct-shadow); }

/* ---- Game "benefits of your branded <game>" cards -----------------
   The benefit grid shared by all 7 game pages. Was a cramped, left-aligned
   4-up (7-card pages left an awkward right-gap); now a WIDER 3-up grid with
   any trailing partial row auto-centred, so 6 → 3+3 and 7 → 3+3+1-centred
   both read as a balanced block. Pages with exactly 4 benefits opt into a
   clean single row via .ct-game-benefits--4. Card treatment reuses the
   restaurant/goal Option-A language wholesale — brand glow + gradient
   top-edge + subtle lift on a NON-transforming wrapper (glow feathers
   cleanly on every side, never clipped) — plus a comfier, larger text
   measure (image → title → body hierarchy) so the whole game set reads as
   one system. */
.ct-game-benefits {
  display: grid; gap: var(--ct-6); align-items: stretch;
  grid-template-columns: repeat(6, minmax(0, 1fr));   /* each card spans 2 → 3 per row */
}
.ct-game-benefits--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.ct-gb { position: relative; display: flex; min-width: 0; grid-column: span 2; }   /* glow wrapper — never transforms; min-width:0 stops the mockup img blowing out the track */
.ct-game-benefits--4 > .ct-gb { grid-column: span 1; }

/* Centre a trailing partial row so it sits balanced under the full rows above
   (3-up only). Single leftover (7th of 7, 4th of 4) → dead-centre; a leftover
   pair (5th of 5, 8th of 8) → centred pair. --4 pages keep their full row. */
.ct-game-benefits:not(.ct-game-benefits--4) > .ct-gb:last-child:nth-child(3n + 1) { grid-column: 3 / span 2; }
.ct-game-benefits:not(.ct-game-benefits--4) > .ct-gb:nth-last-child(2):nth-child(3n + 1) { grid-column: 2 / span 2; }
.ct-game-benefits:not(.ct-game-benefits--4) > .ct-gb:last-child:nth-child(3n + 2) { grid-column: 4 / span 2; }

/* Card fills the wrapper → equal-height rows, aligned internals. .ct-prod keeps
   the flush image + rounded overflow-clip; we add the column flow + hover lift. */
.ct-gb__card {
  flex: 1; display: flex; flex-direction: column;
  transition: transform var(--ct-dur-3) var(--ct-ease), box-shadow var(--ct-dur-3) var(--ct-ease), border-color var(--ct-dur-3) var(--ct-ease);
}
@media (hover: hover) and (pointer: fine) {
  .ct-gb:hover .ct-gb__card { transform: translateY(-4px); }
}

/* Readability: bigger title for clear hierarchy, and a larger/looser body with
   more breathing room. Body colour stays --ct-sub-ink (#565a73 on white =
   6.8:1 — passes WCAG AA); the wider card + step-up in size fixes the choppy,
   4-words-per-line measure. */
.ct-gb .ct-prod__title { font-size: 1.3rem; line-height: 1.3; padding-top: var(--ct-6); }
.ct-gb .ct-prod__text  { font-size: var(--ct-fs-body-lg); line-height: 1.7; padding-bottom: var(--ct-6); }

/* Gradient top-edge accent — lights up on hover, clipped to the card's rounded top. */
.ct-gb__card::before {
  content: ""; position: absolute; z-index: 2; top: 0; left: 0; right: 0; height: 4px;
  background: var(--ct-gradient);
  opacity: 0; transform: scaleX(.55); transform-origin: left center;
  transition: opacity var(--ct-dur-3) var(--ct-ease), transform var(--ct-dur-3) var(--ct-ease);
  pointer-events: none;
}
/* Soft brand-gradient glow behind the card — blooms symmetrically and feathers
   to transparent on every side (blur, no hard edge). Lives on the wrapper
   (z-index:-1, un-clipped) so the card's lift can't trap it in a stacking context. */
.ct-gb::before {
  content: ""; position: absolute; z-index: -1; inset: -2px;
  border-radius: var(--ct-r-card);
  background: var(--ct-gradient); filter: blur(24px);
  opacity: 0; transform: scale(.96);
  transition: opacity var(--ct-dur-3) var(--ct-ease), transform var(--ct-dur-3) var(--ct-ease);
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  .ct-gb:hover::before { opacity: .3; transform: scale(1); }
  .ct-gb:hover .ct-gb__card::before { opacity: 1; transform: scaleX(1); }
  .ct-gb:hover .ct-gb__card { box-shadow: var(--ct-shadow); border-color: #dfe2ee; }
}
/* Playful scroll-in: the mockup settles from a slight zoom, delay tracks the
   same --ct-reveal-i stagger index the cards fade in on (matches restaurant/goal). */
@media (prefers-reduced-motion: no-preference) {
  .ct-game-benefits.ct-stagger .ct-reveal.is-visible .ct-prod__media img {
    animation: ct-gb-prodpop 620ms var(--ct-ease) both;
    animation-delay: calc(var(--ct-reveal-i, 0) * 70ms + 140ms);
  }
}
@keyframes ct-gb-prodpop { 0% { transform: scale(1.08); } 100% { transform: scale(1); } }

@media (max-width: 991px) {
  .ct-game-benefits, .ct-game-benefits--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ct-game-benefits > .ct-gb,
  .ct-game-benefits--4 > .ct-gb,
  .ct-game-benefits:not(.ct-game-benefits--4) > .ct-gb:last-child:nth-child(3n + 1),
  .ct-game-benefits:not(.ct-game-benefits--4) > .ct-gb:nth-last-child(2):nth-child(3n + 1),
  .ct-game-benefits:not(.ct-game-benefits--4) > .ct-gb:last-child:nth-child(3n + 2) { grid-column: auto; }
}
@media (max-width: 575px) {
  .ct-game-benefits, .ct-game-benefits--4 { grid-template-columns: minmax(0, 1fr); }
}
@media (prefers-reduced-motion: reduce) {
  .ct-gb__card, .ct-gb::before, .ct-gb__card::before { transition: none !important; }
  .ct-gb:hover .ct-gb__card { transform: none !important; }     /* no lift */
  .ct-gb:hover::before { opacity: 0 !important; }               /* no glow bloom */
  .ct-game-benefits .ct-prod__media img { animation: none !important; }
}

/* ---- Goal solutions pages (.ct-goal) -----------------------------
   Shared layout for the 3 goal-based Solutions-dropdown pages:
   Attract more customers / Digitalize existing loyalty or promotions /
   Retain existing customers. Each is markup-only and reuses these
   classes — no per-file <style>, so the three never fork. Same card
   language as the restaurant/retail product grids: Option-A on-hover
   brand glow + gradient top-edge + staggered scroll-reveal "pop" on the
   mockups, so all three read as one system. Card count varies per page
   (3 / 5 / 8) — the auto-flow 3-col grid handles any count (trailing
   partial rows left-aligned). */
.ct-goal { overflow-x: clip; background: var(--ct-bg); color: var(--ct-ink); }

/* Hero — centered copy sitting on the shared ambient gradient (below). */
.ct-goal-hero { position: relative; overflow: hidden; padding-top: clamp(120px, 16vw, 220px);
    padding-bottom: clamp(2.25rem, 5vw, 3.25rem); text-align: center; }
.ct-goal-hero > .ct-container { position: relative; z-index: 1; }
.ct-goal-hero__glow { left: 50%; top: -18%; transform: translateX(-50%); opacity: .2; animation-duration: 24s; }
.ct-goal-hero__copy { max-width: 900px; margin-inline: auto; }
.ct-goal-hero .ct-lead { margin-inline: auto; }

/* Hero flows straight into the grid — one calm gap, no dead space. The cards
   section drops its own top padding so the hero's (reduced) padding-bottom is
   the SINGLE gap between the intro and the cards. */
.ct-goal-cards { padding-top: 0; }

/* Ambient brand gradient behind the hero, bleeding down behind the first cards.
   Reuses the pricing engine wholesale — the [data-ct-pricing] pale palette, the
   .ct-pbg / .ct-pbg__blob CSS drift (ct-pbg-float) and the ct-pricing-bg.js
   cursor parallax — so it reads as the same calm, barely-there wash we settled
   on for pricing. Height-capped and soft-masked at the bottom so it feathers out
   continuously instead of cutting off on a hard edge; the solid cards stay fully
   legible on top (section content is lifted above the wash). The two sections
   go transparent so the main-level wash shows through them; the page's white
   base comes from .ct-goal itself, and the solid cards keep their own surface. */
.ct-goal-hero, .ct-goal-cards { position: relative; z-index: 1; background: transparent; }
.ct-goal .ct-pbg {
    bottom: auto;
    height: clamp(720px, 92vh, 1180px);
    -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 50%, transparent 100%);
            mask-image: linear-gradient(to bottom, #000 0%, #000 50%, transparent 100%);
}

/* Product/solution cards — polished card + on-hover brand glow. The glow sits
   on a NON-transformed wrapper so the inner card's lift can't trap it in a
   stacking context (it feathers cleanly on all sides). */
.ct-goal-grid { display: grid; gap: var(--ct-6); grid-template-columns: repeat(3, 1fr); align-items: stretch; }
.ct-goal-prod { position: relative; display: flex; }                 /* glow wrapper (never transforms) */
.ct-goal-prod__card { flex: 1; display: flex; flex-direction: column; gap: var(--ct-2); overflow: hidden; }
.ct-goal-prod__card .ct-card__title { font-family: var(--ct-font-display); font-weight: 700; font-size: 1.2rem; margin-top: var(--ct-4); }
.ct-goal-prod__ex { display: block; }
/* Landscape ~3:2 bespoke card image fills the framed thumbnail. */
.ct-goal-prod__media { display: block; aspect-ratio: 3 / 2; background: var(--ct-bg-subtle); border-radius: var(--ct-r-sm); overflow: hidden; }
.ct-goal-prod__media img { width: 100%; height: 100%; object-fit: cover; display: block; will-change: transform;
    transition: transform var(--ct-dur-3) var(--ct-ease); }
.ct-goal-prod__arrow { margin-top: auto; padding-top: var(--ct-4); color: var(--ct-accent); font-size: 1rem; line-height: 1; transition: transform var(--ct-dur-2) var(--ct-ease); }
.ct-goal-prod__card:hover .ct-goal-prod__arrow { transform: translateX(4px); }
/* Gradient top-edge accent — lights up on hover, clipped to the card's rounded top. */
.ct-goal-prod__card::before {
    content: ""; position: absolute; z-index: 2; top: 0; left: 0; right: 0; height: 4px;
    background: var(--ct-gradient);
    opacity: 0; transform: scaleX(.55); transform-origin: left center;
    transition: opacity var(--ct-dur-3) var(--ct-ease), transform var(--ct-dur-3) var(--ct-ease);
    pointer-events: none;
}
/* Soft brand-gradient glow behind the card (on the wrapper, z-index:-1). */
.ct-goal-prod::before {
    content: ""; position: absolute; z-index: -1; inset: -2px;
    border-radius: var(--ct-r-card);
    background: var(--ct-gradient); filter: blur(24px);
    opacity: 0; transform: scale(.96);
    transition: opacity var(--ct-dur-3) var(--ct-ease), transform var(--ct-dur-3) var(--ct-ease);
    pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
    .ct-goal-prod:hover::before { opacity: .3; transform: scale(1); }
    .ct-goal-prod:hover .ct-goal-prod__card::before { opacity: 1; transform: scaleX(1); }
    .ct-goal-prod:hover .ct-goal-prod__media img { transform: scale(1.04); }
}
/* Playful scroll-in reveal — mockup settles from a slight zoom; delay tracks
   the same --ct-reveal-i stagger index the cards fade in on. */
@media (prefers-reduced-motion: no-preference) {
    .ct-goal-grid.ct-stagger .ct-reveal.is-visible .ct-goal-prod__media img {
        animation: ct-goal-prodpop 620ms var(--ct-ease) both;
        animation-delay: calc(var(--ct-reveal-i, 0) * 70ms + 140ms);
    }
}
@keyframes ct-goal-prodpop {
    0%   { transform: scale(1.08); }
    100% { transform: scale(1); }
}
@media (max-width: 991px) { .ct-goal-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 575px) { .ct-goal-grid { grid-template-columns: 1fr; } }
@media (prefers-reduced-motion: reduce) {
    .ct-goal-prod::before, .ct-goal-prod__card::before, .ct-goal-prod__arrow, .ct-goal-prod__media img { transition: none !important; }
    .ct-goal-prod:hover::before { opacity: 0 !important; }
    .ct-goal-prod:hover .ct-goal-prod__arrow { transform: none !important; }
    .ct-goal-prod:hover .ct-goal-prod__media img { transform: none !important; }
    .ct-goal-prod__media img { animation: none !important; }
}

/* ---- Case-study card (.ct-case) — full-bleed hero + body ---------- */
.ct-case { padding: 0; overflow: hidden; text-align: left; }
.ct-case__media { display: block; width: 100%; aspect-ratio: 3 / 2; background: var(--ct-bg-subtle); overflow: hidden; }
.ct-case__media img { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; transition: transform var(--ct-dur-3) var(--ct-ease); }
.ct-case:hover .ct-case__media img { transform: scale(1.05); }
.ct-case__body { display: block; padding: var(--ct-6); }
.ct-case__soort { display: block; margin-bottom: var(--ct-2); }
.ct-case__title { margin: 0 0 var(--ct-2); }

/* ---- "How it works" step flow (.ct-steps-flow) -------------------
   Connected, staggered, equal-height step cards. */
.ct-steps-flow { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--ct-8); position: relative; align-items: stretch; }
.ct-step { position: relative; text-align: center; display: flex; flex-direction: column; }
.ct-step__num {
  position: relative; z-index: 2; display: inline-grid; place-items: center;
  width: 48px; height: 48px; margin-bottom: var(--ct-5);
  border-radius: var(--ct-r-pill); background: var(--ct-gradient); color: #fff;
  font-family: var(--ct-font-display); font-weight: 700; font-size: 1.2rem;
  box-shadow: var(--ct-shadow);
}
.ct-step__num::before { content: ""; position: absolute; inset: -6px; border-radius: 50%; border: 2px solid var(--ct-grad-mid); opacity: 0; }
.ct-step.is-visible .ct-step__num::before { animation: ct-step-pulse 3s var(--ct-ease) infinite; }
.ct-step:nth-child(2) .ct-step__num::before { animation-delay: .6s; }
.ct-step:nth-child(3) .ct-step__num::before { animation-delay: 1.2s; }
@keyframes ct-step-pulse { 0% { transform: scale(.8); opacity: .5; } 70% { opacity: 0; } 100% { transform: scale(1.3); opacity: 0; } }
.ct-step__card {
  background: var(--ct-surface); border: 1px solid var(--ct-line);
  border-radius: var(--ct-r-card); box-shadow: var(--ct-shadow-lg); overflow: hidden;
  transition: transform var(--ct-dur-3) var(--ct-ease), box-shadow var(--ct-dur-3) var(--ct-ease);
  flex: 1 1 auto; display: flex; flex-direction: column; height: 100%;
}
.ct-step__card:hover { transform: translateY(-5px); box-shadow: var(--ct-shadow-lg); }
.ct-step__media { flex: none; min-height: 0; aspect-ratio: 16 / 10; background: var(--ct-gradient-soft); display: grid; place-items: center; padding: var(--ct-4); }
.ct-step__media img { width: 100%; height: 100%; object-fit: contain; display: block; }
.ct-step__text { flex: 1 1 auto; padding: var(--ct-5) var(--ct-6) var(--ct-6); text-align: center; }
.ct-step__text h3 { margin-bottom: var(--ct-2); }
.ct-step:not(:last-child)::after {
  content: ""; position: absolute; top: 23px; left: 50%; z-index: 0;
  width: calc(100% + var(--ct-8)); height: 2px;
  background: linear-gradient(90deg, var(--ct-grad-from), var(--ct-grad-to)); opacity: .35;
}
@media (max-width: 767px) {
  .ct-steps-flow { grid-template-columns: 1fr; gap: var(--ct-10); }
  .ct-step:not(:last-child)::after {
    top: 48px; left: 50%; transform: translateX(-50%);
    width: 2px; height: calc(100% + var(--ct-10) - 48px);
    background: linear-gradient(180deg, var(--ct-grad-from), var(--ct-grad-to));
  }
}

/* ---- Integrations grid (.ct-int) ---------------------------------- */
.ct-int-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--ct-3); }
.ct-int {
  display: flex; align-items: center; gap: var(--ct-3); padding: var(--ct-4) var(--ct-5);
  border: 1px solid var(--ct-line); border-radius: var(--ct-r-sm); background: var(--ct-surface);
  text-decoration: none; color: var(--ct-ink); font-weight: 600; font-size: var(--ct-fs-small);
  transition: transform var(--ct-dur-2) var(--ct-ease), box-shadow var(--ct-dur-2) var(--ct-ease), border-color var(--ct-dur-2) var(--ct-ease);
}
.ct-int:hover { transform: translateY(-3px); box-shadow: var(--ct-shadow); border-color: #dfe2ee; color: var(--ct-ink); }
.ct-int i { color: var(--ct-grad-mid); font-size: 1.25rem; width: 22px; text-align: center; }
@media (max-width: 767px) { .ct-int-grid { grid-template-columns: repeat(2, 1fr); } }

/* ---- FAQ accordion (.ct-faq) — native <details>, no JS ------------ */
.ct-faq { max-width: 820px; margin-inline: auto; }
.ct-faq details { border: 1px solid var(--ct-line); border-radius: var(--ct-r-sm); background: var(--ct-surface); margin-bottom: var(--ct-3); }
.ct-faq summary {
  list-style: none; cursor: pointer; padding: var(--ct-5) var(--ct-6);
  font-family: var(--ct-font-display); font-weight: 700; font-size: 1.1875rem; line-height: 1.35;
  color: var(--ct-ink);
  display: flex; align-items: center; justify-content: space-between; gap: var(--ct-4);
}
.ct-faq summary::-webkit-details-marker { display: none; }
.ct-faq summary::after { content: "\f067"; font-family: "Font Awesome 5 Free"; font-weight: 900; font-size: .85rem; color: var(--ct-grad-mid); transition: transform var(--ct-dur-2) var(--ct-ease); }
.ct-faq details[open] summary::after { content: "\f068"; }
.ct-faq summary:focus-visible { outline: 3px solid var(--ct-grad-mid); outline-offset: 2px; border-radius: var(--ct-r-sm); }
.ct-faq__a { padding: 0 var(--ct-6) var(--ct-5); font-family: var(--ct-font-body); font-size: var(--ct-fs-body); color: var(--ct-sub-ink); line-height: var(--ct-lh-body); }

/* ---- Pricing cards (.ct-plan) ------------------------------------- */
.ct-pricing__grid { align-items: stretch; margin-top: var(--ct-12); }
/* NOTE: do NOT set an explicit height here. `.ct-pricing__grid` is a flex row
   with align-items:stretch, which only equalises card heights when the item's
   cross size is auto — a `height:100%` defeats the stretch and each card falls
   back to its own content height (unequal bottoms, CTAs off the shared line). */
.ct-plan { display: flex; flex-direction: column; text-align: left; padding: var(--ct-6); }
.ct-plan__name { font-family: var(--ct-font-display); font-weight: 700; font-size: 1.375rem; margin: 0; color: var(--ct-ink); }
.ct-plan__for { color: var(--ct-sub-ink); font-size: var(--ct-fs-small); line-height: 1.45; margin: var(--ct-2) 0 var(--ct-5); }
.ct-plan__price { display: flex; align-items: baseline; gap: 4px; }
.ct-plan__cur { font-family: var(--ct-font-display); font-weight: 700; font-size: 1.5rem; color: var(--ct-ink); }
.ct-plan__amount { font-family: var(--ct-font-display); font-weight: 700; font-size: 2.75rem; line-height: 1; color: var(--ct-ink); }
.ct-plan__per { color: var(--ct-faint-ink); font-size: var(--ct-fs-small); }
.ct-plan__features { list-style: none; margin: var(--ct-6) 0; padding: 0; display: grid; gap: var(--ct-3); }
.ct-plan__features li { display: flex; align-items: flex-start; gap: var(--ct-3); font-size: var(--ct-fs-small); line-height: 1.45; color: var(--ct-sub-ink); }
.ct-plan__features i { color: var(--ct-grad-mid); margin-top: 4px; flex: 0 0 auto; font-size: .85em; }
.ct-plan__cta { margin-top: auto; width: 100%; justify-content: center; white-space: nowrap; }
.ct-plan--popular { border-color: var(--ct-grad-mid); box-shadow: var(--ct-glow); padding-top: calc(var(--ct-6) + var(--ct-8)); }
.ct-plan--popular .ct-plan__badge { position: absolute; top: var(--ct-4); left: 50%; transform: translateX(-50%); white-space: nowrap; z-index: 1; }
@media (min-width: 992px) { .ct-plan--popular { translate: 0 -14px; } }

/* ---- Ambient gradient background (.ct-pbg) — THE calm/pale gradient
   standard. Blurred brand-coloured blobs; ambient drift is pure CSS
   (below). Optional cursor parallax is added by ct-pricing-bg.js
   (desktop only, reduced-motion-safe). Put it behind heroes / pricing;
   avoid behind long text bodies or forms. Tune via the custom props. */
[data-ct-pricing] {
  position: relative; overflow: hidden; isolation: isolate;
  --pbg-blur   : 88px;   /* high blur so blobs MELT together — no hard cores */
  --pbg-opacity: .22;    /* faint wash — section reads mostly white          */
  --pbg-drift  : 5%;     /* base ambient drift distance per blob             */
  --pbg-mint   : #d9f3e6;  /* pale mint                                       */
  --pbg-blue   : #dce3fb;  /* soft periwinkle                                 */
  --pbg-purple : #e7def6;  /* light lavender                                  */
  --pbg-pink   : #f6def1;  /* pale pink                                       */
}
[data-ct-pricing] > .ct-container { position: relative; z-index: 1; }  /* content above the gradient */
.ct-pbg { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.ct-pbg__blob {
  position: absolute; border-radius: 50%;
  filter: blur(var(--pbg-blur)); opacity: var(--pbg-opacity);
  will-change: transform, translate, scale;
  transform: translate(0, 0);                 /* ct-pricing-bg.js writes cursor parallax here */
  animation: ct-pbg-float var(--dur, 24s) var(--ct-ease) var(--delay, 0s) infinite;
}
@keyframes ct-pbg-float {
  0%   { translate: 0 0;                                        scale: 1; }
  33%  { translate: var(--dx) var(--dy);                        scale: var(--sc, 1.12); }
  66%  { translate: calc(var(--dx) * -.7) calc(var(--dy) * -.5); scale: 1; }
  100% { translate: 0 0;                                        scale: 1; }
}

/* ---- Reduced motion for all extracted components ------------------ */
@media (prefers-reduced-motion: reduce) {
  .ct-prod__media img,
  .ct-case__media img { transition: none !important; }
  .ct-prod:hover .ct-prod__media img,
  .ct-case:hover .ct-case__media img { transform: none !important; }
  .ct-step__num::before { animation: none !important; }
  .ct-step__card { transition: none !important; }
  .ct-step__card:hover { transform: none !important; }
  .ct-int, .ct-faq summary::after { transition: none !important; }
  .ct-pbg__blob { animation: none !important; transform: none !important; }
}

/* =====================================================================
   USE-CASE CAROUSEL RESKIN  (.ct-uc-carousel) — modern SaaS elevation
   ---------------------------------------------------------------------
   Reskins the legacy owl-carousel slides produced by Database::getUsecase()
   into self-contained, ELEVATED ct- cards WITHOUT touching the DB method or
   its data. Add .ct-uc--glow (Option A, LOCKED IN by Tom) on the .customer-
   slider.ct-uc-carousel wrapper: on hover a soft brand-gradient glow blooms
   behind the card and a gradient top-edge lights up (desktop pointer only;
   static on touch + prefers-reduced-motion).
   Targets the exact classes the method emits (both DB branches):
     .item > .customer-block > p(desc) + img(logo) + .clearfix.mt-10
             > .customer-info(h5.name + span=category) + button(Read More)
     .item > p + .author-info  (products-overview/home branch — handled too)
   CSS-only reorder makes each case ONE tidy card: logo -> description -> name
   -> category -> Read More. The inline height:250px on the <p> that caused the
   dead gap is neutralised with height:auto !important.
   SPACING: a REAL CSS gutter (padding-inline on each .owl-item) + outer padding
   on the carousel give a roomy, even rhythm that is visible in the static
   preview too — it does NOT depend on owl's JS `margin`. On the live pass, set
   owl's `margin` option to 0 so the CSS gutter isn't doubled (see PENDING note).
   No DB/JS/data change; owl init, lazy, IDs, onclick all preserved.
   ===================================================================== */
/* SPACE AROUND the whole block — outer padding OUTSIDE owl's mask, so the first
   and last cards never jam against the container edges, with calm room above and
   below the row. (.ct-uc-carousel IS the .owl-carousel element once owl inits, so
   this pads inside it and simply insets .owl-stage-outer — off-screen slides stay
   masked; owl re-measures the narrower stage, paging unaffected.) The inline value
   matches each owl-item's side padding below, so OUTER space == HALF the between-
   card gutter's contribution → the first card sits a full gutter in from the edge. */
.ct-uc-carousel { padding: var(--ct-6) var(--ct-5); }

.ct-uc-carousel .owl-stage { display: flex; }            /* equal-height cards across the row */
/* Real CSS GUTTER between cards: each slide's card is inset inside its owl-item by
   side padding, so adjacent cards are separated by padding-right + padding-left =
   2×var(--ct-5) (40px) of clear space — visible even before owl's JS `margin` runs
   (e.g. in the static preview). box-sizing:border-box keeps the inset INSIDE owl's
   computed item width, so paging math is untouched. LIVE: set owl `margin:0`. */
.ct-uc-carousel .owl-item  { display: flex; padding-inline: var(--ct-5); }
/* column (not row) so the products-overview `if` branch (description as a sibling
   of the author block) stacks instead of sitting side-by-side. position:relative
   makes the .item the anchor for Option A's behind-the-card glow. */
.ct-uc-carousel .item { display: flex; flex-direction: column; width: 100%; height: auto; position: relative; }

/* The card. overflow:hidden clips the top-edge accent to the rounded corners.
   z-index:0 keeps the solid surface above Option A's glow. */
.ct-uc-carousel .customer-block,
.ct-uc-carousel .item > .author-info {
  position: relative; z-index: 0; overflow: hidden;
  display: flex; flex-direction: column; width: 100%; flex: 1 1 auto; text-align: left;
  background: var(--ct-surface); border: 1px solid var(--ct-line);
  border-radius: var(--ct-r-card); padding: var(--ct-8);
  box-shadow: var(--ct-shadow-sm);
  transition: transform var(--ct-dur-3) var(--ct-ease), box-shadow var(--ct-dur-3) var(--ct-ease), border-color var(--ct-dur-3) var(--ct-ease);
}
/* Gentle lift + shadow deepen on hover.
   Guarded to real pointers so touch taps don't get a stuck hover state. */
@media (hover: hover) and (pointer: fine) {
  .ct-uc-carousel .item:hover .customer-block,
  .ct-uc-carousel .item:hover > .author-info { transform: translateY(-6px); box-shadow: var(--ct-shadow-lg); border-color: #dfe2ee; }
}

/* CLIP FIX — owl masks off-screen slides with overflow:hidden on .owl-stage-outer,
   which was slicing the hover lift + the Option-A glow at the viewport edges. Give
   the mask breathing room on ALL FOUR sides with the same trick: symmetric padding
   + matching negative margin nets ZERO layout shift (the margin-box is unchanged,
   so the heading above and the arrows below don't move) while the clip region grows
   PAST the visible cards — so the glow can feather out to transparent instead of
   being cut with a hard edge, even on the left/right-most card.
     • block  var(--ct-10)=40px → room for the top-edge + vertical glow bloom.
     • inline var(--ct-5) =20px → room for the glow to feather out sideways.
   Off-screen slides STAY MASKED: the 20px horizontal extension only ever exposes
   the empty side-padding of the neighbouring owl-item (the gutter), never its card
   content, which sits a further ~20px out. The margins are symmetric, so stage-outer
   width is unchanged → owl's paging math is untouched.
   (LIVE-CHECK: confirm the feather stays soft with real slides on both sides.) */
.ct-uc-carousel .owl-stage-outer {
  padding: var(--ct-10) var(--ct-5);
  margin: calc(-1 * var(--ct-10)) calc(-1 * var(--ct-5));
}

/* Vertical order: logo (1) -> description (2) -> attribution (3, pinned to bottom) */
.ct-uc-carousel .customer-block > img { order: 1; }
.ct-uc-carousel .customer-block > p   { order: 2; }
.ct-uc-carousel .customer-block > div  { order: 3; margin-top: auto; }

/* Logo: fixed-height band (every logo shares the same vertical slot) + object-fit
   contain, left-aligned, so shapes never distort or jump between slides. */
.ct-uc-carousel .customer-block img,
.ct-uc-carousel .author-img {
  height: 48px !important; width: auto !important; max-width: 62% !important;
  object-fit: contain; object-position: left center;
  margin: 0 0 var(--ct-6) !important; opacity: 1 !important; align-self: flex-start;
}

/* Description: kill the inline height:250px + tidy typography. Clamp keeps a calm,
   consistent rhythm when a case runs long (equal-height still pins the
   attribution to the bottom of every card). */
.ct-uc-carousel .customer-block p,
.ct-uc-carousel .item > p {
  height: auto !important; margin: 0 0 var(--ct-6) !important; padding: 0 !important;
  font-family: var(--ct-font-body); font-size: var(--ct-fs-body); line-height: var(--ct-lh-body); color: var(--ct-sub-ink);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 5; overflow: hidden;
}

/* Attribution block (name + category + read-more) stacks tidily at the bottom */
.ct-uc-carousel .customer-block > div,
.ct-uc-carousel .author-info .name-info { display: flex; flex-direction: column; align-items: flex-start; }
.ct-uc-carousel .name,
.ct-uc-carousel .customer-block h5, .ct-uc-carousel .author-info h6 {
  font-family: var(--ct-font-display); font-weight: 700; font-size: 1.125rem; color: var(--ct-ink); margin: 0;
}
.ct-uc-carousel .customer-info span,
.ct-uc-carousel .name-info span {
  display: block; font-size: var(--ct-fs-small); color: var(--ct-faint-ink);
  text-transform: uppercase; letter-spacing: .06em; margin-top: 4px;
}

/* Read More: restyle the legacy <button> as the flat accent link (a.ct-link look);
   the arrow nudges right on card hover. */
.ct-uc-carousel .customer-block button,
.ct-uc-carousel .author-info button {
  align-self: flex-start; margin: var(--ct-5) 0 0 !important; padding: 0 !important;
  background: none !important; border: 0 !important; cursor: pointer;
  font-family: var(--ct-font-body); font-weight: 600; font-size: var(--ct-fs-body);
  color: var(--ct-accent) !important; display: inline-flex; align-items: center; gap: var(--ct-2);
}
.ct-uc-carousel .customer-block button i,
.ct-uc-carousel .author-info button i { transition: transform var(--ct-dur-2) var(--ct-ease); }
@media (hover: hover) and (pointer: fine) {
  .ct-uc-carousel .item:hover button i { transform: translateX(4px); }
}
.ct-uc-carousel .customer-block button:hover,
.ct-uc-carousel .author-info button:hover { text-decoration: underline; }

/* ---- Option A (.ct-uc--glow): on-hover gradient glow + top-edge accent -------
   Pure opacity/transform, desktop pointer only, so it stays a calm premium
   accent — motion on interaction, never constant. */
.ct-uc--glow .customer-block::before,
.ct-uc--glow .item > .author-info::before {          /* gradient top-edge, lights up on hover */
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px;
  background: var(--ct-gradient);
  opacity: 0; transform: scaleX(.55); transform-origin: left center;
  transition: opacity var(--ct-dur-3) var(--ct-ease), transform var(--ct-dur-3) var(--ct-ease);
  pointer-events: none;
}
.ct-uc--glow .item::before {                          /* soft brand-gradient glow behind the card */
  content: ""; position: absolute; z-index: -1;
  /* Blooms SYMMETRICALLY just past the card on every side and feathers to
     transparent (blur 26) — a soft radial-feeling halo, no straight/angular edge.
     The .owl-stage-outer clip region is extended (above) so this feather is never
     sliced, on any card including the left/right-most. */
  inset: -2px;
  border-radius: var(--ct-r-lg);
  background: var(--ct-gradient); filter: blur(26px);
  opacity: 0; transform: scale(.96);
  transition: opacity var(--ct-dur-3) var(--ct-ease), transform var(--ct-dur-3) var(--ct-ease);
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  .ct-uc--glow .item:hover .customer-block::before,
  .ct-uc--glow .item:hover > .author-info::before { opacity: 1; transform: scaleX(1); }
  .ct-uc--glow .item:hover::before { opacity: .32; transform: scale(1); }
}

/* Carousel controls: pull the prev/next arrows out of the middle to a centered
   row below, styled as ct- circular buttons. */
.ct-uc-carousel .owl-nav { display: flex !important; justify-content: center; gap: var(--ct-3); margin-top: var(--ct-10); }
.ct-uc-carousel .owl-nav button.owl-prev,
.ct-uc-carousel .owl-nav button.owl-next {
  position: static !important; margin: 0 !important; top: auto !important; transform: none !important;
  width: 46px; height: 46px; border-radius: var(--ct-r-pill);
  background: var(--ct-surface) !important; border: 1px solid var(--ct-line) !important;
  color: var(--ct-ink) !important; display: grid !important; place-items: center;
  box-shadow: var(--ct-shadow-sm); font-size: 1rem;
  transition: border-color var(--ct-dur-2) var(--ct-ease), color var(--ct-dur-2) var(--ct-ease), transform var(--ct-dur-2) var(--ct-ease);
}
.ct-uc-carousel .owl-nav button.owl-prev:hover,
.ct-uc-carousel .owl-nav button.owl-next:hover { border-color: var(--ct-accent) !important; color: var(--ct-accent) !important; transform: translateY(-2px); }
.ct-uc-carousel .owl-nav button.disabled { opacity: .4; }
.ct-uc-carousel .owl-dots { text-align: center; margin-top: var(--ct-4); }

@media (prefers-reduced-motion: reduce) {
  .ct-uc-carousel .customer-block,
  .ct-uc-carousel .item > .author-info,
  .ct-uc-carousel .customer-block button i,
  .ct-uc-carousel .author-info button i,
  .ct-uc-carousel .owl-nav button { transition: none !important; }
  .ct-uc-carousel .item:hover .customer-block,
  .ct-uc-carousel .item:hover > .author-info,
  .ct-uc-carousel .item:hover button i,
  .ct-uc-carousel .owl-nav button:hover { transform: none !important; }
  .ct-uc--glow .item::before,
  .ct-uc--glow .customer-block::before,
  .ct-uc--glow .item > .author-info::before { transition: none !important; }        /* no glow motion */
}

/* =====================================================================
   PROMO BAR (.ct-promobar) — top promotional strip
   ---------------------------------------------------------------------
   Replaces the legacy flat-red alert. On-brand: solid --ct-accent (#3c41e6,
   white text ≈ 7.5:1 — passes WCAG AA at ANY size) with a soft blue->purple
   gradient sheen (the decorative --ct-grad tones, kept low-opacity so the
   accent base still governs contrast) for a premium, not-shouty look.
   Rendered ABOVE .main-page-wrapper in index.php; visibility is gated by the
   $PROMO_BANNER_ENABLED master switch + the DB promo window.
   ===================================================================== */
.ct-promobar {
  position: relative;
  z-index: 1010;                              /* clears the fixed nav      */
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--ct-2);
  min-height: 44px;                           /* stable height above nav   */
  padding: 10px 52px;                         /* right room for the ×      */
  background: var(--ct-accent);
  background-image:
    radial-gradient(120% 220% at 12% 0%, rgba(122, 92, 255, .55) 0%, rgba(122, 92, 255, 0) 55%),
    radial-gradient(130% 240% at 100% 120%, rgba(177, 75, 255, .50) 0%, rgba(177, 75, 255, 0) 55%);
  color: #fff;
  font-family: var(--ct-font-body);
  font-size: clamp(.8rem, 2.7vw, .95rem);     /* scales down to help 1 line on mobile */
  line-height: 1.3;
  text-align: center;
}
.ct-promobar__link {
  position: static;                           /* stretched-link makes the whole bar clickable */
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  max-width: 100%;
  color: #fff;
  font-weight: 600;
  text-decoration: none;
  text-wrap: balance;                         /* if it must wrap, wrap tidily (no orphan word) */
}
.ct-promobar__link::after {                   /* full-bar hit area */
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}
.ct-promobar__link:hover,
.ct-promobar__link:focus-visible { color: #fff; }
.ct-promobar__link:hover .ct-promobar__text { text-decoration: underline; }
.ct-promobar__text :is(strong, b, span) { color: inherit; }  /* neutralise legacy inline colors (e.g. red) from DB copy */
.ct-promobar__arrow {
  flex: 0 0 auto;
  transition: transform var(--ct-dur-1) var(--ct-ease);
}
.ct-promobar__link:hover .ct-promobar__arrow { transform: translateX(3px); }
.ct-promobar__close {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;                                 /* above the stretched link  */
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: var(--ct-r-pill);
  background: rgba(255, 255, 255, .14);
  color: #fff;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--ct-dur-1) var(--ct-ease);
}
.ct-promobar__close:hover,
.ct-promobar__close:focus-visible { background: rgba(255, 255, 255, .30); outline: none; }
@media (max-width: 575px) {
  .ct-promobar { padding: 9px 44px; gap: var(--ct-1); }
  .ct-promobar__close { right: 6px; }
}
@media (prefers-reduced-motion: reduce) {
  .ct-promobar__arrow,
  .ct-promobar__close { transition: none !important; }
  .ct-promobar__link:hover .ct-promobar__arrow { transform: none !important; }
}

/* =====================================================================
   SITE FOOTER (.ct-footer) — shared partial, renders site-wide
   ---------------------------------------------------------------------
   Replaces the legacy light `.theme-footer-one`. Strong page-closing dark
   section on --ct-brand-dark (#101028). Text tokens:
     • headings / labels     -> --ct-on-dark      (#ffffff, ~18.5:1)
     • body / links          -> --ct-on-dark-sub  (#acaceb, ~9:1  — AA)
     • link hover / active    -> --ct-accent-on-dark (#7c86ff, ~5.9:1 — AA)
   Four columns (brand+contact · Latest News · Explore · Keep In Touch),
   a hairline legal row, and an optional oversized wordmark. The Latest
   News list markup is emitted by Database::getLatest(2,'footer') — we only
   style its .footer-blog-* classes here, never the data source.
   ===================================================================== */
.ct-footer {
  position: relative;
  background: var(--ct-brand-dark);
  color: var(--ct-on-dark-sub);
  font-family: var(--ct-font-body);
  padding-top: clamp(3.5rem, 7vw, 6rem);
  overflow: hidden;                       /* clips the wordmark bleed */
}
.ct-footer__inner {
  width: 100%;
  max-width: var(--ct-container);
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 4vw, 2.5rem);
}

/* ---- Column grid --------------------------------------------------- */
.ct-footer__grid {
  display: grid;
  grid-template-columns: 1.5fr 1.1fr 1fr 1.2fr;
  gap: clamp(2rem, 4vw, 3.5rem);
  padding-bottom: clamp(2.5rem, 5vw, 4rem);
}
@media (max-width: 991px) {
  .ct-footer__grid { grid-template-columns: 1fr 1fr; row-gap: var(--ct-10); }
}
@media (max-width: 575px) {
  .ct-footer__grid { grid-template-columns: 1fr; row-gap: var(--ct-8); }
}
.ct-footer__col { min-width: 0; }         /* let long news titles wrap, not overflow */

/* ---- Column headings ---------------------------------------------- */
.ct-footer__title {
  font-family: var(--ct-font-display);
  font-weight: 700;
  font-size: var(--ct-fs-small);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ct-on-dark);
  margin: 0 0 var(--ct-5);
}

/* ---- Brand / contact column --------------------------------------- */
.ct-footer__logo { display: inline-block; margin-bottom: var(--ct-5); }
.ct-footer__logo img {
  height: 34px; width: auto; display: block;
  /* logo3.svg is a dark/colour wordmark for light bg — force it white on dark */
  filter: brightness(0) invert(1);
}
.ct-footer__q {
  font-size: var(--ct-fs-body);
  color: var(--ct-on-dark-sub);
  margin: 0 0 var(--ct-3);
}
.ct-footer__emails { display: flex; flex-direction: column; gap: var(--ct-1); margin-bottom: var(--ct-6); }
.ct-footer__email {
  color: var(--ct-on-dark-sub);
  font-size: var(--ct-fs-body);
  text-decoration: none;
  width: max-content;
  transition: color var(--ct-dur-2) var(--ct-ease);
}
.ct-footer__email:hover,
.ct-footer__email:focus-visible { color: var(--ct-accent-on-dark); }

/* ---- Language selector -------------------------------------------- */
.ct-footer__lang { display: block; }
.ct-footer__lang-label {
  display: block;
  font-size: var(--ct-fs-small);
  color: var(--ct-on-dark-sub);
  margin-bottom: var(--ct-2);
}
.ct-footer__select {
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  width: 100%; max-width: 240px;
  padding: 10px 40px 10px 14px;
  font-family: var(--ct-font-body);
  font-size: var(--ct-fs-small);
  color: var(--ct-on-dark);
  background-color: rgba(255,255,255,.06);
  border: 1px solid var(--ct-line-dark);
  border-radius: var(--ct-r-sm);
  cursor: pointer;
  /* chevron */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23acaceb' d='M6 8 0 2l1.4-1.4L6 5.2 10.6.6 12 2z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  transition: border-color var(--ct-dur-2) var(--ct-ease), background-color var(--ct-dur-2) var(--ct-ease);
}
.ct-footer__select:hover { border-color: rgba(255,255,255,.28); }
.ct-footer__select:focus-visible { outline: 3px solid var(--ct-accent-on-dark); outline-offset: 2px; }
/* option list: OS-drawn, but most engines honour these so it isn't white-on-white */
.ct-footer__select option { color: #14152b; background: #fff; }

/* ---- Explore links ------------------------------------------------- */
.ct-footer__links { list-style: none; margin: 0; padding: 0; }
.ct-footer__links li + li { margin-top: var(--ct-3); }
.ct-footer__links a {
  color: var(--ct-on-dark-sub);
  font-size: var(--ct-fs-body);
  text-decoration: none;
  transition: color var(--ct-dur-2) var(--ct-ease);
}
.ct-footer__links a:hover,
.ct-footer__links a:focus-visible { color: var(--ct-accent-on-dark); }

/* ---- Latest News (DB-driven markup: .footer-blog-*) ---------------- */
.ct-footer__news ul { list-style: none; margin: 0; padding: 0; }
.ct-footer__news .footer-blog-li + .footer-blog-li { margin-top: var(--ct-4); }
.ct-footer__news .footer-blog-li a {
  display: block; text-decoration: none;
  transition: color var(--ct-dur-2) var(--ct-ease);
}
.ct-footer__news .footer-blog-title {
  margin: 0 0 2px;
  font-size: var(--ct-fs-body);
  line-height: 1.4;
  color: var(--ct-on-dark);
  transition: color var(--ct-dur-2) var(--ct-ease);
}
.ct-footer__news .footer-blog-li a:hover .footer-blog-title,
.ct-footer__news .footer-blog-li a:focus-visible .footer-blog-title { color: var(--ct-accent-on-dark); }
.ct-footer__news .footer-blog-date { display: block; }
.ct-footer__news .footer-blog-date small {
  font-size: var(--ct-fs-small);
  color: var(--ct-faint-ink);
  letter-spacing: .02em;
}

/* ---- Keep In Touch: social icon buttons --------------------------- */
.ct-footer__social {
  list-style: none; margin: 0 0 var(--ct-6); padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--ct-3);
}
.ct-footer__social a {
  display: grid; place-items: center;
  width: 42px; height: 42px;
  border-radius: var(--ct-r-pill);
  background: rgba(255,255,255,.08);
  border: 1px solid rgba(255,255,255,.14);
  color: var(--ct-on-dark);
  font-size: 1.05rem;
  text-decoration: none;
  transition: transform var(--ct-dur-2) var(--ct-ease),
              background-color var(--ct-dur-2) var(--ct-ease),
              border-color var(--ct-dur-2) var(--ct-ease),
              color var(--ct-dur-2) var(--ct-ease);
}
.ct-footer__social a:hover,
.ct-footer__social a:focus-visible {
  transform: translateY(-2px);
  background: var(--ct-accent-on-dark);
  border-color: var(--ct-accent-on-dark);
  color: #101028;                          /* dark glyph on the accent — high contrast */
}

/* ---- Keep In Touch: CTA buttons ----------------------------------- */
.ct-footer__ctas { display: flex; flex-direction: column; align-items: flex-start; gap: var(--ct-3); }
.ct-footer__ctas .ct-btn { width: 100%; max-width: 260px; justify-content: center; }
/* the shared .ct-btn--ghost has no :hover of its own — give it a subtle one on
   the dark footer (brighter fill + on-dark border) */
.ct-footer .ct-btn--ghost:hover,
.ct-footer .ct-btn--ghost:focus-visible {
  background: rgba(255,255,255,.16);
  border-color: var(--ct-on-dark);
  color: var(--ct-on-dark);
  transform: translateY(-2px);
}
@media (prefers-reduced-motion: reduce) {
  .ct-footer .ct-btn--ghost:hover { transform: none !important; }
}

/* ---- Bottom legal row --------------------------------------------- */
.ct-footer__bottom {
  border-top: 1px solid var(--ct-line-dark);
  padding: var(--ct-6) 0;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--ct-3) var(--ct-6);
}
.ct-footer__copy { margin: 0; font-size: var(--ct-fs-small); color: var(--ct-faint-ink); }
.ct-footer__legal { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--ct-3) var(--ct-5); }
.ct-footer__legal a {
  font-size: var(--ct-fs-small);
  color: var(--ct-on-dark-sub);
  text-decoration: none;
  transition: color var(--ct-dur-2) var(--ct-ease);
}
.ct-footer__legal a:hover,
.ct-footer__legal a:focus-visible { color: var(--ct-accent-on-dark); }
@media (max-width: 575px) {
  .ct-footer__bottom { flex-direction: column; align-items: flex-start; }
}

/* ---- Oversized closing wordmark (agency-style, optional decoration) -
   Purely decorative (aria-hidden in markup). Low-opacity white so it reads
   as texture, never competes with the content above. Clipped by the
   .ct-footer overflow:hidden so its tight tracking can bleed edge-to-edge. */
.ct-footer__wordmark {
  margin: 0;
  padding-top: var(--ct-2);
  font-family: var(--ct-font-display);
  font-weight: 700;
  font-size: clamp(2.5rem, 13vw, 12rem);
  line-height: .8;
  letter-spacing: var(--ct-tracking-tight);
  text-align: center;
  color: rgba(255,255,255,.07);
  white-space: nowrap;
  user-select: none;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .ct-footer__social a,
  .ct-footer__email,
  .ct-footer__links a,
  .ct-footer__legal a,
  .ct-footer__select { transition: none !important; }
  .ct-footer__social a:hover { transform: none !important; }
}

/* =====================================================================
   USE-CASE CAROUSEL — products-overview `if` branch fix (Batch 4)
   ---------------------------------------------------------------------
   getUsecase()'s products-overview/home branch emits `.item > p (desc) +
   .author-info (logo+name+button)` — the description is a SIBLING of the
   author block, so the base reskin (which cards `.item > .author-info`)
   left the description floating OUTSIDE the card. Here the WHOLE `.item`
   becomes the card (quote-style: description on top, logo + name + Read More
   pinned to the bottom); `.author-info` is demoted to a plain group. Uses
   :has() (modern browsers); older browsers fall back to the base rule.
   Additive — does not touch the shared glow (Option A/B) rules above.
   ===================================================================== */
.ct-uc-carousel .item:has(> .author-info) {
  position: relative; z-index: 0; overflow: hidden;
  background: var(--ct-surface); border: 1px solid var(--ct-line);
  border-radius: var(--ct-r-card); padding: var(--ct-8); box-shadow: var(--ct-shadow-sm);
  transition: transform var(--ct-dur-3) var(--ct-ease), box-shadow var(--ct-dur-3) var(--ct-ease), border-color var(--ct-dur-3) var(--ct-ease);
}
.ct-uc-carousel .item:has(> .author-info) > .author-info {
  background: none !important; border: 0 !important; border-radius: 0 !important;
  padding: 0 !important; box-shadow: none !important; overflow: visible;
  margin-top: auto; display: flex; flex-direction: column; align-items: flex-start;
}
@media (hover: hover) and (pointer: fine) {
  .ct-uc-carousel .item:has(> .author-info):hover { transform: translateY(-6px); box-shadow: var(--ct-shadow-lg); border-color: #dfe2ee; }
  /* cancel the base rule's inner-lift so only the whole card lifts */
  .ct-uc-carousel .item:has(> .author-info):hover > .author-info { transform: none; box-shadow: none; border-color: transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .ct-uc-carousel .item:has(> .author-info) { transition: none !important; }
  .ct-uc-carousel .item:has(> .author-info):hover { transform: none !important; }
}

/* =====================================================================
   FORM CONTROLS  (.ct-form / .ct-input / .ct-textarea)
   ---------------------------------------------------------------------
   Shared form-field styling (first used on contact_us). Inputs sit on the
   card surface with a hairline border; focus uses the flat accent. Pair a
   field wrapper .ct-field with an error node .ct-field__error (shown by the
   page's own validation JS). Never put a gradient behind a form.
   ===================================================================== */
.ct-field { margin-bottom: var(--ct-4); }
/* Visible field label (pairs with .ct-input/.ct-select). Use a real
   <label for> so it stays keyboard/AT-associated with its control. */
.ct-label {
  display: block; margin-bottom: var(--ct-2);
  font-family: var(--ct-font-body); font-size: var(--ct-fs-small); font-weight: 600;
  color: var(--ct-ink); line-height: 1.3;
}
.ct-label .ct-req { color: #d9534f; font-weight: 600; }
.ct-input,
.ct-textarea,
.ct-select {
  width: 100%; display: block;
  font-family: var(--ct-font-body); font-size: var(--ct-fs-body); line-height: 1.4;
  color: var(--ct-ink); background: var(--ct-surface);
  border: 1px solid var(--ct-line); border-radius: var(--ct-r-sm);
  padding: 13px 16px;
  transition: border-color var(--ct-dur-2) var(--ct-ease), box-shadow var(--ct-dur-2) var(--ct-ease);
}
.ct-input::placeholder,
.ct-textarea::placeholder { color: var(--ct-faint-ink); }
.ct-input:focus,
.ct-textarea:focus,
.ct-select:focus { outline: none; border-color: var(--ct-accent); box-shadow: 0 0 0 3px rgba(60, 65, 230, .15); }
.ct-textarea { min-height: 150px; resize: vertical; }
/* Native <select> restyled to match .ct-input: hide the OS chevron and paint
   our own so it reads as one control family. Extra right padding clears it. */
.ct-select {
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  padding-right: 44px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 16px center;
}
[data-theme="dark"] .ct-select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23acaceb' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}
.ct-field__error { color: #d9534f; font-size: var(--ct-fs-small); margin-top: var(--ct-2); }
/* Field-level invalid state: page validation JS toggles .error on the control
   (register/contact pattern) — turn the border/ring red to match the message. */
.ct-input.error,
.ct-select.error,
.ct-textarea.error { border-color: #d9534f; box-shadow: 0 0 0 3px rgba(217, 83, 79, .15); }
/* intl-tel-input widget: let its .iti wrapper span the full field width so the
   phone control lines up with the other inputs. */
.ct-form .iti { width: 100%; display: block; }
@media (prefers-reduced-motion: reduce) {
  .ct-input, .ct-textarea, .ct-select { transition: none !important; }
}

/* =====================================================================
   SOLUTION-DETAIL PAGES  (.ct-sd-*)
   ---------------------------------------------------------------------
   The 16 goal-DETAIL pages the Solutions hubs (Attract / Digitalize /
   Retain) link to: build-engaging-offers, digitalize-your-paper-loyalty-
   cards, gain-digital-insights, … They live in 16 separate includes/
   files (the router resolves each page KEY straight to its own file), but
   they are ONE design assembled from a closed set of blocks — all rendered
   by essentials/soldetail-blocks.php, so the 16 can never fork.

   Layout only, on top of the shared primitives (.ct-section / .ct-split /
   .ct-grid--3 / .ct-card / .ct-sol-hero). No new colours, radii or shadows.

   MEDIA NOTE (why this block strips the .ct-split__media framing): every
   hero + feature-row asset on these pages is a TRANSPARENT-background
   product mockup that already carries its own baked-in soft shadow. The
   system's default media radius + box-shadow would draw a visible rounded
   card edge around that transparency, which reads as a rendering bug. The
   use-case card photos are the exception — real 360x240 images, framed at
   3/2, their exact native ratio, so cover neither crops nor distorts.
   ===================================================================== */

/* ---- Hero -----------------------------------------------------------
   Composes with .ct-sol-hero (navbar clearance + optional glow). Only the
   media framing differs: these assets range from 0.75 (portrait phone) to
   1.54 (wide scene), so the image keeps its NATURAL ratio and is bounded on
   BOTH axes — max-width by the column, max-height so the portrait ones
   don't tower over the copy. Centred in the cell either way. */
.ct-sd-hero__media { display: flex; justify-content: center; align-items: center; }
.ct-sd-hero__media img {
  width: auto; height: auto;
  max-width: 100%; max-height: 560px;
  display: block;
  border-radius: 0; box-shadow: none;   /* transparent mockups — see MEDIA NOTE */
}
@media (max-width: 991px) { .ct-sd-hero__media img { max-height: 420px; } }

/* ---- Feature rows (alternating media + points) ----------------------
   .ct-sd-split rides on .ct-split for the two-column layout and only
   neutralises the media framing (see MEDIA NOTE). Each point is a real
   .ct-split__point h3 + body copy — the legacy markup used <strong> inside
   a <p>, which looked like a heading but wasn't one for AT or search. */
.ct-sd-split .ct-split__media img { border-radius: 0; box-shadow: none; }
.ct-sd-split .ct-split__media { display: flex; justify-content: center; }
.ct-sd-split .ct-split__media img { max-height: 520px; width: auto; max-width: 100%; }

/* ---- Wide showcase shot (the "customize the design" block) ----------
   Centred copy over a single full-bleed-ish image. The legacy version
   pulled itself up with a stack of negative margins (-333px … -112px per
   breakpoint) to overlap the band below it; that hack is gone — normal
   section rhythm now, no overlap to maintain. */
.ct-sd-shot { text-align: center; }
.ct-sd-shot__img { margin-top: clamp(2.5rem, 6vw, 4.5rem); }
.ct-sd-shot__img img {
  width: 100%; height: auto; max-width: 1080px;
  margin-inline: auto; display: block;
  border-radius: 0; box-shadow: none;
}

/* ---- Use-case cards -------------------------------------------------
   One real <a> per case (legacy used onclick=location.href on a <div>, so
   the card was unreachable by keyboard and invisible to crawlers — same
   destination, now a link). Photo area is the cards' native 3/2. */
.ct-sd-case { display: flex; flex-direction: column; padding: 0; overflow: hidden; text-align: left; }
.ct-sd-case__media { aspect-ratio: 3 / 2; width: 100%; background: var(--ct-bg-subtle); overflow: hidden; }
.ct-sd-case__media img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform var(--ct-dur-3) var(--ct-ease);
}
.ct-sd-case__body { flex: 1; display: flex; flex-direction: column; padding: var(--ct-6); }
.ct-sd-case__more { margin-top: auto; padding-top: var(--ct-4); display: inline-flex; align-items: center; gap: .4em; font-weight: 600; color: var(--ct-accent); }
.ct-sd-case__more i { font-size: .8em; transition: transform var(--ct-dur-2) var(--ct-ease); }
@media (hover: hover) and (pointer: fine) {
  .ct-sd-case:hover .ct-sd-case__media img { transform: scale(1.04); }
  .ct-sd-case:hover .ct-sd-case__more i { transform: translateX(3px); }
}
@media (prefers-reduced-motion: reduce) {
  .ct-sd-case__media img, .ct-sd-case__more i { transition: none !important; }
  .ct-sd-case:hover .ct-sd-case__media img { transform: none !important; }
}

/* ---- Trial CTA band -------------------------------------------------
   Copy left, single button right (the legacy break-trial layout), stacking
   to centred on mobile. Dark band, so the button sits on --ct-bg-dark. */
.ct-sd-cta__grid { display: grid; grid-template-columns: 1fr auto; gap: clamp(1.5rem, 4vw, 3rem); align-items: center; }
.ct-sd-cta__sub { color: var(--ct-on-dark-sub); margin: var(--ct-3) 0 0; font-size: var(--ct-fs-body-lg); line-height: var(--ct-lh-body); }
@media (max-width: 767px) {
  .ct-sd-cta__grid { grid-template-columns: 1fr; justify-items: center; text-align: center; }
}

/* ---- Numbered step cards (legacy .solutions_cards + .stepper_bg) ----
   Used by increase_store_visits, blend_physical_and_digital_marketing,
   re_engage_customers_with_personalization, gain_digital_insights. Legacy
   drew a 50px #3c41e6 circle holding the step number; that colour is the
   canonical --ct-accent, so the token is used rather than the raw hex. */
.ct-sd-step { text-align: center; }
.ct-sd-step__num {
  display: inline-grid; place-items: center;
  width: 50px; height: 50px;
  border-radius: 50%;
  background: var(--ct-accent); color: #fff;
  font-family: var(--ct-font-display); font-weight: 700; font-size: 1.35rem;
  line-height: 1; margin-bottom: var(--ct-4);
}
.ct-sd-step .ct-card__text { text-align: center; }

/* ---- Plain bullet list (legacy .solutions_bulletpoints > ul) --------
   A standalone list block with no media beside it. Legacy relied on the
   theme's generic `ul li` styling; this restates it on ct- tokens so it no
   longer depends on the legacy stylesheet. */
.ct-sd-bullets { list-style: none; margin: 0; padding: 0; max-width: 860px; margin-inline: auto; }
.ct-sd-bullets li {
  position: relative; padding-left: var(--ct-8);
  font-family: var(--ct-font-body); font-size: var(--ct-fs-body-lg);
  line-height: var(--ct-lh-body); color: var(--ct-sub-ink);
}
.ct-sd-bullets li + li { margin-top: var(--ct-5); }
.ct-sd-bullets li::before {
  content: ""; position: absolute; left: 0; top: .55em;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--ct-gradient);
}
.ct-section--dark .ct-sd-bullets li { color: var(--ct-on-dark-sub); }

/* ---- Two-column point grid (legacy dark .break-home-section > 2 cols) --
   A heading over two columns of title+text pairs, used on dark bands by
   digitalize_your_paper_vouchers (6 pairs) and get_more_digital_insights
   (4 pairs). Legacy filled the columns COLUMN-major (first half left,
   second half right); the caller chunks to match, so this is a plain grid. */
.ct-sd-pointgrid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 4vw, 3.5rem); }
.ct-sd-point + .ct-sd-point { margin-top: var(--ct-8); }
.ct-sd-point h3 { margin-bottom: var(--ct-2); }
.ct-sd-point p  { margin: 0; }
.ct-section--dark .ct-sd-point h3 { color: var(--ct-on-dark); }
.ct-section--dark .ct-sd-point p  { color: var(--ct-on-dark-sub); }
@media (max-width: 767px) { .ct-sd-pointgrid { grid-template-columns: 1fr; } }

/* ---- Trial band with artwork instead of a button --------------------
   boosting_digital_in_store_loyalty_rewards ends on the dark band but puts
   a client image where the other pages put the CTA. Wider second column so
   the artwork reads, and the media is centred when it stacks. */
.ct-sd-cta__grid--media { grid-template-columns: 1.35fr 1fr; }
.ct-sd-cta__media img { width: 100%; height: auto; max-width: 380px; display: block; margin-inline: auto; border-radius: var(--ct-r-sm); }
@media (max-width: 767px) { .ct-sd-cta__grid--media { grid-template-columns: 1fr; } }
