/* ============================================================================
   bundle-signature.css — TIER 3 (Signature).

   The motion vocabulary here is drawn from the reference sites the offer is
   benchmarked against — bragg.com (marquee strips, oval-masked media,
   word-by-word reveals), bahamabucks.com (floating ambient elements, generous
   radii). Deliberately NOT copied: 50-jahre-hitparade.ch's Three.js WebGL
   scrollytelling, which is a bespoke per-site build and cannot be a template
   feature at this price.

   Everything degrades. A marquee that never animates is a static row of words;
   a horizontal gallery whose JS fails is a normal scrollable strip; masked
   media falls back to a rectangle. Nothing here can produce a blank page.
   ========================================================================== */

/* ---------- Video hero ---------- */
.hero-video{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transition:opacity 1.1s var(--ease-out);
}
.hero-video.is-on{opacity:1}

/* ============================================================================
   1. MARQUEE — infinite ticker strip
   The single highest signal-per-byte "modern site" device; bragg.com uses 87 of
   them. Pure CSS animation on a duplicated track, so it runs on the compositor
   and keeps going even when the main thread is busy.
   ========================================================================== */
.marquee{
  --marquee-duration:38s;
  position:relative;overflow:hidden;
  padding-block:clamp(18px,2.2vw,30px);
  border-block:1px solid var(--border-soft);
  background:var(--bg-elev-2);
  /* Fade the ends so items enter and leave rather than being chopped off. */
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
          mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
}
.marquee-track{
  display:flex;width:max-content;gap:clamp(28px,4vw,64px);
  animation:marquee-scroll var(--marquee-duration) linear infinite;
  will-change:transform;
}
.marquee:hover .marquee-track{animation-play-state:paused}
.marquee-item{
  display:inline-flex;align-items:center;gap:clamp(28px,4vw,64px);
  font-family:var(--font-display);
  font-size:clamp(1.1rem,2.4vw,2rem);
  font-weight:var(--display-weight,600);
  letter-spacing:var(--display-tracking,-0.02em);
  color:var(--fg);white-space:nowrap;
}
/* Separator dot between items, drawn rather than typed so it never wraps. */
.marquee-item::after{
  content:"";width:8px;height:8px;border-radius:50%;
  background:var(--accent);flex:none;
}
.marquee--accent .marquee-item{color:var(--accent)}
.marquee--outline .marquee-item{
  color:transparent;-webkit-text-stroke:1px var(--fg-muted);
}
/* Translating exactly -50% works because the track is rendered twice. */
@keyframes marquee-scroll{ from{transform:translate3d(0,0,0)} to{transform:translate3d(-50%,0,0)} }
@media (prefers-reduced-motion:reduce){
  .marquee-track{animation:none;width:100%;justify-content:center;flex-wrap:wrap}
  .marquee-dup{display:none} /* one copy is enough when it is not moving */
}

/* ============================================================================
   2. HORIZONTAL SCROLL GALLERY
   Vertical scroll drives horizontal movement through the work. For a trades
   business this is the most persuasive section on the page — it turns a grid
   of thumbnails into a walk-through.

   Base state is a normal horizontally scrollable strip, so with no JS a
   visitor can still swipe through every image.
   ========================================================================== */
.hscroll{position:relative}
.hscroll-viewport{
  overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;
  scrollbar-width:none;
}
.hscroll-viewport::-webkit-scrollbar{display:none}
.hscroll-track{
  display:flex;gap:clamp(14px,2vw,28px);
  padding-inline:var(--space-gutter);width:max-content;
}
.hscroll-item{
  flex:none;width:clamp(260px,42vw,540px);scroll-snap-align:center;
}
.hscroll-item img{
  width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--radius-lg);
}
.hscroll-item figcaption{
  margin-top:12px;font-size:.88rem;color:var(--fg-muted);
}

/* Pinned mode — added by JS only when it can drive the scroll. Until then the
   section behaves as the plain swipeable strip above. */
.hscroll.is-pinned{height:auto}
.hscroll.is-pinned .hscroll-viewport{overflow:hidden;scroll-snap-type:none}
.hscroll.is-pinned .hscroll-sticky{
  position:sticky;top:0;height:100vh;display:flex;flex-direction:column;
  justify-content:center;overflow:hidden;
}
.hscroll.is-pinned .hscroll-track{
  will-change:transform;transition:none;
}
.hscroll-progress{
  height:2px;background:var(--border-soft);margin:clamp(20px,3vw,34px) var(--space-gutter) 0;
  position:relative;overflow:hidden;
}
.hscroll-progress span{
  position:absolute;inset-block:0;left:0;width:0%;background:var(--accent);
}

/* ============================================================================
   3. SHAPED MEDIA
   bragg.com masks its imagery into ovals and arches. Swapping a rectangle for
   an arch is a one-property change that reads as deliberate art direction.
   ========================================================================== */
html[data-media="arch"] .split-media img,
html[data-media="arch"] .service-card .media img{
  border-radius:50% 50% var(--radius-lg) var(--radius-lg) / 28% 28% var(--radius-lg) var(--radius-lg);
}
html[data-media="oval"] .split-media img{ border-radius:50%/38% }
html[data-media="oval"] .service-card .media img{ border-radius:var(--radius-lg) }

/* ============================================================================
   4. FLOATING AMBIENT SHAPES
   bahamabucks.com drifts soft decorative blobs behind its content. Purely
   atmospheric, pointer-events:none, and hidden entirely under reduced motion.
   ========================================================================== */
.ambient{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:0}
.ambient span{
  position:absolute;border-radius:50%;filter:blur(46px);opacity:.16;
  background:var(--accent);
  animation:drift var(--drift-dur,26s) ease-in-out infinite alternate;
}
.ambient span:nth-child(1){width:38vmax;height:38vmax;top:-12%;left:-8%;--drift-dur:31s}
.ambient span:nth-child(2){width:26vmax;height:26vmax;bottom:-10%;right:-6%;background:var(--alt,var(--accent));--drift-dur:24s;animation-delay:-6s}
.ambient span:nth-child(3){width:18vmax;height:18vmax;top:38%;right:22%;--drift-dur:38s;animation-delay:-14s;opacity:.1}
@keyframes drift{
  from{transform:translate3d(0,0,0) scale(1)}
  to{transform:translate3d(4vw,-3vh,0) scale(1.16)}
}
@media (prefers-reduced-motion:reduce){ .ambient{display:none} }
/* Content must sit above the ambient layer or the blobs wash over the type. */
.statement .wrap,.cta-band .wrap{position:relative;z-index:2}

/* ============================================================================
   5. MAGNETIC BUTTONS + CARD TILT
   Cursor-reactive micro-interaction. Translation is applied by JS via custom
   properties so the CSS keeps ownership of the easing.
   ========================================================================== */
.btn.is-magnetic{
  transform:translate3d(var(--mx,0),var(--my,0),0);
  transition:transform .32s var(--ease-out),background var(--transition-interactive),box-shadow var(--transition-interactive);
}
.btn.is-magnetic.is-pulling{transition:transform .08s linear}

.tilt{transform-style:preserve-3d;transition:transform .4s var(--ease-out)}
.tilt.is-tilting{transition:transform .1s linear}
.tilt-inner{transform:rotateX(var(--tilt-x,0deg)) rotateY(var(--tilt-y,0deg));transition:inherit}

/* The tilt wrapper is injected between a card and its children, which would
   otherwise break the card's own flex context — `.service-card .body{flex:1}`
   stops equalising card heights because .body is no longer a flex child of the
   card. Make the wrapper transparent to layout so it only carries the rotation.
   Cards look fine with evenly-matched copy; this is what keeps them aligned
   when one service description runs three lines longer than another. */
.service-card > .tilt-inner{
  display:flex;flex-direction:column;flex:1;min-height:0;
}
@media (hover:none){ .btn.is-magnetic,.tilt{transform:none!important} }

/* ============================================================================
   6. WORD-BY-WORD SCROLL REVEAL (statement band)
   ========================================================================== */
.statement{background:var(--bg-elev-2);position:relative;overflow:hidden}
.statement .wrap{padding-block:clamp(90px,13vw,190px)}
.statement h2{font-size:var(--text-3xl);max-width:20ch}
.statement-media{position:absolute;inset:0;z-index:0}
.statement-media img,.statement-media video{width:100%;height:100%;object-fit:cover}
.statement-scrim{
  position:absolute;inset:0;z-index:1;pointer-events:none;
  background:
    linear-gradient(180deg, var(--bg) 0%, color-mix(in srgb, var(--bg) 55%, transparent) 24%,
                    color-mix(in srgb, var(--bg) 55%, transparent) 76%, var(--bg) 100%),
    linear-gradient(90deg, color-mix(in srgb, var(--bg) 82%, transparent) 0%,
                    color-mix(in srgb, var(--bg) 20%, transparent) 62%,
                    color-mix(in srgb, var(--bg) 36%, transparent) 100%);
}

/* ============================================================================
   7. BEFORE / AFTER SLIDER  (unchanged contract)
   ========================================================================== */
.ba{
  position:relative;overflow:hidden;border-radius:var(--radius-lg);
  background:var(--bg-elev-2);line-height:0;touch-action:pan-y;
}
.ba img{width:100%;aspect-ratio:3/2;object-fit:cover;display:block;user-select:none;-webkit-user-drag:none}
.ba-before{position:absolute;inset:0;height:100%}
.ba.is-live{cursor:ew-resize}
.ba.is-dragging{cursor:grabbing}
.ba.is-dragging .ba-before{transition:none}
.ba-handle{
  position:absolute;top:0;bottom:0;left:50%;z-index:3;
  width:44px;margin-left:-22px;padding:0;
  background:transparent;border:0;cursor:ew-resize;display:grid;place-items:center;
}
.ba-handle::before{
  content:"";position:absolute;top:0;bottom:0;left:21px;width:2px;
  background:#fff;box-shadow:0 0 12px rgba(0,0,0,.5);
}
.ba-handle::after{
  content:"";width:40px;height:40px;border-radius:50%;
  background:#fff;box-shadow:0 4px 16px rgba(0,0,0,.4);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23222' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='14 7 9 12 14 17'/%3E%3Cpolyline points='10 7 15 12 10 17' transform='translate(5 0)'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:center;background-size:22px 22px;
}
.ba-handle:focus-visible{outline:none}
.ba-handle:focus-visible::after{box-shadow:0 0 0 3px var(--accent),0 4px 16px rgba(0,0,0,.4)}
.ba-tag{
  position:absolute;top:14px;z-index:2;padding:6px 13px;border-radius:100px;
  font-family:var(--font-body);font-size:.72rem;font-weight:600;
  letter-spacing:.14em;text-transform:uppercase;line-height:1;
  background:rgba(0,0,0,.62);color:#fff;backdrop-filter:blur(6px);
}
.ba-tag-before{left:14px}
.ba-tag-after{right:14px}
@media (max-width:640px){
  .ba-tag{font-size:.64rem;padding:5px 10px}
  .ba-handle::after{width:34px;height:34px;background-size:19px 19px}
}
