/* Storefront fx "trust" — /report-scammer hero (livewire/sf-trust/scammer-list, .sf-fx-scam).
 * Hover (tap on touch) the scammer and he gets a beating that CHARGES UP like the login heading
 * (owner 2026-09-29: boxing gloves and clubs, no arms, many punches landing close together,
 * faster and faster; the ตะลุมบอน is smoke only, thicker and thicker; no "จับได้แล้ว!" stamp).
 * pkg-fx-trust.js makes the hits and steps the level (data-charge 1 → 4, 0.8 s apart); this file
 * is the looks:
 *   level 1–2  punches / clubs from all sides (.sf-fx-hit), jolts, burst words (.sf-fx-burst)
 *   level 2–4  smoke gathers round him, see-through first, thicker each level; he trembles
 *   .is-caught (3.7 s) the smoke clears → dazed (grey, slumped, plaster, dizzy stars) → cage → padlock
 * The stage is a size container (cqw = 1% of its width). Art (768²): hood top ≈ (52%, 5%),
 * eyes ≈ (43–61%, 33%). Reduced motion / effects off: no charge — he is just shown grey, caged.
 * Frames: node ai_test/scripts/scam-brawl-frames.mjs <tag> */

/* Gloves, clubs and smoke reach past the art, but never over the headline / search box in the
   next column. Every fight part sits in .sf-fx-scam-fx, a layer 220% of the stage (-60% …
   +160%) masked to fade out just past the text column's edge; .sf-fx-scam-box inside it is the
   stage again, so the parts keep stage percentages. --sf-fx-gap = stage left → text column edge
   (the figure's side margin + the hero's 40px column gap), computed where cqw is the figure. */
@property --sf-fx-gap { syntax: '<length>'; inherits: true; initial-value: 40px; }
.sf-trust-hero-art:has(> .sf-fx-scam) { container-type: inline-size; }
.sf-fx-scam { position: relative; width: min(100%, 300px); aspect-ratio: 1; cursor: pointer; isolation: isolate; container-type: inline-size;
  --sf-fx-gap: calc((100cqw - min(100cqw, 300px)) / 2 + 40px); }
.sf-fx-scam-fx { position: absolute; z-index: 4; left: -60%; top: -60%; width: 220%; height: 220%; pointer-events: none;
  -webkit-mask-image: linear-gradient(90deg, transparent calc(60cqw - var(--sf-fx-gap) + 6px), #000 calc(60cqw - var(--sf-fx-gap) + 36px));
  mask-image: linear-gradient(90deg, transparent calc(60cqw - var(--sf-fx-gap) + 6px), #000 calc(60cqw - var(--sf-fx-gap) + 36px)); }
.sf-fx-scam-box { position: absolute; left: calc(60% / 2.2); top: calc(60% / 2.2); width: calc(100% / 2.2); height: calc(100% / 2.2); }
.sf-fx-scam > svg, .sf-fx-scam > span, .sf-fx-scam-box > svg, .sf-fx-scam-box > span { position: absolute; pointer-events: none; opacity: 0; }
.sf-fx-scam-defs { width: 0; height: 0; overflow: hidden; }
.sf-fx-scam-guy { position: relative; z-index: 1; transform-origin: 50% 60%; transition: transform .3s ease; }
.sf-fx-scam-guy > img { display: block; width: 100%; height: auto; position: relative; z-index: 1; transition: filter .4s ease; }

/* ── the hits (made by the script; it sets their place, size and motion). The smoke (z 6) sits
   over the gloves (z 5), so at the top levels they plunge into it; the words (z 7) stay on top ── */
.sf-fx-hit { z-index: 5; }
.sf-fx-hit.is-club { filter: drop-shadow(0 6px 8px #000a); }
/* a club's downswing: white arcs along its head's path, round the grip (pkg-fx-trust.js swoosh) */
.sf-fx-swoosh { z-index: 5; left: 0; top: 0; width: 100%; height: 100%; overflow: visible; fill: none; stroke: #fff; stroke-linecap: round; filter: blur(.6px); }
/* a punch from the viewer's side is nearest of all: over the smoke and the words */
.sf-fx-hit.is-front { z-index: 8; filter: drop-shadow(0 10px 14px #000b); }
.sf-fx-burst { z-index: 7; }
.sf-fx-burst use { stroke-width: 6; stroke-linejoin: round; }
.sf-fx-burst text { font: 900 42px/1 var(--v2-display, sans-serif); text-anchor: middle; stroke-width: 7px; stroke-linejoin: round; paint-order: stroke; }

/* ── the smoke. Box = the stage + a margin; its middle ≈ (50%, 48%). Thicker per level ── */
.sf-fx-scam-brawl { z-index: 6; left: -6%; top: -8%; width: 112%; height: 108%; transform-origin: 50% 48%; transform: scale(.7);
  transition: opacity .8s ease, transform .8s ease; }
.sf-fx-scam[data-charge="2"] .sf-fx-scam-brawl { opacity: .35; transform: scale(.85); }
.sf-fx-scam[data-charge="3"] .sf-fx-scam-brawl { opacity: .62; transform: scale(.94); }
.sf-fx-scam[data-charge="4"] .sf-fx-scam-brawl { opacity: .95; transform: scale(1.02); }
.sf-fx-scam-brawl-in { position: absolute; inset: 0; }
.sf-fx-scam[data-charge="3"] .sf-fx-scam-brawl-in { animation: sfFxBrawlShake .2s linear infinite; }
.sf-fx-scam[data-charge="4"] .sf-fx-scam-brawl-in { animation: sfFxBrawlShake .12s linear infinite; }
.sf-fx-brawl-smoke, .sf-fx-brawl-spurt { position: absolute; inset: 0; }
.sf-fx-brawl-smoke::before { content: ''; position: absolute; left: 3%; top: 1%; width: 94%; aspect-ratio: 1; border-radius: 50%;
  background: radial-gradient(closest-side, #ff006e30, #ff006e10 70%, transparent); }
.sf-fx-brawl-smoke i, .sf-fx-brawl-spurt i { position: absolute; aspect-ratio: 1; border-radius: 50%; filter: blur(6px); will-change: transform; opacity: 0;
  background: radial-gradient(closest-side, #e6e1ee 0 46%, #cbc4d9f2 64%, #a198b5b8 82%, #6f668a00 100%); }
.sf-fx-brawl-smoke i { transition: opacity .7s ease; }
.sf-fx-brawl-smoke i:nth-child(1)  { left: 22%; top: 20%; width: 56%; --bt: .50s; }
.sf-fx-brawl-smoke i:nth-child(2)  { left: 30%; top: 4%;  width: 40%; --bx: -3%; --by: -4%; --bt: .42s; }
.sf-fx-brawl-smoke i:nth-child(3)  { left: 58%; top: 30%; width: 40%; --bx: 5%;  --by: 2%;  --bt: .46s; }
.sf-fx-brawl-smoke i:nth-child(4)  { left: 30%; top: 56%; width: 40%; --bx: -2%; --by: 5%;  --bt: .44s; }
.sf-fx-brawl-smoke i:nth-child(5)  { left: 6%;  top: 24%; width: 40%; --bx: -5%; --by: -2%; --bt: .48s; }
.sf-fx-brawl-smoke i:nth-child(6)  { left: 50%; top: 10%; width: 40%; --bx: 4%;  --by: -3%; --bt: .38s; }
.sf-fx-brawl-smoke i:nth-child(7)  { left: 52%; top: 50%; width: 40%; --bx: 3%;  --by: 5%;  --bt: .40s; }
.sf-fx-brawl-smoke i:nth-child(8)  { left: 10%; top: 46%; width: 40%; --bx: -5%; --by: 3%;  --bt: .36s; }
.sf-fx-brawl-smoke i:nth-child(9)  { left: 14%; top: 6%;  width: 38%; --bx: -4%; --by: -5%; --bt: .40s; }
.sf-fx-brawl-smoke i:nth-child(10) { left: 40%; top: 22%; width: 30%; --bx: 3%;  --by: -3%; --bt: .34s; }
.sf-fx-brawl-smoke i:nth-child(11) { left: 20%; top: 42%; width: 28%; --bx: -3%; --by: 3%;  --bt: .38s; }
.sf-fx-brawl-smoke i:nth-child(12) { left: 50%; top: 50%; width: 26%; --bx: 3%;  --by: 3%;  --bt: .42s; }
.sf-fx-brawl-smoke i:nth-child(n+10) { --po: .5; background: radial-gradient(closest-side, #a89fbd 0 36%, #8f86a5c0 62%, #6f668a00 100%); }
/* more puffs join at each level: 3 → 7 → all 12 (the darker core last) */
.sf-fx-scam[data-charge="2"] .sf-fx-brawl-smoke i:nth-child(-n+3),
.sf-fx-scam[data-charge="3"] .sf-fx-brawl-smoke i:nth-child(-n+7),
.sf-fx-scam[data-charge="4"] .sf-fx-brawl-smoke i { opacity: var(--po, 1); }
.sf-fx-scam[data-charge="4"] .sf-fx-brawl-smoke i:nth-child(n+8) { transition-delay: .3s; }
.sf-fx-scam:is([data-charge="2"], [data-charge="3"], [data-charge="4"]) .sf-fx-brawl-smoke i { animation: sfFxBillow var(--bt, .4s) ease-in-out infinite alternate; }
/* puffs bursting off the edge at the top level (the first version's drift-out) */
.sf-fx-brawl-spurt i { width: 34%; background: radial-gradient(closest-side, #d8d2e4e6 0 30%, #a9a1bd99 60%, #6f668a00 100%); }
.sf-fx-brawl-spurt i:nth-child(1) { left: 61.2%; top: 41%;   --dx: 66%;  --dy: 24%;  --d: .00s; }
.sf-fx-brawl-spurt i:nth-child(2) { left: 45.7%; top: 58.6%; --dx: 30%;  --dy: 63%;  --d: .18s; }
.sf-fx-brawl-spurt i:nth-child(3) { left: 22.7%; top: 59.6%; --dx: -24%; --dy: 66%;  --d: .06s; }
.sf-fx-brawl-spurt i:nth-child(4) { left: 5.8%;  top: 43.6%; --dx: -63%; --dy: 30%;  --d: .24s; }
.sf-fx-brawl-spurt i:nth-child(5) { left: 4.8%;  top: 19.8%; --dx: -66%; --dy: -24%; --d: .12s; }
.sf-fx-brawl-spurt i:nth-child(6) { left: 20.3%; top: 2.2%;  --dx: -30%; --dy: -63%; --d: .30s; }
.sf-fx-brawl-spurt i:nth-child(7) { left: 43.3%; top: 1.2%;  --dx: 24%;  --dy: -66%; --d: .03s; }
.sf-fx-brawl-spurt i:nth-child(8) { left: 60.2%; top: 17.2%; --dx: 63%;  --dy: -30%; --d: .20s; }
.sf-fx-scam[data-charge="4"] .sf-fx-brawl-spurt i { animation: sfFxSpurt .7s var(--d) ease-out infinite; }

/* he trembles harder as it charges (the script's jolts use `translate` / `rotate` on top) */
.sf-fx-scam[data-charge="3"] .sf-fx-scam-guy { animation: sfFxTremble .2s linear infinite; }
.sf-fx-scam[data-charge="4"] .sf-fx-scam-guy { animation: sfFxTremble .11s linear infinite; }

/* ── dazed (inside .sf-fx-scam-guy, so they sway with him) + caged ── */
.sf-fx-scam-plaster { position: absolute; z-index: 2; left: 55%; top: 16%; width: 13%; aspect-ratio: 1; opacity: 0; transform: scale(0) rotate(-40deg); }
.sf-fx-scam-plaster::before, .sf-fx-scam-plaster::after { content: ''; position: absolute; left: -10%; top: 35%; width: 120%; height: 30%; border-radius: 99px;
  background: linear-gradient(90deg, #e3ae7c 0 33%, #f8e2c6 33% 67%, #e3ae7c 67%); box-shadow: 0 1px 3px #000a, inset 0 -1px 0 #0003; transform: rotate(45deg); }
.sf-fx-scam-plaster::after { transform: rotate(-45deg); }
.sf-fx-scam-dizzy { position: absolute; z-index: 3; left: 52%; top: 5%; width: 0; height: 0; opacity: 0; }
.sf-fx-scam-dizzy i { position: absolute; left: 0; top: 0; filter: drop-shadow(0 0 4px #ffd21f) drop-shadow(0 1px 1px #000); }
.sf-fx-scam-dizzy b { position: absolute; display: block; width: 8cqw; aspect-ratio: 1; margin: -4cqw 0 0 -4cqw;
  background: radial-gradient(circle at 50% 50%, #fffbe0, #ffd21f 50%, #ff9a1f);
  clip-path: polygon(50% 0, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%); }
.sf-fx-scam-cage  { z-index: 7; left: -4%; top: -4%; width: 108%; height: 108%; transform: translateY(-120%);
  filter: drop-shadow(0 10px 18px #000d) drop-shadow(0 0 6px #00f0ff55); }
.sf-fx-scam-lock  { z-index: 8; left: 44%; top: 42%; width: 13%; transform: scale(0) rotate(-35deg); filter: drop-shadow(0 4px 8px #000c); }

/* ── the end. Caught = the end state as plain styles: the storefront's reduced-motion and
   .effects-off rules put `animation: none !important` on everything (colorful.css), and then
   this is what shows. Every animation below has fill `both`, so it overrides these while it runs. */
.sf-fx-scam.is-caught :is(.sf-fx-scam-cage, .sf-fx-scam-lock) { opacity: 1; transform: none; }
.sf-fx-scam.is-caught .sf-fx-scam-plaster { opacity: 1; transform: rotate(-12deg); }
.sf-fx-scam.is-caught .sf-fx-scam-guy { transform: translate(0, 2.5%) rotate(-4deg) scale(.97); transition: transform .4s ease .15s; }
.sf-fx-scam.is-caught .sf-fx-scam-guy > img { filter: drop-shadow(0 20px 40px #000c) grayscale(.5) brightness(.7); transition: filter .5s ease .2s; }
.sf-fx-scam.is-caught .sf-fx-scam-brawl { animation: sfFxSmokeClear .55s ease-in both; }
.sf-fx-scam.is-caught .sf-fx-scam-plaster { animation: sfFxPlaster .35s .3s cubic-bezier(.2, 1.6, .4, 1) both; }
.sf-fx-scam.is-caught .sf-fx-scam-dizzy { animation: sfFxOn .3s .3s both; }
.sf-fx-scam.is-caught .sf-fx-scam-dizzy i { animation: sfFxOrbitX .6s ease-in-out infinite alternate; }
.sf-fx-scam.is-caught .sf-fx-scam-dizzy b { animation: sfFxOrbitY .6s -.3s ease-in-out infinite alternate; }
.sf-fx-scam.is-caught .sf-fx-scam-dizzy i:nth-child(2) { animation-delay: -.4s; }
.sf-fx-scam.is-caught .sf-fx-scam-dizzy i:nth-child(2) b { animation-delay: -.7s; }
.sf-fx-scam.is-caught .sf-fx-scam-dizzy i:nth-child(3) { animation-delay: -.8s; }
.sf-fx-scam.is-caught .sf-fx-scam-dizzy i:nth-child(3) b { animation-delay: -1.1s; }
.sf-fx-scam.is-caught .sf-fx-scam-cage { animation: sfFxScamCage .6s .45s cubic-bezier(.5, 0, .75, 1) both; }
.sf-fx-scam.is-caught .sf-fx-scam-lock { animation: sfFxScamLock .4s 1s cubic-bezier(.2, 1.6, .4, 1) both; }

@keyframes sfFxTremble {
  0%, 100% { transform: translate(0, 0) rotate(0); }
  25%      { transform: translate(1.4%, -1%) rotate(1.2deg); }
  50%      { transform: translate(-1.2%, 1%) rotate(-1.4deg); }
  75%      { transform: translate(1%, 1.2%) rotate(.8deg); }
}
@keyframes sfFxBrawlShake {
  0%, 100% { transform: translate(0, 0) rotate(0); }
  25%      { transform: translate(1.6%, -1.2%) rotate(1.2deg); }
  50%      { transform: translate(-1.4%, 1.4%) rotate(-1deg); }
  75%      { transform: translate(1.2%, 1.2%) rotate(.8deg); }
}
@keyframes sfFxBillow {
  from { transform: translate(0, 0) scale(.9); }
  to   { transform: translate(var(--bx, 0), var(--by, 0)) scale(1.1); }
}
@keyframes sfFxSpurt {
  0%   { opacity: 0; transform: translate(0, 0) scale(.4); }
  30%  { opacity: .9; }
  100% { opacity: 0; transform: translate(var(--dx), var(--dy)) scale(1.5); }
}
@keyframes sfFxSmokeClear {
  from { opacity: .95; transform: scale(1.02); }
  to   { opacity: 0;   transform: scale(1.3); }
}
@keyframes sfFxPlaster {
  0%   { opacity: 0; transform: scale(0) rotate(-40deg); }
  100% { opacity: 1; transform: scale(1) rotate(-12deg); }
}
@keyframes sfFxOrbitX {
  from { transform: translateX(-15cqw); }
  to   { transform: translateX(15cqw); }
}
@keyframes sfFxOrbitY {
  from { transform: translateY(-3.5cqw) scale(.65); }
  to   { transform: translateY(3.5cqw) scale(1.1); }
}
@keyframes sfFxScamCage {
  0%   { opacity: 1; transform: translateY(-120%); }
  70%  { opacity: 1; transform: translateY(4%); }
  85%  { opacity: 1; transform: translateY(-2%); }
  100% { opacity: 1; transform: translateY(0); }
}
@keyframes sfFxScamLock {
  0%   { opacity: 0; transform: scale(0) rotate(-35deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}
@keyframes sfFxOn { from { opacity: 0; } to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .sf-fx-scam.is-caught, .sf-fx-scam.is-caught * { animation: none !important; }
  .sf-fx-scam.is-caught .sf-fx-scam-guy, .sf-fx-scam.is-caught .sf-fx-scam-guy > img { transition: none; }
}
