/* ============================================================================
   ForkFox image effects library — shared by ALL Dish + Carte articles.
   - .fx-img.fx-<name>  : 10 motion effects for static images. Each effect is
     a static pre-state + a visible scroll-triggered ENTRANCE (2.2–3.4s,
     fired when /js/effects.js adds .fx-go on viewport entry) that hands off
     to a slow ambient loop starting exactly where the entrance ends.
     Pre-states set animation:none so per-article load-time loops (kenBurns,
     closingBurns, infoIntro) can't run before the trigger — the two-class
     selectors deliberately out-rank those per-article inline rules
     (.hero-photo / .mid-photo / .closing-photo / .hero-img / .info-image img)
     without touching their keyframes (kenBurns etc. stay referenced elsewhere).
   - .img-overlay.fx-ov : animated editorial text overlays (eyebrow + line),
     10 entrance styles triggered by /js/effects.js adding .fx-in on scroll.
   All motion is transform/filter/opacity only (GPU-cheap).
   ========================================================================== */

/* ── 1. IMAGE MOTION ──────────────────────────────────────────────────── */
.fx-img{will-change:transform,filter}

/* 1 · zoom-in — slow push toward the subject */
.fx-img.fx-zoom-in{transform:scale(1);animation:none}
.fx-img.fx-zoom-in.fx-go{animation:fxZoomInGo 3s cubic-bezier(.22,1,.36,1) forwards,fxZoomInAmb 24s ease-in-out 3s infinite alternate}
@keyframes fxZoomInGo{0%{transform:scale(1)}100%{transform:scale(1.09)}}
@keyframes fxZoomInAmb{0%{transform:scale(1.09)}100%{transform:scale(1.18)}}

/* 2 · zoom-out — arrives tight, settles back */
.fx-img.fx-zoom-out{transform:scale(1.24);animation:none}
.fx-img.fx-zoom-out.fx-go{animation:fxZoomOutGo 3.2s cubic-bezier(.22,1,.36,1) forwards,fxZoomOutAmb 26s ease-in-out 3.2s infinite alternate}
@keyframes fxZoomOutGo{0%{transform:scale(1.24)}100%{transform:scale(1.1)}}
@keyframes fxZoomOutAmb{0%{transform:scale(1.1)}100%{transform:scale(1.04)}}

/* 3 · pan-lateral — glides in from the left, keeps drifting */
.fx-img.fx-pan-lateral{transform:scale(1.14) translateX(-4%);animation:none}
.fx-img.fx-pan-lateral.fx-go{animation:fxPanLatGo 3s cubic-bezier(.22,1,.36,1) forwards,fxPanLatAmb 28s ease-in-out 3s infinite alternate}
@keyframes fxPanLatGo{0%{transform:scale(1.14) translateX(-4%)}100%{transform:scale(1.14) translateX(0)}}
@keyframes fxPanLatAmb{0%{transform:scale(1.14) translateX(0)}100%{transform:scale(1.14) translateX(2.2%)}}

/* 4 · pan-diagonal — corner glide */
.fx-img.fx-pan-diagonal{transform:scale(1.14) translate(-3.5%,-2.5%);animation:none}
.fx-img.fx-pan-diagonal.fx-go{animation:fxPanDiagGo 3.2s cubic-bezier(.22,1,.36,1) forwards,fxPanDiagAmb 30s ease-in-out 3.2s infinite alternate}
@keyframes fxPanDiagGo{0%{transform:scale(1.14) translate(-3.5%,-2.5%)}100%{transform:scale(1.14) translate(0,0)}}
@keyframes fxPanDiagAmb{0%{transform:scale(1.14) translate(0,0)}100%{transform:scale(1.14) translate(2%,1.4%)}}

/* 5 · breathe — wakes out of shadow, then breathes */
.fx-img.fx-breathe{transform:scale(1.03);filter:brightness(.72) saturate(.9);animation:none}
.fx-img.fx-breathe.fx-go{animation:fxBreatheGo 2.6s ease-out forwards,fxBreatheAmb 22s ease-in-out 2.6s infinite alternate}
@keyframes fxBreatheGo{0%{transform:scale(1.03);filter:brightness(.72) saturate(.9)}100%{transform:scale(1.09);filter:brightness(1) saturate(1)}}
@keyframes fxBreatheAmb{0%{transform:scale(1.09);filter:brightness(1)}100%{transform:scale(1.15);filter:brightness(1.06)}}

/* 6 · focus-settle — blur racks into focus (scroll-triggered) */
.fx-img.fx-focus-settle{transform:scale(1.16);filter:blur(6px) brightness(.7);animation:none}
.fx-img.fx-focus-settle.fx-go{animation:fxFocusIn 2.2s ease-out forwards,fxSettleDrift 26s ease-in-out 2.2s infinite alternate}
@keyframes fxFocusIn{0%{transform:scale(1.16);filter:blur(6px) brightness(.7)}100%{transform:scale(1.08);filter:blur(0) brightness(1)}}
@keyframes fxSettleDrift{0%{transform:scale(1.08)}100%{transform:scale(1.14) translate(1%,-.8%)}}

/* 7 · warm-shift — cold print warms up + pushes in */
.fx-img.fx-warm-shift{transform:scale(1.06);filter:saturate(.55) brightness(.85);animation:none}
.fx-img.fx-warm-shift.fx-go{animation:fxWarmGo 3.4s ease-out forwards,fxWarmAmb 26s ease-in-out 3.4s infinite alternate}
@keyframes fxWarmGo{0%{transform:scale(1.06);filter:saturate(.55) brightness(.85)}100%{transform:scale(1.12);filter:saturate(1.05) brightness(1.02)}}
@keyframes fxWarmAmb{0%{transform:scale(1.12);filter:saturate(1.05) brightness(1.02)}100%{transform:scale(1.16);filter:saturate(1.12) brightness(1.04)}}

/* 8 · tilt-drift — rights itself, then sways */
.fx-img.fx-tilt-drift{transform:scale(1.16) rotate(-1.4deg);animation:none}
.fx-img.fx-tilt-drift.fx-go{animation:fxTiltGo 3s cubic-bezier(.22,1,.36,1) forwards,fxTiltAmb 28s ease-in-out 3s infinite alternate}
@keyframes fxTiltGo{0%{transform:scale(1.16) rotate(-1.4deg)}100%{transform:scale(1.16) rotate(0)}}
@keyframes fxTiltAmb{0%{transform:scale(1.16) rotate(0)}100%{transform:scale(1.16) rotate(.9deg)}}

/* 9 · rise — lifts into place, keeps rising slowly */
.fx-img.fx-rise{transform:scale(1.14) translateY(4.5%);animation:none}
.fx-img.fx-rise.fx-go{animation:fxRiseGo 3s cubic-bezier(.22,1,.36,1) forwards,fxRiseAmb 26s ease-in-out 3s infinite alternate}
@keyframes fxRiseGo{0%{transform:scale(1.14) translateY(4.5%)}100%{transform:scale(1.14) translateY(0)}}
@keyframes fxRiseAmb{0%{transform:scale(1.14) translateY(0)}100%{transform:scale(1.14) translateY(-2.4%)}}

/* 10 · origin-drift — off-center push-in */
.fx-img.fx-origin-drift{transform-origin:30% 38%;transform:scale(1.02);animation:none}
.fx-img.fx-origin-drift.fx-go{animation:fxOriginGo 3.2s cubic-bezier(.22,1,.36,1) forwards,fxOriginAmb 28s ease-in-out 3.2s infinite alternate}
@keyframes fxOriginGo{0%{transform:scale(1.02)}100%{transform:scale(1.12)}}
@keyframes fxOriginAmb{0%{transform:scale(1.12)}100%{transform:scale(1.2)}}

/* ── 2. TEXT OVERLAYS ─────────────────────────────────────────────────── */
.img-overlay{position:absolute;bottom:24px;left:32px;right:32px;z-index:6;pointer-events:none;
  --ov-accent:#FA2A52;--ov-accent2:#F97316}
.img-overlay.fx-carte{--ov-accent:#F59E0B;--ov-accent2:#F59E0B}
.img-overlay .ov-eyebrow{font-family:monospace;font-size:11px;color:var(--ov-accent2);
  text-transform:uppercase;letter-spacing:.2em;font-weight:700;margin-bottom:8px;
  text-shadow:0 2px 12px rgba(0,0,0,.8)}
.img-overlay .ov-line{font-family:'Geologica',sans-serif;font-weight:800;
  font-size:clamp(16px,2.4vw,22px);line-height:1.3;color:#fff;letter-spacing:-.01em;
  text-shadow:0 2px 16px rgba(0,0,0,.75)}

/* default hidden state — every style reveals on .fx-in */
.fx-ov .ov-eyebrow,.fx-ov .ov-line{opacity:0}

/* 1 · typewriter — line wipes on in steps, caret blinks then fades */
.fx-ov.fx-ov-typewriter .ov-line{display:inline-block;clip-path:inset(0 100% 0 0)}
.fx-ov.fx-ov-typewriter.fx-in .ov-eyebrow{opacity:1;transition:opacity .5s ease .1s}
.fx-ov.fx-ov-typewriter.fx-in .ov-line{opacity:1;animation:ovType 1.6s steps(26,end) .3s forwards}
.fx-ov.fx-ov-typewriter.fx-in .ov-line::after{content:'';display:inline-block;width:3px;height:.95em;
  margin-left:4px;vertical-align:-.12em;background:var(--ov-accent);animation:ovCaret .7s step-end 5,ovCaretOut .3s ease 3.5s forwards}
@keyframes ovType{0%{clip-path:inset(0 100% 0 0)}100%{clip-path:inset(0 0 0 0)}}
@keyframes ovCaret{0%,100%{opacity:1}50%{opacity:0}}
@keyframes ovCaretOut{to{opacity:0}}

/* 2 · words — each word floats up with its own delay (spans injected at patch) */
.fx-ov.fx-ov-words .ov-line{opacity:1}
.fx-ov.fx-ov-words .ov-line .w{display:inline-block;opacity:0;transform:translateY(14px);
  transition:opacity .55s ease,transform .55s cubic-bezier(.22,1,.36,1)}
.fx-ov.fx-ov-words.fx-in .ov-eyebrow{opacity:1;transition:opacity .5s ease}
.fx-ov.fx-ov-words.fx-in .ov-line .w{opacity:1;transform:translateY(0);
  transition-delay:calc(.15s + var(--i,0)*.09s),calc(.15s + var(--i,0)*.09s)}

/* 3 · wipe — accent bar sweeps, text revealed behind it */
.fx-ov.fx-ov-wipe{position:absolute}
.fx-ov.fx-ov-wipe .ov-line{clip-path:inset(0 100% 0 0)}
.fx-ov.fx-ov-wipe::before{content:'';position:absolute;left:0;bottom:0;height:3px;width:100%;
  background:linear-gradient(90deg,var(--ov-accent),transparent);transform:scaleX(0);transform-origin:left}
.fx-ov.fx-ov-wipe.fx-in::before{animation:ovBar 1s cubic-bezier(.22,1,.36,1) forwards}
.fx-ov.fx-ov-wipe.fx-in .ov-eyebrow{opacity:1;transition:opacity .5s ease .15s}
.fx-ov.fx-ov-wipe.fx-in .ov-line{opacity:1;animation:ovType 1.1s cubic-bezier(.22,1,.36,1) .25s forwards}
@keyframes ovBar{0%{transform:scaleX(0)}100%{transform:scaleX(1)}}

/* 4 · blur-settle — letters drift together out of blur */
.fx-ov.fx-ov-blur-settle .ov-line{letter-spacing:.3em;filter:blur(8px)}
.fx-ov.fx-ov-blur-settle.fx-in .ov-eyebrow{opacity:1;transition:opacity .6s ease .5s}
.fx-ov.fx-ov-blur-settle.fx-in .ov-line{opacity:1;letter-spacing:-.01em;filter:blur(0);
  transition:opacity .9s ease,letter-spacing 1.2s cubic-bezier(.22,1,.36,1),filter 1.1s ease}

/* 5 · split — eyebrow slides from left, line from right */
.fx-ov.fx-ov-split .ov-eyebrow{transform:translateX(-26px)}
.fx-ov.fx-ov-split .ov-line{transform:translateX(26px)}
.fx-ov.fx-ov-split.fx-in .ov-eyebrow{opacity:1;transform:translateX(0);
  transition:opacity .6s ease,transform .7s cubic-bezier(.22,1,.36,1)}
.fx-ov.fx-ov-split.fx-in .ov-line{opacity:1;transform:translateX(0);
  transition:opacity .6s ease .2s,transform .7s cubic-bezier(.22,1,.36,1) .2s}

/* 6 · band — line rises out of an overflow mask behind an accent band */
.fx-ov.fx-ov-band .ov-mask{display:block;overflow:hidden}
.fx-ov.fx-ov-band .ov-line{transform:translateY(110%)}
.fx-ov.fx-ov-band::after{content:'';position:absolute;left:0;bottom:-8px;width:46px;height:3px;
  background:var(--ov-accent);transform:scaleX(0);transform-origin:left}
.fx-ov.fx-ov-band.fx-in .ov-eyebrow{opacity:1;transition:opacity .5s ease}
.fx-ov.fx-ov-band.fx-in .ov-line{opacity:1;transform:translateY(0);
  transition:opacity .4s ease .15s,transform .85s cubic-bezier(.22,1,.36,1) .15s}
.fx-ov.fx-ov-band.fx-in::after{animation:ovBar .7s cubic-bezier(.22,1,.36,1) .6s forwards}

/* 7 · highlight — accent block sweeps behind the line, text fades in over it */
.fx-ov.fx-ov-highlight .ov-line{position:relative;display:inline-block;padding:2px 10px}
.fx-ov.fx-ov-highlight .ov-line::before{content:'';position:absolute;inset:0;z-index:-1;
  background:linear-gradient(135deg,var(--ov-accent),rgba(0,0,0,.55));opacity:.85;border-radius:4px;
  transform:scaleX(0);transform-origin:left}
.fx-ov.fx-ov-highlight.fx-in .ov-eyebrow{opacity:1;transition:opacity .5s ease}
.fx-ov.fx-ov-highlight.fx-in .ov-line{opacity:1;transition:opacity .45s ease .5s}
.fx-ov.fx-ov-highlight.fx-in .ov-line::before{animation:ovBar .8s cubic-bezier(.22,1,.36,1) .15s forwards}

/* 8 · rise-skew — the hero-tagline fadeUp's editorial cousin */
.fx-ov.fx-ov-rise-skew .ov-eyebrow{transform:translateY(12px)}
.fx-ov.fx-ov-rise-skew .ov-line{transform:translateY(20px) skewY(1.6deg)}
.fx-ov.fx-ov-rise-skew.fx-in .ov-eyebrow{opacity:1;transform:translateY(0);
  transition:opacity .6s ease,transform .7s cubic-bezier(.22,1,.36,1)}
.fx-ov.fx-ov-rise-skew.fx-in .ov-line{opacity:1;transform:translateY(0) skewY(0);
  transition:opacity .7s ease .18s,transform .8s cubic-bezier(.22,1,.36,1) .18s}

/* 9 · track-in — wide tracking + slight zoom settling to normal */
.fx-ov.fx-ov-track-in .ov-line{letter-spacing:.45em;transform:scale(1.04)}
.fx-ov.fx-ov-track-in.fx-in .ov-eyebrow{opacity:1;transition:opacity .6s ease .4s}
.fx-ov.fx-ov-track-in.fx-in .ov-line{opacity:1;letter-spacing:-.01em;transform:scale(1);
  transition:opacity .8s ease,letter-spacing 1.3s cubic-bezier(.22,1,.36,1),transform 1.3s cubic-bezier(.22,1,.36,1)}

/* 10 · flip — line tips up from the baseline in light 3D */
.fx-ov.fx-ov-flip{perspective:600px}
.fx-ov.fx-ov-flip .ov-line{transform:rotateX(70deg);transform-origin:50% 100%}
.fx-ov.fx-ov-flip.fx-in .ov-eyebrow{opacity:1;transition:opacity .5s ease}
.fx-ov.fx-ov-flip.fx-in .ov-line{opacity:1;transform:rotateX(0);
  transition:opacity .55s ease .15s,transform .9s cubic-bezier(.22,1,.36,1) .15s}

/* ── 3. OVERLAY PLACEMENT ZONES ───────────────────────────────────────────
   Default (.ov-pos-bl or no class) = bottom-left, as before. Mid-article
   images draw one of 5 zones from fx_assign.overlay_placement. */
.img-overlay.ov-pos-br{text-align:right}
.img-overlay.ov-pos-c{bottom:50%;transform:translateY(50%);text-align:center}
.img-overlay.ov-pos-ul{bottom:auto;top:24px}
.img-overlay.ov-pos-ur{bottom:auto;top:24px;text-align:right}
/* right-aligned zones: band/wipe accent bars anchor right instead of left */
.img-overlay.ov-pos-br.fx-ov-band::after,.img-overlay.ov-pos-ur.fx-ov-band::after{
  left:auto;right:0;transform-origin:right}
.img-overlay.ov-pos-br.fx-ov-wipe::before,.img-overlay.ov-pos-ur.fx-ov-wipe::before{
  background:linear-gradient(270deg,var(--ov-accent),transparent);transform-origin:right}

/* ── 4. KEYWORD ACCENT (one key word/phrase per snippet, red or orange) ──
   fx_assign.keyword_color deals a 50/50 split; the phrase itself is
   wrapped in <em class="k"> by the patcher / generators. */
.ov-key-red .k,.ov-key-orange .k{font-style:normal}
.ov-key-red .k{color:#FA2A52}
.ov-key-orange .k{color:#F59E0B}
/* dish closing newsletter hook: per-article CSS hardcodes red — orange half
   overrides at higher specificity, red half needs no change */
.closing-content.ov-key-orange .hook em{color:#F59E0B}

/* ── 5. HERO TAGLINE AS fx-ov (.ov-hero) ──────────────────────────────────
   Dish static heroes: tagline children renamed .eyebrow/.line →
   .ov-eyebrow/.ov-line so the 10 entrance styles apply; .ov-hero restores
   the tagline's own typography (position/centering stay on .hero-tagline). */
.ov-hero{--ov-accent:#FA2A52;--ov-accent2:#F97316}
.ov-hero .ov-eyebrow{font-family:monospace;font-size:12px;color:var(--ov-accent2);
  text-transform:uppercase;letter-spacing:.2em;font-weight:700;margin-bottom:14px;
  text-shadow:0 2px 12px rgba(0,0,0,.8)}
.ov-hero .ov-line{font-family:'Geologica',sans-serif;font-weight:800;
  font-size:clamp(26px,3.6vw,44px);line-height:1.18;color:#fff;letter-spacing:-.02em;
  text-shadow:0 2px 24px rgba(0,0,0,.75)}

/* ── 6. ACCESSIBILITY + MOBILE ────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce){
  .fx-img,.hero-photo,.mid-photo,.closing-photo,.hero-img,.info-image img{
    animation:none !important;transform:none !important;filter:none !important}
  .fx-ov .ov-eyebrow,.fx-ov .ov-line,.fx-ov .ov-line .w,.fx-ov .ov-line::after,
  .fx-ov::before,.fx-ov::after,.fx-ov .ov-line::before{
    animation:none !important;transition:none !important;opacity:1 !important;
    transform:none !important;clip-path:none !important;filter:none !important;
    letter-spacing:normal !important}
  .fx-ov.fx-ov-typewriter .ov-line::after{content:none !important}
}
@media (max-width:640px){
  .img-overlay{bottom:14px;left:16px;right:16px}
  .img-overlay .ov-line{font-size:15px}
  .img-overlay .ov-eyebrow{font-size:10px;margin-bottom:5px}
  .img-overlay.ov-pos-ul,.img-overlay.ov-pos-ur{bottom:auto;top:14px}
  .img-overlay.ov-pos-c{bottom:50%;top:auto}
}
