/* ==========================================================================
   The Social SZN — design system
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Bricolage+Grotesque:opsz,wght@12..96,400;12..96,500;12..96,600;12..96,700;12..96,800&family=Inter:wght@400;500;600;700&display=swap');

:root{
  /* brand */
  --bg: #292929;
  --bg-alt: #1e1e1e;
  --panel: #333333;
  --panel-light: #3d3d3d;
  --line: rgba(255,255,255,0.1);
  --text: #f3f2f0;
  --text-dim: #b7b5b2;

  --c1: #7f86ff; /* periwinkle */
  --c2: #5b5ff0; /* indigo */
  --c3: #c651ff; /* magenta */
  --c4: #ffbb1c; /* gold */

  --gradient: linear-gradient(90deg, var(--c2), var(--c1) 35%, var(--c3) 70%, var(--c4));
  --gradient-soft: linear-gradient(135deg, rgba(127,134,255,0.25), rgba(198,81,255,0.18) 50%, rgba(255,187,28,0.2));

  --font-display: 'Bricolage Grotesque', sans-serif;
  --font-script: 'Instrument Serif', serif;
  --font-body: 'Inter', sans-serif;

  --radius: 18px;
  --radius-sm: 10px;
  --container: 1320px;
}

*, *::before, *::after{ box-sizing: border-box; }
html{ scroll-behavior: smooth; overflow-x: hidden; overscroll-behavior-x: none; }
body{
  margin:0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-x: none;
}
/* The real, now-confirmed root cause of the nav-goes-missing bug: setting
   overflow-x (any value other than visible) on body makes its computed
   overflow-y become auto too — a body with non-visible overflow is a
   scroll container, and .nav's position:sticky ends up anchored to THAT
   container instead of the actual page viewport. Safari in particular
   then simply fails to keep it stuck at all as you scroll. This exact
   diagnosis was made once before on this project, reverted under the
   (wrong) theory that it wasn't the real fix, and reintroduced two turns
   ago to solve horizontal rubber-band/"I can zoom out" — which is what
   broke sticky nav again. overscroll-behavior-x:none stops the
   rubber-band/bounce-scroll on both html and body WITHOUT touching
   overflow at all, so it can't create a scroll-container ancestor for
   .nav to get anchored to — html keeps its own overflow-x:hidden (the
   viewport-propagating root element, not an extra nested container, so
   it's safe), but body must never have overflow-x set to anything but
   the default again. */
img{ max-width:100%; display:block; }
a{ color:inherit; text-decoration:none; }
ul{ list-style:none; margin:0; padding:0; }
button{ font-family:inherit; cursor:pointer; }

.container{
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 32px;
}

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

/* Hero headline only — shifts between blue and light purple as the mouse
   moves across the hero. The plasma canvas behind it is untouched. */
.hero-headline-gradient{
  background: linear-gradient(90deg, var(--c2), var(--c1) 50%, var(--c3));
  background-size: 340% 100%;
  background-position: 0% 50%;
  -webkit-background-clip: text;
  background-clip: text;
  transition: background-position 0.35s ease-out;
}

.eyebrow{
  font-family: var(--font-body);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--c1);
}

h1,h2,h3,h4{
  font-family: var(--font-display);
  font-weight: 700;
  margin: 0;
  letter-spacing: -0.01em;
}

.section{ padding: 78px 0; }
.section-tight{ padding: 50px 0; }

@media (max-width: 768px){
  .section{ padding: 54px 0; }
  .section-tight{ padding: 36px 0; }
  .container{ padding: 0 20px; }
}
@media (max-width: 420px){
  .container{ padding: 0 14px; }
}

/* ---------- Buttons ---------- */
.btn{
  display:inline-flex;
  align-items:center;
  gap:8px;
  padding: 14px 28px;
  border-radius: 100px;
  font-weight: 600;
  font-size: 14.5px;
  border: none;
  transition: transform .25s ease, box-shadow .25s ease, opacity .25s ease;
  white-space: nowrap;
}
.btn-primary{
  background: var(--gradient);
  color: #16161a;
}
.btn-primary:hover{ transform: translateY(-2px); box-shadow: 0 10px 30px rgba(127,134,255,0.35); }
.btn-outline{
  position: relative;
  z-index: 0;
  background: transparent;
  color: var(--text);
  border: 1.5px solid transparent;
}
.btn-outline::before{
  content:'';
  position:absolute;
  inset:0;
  border-radius: inherit;
  padding: 1.5px;
  background: var(--gradient);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  z-index: -1;
  pointer-events: none;
}
.btn-outline:hover{ transform: translateY(-2px); box-shadow: 0 10px 26px rgba(198,81,255,0.25); }

/* ---------- Nav ---------- */
/* Found the real bug: `backdrop-filter` on an element makes it a
   "containing block" for any position:fixed descendant in Safari/WebKit
   (same rule as `transform`/`filter`) — so .nav-links (fixed, top:68px,
   bottom:0) was resolving those offsets against .nav's own ~70px-tall bar
   instead of the actual viewport, collapsing the "full screen" mobile menu
   down to a sliver just tall enough for "Home" before the real page showed
   through underneath it. .nav keeps position:sticky exactly as before —
   only the blur moves onto a ::before pseudo-element instead of living on
   .nav itself, so .nav is no longer a containing block for .nav-links. */
/* Sticky, and always visible/interactive — it never hides or slides
   away. Safari has a real, separate bug where adding `opacity`/
   `transition` directly to a `position: sticky` element can make it stop
   sticking altogether (same family of issue as the backdrop-filter bug
   above) — so the scroll-based dim below is deliberately NOT on .nav
   itself. .nav stays position:sticky with no other properties that could
   affect it; the actual fade lives on .nav::before (the background) and
   .nav-inner (the content), which dim together when JS adds
   .nav--scrolled to the parent. Full strength at the very top of the
   page, a bit dimmer once scrolled, back to full the moment you're back
   at the top or hovering the bar. */
.nav{
  position: sticky;
  top:0;
  z-index: 100;
  border-bottom: 1px solid var(--line);
}
.nav::before{
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(41,41,41,0.75);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  z-index: -1;
  opacity: 1;
  transition: opacity .3s ease;
}
.nav-inner{ opacity: 1; transition: opacity .3s ease; }
.nav.nav--scrolled::before,
.nav.nav--scrolled .nav-inner{ opacity: 0.6; }
.nav.nav--scrolled:hover::before,
.nav.nav--scrolled:hover .nav-inner{ opacity: 1; }
.nav-inner{
  max-width: var(--container);
  margin: 0 auto;
  padding: 18px 32px;
  display:flex;
  align-items:center;
  justify-content:space-between;
}
.logo{
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 22px;
  letter-spacing: -0.02em;
  display: inline-flex;
  align-items: center;
}
.logo span{ font-weight: 400; font-family: var(--font-script); font-style: italic; }
.logo-img{
  height: 46px;
  width: auto;
  display: block;
}
.footer-top .logo-img{ height: 38px; }

.nav-links{
  display:flex;
  align-items:center;
  gap: 34px;
}
.nav-links a{
  font-size: 14.5px;
  font-weight: 500;
  color: var(--text-dim);
  transition: color .2s ease;
}
.nav-links a:hover, .nav-links a.active{ color: var(--text); }

.nav-item.has-dropdown{ position: relative; }
.dropdown-toggle{ display:flex; align-items:center; gap:5px; background:none; border:none; color: var(--text-dim); font-size:14.5px; font-weight:500; padding:0; font-family:inherit; }
.dropdown-toggle:hover{ color: var(--text); }
.dropdown-toggle svg{ transition: transform .2s ease; }
.nav-item.open .dropdown-toggle svg{ transform: rotate(180deg); }
.dropdown-menu{
  position:absolute;
  top: calc(100% + 18px);
  left: 50%;
  transform: translateX(-50%) translateY(-8px);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px;
  min-width: 220px;
  opacity:0;
  pointer-events:none;
  transition: opacity .2s ease, transform .2s ease;
  box-shadow: 0 20px 40px rgba(0,0,0,0.35);
}
.nav-item.open .dropdown-menu{
  opacity:1;
  pointer-events:auto;
  transform: translateX(-50%) translateY(0);
}
.dropdown-menu a{
  display:block;
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 14px;
  color: var(--text-dim);
}
.dropdown-menu a:hover{ background: var(--panel-light); color: var(--text); }

.nav-cta{ display:flex; align-items:center; gap:18px; }
.nav-toggle{
  display:none;
  background:none; border:none;
  appearance:none; -webkit-appearance:none;
  padding:0; margin:0;
  width: 24px; height: 18px;
  position:relative;
}
.nav-toggle span{
  position:absolute; left:0; right:0; height:1.5px; background:var(--text); border-radius:2px;
  transition: all .25s ease;
}
.nav-toggle span:nth-child(1){ top:0; }
.nav-toggle span:nth-child(2){ top:8px; }
.nav-toggle span:nth-child(3){ top:16px; }
.nav-toggle.open span:nth-child(1){ transform: translateY(8px) rotate(45deg); }
.nav-toggle.open span:nth-child(2){ opacity:0; }
.nav-toggle.open span:nth-child(3){ transform: translateY(-8px) rotate(-45deg); }

@media (max-width: 900px){
  /* Full-screen, centered menu instead of a left-aligned list starting
     right under the nav bar — large uppercase links, generous spacing,
     vertically and horizontally centered so it's easy to hit with a
     thumb from anywhere on the screen, rather than everything being
     bunched up near the top-left. */
  .nav-links{
    position: fixed;
    inset: 0;
    background: var(--bg-alt);
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 40px 28px;
    gap: 38px;
    transform: translateX(100%);
    transition: transform .3s ease;
    overflow-y:auto;
  }
  .nav-links.open{ transform: translateX(0); }
  .nav-links a{
    font-family: var(--font-display);
    font-size: 26px;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
  }
  /* Small underline accent below whichever page you're already on, same
     idea as the reference screenshot — an easy, immediate "you are here"
     instead of relying on a subtle color difference alone. */
  .nav-links a.active{
    position: relative;
    padding-bottom: 16px;
  }
  .nav-links a.active::after{
    content: '';
    position: absolute;
    left: 50%;
    bottom: 0;
    transform: translateX(-50%);
    width: 54px;
    height: 2px;
    background: var(--c4);
  }
  .nav-item.has-dropdown{ width:100%; display:flex; flex-direction:column; align-items:center; }
  .nav-item.has-dropdown .dropdown-toggle{
    font-family: var(--font-display);
    font-size: 26px;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    gap: 8px;
  }
  .dropdown-menu{ position:static; transform:none; opacity:1; pointer-events:auto; display:none; box-shadow:none; border:none; background:none; padding: 24px 0 0; width:100%; text-align:center; }
  /* .nav-item.open .dropdown-menu (desktop rule, up above) sets
     transform: translateX(-50%) translateY(0) to center the dropdown
     under its toggle — two classes, so it beats the single-class mobile
     reset directly above even inside this media query. On mobile the
     menu is position:static now, not centered under anything, so that
     leftover translateX(-50%) was shifting the whole submenu left by
     half its own width — pushing "Event Content Creation" and "Social
     Media Management" half off the left edge of the screen. Redeclaring
     the same two-class selector here (same specificity, later in the
     file) is what's needed to actually win over it on mobile. */
  .nav-item.open .dropdown-menu{ display:flex; flex-direction:column; gap:20px; transform:none; }
  .dropdown-menu a{
    padding: 0;
    border-radius: 0;
    font-size: 15px;
    letter-spacing: 0.05em;
    text-transform: uppercase;
  }
  .dropdown-menu a:hover{ background:none; color: var(--text); }
  .nav-toggle{
    display:block;
    /* .nav-links now covers the entire screen (inset:0), including the
       spot where this button already sits in the bar above it — without
       a z-index higher than the overlay, the open (X) state of this same
       button would render underneath its own menu once open. */
    position: relative;
    z-index: 20;
  }
  /* .nav-cta is still a flex item in .nav-inner even once its only child is
     hidden, so it was sitting empty between the toggle and the true right
     edge — with justify-content:space-between that pushed the toggle
     toward the middle instead of the corner. Hiding the whole container
     (not just the button inside it) makes the toggle the last flex item,
     which is what actually lands it at the right edge. */
  .nav-cta{ display:none; }
  /* A second, mobile-only copy of the "Let's Talk" link lives at the
     bottom of the centered menu itself (styled like the reference's
     "GET IN TOUCH") — plain text matching the other links rather than
     the pill-button treatment .btn-outline uses everywhere else, since a
     bordered button reads like a distinct, secondary control here rather
     than the last item in one continuous list of destinations. */
  .nav-links-cta{
    display: block;
    font-family: var(--font-display);
    font-size: 16px;
    font-weight: 500;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-dim);
    border: none;
    background: none;
    padding: 0;
  }
  .nav-links-cta:hover{ color: var(--text); transform:none; box-shadow:none; }
}
@media (min-width: 901px){
  .nav-links-cta{ display: none; }
}

/* ---------- Hero ---------- */
.hero{
  position: relative;
  min-height: 82vh;
  display:flex;
  align-items:flex-end;
  overflow:hidden;
}
.hero-media{
  position:absolute; inset:0;
  background: var(--bg-alt);
  overflow: hidden;
}
.hero-media::before{
  content:'';
  position:absolute; inset:0;
  background: rgba(0,0,0,0.15);
  z-index: 1;
}
.hero-media::after{
  content:'';
  position:absolute; inset:0;
  background: linear-gradient(180deg, rgba(41,41,41,0) 0%, rgba(41,41,41,0.55) 60%, var(--bg) 100%);
  z-index: 1;
}

/* ---------- Hero aura: plasma / lava-lamp canvas ----------
   A low-res canvas is animated with a plasma algorithm and scaled up to
   fill the hero, so color fills the whole area (not isolated blobs). Moving
   the mouse creates a ripple in the color field at the cursor's position —
   it doesn't add a light source, it disturbs the plasma itself, like touching
   the surface of a lava lamp. A light blur just smooths the low-res pixels
   into flowing shapes instead of looking blocky. */
.aura-canvas{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  filter: blur(38px) saturate(1.3) contrast(1.05);
}
.hero-content{
  position:relative;
  z-index:2;
  padding: 60px 0 90px;
  width:100%;
}
/* .hero-content carries both .container and .hero-content classes; this
   rule's own padding shorthand (0 left/right) overrides .container's side
   padding entirely since it's declared later in the file. On desktop the
   max-width centering hides the loss (there's already plenty of margin),
   but on mobile there's nothing left to keep the text off the screen edge
   — so restore just the horizontal padding, mobile only. */
@media (max-width: 768px){
  .hero-content{ padding-left: 20px; padding-right: 20px; }
}
.hero-logo{ width: min(560px, 62%); height: auto; display:block; margin-bottom: 28px; }
@media (max-width: 640px){ .hero-logo{ width: min(320px, 78%); margin-bottom: 18px; } }
.hero-tag{ margin-bottom: 22px; color: var(--text-dim); font-size: 13.5px; letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap; }
@media (max-width: 760px){
  .hero-tag{ white-space: normal; }
}
.hero h1{
  font-size: clamp(40px, 6vw, 76px);
  line-height: 1.02;
}
.hero h1 .script{
  font-family: var(--font-script);
  font-style: italic;
  font-weight: 400;
  display:block;
}
.hero p{
  max-width: 520px;
  color: var(--text-dim);
  font-size: 17px;
  margin: 26px 0 34px;
}
.hero-actions{ display:flex; gap:16px; flex-wrap:wrap; }
/* Scroll-down cue: sits centered at the very bottom of the hero, above the
   fade into the next section. The bounce is on the icon only (not the
   whole link) so the "Scroll" label stays readable instead of jittering
   with it. */
.scroll-cue{
  position: absolute;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  color: var(--text-dim);
  text-decoration: none;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.75;
  transition: opacity .25s ease, color .25s ease;
}
.scroll-cue:hover{ opacity: 1; color: var(--text); }
.scroll-cue svg{ animation: scrollCueBounce 1.8s ease-in-out infinite; }
@keyframes scrollCueBounce{
  0%, 100%{ transform: translateY(0); }
  50%{ transform: translateY(7px); }
}
@media (max-width: 640px){ .scroll-cue{ bottom: 14px; } }

/* ---------- Placeholder blocks ---------- */
.placeholder{
  background: var(--gradient-soft), repeating-linear-gradient(45deg, rgba(255,255,255,0.03) 0 12px, transparent 12px 24px);
  background-color: var(--panel);
  border: 1px dashed rgba(255,255,255,0.22);
  display:flex;
  align-items:center;
  justify-content:center;
  color: var(--text-dim);
  font-size: 12.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align:center;
  border-radius: var(--radius);
  min-height: 180px;
}
.placeholder-label{
  background: rgba(0,0,0,0.55);
  border: 1px solid rgba(255,255,255,0.15);
  color: var(--text-dim);
  padding: 6px 12px;
  border-radius: 100px;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* ---------- Trusted by ---------- */
.trusted{
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: 40px 0;
}
.trusted p{ color: var(--text-dim); font-size: 12.5px; text-transform: uppercase; letter-spacing: 0.12em; margin: 0 0 26px; text-align:center; }

.trusted-marquee{
  overflow: hidden;
  width: 100%;
  -webkit-mask-image: linear-gradient(90deg, transparent, black 10%, black 90%, transparent);
  mask-image: linear-gradient(90deg, transparent, black 10%, black 90%, transparent);
}
.trusted-track{
  display: flex;
  align-items: center;
  gap: 64px;
  width: max-content;
  animation: trusted-scroll 32s linear infinite;
}
.trusted-track:hover{ animation-play-state: paused; }
@keyframes trusted-scroll{
  from{ transform: translateX(0); }
  to{ transform: translateX(-50%); }
}
.trusted-logo{
  height: 34px;
  width: auto;
  flex-shrink: 0;
  opacity: 0.65;
  transition: opacity .2s ease;
}
.trusted-logo:hover{ opacity: 1; }

/* Individual height overrides — each logo's own artwork has different amounts of
   built-in padding (icons, stacked subtext, emblems), so a single shared height
   makes some look bigger than others. These are tuned so they all read as the
   same visual size sitting next to each other. */
.trusted-logo.tl-mgm{ height: 52px; }
.trusted-logo.tl-excalibur{ height: 42px; }
.trusted-logo.tl-nyny{ height: 50px; }
.trusted-logo.tl-durango{ height: 46px; }
.trusted-logo.tl-station-casinos{ height: 44px; }
.trusted-logo.tl-gvr{ height: 58px; }
.trusted-logo.tl-escape-it{ height: 54px; }
.trusted-logo.tl-marilyn-monroe{ height: 42px; }
.trusted-logo.tl-sports-illustrated{ height: 40px; }
.trusted-logo.tl-authentic{ height: 48px; }

@media (max-width: 600px){
  .trusted-logo{ height: 26px; }
  .trusted-logo.tl-mgm{ height: 38px; }
  .trusted-logo.tl-excalibur{ height: 32px; }
  .trusted-logo.tl-nyny{ height: 36px; }
  .trusted-logo.tl-durango{ height: 34px; }
  .trusted-logo.tl-station-casinos{ height: 32px; }
  .trusted-logo.tl-gvr{ height: 42px; }
  .trusted-logo.tl-escape-it{ height: 40px; }
  .trusted-logo.tl-marilyn-monroe{ height: 32px; }
  .trusted-logo.tl-sports-illustrated{ height: 30px; }
  .trusted-logo.tl-authentic{ height: 36px; }
  .trusted-track{ gap: 44px; }
}

/* ---------- Approach ---------- */
.approach{ display:grid; grid-template-columns: 1fr 1.1fr; gap: 70px; align-items:center; }
.approach-media{ aspect-ratio: 4/5; border-radius: var(--radius); }
/* The Our Approach photo is a wide 6-image collage, not a tall single
   portrait — override the box to its own natural landscape ratio (rather
   than force-cropping it into the 4/5 portrait shape above) so none of
   the six photos in the mosaic get cut off. */
.approach-media.has-photo{ aspect-ratio: 1366/996; }
.approach-media.has-photo{ background:none; border:0; padding:0; overflow:hidden; }
.approach-media.has-photo img{
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: inherit;
}
.approach h2{ font-size: clamp(28px, 3.6vw, 42px); line-height:1.15; margin: 16px 0 22px; }
.approach p{ color: var(--text-dim); max-width: 460px; }
@media (max-width: 860px){
  .approach{ grid-template-columns:1fr; gap:36px; }
  /* Mobile only: title above the photo, photo, then the rest of the copy
     below it — .approach-text drops its own box (display:contents) so its
     two children (.approach-heading, .approach-body) become direct grid
     items of .approach and can be reordered independently around the
     photo. Desktop is untouched: .approach-text stays a normal block,
     heading+body just stack together in the right-hand column like before. */
  .approach-text{ display:contents; }
  .approach-heading{ order:1; }
  .approach-media{ order:2; }
  .approach-body{ order:3; }
}

/* ---------- Stats ---------- */
.stats{
  display:grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 22px;
}
.stat-card{
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 34px 24px;
  text-align:center;
}
.stat-num{
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(34px, 4vw, 48px);
  background: var(--gradient);
  -webkit-background-clip:text;
  background-clip:text;
  color:transparent;
  display:block;
  line-height:1;
}
.stat-label{ margin-top: 12px; color: var(--text-dim); font-size: 13.5px; text-transform:uppercase; letter-spacing:0.06em; }
@media (max-width: 760px){
  .stats{ grid-template-columns: repeat(2,1fr); }
}

/* ---------- Process ---------- */
/* Collapsible instead of all 5 steps shown open at once (title + a full
   paragraph each) — same independent-accordion behavior as the Services
   page (.process-item/.process-q/.process-a mirror .service-item/-q/-a),
   just with the number+title on the left and the plus on the right
   instead of plus-then-label, per what this section asked for. Useful
   but secondary info (the how), so collapsed rows let it take up a lot
   less space by default while still being one tap away. */
.process-list{ display:flex; flex-direction:column; margin-top: 50px; }
.process-item{ border-top: 1px solid var(--line); }
.process-item:last-child{ border-bottom: 1px solid var(--line); }
.process-q{
  width:100%;
  background:none;
  border:none;
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap: 16px;
  padding: 26px 0;
  text-align:left;
  cursor:pointer;
  color: var(--text);
}
.process-q-label{
  display:flex;
  align-items:center;
  gap: 22px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 21px;
}
.process-num{
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 30px;
  background: var(--gradient);
  -webkit-background-clip:text;
  background-clip:text;
  color:transparent;
  flex-shrink: 0;
  min-width: 44px;
}
.process-item .plus{
  color: var(--c1);
  font-size: 22px;
  font-weight:700;
  flex-shrink: 0;
  display:inline-block;
  transition: transform .3s ease;
}
.process-item.open .plus{ transform: rotate(45deg); }
.process-a{
  max-height:0;
  overflow:hidden;
  transition: max-height .3s ease;
}
.process-a p{ padding: 0 0 26px 66px; color: var(--text-dim); max-width:560px; margin:0; }
@media (max-width: 620px){
  .process-q-label{ font-size: 18px; gap: 16px; }
  .process-num{ font-size: 24px; min-width: 36px; }
  .process-a p{ padding-left: 52px; }
}

/* ---------- Reviews ---------- */
.reviews-grid{ display:grid; grid-template-columns: repeat(3,1fr); gap: 22px; }
.review-card{
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 30px;
}
.review-stars{ color: var(--c4); letter-spacing:3px; margin-bottom:16px; font-size:15px; }
.review-card p{ color: var(--text-dim); font-size:15px; }
.review-name{ margin-top:20px; font-weight:600; color: var(--text); font-size:14.5px; }
.review-role{ color: var(--text-dim); font-size: 13px; }
@media (max-width: 900px){ .reviews-grid{ grid-template-columns:1fr; } }

/* ---------- FAQ ---------- */
.faq-header{ margin-bottom: 30px; }
.faq-item{
  border-top: 1px solid var(--line);
}
.faq-item:last-child{ border-bottom: 1px solid var(--line); }
.faq-q{
  width:100%;
  background:none;
  border:none;
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap: 20px;
  padding: 26px 4px;
  text-align:left;
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 18px;
}
.faq-q svg{ flex-shrink:0; transition: transform .3s ease; color: var(--c1); }
.faq-item.open .faq-q svg{ transform: rotate(180deg); }
.faq-a{
  max-height:0;
  overflow:hidden;
  transition: max-height .3s ease;
}
.faq-a p{ padding: 0 4px 26px; color: var(--text-dim); max-width: 640px; margin:0; }

/* ---------- Section heading w/ arrow ---------- */
.heading-row{
  display:flex;
  align-items:center;
  gap: 26px;
  flex-wrap:wrap;
}
.heading-row h2{
  font-family: var(--font-script);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(32px, 4.6vw, 52px);
}
.heading-row svg{ flex-shrink:0; color: var(--c1); }
.heading-sub{ color: var(--text-dim); max-width: 560px; margin-top: 18px; }

/* ---------- Page hero (small) ---------- */
.page-hero{ padding: 70px 0 20px; }
/* Same .container + .page-hero double-class issue as .hero-content above —
   restore the horizontal padding this rule strips out, mobile only. */
@media (max-width: 768px){
  .page-hero{ padding-left: 20px; padding-right: 20px; }
}
.page-hero .eyebrow{ display:block; margin-bottom: 14px; }
.page-hero h1{ font-size: clamp(34px, 5vw, 58px); }

/* ---------- Footer ---------- */
.site-footer{
  border-top: 1px solid var(--line);
  padding: 60px 0 30px;
}
.footer-top{
  display:flex;
  justify-content:space-between;
  gap: 40px;
  flex-wrap:wrap;
  padding-bottom: 40px;
  border-bottom: 1px solid var(--line);
}
.footer-cols{ display:flex; gap: 70px; flex-wrap:wrap; }
.footer-col h4{ font-family: var(--font-body); font-size: 13px; text-transform:uppercase; letter-spacing:0.08em; color: var(--text-dim); margin-bottom: 16px; font-weight:600; }
.footer-col a{ display:block; color: var(--text-dim); font-size: 14.5px; margin-bottom: 10px; }
.footer-col a:hover{ color: var(--text); }
.footer-social{ display:flex; gap:16px; margin-top:4px; }
.footer-social a{ display:inline-flex; color: var(--text-dim); margin-bottom:0; }
.footer-social a:hover{ color: var(--text); }
.footer-bottom{
  display:flex;
  justify-content:space-between;
  align-items:center;
  padding-top: 26px;
  color: var(--text-dim);
  font-size: 13px;
  flex-wrap:wrap;
  gap: 12px;
}

/* ---------- Portfolio ---------- */
.portfolio-category{ margin-bottom: 90px; }
.portfolio-category:last-child{ margin-bottom:0; }
.cat-head{ display:flex; align-items:baseline; justify-content:space-between; gap:20px; flex-wrap:wrap; margin-bottom: 30px; }
.cat-head h2{ font-size: clamp(26px,3vw,36px); }
.cat-head p{ color: var(--text-dim); max-width: 420px; margin:0; }
.portfolio-grid{ display:grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.portfolio-grid .placeholder{ aspect-ratio: 4/5; min-height:unset; }
.portfolio-grid .placeholder.wide{ grid-column: span 2; aspect-ratio: unset; }
/* Real photo tiles reuse the same grid slots as the old dashed-border
   placeholders (same aspect-ratio, same "wide" spanning rule) but swap
   the dashed/gradient placeholder look for an actual cropped photo.
   .wide gets a fixed landscape ratio (rather than the placeholder's
   "unset", which only worked because there was no real image dictating
   a height) so a real wide photo always crops to a predictable shape
   instead of rendering at whatever its native aspect ratio happens to be. */
.portfolio-grid .placeholder.has-photo{
  background: none;
  border: 0;
  padding: 0;
  overflow: hidden;
}
.portfolio-grid .placeholder.has-photo.wide{ aspect-ratio: 16/10; }
.portfolio-grid .placeholder.has-photo img{
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: inherit;
}
@media (max-width: 760px){
  .portfolio-grid{ grid-template-columns: repeat(2,1fr); }
  .portfolio-grid .placeholder.wide{ grid-column: span 2; }
}

/* ---------- Photography: collapsible photo categories ----------
   Same open/close-independently accordion pattern as the Services
   section (.service-item/.service-q/.service-a) — max-height animated
   by JS rather than left at "auto", so the collapse/expand can actually
   transition instead of snapping. Each category holds every photo in
   its folder (not a curated preview), since the point is to browse the
   full set rather than a taste of it. */
.photo-cats{ margin-top: 10px; }
.photo-cat{ border-top: 1px solid var(--line); }
.photo-cat:last-child{ border-bottom: 1px solid var(--line); }
.photo-cat-q{
  width:100%;
  background:none;
  border:none;
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap: 16px;
  padding: 24px 2px;
  text-align:left;
  cursor:pointer;
  color: var(--text);
}
.photo-cat-q .cat-title{
  font-family: var(--font-display);
  font-size: clamp(18px, 2.2vw, 22px);
  font-weight: 600;
}
.photo-cat-q .cat-count{
  color: var(--text-dim);
  font-size: 13px;
  font-weight: 400;
  font-family: var(--font-body);
  margin-left: 6px;
}
.photo-cat-q .plus{
  color: var(--c1);
  font-size: 22px;
  font-weight:700;
  flex-shrink:0;
  transition: transform .3s ease;
}
.photo-cat.open .plus{ transform: rotate(45deg); }
.photo-cat-a{
  max-height:0;
  overflow:hidden;
  transition: max-height .4s ease;
}
.photo-cat-grid{
  display:grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
  padding: 4px 2px 34px;
}
.photo-cat-grid img{
  width:100%;
  aspect-ratio: 4/5;
  object-fit: cover;
  border-radius: var(--radius-sm);
  display:block;
  background: var(--panel);
}
@media (max-width: 900px){
  .photo-cat-grid{ grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 620px){
  .photo-cat-grid{ grid-template-columns: repeat(2, 1fr); gap: 10px; }
}

/* ---------- Photo lightbox ---------- */
/* Built once by JS and appended to <body>, reused for every category grid
   on the page. display:none until .is-open so it never intercepts clicks
   or shows up in the layout while closed. */
.lightbox{
  display:none;
  position: fixed;
  inset: 0;
  z-index: 500;
  background: rgba(10,10,10,0.94);
  align-items: center;
  justify-content: center;
}
.lightbox.is-open{ display:flex; }
/* Locks background scroll while the lightbox is open, same idea as any
   modal — without this, a swipe meant for lightbox nav on mobile could
   also scroll the page underneath it. */
body.lightbox-locked{ overflow: hidden; }
.lightbox-stage{
  display:flex;
  align-items:center;
  justify-content:center;
  width: 100%;
  height: 100%;
  padding: 70px 90px;
  box-sizing: border-box;
}
.lightbox-img{
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: var(--radius-sm);
  box-shadow: 0 30px 80px rgba(0,0,0,0.5);
}
.lightbox-close{
  position: absolute;
  top: 20px;
  right: 20px;
  z-index: 2;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: rgba(255,255,255,0.08);
  border: 1px solid var(--line);
  color: #fff;
  display:flex;
  align-items:center;
  justify-content:center;
  cursor:pointer;
  transition: background .2s ease;
}
.lightbox-close:hover{ background: rgba(255,255,255,0.18); }
.lightbox-close svg{ width: 18px; height: 18px; }
.lightbox-nav{
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: rgba(255,255,255,0.08);
  border: 1px solid var(--line);
  color: #fff;
  display:flex;
  align-items:center;
  justify-content:center;
  cursor:pointer;
  transition: background .2s ease, transform .2s ease;
}
.lightbox-nav:hover{ background: rgba(255,255,255,0.18); }
.lightbox-nav svg{ width: 20px; height: 20px; }
.lightbox-prev{ left: 20px; }
.lightbox-next{ right: 20px; }
.lightbox-counter{
  position: absolute;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  z-index: 2;
  color: var(--text-dim);
  font-size: 13px;
  letter-spacing: 0.04em;
  background: rgba(255,255,255,0.06);
  padding: 6px 14px;
  border-radius: 999px;
}
/* Below 860px the on-screen prev/next arrows step aside for the swipe
   gesture (handled entirely by JS on .lightbox-stage) — keeping both
   would mean two competing ways to navigate the same photo on a screen
   that's already tight on room. */
@media (max-width: 860px){
  .lightbox-nav{ display:none; }
  .lightbox-stage{ padding: 70px 20px; }
}

/* ---------- Services (accordion) ---------- */
.services-list{ display:grid; grid-template-columns: 1fr 1fr; gap: 0 60px; margin-top: 60px; }
.services-col .service-item{ border-top: 1px solid var(--line); }
.services-col .service-item:last-child{ border-bottom: 1px solid var(--line); }
.service-q{
  width:100%;
  background:none;
  border:none;
  display:flex;
  align-items:center;
  gap: 16px;
  padding: 22px 0;
  text-align:left;
  cursor:pointer;
  color: var(--text);
  font-size: 19px;
  font-family: var(--font-display);
  font-weight: 500;
}
.service-item .plus{
  color: var(--c1);
  font-size: 20px;
  font-weight:700;
  display:inline-block;
  transition: transform .3s ease;
}
.service-item.open .plus{ transform: rotate(45deg); }
.service-a{
  max-height:0;
  overflow:hidden;
  transition: max-height .3s ease;
}
.service-a p{ padding: 0 0 22px 36px; color: var(--text-dim); font-size:15px; max-width:520px; margin:0; }
@media (max-width: 760px){
  .services-list{ grid-template-columns: 1fr; }
}

/* ---------- About ---------- */
.about-hero{ display:grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items:center; }
.about-hero .placeholder{ aspect-ratio: 3/4; }
.about-portrait{
  width: 100%;
  aspect-ratio: 3/4;
  object-fit: cover;
  border-radius: var(--radius);
  border: 1px solid var(--line);
}
@media (max-width: 860px){ .about-hero{ grid-template-columns:1fr; } }

.values-grid{ display:grid; grid-template-columns: repeat(3,1fr); gap:22px; margin-top:50px; }
.value-card{ background: var(--panel); border:1px solid var(--line); border-radius: var(--radius); padding: 32px; }
.value-card h3{ font-size:19px; margin-bottom:10px; }
.value-card p{ color: var(--text-dim); font-size:14.5px; margin:0; }
@media (max-width: 860px){ .values-grid{ grid-template-columns:1fr; } }

/* CTA band */
.cta-band{
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 28px;
  padding: 70px 50px;
  text-align:center;
}
.cta-band h2{ font-size: clamp(28px,4vw,44px); margin-bottom:20px; }
.cta-band p{ color: var(--text-dim); max-width:480px; margin: 0 auto 30px; }

/* ---------- Portfolio hub (overview cards) ---------- */
.portfolio-hub-grid{ display:grid; grid-template-columns: repeat(2,1fr); gap:22px; margin-top:50px; }
.portfolio-hub-card{
  position:relative;
  border-radius: var(--radius);
  overflow:hidden;
  min-height: 320px;
  display:flex;
  align-items:flex-end;
  padding: 34px;
  border: 1px solid var(--line);
}
.portfolio-hub-card h2{ font-size: 26px; margin-bottom:10px; }
.portfolio-hub-card p{ color: var(--text-dim); font-size:14.5px; max-width:340px; margin:0 0 18px; }
.portfolio-hub-card .btn{ position:relative; z-index:2; }
.portfolio-hub-card-inner{ position:relative; z-index:2; }
@media (max-width: 760px){ .portfolio-hub-grid{ grid-template-columns:1fr; } }

/* ---------- Portfolio category tabs ---------- */
.portfolio-tabs{ display:flex; gap:12px; flex-wrap:wrap; margin: 30px 0 10px; }
.portfolio-tabs a{
  padding:10px 20px;
  border-radius:100px;
  border:1px solid var(--line);
  font-size:14px;
  color: var(--text-dim);
  transition: all .2s ease;
}
.portfolio-tabs a:hover{ color: var(--text); border-color: var(--c1); }
.portfolio-tabs a.active{ background: var(--gradient); color:#16161a; border-color:transparent; font-weight:600; }

/* ---------- Contact page ---------- */
.contact-hero{ padding: 70px 0 40px; border-bottom: 1px solid var(--line); }
.contact-hero h1{ font-size: clamp(36px, 6vw, 64px); margin: 16px 0 26px; line-height:1.05; }
.contact-sub{ color: var(--text-dim); font-size:17px; max-width:560px; padding-top:20px; border-top:1px solid var(--line); }

.contact-grid{ display:grid; grid-template-columns: 0.8fr 1.2fr; gap: 80px; }
@media (max-width: 860px){ .contact-grid{ grid-template-columns:1fr; gap:50px; } }

.info-block{ margin-bottom:38px; }
.info-block a, .info-block p{ font-size:19px; margin-top:10px; color: var(--text); }
.info-block a:hover{ color: var(--c1); }
.social-links{ display:flex; gap:18px; margin-top:10px; }
.social-links a{ display:inline-flex; color: var(--text-dim); }
.social-links a:hover{ color: var(--text); }

.contact-form .form-row{ display:grid; grid-template-columns:1fr 1fr; gap:30px; }
.form-field{ margin-bottom:34px; }
.form-field label{
  display:block; text-transform:uppercase; letter-spacing:0.1em; font-size:12.5px;
  color: var(--text-dim); margin-bottom:12px; font-weight:600;
}
.form-field input, .form-field select, .form-field textarea{
  width:100%;
  background:none;
  border:none;
  border-bottom: 1px solid var(--line);
  color: var(--text);
  font-family: var(--font-body);
  font-size:16px;
  padding: 10px 0;
  outline:none;
  transition: border-color .2s ease;
}
.form-field select option{ background: var(--panel); color: var(--text); }
.form-field input::placeholder, .form-field textarea::placeholder{ color: rgba(255,255,255,0.3); }
.form-field input:focus, .form-field select:focus, .form-field textarea:focus{ border-color: var(--c1); }
.form-field select{ appearance:none; -webkit-appearance:none; cursor:pointer; }
.form-field textarea{ resize:vertical; min-height:90px; }
.form-note{ color: var(--text-dim); font-size:13px; margin-top:-14px; margin-bottom:28px; }
.form-status{ font-size:14px; margin:-10px 0 18px; min-height:20px; }
.form-status.is-success{ color: var(--c1); }
.form-status.is-error{ color: var(--c4); }
.form-success{
  text-align:center;
  padding:70px 30px;
  border:1px solid var(--line);
  border-radius:var(--radius);
  background: var(--panel);
}
.form-success h3{ font-size:clamp(22px,2.6vw,28px); margin-bottom:10px; }
.form-success p{ color: var(--text-dim); }
@media (max-width:600px){ .contact-form .form-row{ grid-template-columns:1fr; } }

/* ---------- Results (Instagram post grid) ---------- */
.results-grid{
  display:grid;
  grid-template-columns: repeat(4, 1fr);
  gap:18px;
  margin-top:44px;
}
/* Desktop framing, matching the 4-video row used on the Social Media
   Management case-study page (.work-case-videos.work-case-videos--four):
   fixed 326px columns (Instagram's real hard-minimum embed width) instead
   of a `zoom`-shrunk 1fr grid, broken out to the full viewport width the
   same way — .results-grid sits inside .container same as that row does,
   so the same width:100vw + negative-offset margin math cancels out
   .container's own centering and lands this row's edges at the true
   viewport edges, buying back enough room for 4 real-width embeds without
   scaling anything down. Scoped to min-width so the existing ≤900px
   horizontal-scroll-strip block below is completely untouched. */
@media (min-width: 901px){
  .results-grid{
    grid-template-columns: repeat(4, 326px);
    justify-content: center;
    gap: 10px;
    width: 100vw;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    padding: 0 32px;
    box-sizing: border-box;
    /* Grid items default to align-items:stretch, so every card would get
       stretched to match whichever one is naturally tallest — since the
       3 Instagram cards no longer have a height cap, they'd otherwise
       force the (shorter, capped) TikTok card's outer box to stretch to
       match, leaving dead space below its clipped content. `start` gives
       each card just its own natural height instead, same fix already
       used on .work-case-videos for the same reason. */
    align-items: start;
  }
  .results-grid .ig-embed-frame{
    margin: 0 auto;
    min-width: 0;
    /* No height cap here — Instagram's card (header, video, action icons,
       likes, caption, "View more") needs its full natural height to avoid
       cutting content off mid-caption, which is what a blanket max-height
       was doing to these 3 cards. */
    /* Same resting-dim/hover-pop treatment as .work-case-videos on the
       Social Media Management page: quiet at 50% until you hover, then
       full brightness with a slight lift. */
    opacity: 0.5;
    transition: transform .25s ease, filter .25s ease, opacity .25s ease;
  }
  .results-grid .ig-embed-frame:hover{
    opacity: 1;
    transform: scale(1.045);
    filter: brightness(1.12);
    position: relative;
    z-index: 5;
  }
  /* TikTok previously had a 560px height cap here to trim its
     related-videos strip/"watch on TikTok" banner — but that cap landed
     mid-caption, hiding the caption text entirely. Dropping the cap so
     it renders at its full natural height too, same as the 3 Instagram
     cards, so nothing gets cut off. */
  .results-grid .ig-embed-frame--tiktok{
    max-height: none;
  }
}
/* Below 900px: same swipeable fan-stack treatment used for the multi-video
   rows on the Social Media Management / Event Content Creation pages
   (.work-case-videos + initWorkCaseFanCarousels there) — full-size active
   card, the rest peeking behind it, tap or swipe to move through them.
   This used to be a plain CSS horizontal scroll-snap strip with a fixed
   zoom + 560px height crop, which (a) shrank the card's layout box while
   the embedded iframe kept its own native width, leaving dead space beside
   it, and (b) cropped taller cards mid-caption. Rebuilt as its own
   independent copy (rff-* classes / initResultsFanCarousel() in main.js)
   rather than reusing .work-case-videos/.wcv-* directly — sharing the
   class would also pull in .work-case-videos' own unscoped desktop rule
   (grid-template-columns: repeat(3,1fr)), which would fight the
   .results-grid desktop 4-column layout above. Keeping this fully separate
   means the desktop rules directly above are completely unaffected, same
   as how the SMM and Event Content Creation carousels were deliberately
   kept independent from each other. Desktop (≥900px) keeps the original
   4-across grid, completely untouched. */
@media (max-width:900px){
  .results-grid{
    display: block;
    position: relative;
    height: min(88vh, 720px);
    min-height: 560px;
    max-width: 340px;
    margin: 32px auto 56px;
    padding: 0;
  }
  .results-grid .ig-embed-frame{
    position: absolute;
    inset: 0;
    width: 100%;
    margin: 0;
    transform-origin: bottom center;
    transition: transform .45s cubic-bezier(.22,.85,.32,1), opacity .45s ease;
  }
  .rff-tap-target{
    position: absolute;
    inset: 0;
    z-index: 5;
    background: transparent;
    border: 0;
    padding: 0;
    cursor: pointer;
    touch-action: pan-y;
  }
  .rff-swipe-edge{
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 15%;
    z-index: 6;
    background: transparent;
    border: 0;
    padding: 0;
    cursor: pointer;
    touch-action: pan-y;
  }
  .rff-hint{
    position: absolute;
    left: 50%;
    bottom: 20px;
    transform: translateX(-50%);
    z-index: 150;
    display: flex;
    align-items: center;
    gap: 6px;
    background: rgba(0,0,0,0.62);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    color: #fff;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 8px 14px;
    border-radius: 999px;
    pointer-events: none;
    opacity: 1;
    transition: opacity .4s ease;
    white-space: nowrap;
  }
  .rff-hint svg{ width: 13px; height: 13px; flex-shrink: 0; animation: rffHintNudge 1.4s ease-in-out infinite; }
  .rff-hint.rff-hint--hidden{ opacity: 0; }
  @keyframes rffHintNudge{
    0%, 100%{ transform: translateX(0); }
    50%{ transform: translateX(4px); }
  }
  .rff-dots{
    position: absolute;
    left: 50%;
    bottom: -30px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 6px;
    z-index: 61;
  }
  .rff-dots span{
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--line);
    transition: width .3s ease, background .3s ease;
    cursor: pointer;
  }
  .rff-dots span.active{
    width: 20px;
    border-radius: 4px;
    background: var(--gradient);
  }
  /* The peeking cards translate up to 95% of the active card's own width
     to each side (plus rotation, which widens their rendered bounding box
     further still) — well past the edges of the 340px carousel box, and
     with nothing here clipping that, it became real page content wider
     than the viewport. iOS Safari responds to that by silently zooming
     the whole page out to fit it, which is what "I can zoom out / swipe
     and the page shifts" was actually describing. Clipped on the section
     itself (not on .results-grid) so the horizontal peek gets cut off at
     the edges instead of pushing the page wider, while the dots — which
     sit just below .results-grid via a small negative offset, well
     inside this section's own bottom margin/padding — stay clear of this
     boundary and are never clipped. */
  #freshFeed{ overflow-x: hidden; }
}
@media (min-width: 901px){
  .rff-tap-target, .rff-dots, .rff-swipe-edge{ display: none; }
}
.ig-post{
  position:relative;
  aspect-ratio: 4/5;
  border-radius: var(--radius);
  overflow:hidden;
  border: 1px solid var(--line);
}
.ig-post .ig-handle{
  position:absolute; top:14px; left:14px;
  display:flex; align-items:center; gap:6px;
  background: rgba(0,0,0,0.55);
  backdrop-filter: blur(6px);
  padding: 6px 10px;
  border-radius:100px;
  font-size:12px;
  color: var(--text);
}
.ig-post .ig-play{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center; }
.ig-post .ig-stats{
  position:absolute; bottom:14px; left:14px; right:14px;
  display:flex; gap:10px; font-size:12px; color: var(--text);
}
.ig-post .ig-stats span{
  display:flex; align-items:center; gap:4px;
  background: rgba(0,0,0,0.55);
  backdrop-filter: blur(6px);
  padding: 5px 10px;
  border-radius:100px;
}
.results-cta{ text-align:center; margin-top:44px; }

/* ---------- Case studies ---------- */
.case-study{
  display:grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: 64px;
  align-items:center;
  padding: 64px 0;
  border-top: 1px solid var(--line);
}
.case-study:last-child{ border-bottom: 1px solid var(--line); }
.case-study.reverse .case-study-media{ order: 2; }
.case-study-media{ display:flex; justify-content:center; }
.case-study-reel{
  position:relative;
  width:100%;
  max-width: 300px;
  aspect-ratio: 9/16;
  border-radius: var(--radius);
  overflow:hidden;
  border: 1px solid var(--line);
}
.case-study-reel .ig-handle{
  position:absolute; top:14px; left:14px;
  display:flex; align-items:center; gap:6px;
  background: rgba(0,0,0,0.55);
  backdrop-filter: blur(6px);
  padding: 6px 10px;
  border-radius:100px;
  font-size:12px;
  color: var(--text);
}
.case-study-reel .ig-play{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center; }
.case-study-stats{ display:flex; gap:30px; flex-wrap:wrap; margin: 24px 0; }
.case-study-stat .num{
  font-family: var(--font-display);
  font-weight:800;
  font-size:26px;
  background: var(--gradient);
  -webkit-background-clip:text;
  background-clip:text;
  color:transparent;
  display:block;
}
.case-study-stat .label{ color: var(--text-dim); font-size:12px; text-transform:uppercase; letter-spacing:0.06em; margin-top:4px; }
.case-study-content p.desc{ color: var(--text-dim); max-width:480px; }
.case-study-content h2{ font-size: clamp(24px,3vw,34px); margin: 12px 0 16px; }
@media (max-width: 860px){
  .case-study{ grid-template-columns:1fr; gap:36px; padding:48px 0; }
  .case-study.reverse .case-study-media{ order:0; }
}

/* ---------- Real Instagram embed frame ---------- */
/* The gradient is a thin outline, not a filled block behind the card — a
   real `border` with a two-layer background (solid fill clipped to the
   padding-box, gradient clipped to the border-box) so the color only ever
   shows as a ring exactly 3px from the card's own edge. The frame is
   simply `width:100%` of whatever column/slot it's in (plain border-box,
   same as the rest of the site) — no shrink-to-fit sizing tricks, so it
   always matches its container exactly and can never overflow into
   whatever sits next to it. If the embed itself ever renders a little
   narrower than that, the gap shows the page's own background (the
   padding-box layer below), not the gradient — so it never looks like a
   colorful block behind a smaller window either way. */
.ig-embed-frame{
  max-width: 360px;
  width: 100%;
  margin: 0 auto;
  border: 3px solid transparent;
  border-radius: calc(var(--radius) + 3px);
  background: linear-gradient(var(--bg), var(--bg)) padding-box, var(--gradient) border-box;
  box-shadow: 0 20px 50px rgba(91,95,240,0.25);
  overflow: hidden;
}
/* Clip whatever Instagram/TikTok renders inside (blockquote or iframe) to
   the frame's own rounded corners, so the embed's square corners never
   poke out past the gradient border. This only clips the outer corners —
   it does NOT force a width, because forcing an iframe's outer box to a
   different width than the platform's own script sized it for squeezes
   the fixed-width content inside it (this is what was cutting off text on
   the sides of the TikTok embed). Width is instead controlled up front by
   the blockquote's own inline style, before the platform's script runs. */
.ig-embed-frame > *{
  display:block;
  margin: 0 auto;
  border-radius: var(--radius);
  overflow: hidden;
}
/* Instagram's own embed.js sets inline styles directly on the blockquote
   once it processes it — background:#FFF, border:0, a light box-shadow,
   and margin:1px — baked in as a light-mode "card" no matter what page
   it's dropped into. Inline styles beat a plain class rule, so that white
   box-shadow/margin was showing through as a mismatched pale border
   sitting inside our dark gradient frame. !important is the only way to
   override an inline style, so it's used here specifically to strip
   Instagram's own chrome and leave just our gradient border visible. */
.ig-embed-frame > *{
  margin: 0 auto !important;
  border: 0 !important;
  box-shadow: none !important;
  background: transparent !important;
}
.ig-embed-placeholder{
  aspect-ratio: 9/16;
  background: var(--panel);
  border-radius: var(--radius);
  overflow: hidden;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:16px;
  color: var(--text-dim);
  font-size:13px;
  text-align:center;
  padding: 24px;
}
.ig-embed-placeholder svg{ opacity:0.6; }

/* ---------- Instagram video wall (masonry) ---------- */
.ig-wall{
  column-count: 3;
  column-gap: 22px;
  margin-top: 44px;
}
.ig-wall .ig-embed-frame{
  break-inside: avoid;
  margin: 0 auto 22px;
  max-width: 100%;
}
@media (max-width: 900px){ .ig-wall{ column-count: 2; } }
@media (max-width: 600px){ .ig-wall{ column-count: 1; } }

/* ---------- Work case (per-brand / per-event case study block) ---------- */
.work-case{
  padding: 64px 0;
  border-top: 1px solid var(--line);
}
.work-case:last-child{ border-bottom: 1px solid var(--line); }
.work-case-header{ max-width: 640px; margin-bottom: 36px; }
.work-case-header h2{ font-size: clamp(26px, 3.2vw, 38px); margin: 12px 0 14px; }
.work-case-header p{ color: var(--text-dim); }

.work-case-label{
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-bottom: 18px;
}

.work-case-videos{
  display:grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
  margin-bottom: 40px;
  /* Grid items default to align-items:stretch, so every card in a row was
     being stretched to match whichever video in that row hydrated tallest
     — and since .ig-embed-frame clips overflow, that extra height showed
     as dead space inside the border at the bottom of the shorter cards.
     align-items:start makes each card just its own natural height. */
  align-items: start;
}
/* A few brands need all 4 top videos shown side by side instead of the
   usual 3. Instagram's embed has a hard 326px minimum width, and 4 of
   those can never fit inside the page's normal 1320px content column no
   matter how tight the gap gets — the container itself has to get out
   of the way. This breaks the row out to the full browser width (with
   the same 32px edge margin the rest of the page uses) instead of
   staying boxed into .container's capped width, which is what actually
   buys back enough room. The math: .container is centered with
   `margin:0 auto`, so its own left edge always sits at exactly
   (viewport - containerWidth)/2 from the true viewport edge — setting
   this row to width:100vw and margin-left/right: calc(50% - 50vw)
   cancels that offset out exactly, regardless of whether .container
   has hit its 1320px cap or not, landing this row's edges at the true
   viewport edges every time. Still scoped to a min-width query so it
   can't out-specificity the 860px/560px mobile collapse rules below —
   those still need to win on narrow screens, same as the 3-video rows.
   On genuinely narrow "desktop" windows there's a physical floor this
   can't get past either (4 × 326px alone is wider than a lot of laptop
   windows) — full-bleed just makes sure the row is using every bit of
   width that's actually available before any cropping happens. */
@media (min-width: 861px){
  .work-case-videos.work-case-videos--four{
    /* repeat(4, 1fr) was an earlier bug here — it divided the row's
       width evenly into 4 wide tracks, but each .ig-embed-frame is
       capped at max-width:360px and centered inside its track via
       margin:0 auto, so most of that width just became empty margin
       inside each track instead of visible gap. Sizing tracks to the
       frame's own real width fixed that.
       Centered, full-bleed row (matches how this looked before): 326px
       is Instagram's hard minimum embed width, so it's used as a fixed
       card size here rather than a range — that's the smallest (and
       therefore closest to the header text's own width) this row can
       get without Instagram's own min-width cropping the embeds. At
       326×4 + 3×10px gaps = 1334px, this row is still a bit wider than
       the 1256px the header text sits in, so centering it will still
       overhang the text by a small, fixed amount either side — that
       gap is Instagram's floor, not something more sizing can close. */
    grid-template-columns: repeat(4, 326px);
    justify-content: center;
    gap: 10px;
    width: 100vw;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    padding: 0 32px;
    box-sizing: border-box;
  }
}
/* The frame was set to width:100% of its (wide) grid column, so the
   gradient border was drawn around the full column width — but Instagram
   renders its embed at a smaller fixed width and centers it inside via
   margin:0 auto, leaving a gap of bare background on both sides.
   width:fit-content was tried to shrink the border to match, but that
   broke the embeds entirely: before Instagram's script hydrates it, the
   blockquote is empty with no intrinsic width, so a fit-content frame
   collapses to ~0 and Instagram's script has nothing real to size
   against. The base .ig-embed-frame rule already caps at max-width:360px
   — a real, fixed number Instagram can size to reliably (this is what
   every other embed on the site already uses without a gap problem) —
   so simply not stretching it past that here, instead of computing width
   from content, fixes the gap without breaking hydration. */
.work-case-videos .ig-embed-frame{ margin: 0 auto; min-width: 0; }
/* Desktop-only hover lift: scale up and brighten the card under the
   cursor, similar in spirit to how the mobile fan carousel visually
   sets the active card apart from the peeking ones behind it. Scoped to
   min-width so touch devices (which fake ":hover" as "last thing you
   tapped") never get stuck with a permanently scaled-up card. */
@media (min-width: 861px){
  .work-case-videos .ig-embed-frame{
    opacity: 0.5;
    transition: transform .25s ease, filter .25s ease, opacity .25s ease;
  }
  .work-case-videos .ig-embed-frame:hover{
    opacity: 1;
    transform: scale(1.045);
    filter: brightness(1.12);
    position: relative;
    z-index: 5;
  }
}
@media (max-width: 860px){ .work-case-videos{ grid-template-columns: repeat(2,1fr); } }
@media (max-width: 560px){ .work-case-videos{ grid-template-columns: 1fr; } }

/* ---------- Work case videos: mobile fan carousel ----------
   On mobile the grid above just stacks every video vertically — a lot
   of scrolling to see 3-4 videos one at a time. Below 860px, JS
   (initWorkCaseFanCarousels in main.js) switches this row to a
   swipeable fanned card stack instead: the active card sits centered
   and full-size, the rest peek out behind it at an angle; tapping a
   peeking card (or a dot) brings it to the front. This block is
   declared after the 860px/560px grid-collapse rules above so it wins
   the cascade at those widths (same specificity, later wins) —
   display:block here overrides their grid-template-columns, which
   becomes a no-op once display isn't grid anyway.
   Height: fixed rather than measured from content (to avoid the
   fragile "measure-after-hydration" approach that broke things
   earlier in this project), but tall enough that a typical embed's
   full card — header, video, likes, caption, comment bar — actually
   fits without .ig-embed-frame's overflow:hidden cropping it off. An
   unusually tall post can still get trimmed at the very bottom, same
   as everywhere else on the site that uses this crop-if-taller
   pattern, but this is sized for the common case to render in full. */
@media (max-width: 860px){
  .work-case-videos{
    display: block;
    position: relative;
    height: min(88vh, 720px);
    min-height: 560px;
    max-width: 340px;
    margin: 0 auto 56px;
    padding: 0;
  }
  .work-case-videos .ig-embed-frame{
    position: absolute;
    inset: 0;
    width: 100%;
    margin: 0;
    transform-origin: bottom center;
    transition: transform .45s cubic-bezier(.22,.85,.32,1), opacity .45s ease;
  }
  /* Every card gets this full-coverage overlay, including the active
     one — a cross-origin iframe never hands a touch/click aimed at it
     to our JS, so catching a swipe that starts anywhere on the card
     (not just a thin edge strip) requires something real sitting on
     top of the whole thing. On a peeking card, a tap brings it to
     front. On the active card, a tap "unlocks" it (JS hides this
     overlay), handing Instagram's own play button, likes, "View
     profile", and comments direct control until you swipe or tap a
     dot to move on, which re-covers the new active card automatically. */
  .wcv-tap-target{
    position: absolute;
    inset: 0;
    z-index: 5;
    background: transparent;
    border: 0;
    padding: 0;
    cursor: pointer;
    touch-action: pan-y;
  }
  /* Once the active card is "unlocked" (tapped), .wcv-tap-target above is
     hidden so Instagram's own controls get the touch directly — but that
     also means a swipe started anywhere over the unlocked card no longer
     reaches our JS at all (cross-origin iframe swallows it). This strip
     sits across the top of every card, at a higher z-index than the
     iframe, and is NEVER hidden — even after unlock — so there's always
     a place to grab and swipe from without needing to re-lock the card
     first. It's deliberately positioned over the embed's header row
     (profile pic/username), not the video body, so it doesn't cover the
     play button or action icons underneath. */
  .wcv-swipe-edge{
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 15%;
    z-index: 6;
    background: transparent;
    border: 0;
    padding: 0;
    cursor: pointer;
    touch-action: pan-y;
  }
  /* One-time "swipe →" hint over the active card so first-time visitors
     know this stack is interactive, not just a static screenshot. Sits
     above the tap-target (z-index) but has pointer-events:none so it
     never blocks the swipe/tap it's advertising. Fades out permanently
     for that row the first time the person actually navigates it. */
  .wcv-hint{
    position: absolute;
    left: 50%;
    bottom: 20px;
    transform: translateX(-50%);
    /* Cards themselves reach z-index 100 at their most active/top-most
       (100 - abs(offset)*10, capped at abs=0), so the hint needs to sit
       above that ceiling or it silently renders underneath the active
       card and never appears — which is exactly what happened with the
       old z-index:56 here. */
    z-index: 150;
    display: flex;
    align-items: center;
    gap: 6px;
    background: rgba(0,0,0,0.62);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    color: #fff;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 8px 14px;
    border-radius: 999px;
    pointer-events: none;
    opacity: 1;
    transition: opacity .4s ease;
    white-space: nowrap;
  }
  .wcv-hint svg{ width: 13px; height: 13px; flex-shrink: 0; animation: wcvHintNudge 1.4s ease-in-out infinite; }
  .wcv-hint.wcv-hint--hidden{ opacity: 0; }
  @keyframes wcvHintNudge{
    0%, 100%{ transform: translateX(0); }
    50%{ transform: translateX(4px); }
  }
  .wcv-dots{
    position: absolute;
    left: 50%;
    bottom: -30px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 6px;
    z-index: 61;
  }
  .wcv-dots span{
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--line);
    transition: width .3s ease, background .3s ease;
    cursor: pointer;
  }
  .wcv-dots span.active{
    width: 20px;
    border-radius: 4px;
    background: var(--gradient);
  }
}
@media (min-width: 861px){
  .wcv-tap-target, .wcv-dots, .wcv-swipe-edge{ display: none; }
}

.work-case-stats{
  display:flex;
  gap: 44px;
  flex-wrap:wrap;
}
.work-case-stat .num{
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 30px;
  background: var(--gradient);
  -webkit-background-clip:text;
  background-clip:text;
  color:transparent;
  display:block;
}
.work-case-stat .label{
  color: var(--text-dim);
  font-size: 12.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-top: 4px;
}

/* ---------- Work case body: single video + supporting text side by side ---------- */
.work-case-body{
  display:grid;
  grid-template-columns: 400px 1fr;
  gap: 48px;
  align-items:start;
}
/* Grid/flex items default to min-width:auto, which means their own content's
   intrinsic minimum size (here, Instagram's hard 326px iframe floor) can
   force the whole grid track wider than the viewport, dragging everything
   else in that row along with it — this is what was pushing the deliverables
   text off the right edge on narrow phones, not just the video itself.
   min-width:0 turns that off so the column actually respects the viewport,
   and only the embed (if it still can't fit) overflows locally. */
.work-case-media{ min-width: 0; }
.work-case-info{ min-width: 0; }
.work-case-media .ig-embed-frame{ max-width: 100%; margin:0; }
/* Real uploaded video files (native <video>, not an Instagram embed) —
   sized by their own natural aspect ratio rather than the fixed 9/16
   .ig-embed-placeholder uses, since these are a mix of portrait and
   landscape footage. */
.work-case-media video{
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius);
  background: #000;
}
/* Below the point where Instagram's own 326px floor stops comfortably
   fitting a narrow phone's available width, shrink the embed's actual
   layout box (not just a visual transform) so it never needs more physical
   width than the screen has — same technique already used for the homepage
   4-video row. */
@media (max-width: 400px){
  .work-case-media .ig-embed-frame,
  .ig-carousel-frame .ig-embed-frame{ zoom: 0.85; }
}
.work-case-info .work-case-label:not(:first-child){ margin-top: 30px; }
.work-case-list{
  list-style:none;
  margin: 0 0 6px;
  padding: 0;
  display:flex;
  flex-direction:column;
  gap: 14px;
  max-width: 560px;
}
.work-case-list li{
  color: var(--text-dim);
  padding-left: 22px;
  position:relative;
  font-size: 15px;
}
.work-case-list li::before{
  content: "+";
  position:absolute;
  left:0;
  top:0;
  color: var(--c1);
  font-weight:700;
}
.work-case-note{ color: var(--text-dim); font-size: 13.5px; margin-top: 14px; max-width:560px; }
.work-case-placeholder .work-case-header{ margin-bottom:0; }
.work-case-placeholder .work-case-header p{ font-style: italic; }
@media (max-width: 860px){
  .work-case-body{ grid-template-columns: 1fr; }
}

/* ---------- Carousel (for events with more than one video) ----
   All frames sit in a normal horizontal flex row inside a viewport that
   just clips overflow-x — nothing is absolutely positioned and nothing is
   ever display:none, so the row's height is whatever the browser's normal
   layout naturally computes it to be (the tallest frame currently in the
   row), never measured or guessed in JS. Advancing the carousel just
   slides that row sideways with a transform, which is what produces the
   next card peeking in at the edge, faded, until you bring it into focus.
   The arrows float directly over the video (not in dedicated side padding)
   because Instagram's embed has a hard ~326px minimum width it will not
   shrink below — reserving 40px of padding on each side for the arrows
   left only ~250px for the video, which is why it was always overflowing
   and getting clipped no matter what the outline looked like. */
.ig-carousel{ position:relative; }
.ig-carousel-viewport{ overflow:hidden; position:relative; }
.ig-carousel-viewport::before,
.ig-carousel-viewport::after{
  content:'';
  position:absolute;
  top:0;
  bottom:0;
  width:56px;
  z-index:3;
  pointer-events:none;
}
.ig-carousel-viewport::before{ left:0; background: linear-gradient(90deg, var(--bg), transparent); }
.ig-carousel-viewport::after{ right:0; background: linear-gradient(270deg, var(--bg), transparent); }
/* No card peeks on the left at the very first slide, or on the right at
   the very last one — fading over nothing just clips the active card's
   own edge for no reason, so hide the fade on whichever side has nothing
   to peek at. */
.ig-carousel.at-start .ig-carousel-viewport::before{ display:none; }
.ig-carousel.at-end .ig-carousel-viewport::after{ display:none; }
.ig-carousel-track{
  display:flex;
  transition: transform .45s cubic-bezier(0.4,0,0.2,1);
}
.ig-carousel-frame{
  flex: 0 0 92%;
  min-width: 0;
  padding: 0 4px;
  box-sizing: border-box;
  opacity: 0.35;
  transition: opacity .45s ease;
}
.ig-carousel-frame.active{ opacity: 1; }
/* The default .ig-embed-frame shrink-wraps to its own content width, which
   is right everywhere else — but inside the carousel that leaves dead
   space next to the card within its slot, and that mismatch is exactly
   what made the "peek" render as a near-full second card instead of a
   small sliver. Filling the slot here keeps the peek math (and the
   gradient outline around it) matched to what's actually visible. */
.ig-carousel-frame .ig-embed-frame{
  width: 100%;
  max-width: 100%;
  margin: 0;
}
.ig-carousel-arrow{
  position:absolute;
  top:50%;
  transform: translateY(-50%);
  width:34px;
  height:34px;
  border-radius:50%;
  background: rgba(20,20,20,0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid rgba(255,255,255,0.25);
  color: #fff;
  display:flex;
  align-items:center;
  justify-content:center;
  cursor:pointer;
  font-size:16px;
  line-height:1;
  z-index:4;
  transition: border-color .2s ease, background .2s ease;
}
/* Instagram's embed is a cross-origin iframe, so touches that land on the
   video itself never reach our own JS — the browser hands them straight to
   Instagram's own document. This transparent layer sits above the iframe so
   we can catch the swipe, but it only exists on touch devices (a mouse
   click still goes straight through to the embed normally on desktop,
   nothing changes there). Trade-off: on a touch phone, tapping the video
   directly won't play/pause it anymore since this layer is catching the
   touch — the arrow buttons (which sit above this layer) still work for
   that. */
.ig-carousel-swipe-catcher{ display:none; }
@media (hover: none) and (pointer: coarse){
  .ig-carousel-swipe-catcher{
    display:block;
    position:absolute;
    inset:0;
    z-index:2;
    touch-action: pan-y;
  }
}
.ig-carousel-arrow:hover{ border-color: var(--c1); background: rgba(20,20,20,0.75); }
.ig-carousel-arrow.prev{ left:12px; }
.ig-carousel-arrow.next{ right:12px; }
.ig-carousel-dots{
  display:flex;
  justify-content:center;
  gap:8px;
  margin-top:16px;
}
.ig-carousel-dots .dot{
  width:7px;
  height:7px;
  border-radius:50%;
  background: var(--line);
  cursor:pointer;
  border:none;
  padding:0;
}
.ig-carousel-dots .dot.active{ background: var(--c1); }

/* ---------- Event carousel: mobile fan stack (.ig-carousel--fan) ----------
   A separate, page-scoped version of the peek-behind fan/stack effect used
   on the Social Media Management page's video rows — deliberately its own
   copy (own classes, own JS function initEventFanCarousels()) rather than
   reusing .work-case-videos/.wcv-*, so nothing here can ever affect that
   page and nothing there can affect this one. Desktop is completely
   untouched: only at ≤860px does this override the normal .ig-carousel
   flex-slide behavior with the fan-stack positioning below; the old
   arrows/dots/edge-fade/swipe-catcher just get hidden at that width so
   the two interaction systems never run at the same time. */
@media (max-width: 860px){
  .ig-carousel--fan .ig-carousel-arrow,
  .ig-carousel--fan .ig-carousel-dots,
  .ig-carousel--fan .ig-carousel-viewport::before,
  .ig-carousel--fan .ig-carousel-viewport::after{
    display: none;
  }
  .ig-carousel--fan .ig-carousel-swipe-catcher{
    display: none !important;
  }
  .ig-carousel--fan .ig-carousel-viewport{
    overflow: visible;
  }
  .ig-carousel--fan .ig-carousel-track{
    display: block;
    position: relative;
    height: min(72vh, 560px);
    min-height: 460px;
    max-width: 320px;
    margin: 0 auto;
  }
  .ig-carousel--fan .ig-carousel-frame{
    position: absolute;
    inset: 0;
    width: 100%;
    flex: none;
    padding: 0;
    margin: 0;
    /* This wrapper (not .ig-embed-frame) is the thing sized to the track's
       fixed height — but .ig-embed-frame inside it isn't height-limited,
       so a captioned Instagram card (header + video + likes + caption,
       often 650-750px tall) was rendering at its own full natural height
       and spilling straight out the bottom of this box, bleeding into the
       deliverables text below it. overflow:hidden here crops it to this
       frame's actual box, same crop-if-taller approach used everywhere
       else on the site for exactly this situation. */
    overflow: hidden;
    border-radius: calc(var(--radius) + 3px);
    transform-origin: bottom center;
    transition: transform .45s cubic-bezier(.22,.85,.32,1), opacity .45s ease;
  }
  .ig-carousel--fan .ig-carousel-frame .ig-embed-frame{
    width: 100%;
    max-width: 100%;
    margin: 0;
  }
  /* Every card gets this full-coverage overlay so a swipe starting
     anywhere on the card (including the active one) is caught before a
     cross-origin Instagram iframe can swallow the touch — same reasoning
     as .wcv-tap-target on the Social Media Management page. A plain tap
     on the active card "unlocks" it (hides this overlay) so Instagram's
     own play button/likes/comments get direct control until you swipe or
     tap a dot to move on. */
  .efc-tap-target{
    position: absolute;
    inset: 0;
    z-index: 5;
    background: transparent;
    border: 0;
    padding: 0;
    cursor: pointer;
    touch-action: pan-y;
  }
  /* Always-on top strip (never hidden, even once a card is unlocked) so
     there's always somewhere to grab and swipe from without needing to
     re-lock the card first — same fix as .wcv-swipe-edge. */
  .efc-swipe-edge{
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 15%;
    z-index: 6;
    background: transparent;
    border: 0;
    padding: 0;
    cursor: pointer;
    touch-action: pan-y;
  }
  .efc-hint{
    position: absolute;
    left: 50%;
    bottom: 20px;
    transform: translateX(-50%);
    z-index: 150;
    display: flex;
    align-items: center;
    gap: 6px;
    background: rgba(0,0,0,0.62);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    color: #fff;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 8px 14px;
    border-radius: 999px;
    pointer-events: none;
    opacity: 1;
    transition: opacity .4s ease;
    white-space: nowrap;
  }
  .efc-hint svg{ width: 13px; height: 13px; flex-shrink: 0; animation: efcHintNudge 1.4s ease-in-out infinite; }
  .efc-hint.efc-hint--hidden{ opacity: 0; }
  @keyframes efcHintNudge{
    0%, 100%{ transform: translateX(0); }
    50%{ transform: translateX(4px); }
  }
  .efc-dots{
    position: absolute;
    left: 50%;
    bottom: -30px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 6px;
    z-index: 61;
  }
  .efc-dots span{
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--line);
    transition: width .3s ease, background .3s ease;
    cursor: pointer;
  }
  .efc-dots span.active{
    width: 20px;
    border-radius: 4px;
    background: var(--gradient);
  }
}
@media (min-width: 861px){
  .efc-tap-target, .efc-dots, .efc-swipe-edge{ display: none; }
}
