/* [frankinsuite prototype] shared sprite-integration extras
   corner peekers, hero tilt, idle acts, hand pointer cursors,
   downloads parcel. Loaded by index.html, wolff.html, ram.html. */

/* exact WolffIdleLayered geometry, reusable at any size */
.mini-rig{position:relative;display:block;width:100%;height:100%}
.mini-rig .w-tail{position:absolute;left:35%;top:62%;width:40%;height:40%;transform-origin:top left;z-index:1}
.mini-rig .w-ears{position:absolute;left:22.667%;top:-13%;width:54%;height:54%;transform-origin:bottom center;z-index:2}
.mini-rig .w-body{position:absolute;left:0;top:0;width:100%;height:100%;z-index:3}

@keyframes x-tail{0%{transform:rotate(0)}20%{transform:rotate(8deg)}40%{transform:rotate(-5.6deg)}60%{transform:rotate(6.4deg)}80%{transform:rotate(-4deg)}100%{transform:rotate(0)}}
@keyframes x-ears{0%{transform:rotate(0) translateY(0)}20%{transform:rotate(3deg) translateY(-.8px)}40%{transform:rotate(-2.4deg) translateY(-1.6px)}60%{transform:rotate(1.8deg) translateY(-1.6px)}80%{transform:rotate(-3deg) translateY(-.8px)}100%{transform:rotate(0) translateY(0)}}

/* ---- corner peekers: ears first, then eyes, then duck away.
   Geometry note (fixed after the faces never cleared the edge): translateY
   percentages resolve against the IMG height (~52px), roughly twice the
   window height, so the visible stops must sit far lower than intuition
   says. Window is 34px tall; 45% keeps only ear tips visible, 18% brings
   the eyes over the edge. z-index high enough to clear card stacking. ---- */
.peek-host{position:relative}
.peeker{
  position:absolute;top:-34px;right:16px;width:52px;height:34px;
  overflow:hidden;pointer-events:none;z-index:500;
}
.peeker img{width:100%;display:block;transform:translateY(100%);animation:x-peek 3.1s ease-in-out 1 both}
@keyframes x-peek{
  0%{transform:translateY(100%)}
  22%{transform:translateY(45%)}   /* ear tips over the edge */
  38%{transform:translateY(45%)}
  55%{transform:translateY(18%)}   /* eyes over the edge */
  78%{transform:translateY(18%)}
  100%{transform:translateY(102%)} /* duck away */
}

/* ---- hero tilt: mascots watch the pointer (rotate composes with transform animations) ---- */
.tiltable{rotate:var(--tilt,0deg);transition:rotate .18s ease-out}

/* ---- idle acts ---- */
.yawn .w-ears{animation:x-eardroop 2.2s ease-in-out 1 !important}
.yawn .w-tail{animation:x-tailsag 2.2s ease-in-out 1 !important}
@keyframes x-eardroop{0%,100%{transform:rotate(0) translateY(0)}35%{transform:rotate(-13deg) translateY(2px)}65%{transform:rotate(-13deg) translateY(2px)}}
@keyframes x-tailsag{0%,100%{transform:rotate(0)}35%{transform:rotate(14deg)}65%{transform:rotate(14deg)}}

/* ---- Frankie zap easter egg ---- */
.mascot{position:relative}
@keyframes x-zapshake{0%{transform:translate(0,0)}25%{transform:translate(-4px,-6px)}50%{transform:translate(6px,2px)}75%{transform:translate(-3px,-2px)}100%{transform:translate(0,0)}}
.zapped-now{animation:x-zapshake .4s ease-out 1}
.x-smoke{position:absolute;pointer-events:none;user-select:none;opacity:0;color:#9aa4b8}
@keyframes x-smoke{
  0%{opacity:0;transform:translate(0,0) scale(.3)}
  30%{opacity:.7}
  60%{opacity:.5}
  100%{opacity:0;transform:translate(var(--sx,8px),var(--sy,-26px)) scale(1.2)}
}

/* ---- hand pointer cursors (owner sprite pairs, 64x64, 2026-07-30):
   native CSS cursors. The point frame is the default state; holding a
   click swaps to the grab frame via :active, mimicking grabbing.
   Landing page: each details card wears its tool's chibi hand.
   Detail pages: the chibi hand is the page default; inside the
   downloads section (#get) the realistic hand takes over for as long
   as the pointer stays within it. Hotspots sit on the pointing tip.
   Fine-pointer devices only; touch keeps native behavior. ---- */
/* !important everywhere: page stylesheets load after this file and carry
   their own cursor:pointer on same-specificity selectors (.ledger-head
   button etc.), which let the native cursor peek through. The realistic
   pair is height-matched to the chibi hands on tight canvases (rev 15). */
@media (hover:hover) and (pointer:fine){
  .card.wolff, .card.wolff *{cursor:url("assets/cursor-wolff-chibi-point.png") 2 2, pointer !important}
  .card.wolff:active, .card.wolff:active *{cursor:url("assets/cursor-wolff-chibi-grab.png") 2 2, pointer !important}
  .card.frankie, .card.frankie *{cursor:url("assets/cursor-frankie-chibi-point.png") 4 6, pointer !important}
  .card.frankie:active, .card.frankie:active *{cursor:url("assets/cursor-frankie-chibi-grab.png") 4 6, pointer !important}

  body.wolff-hands, body.wolff-hands *{cursor:url("assets/cursor-wolff-chibi-point.png") 2 2, auto !important}
  body.wolff-hands:active, body.wolff-hands:active *{cursor:url("assets/cursor-wolff-chibi-grab.png") 2 2, auto !important}
  body.wolff-hands #get, body.wolff-hands #get *{cursor:url("assets/cursor-wolff-real-point.png") 0 28, auto !important}
  body.wolff-hands #get:active, body.wolff-hands #get:active *{cursor:url("assets/cursor-wolff-real-grab.png") 0 28, auto !important}

  body.frankie-hands, body.frankie-hands *{cursor:url("assets/cursor-frankie-chibi-point.png") 4 6, auto !important}
  body.frankie-hands:active, body.frankie-hands:active *{cursor:url("assets/cursor-frankie-chibi-grab.png") 4 6, auto !important}
  body.frankie-hands #get, body.frankie-hands #get *{cursor:url("assets/cursor-frankie-real-point.png") 0 11, auto !important}
  body.frankie-hands #get:active, body.frankie-hands #get:active *{cursor:url("assets/cursor-frankie-real-grab.png") 0 11, auto !important}
}

/* ---- hero mascot charm (detail pages): the landing bubbles + eggs,
        attached to the hero sprite so speech and easter eggs are available
        wherever the mascots are. Class-toggled (.on) rather than :hover so
        the JS listeners drive it on plain spans and imgs. ---- */
.charm-host{position:relative;display:block}
.charm-bubble{
  position:absolute;bottom:calc(100% + 14px);left:50%;width:15.5rem;max-width:70vw;
  transform:translateX(-50%) scale(.6);transform-origin:50% 100%;
  background:linear-gradient(180deg,#fdf6e6,#efe3c8);color:#2b2118;
  border-radius:.9rem;padding:.8rem 1rem;font-size:.98rem;line-height:1.45;
  font-family:Georgia,'Iowan Old Style','Times New Roman',serif;
  box-shadow:0 18px 50px rgba(0,0,0,.55);opacity:0;filter:blur(6px);visibility:hidden;
  transition:opacity .28s ease,transform .34s cubic-bezier(.34,1.56,.64,1),filter .28s ease,visibility 0s .34s;
  pointer-events:none;z-index:600;
}
.charm-bubble::after{
  content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);
  border:10px solid transparent;border-top-color:#efe3c8;
}
.charm-bubble.on{
  opacity:1;transform:translateX(-50%) scale(1);filter:blur(0);visibility:visible;
  transition:opacity .28s ease,transform .34s cubic-bezier(.34,1.56,.64,1),filter .28s ease,visibility 0s 0s;
}
.charm-bubble .who{display:block;font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;color:#8a6d33;margin-bottom:.25rem}
.charm-bubble.frankie .who{color:#4d7a33}
/* the RAM hero img carries its own bob animation, which would otherwise
   outrank the zap shake; the zap must win while it runs */
.hero .frankie-hero.zapped-now{animation:x-zapshake .4s ease-out 1 !important}

/* ---- smoke cloud effect (owner mp4: white smoke on a pure black frame;
        mix-blend-mode screen makes the black vanish over the dark palette;
        positioned by its center via the negative margins) ---- */
.smokefx{
  position:fixed;left:0;top:0;width:360px;height:203px;
  margin-left:-180px;margin-top:-101px;
  pointer-events:none;z-index:1200;mix-blend-mode:screen;display:none;
}

/* ---- downloads parcel ---- */
.parcel{
  position:fixed;width:15px;height:12px;z-index:1000;pointer-events:none;
  background:linear-gradient(135deg,#e8bd63,#c9973f);border-radius:2px;
  box-shadow:0 1px 3px rgba(0,0,0,.55);
}
.parcel::before{content:"";position:absolute;left:50%;top:0;bottom:0;width:2px;margin-left:-1px;background:rgba(43,33,24,.55)}
.parcel::after{content:"";position:absolute;top:50%;left:0;right:0;height:2px;margin-top:-1px;background:rgba(43,33,24,.55)}

html.reduce-motion .peeker,html.reduce-motion .parcel{display:none !important}
html.reduce-motion .tiltable{rotate:0deg !important}

/* ---- motion notice (only rendered when animations are off) ---- */
.motion-notice{
  display:block;margin:1.2rem 0 0;padding:1rem 1.15rem;
  background:linear-gradient(180deg,rgba(24,32,54,.92),rgba(19,26,44,.92));
  border:1px solid #2a3552;border-left:3px solid #c9973f;border-radius:.75rem;
  color:#c9bda4;font-size:.95rem;line-height:1.6;
}
.motion-notice .mn-head{color:#e8bd63;font-size:1rem;letter-spacing:.04em;margin:0 0 .35rem}
.motion-notice .mn-why{margin:0 0 .8rem}
.motion-notice .mn-btn{
  font:inherit;font-size:.88rem;cursor:pointer;color:#1d1508;
  background:linear-gradient(135deg,#e8bd63,#ff9a3d);border:none;
  border-radius:.5rem;padding:.55rem 1.05rem;box-shadow:0 5px 16px rgba(255,154,61,.3);
}
.motion-notice .mn-btn:hover,.motion-notice .mn-btn:focus-visible{filter:brightness(1.07)}
.motion-notice .mn-guide{margin-top:.5rem}
.motion-notice .mn-guide summary{cursor:pointer;color:#e8bd63;font-size:.9rem}
.motion-notice .mn-guide ul{margin:.6rem 0 .4rem 1.1rem}
.motion-notice .mn-guide li{margin:.28rem 0}
.motion-notice .mn-note{font-size:.85rem;color:#8a7a63;margin-top:.5rem}

/* ---- motion toggle (in the nav, beside The Tools) ----
   Rockwell Extra Bold is a system font on Windows; the stack degrades to
   Rockwell, then the site's Georgia serif elsewhere. No webfont is loaded:
   the no-third-party-hosting invariant applies to fonts too. ---- */
.motion-toggle{
  font-family:"Rockwell Extra Bold","Rockwell Nova Extra Bold","Rockwell",Georgia,serif;
  font-weight:800;font-size:.82rem;letter-spacing:.06em;white-space:nowrap;
  margin-left:1.4rem;padding:.4rem .85rem;border-radius:.5rem;cursor:pointer;
  border:1px solid #7d3f1c;
  /* vibrant, shiny copper */
  background:linear-gradient(150deg,#ffc38f 0%,#e0813f 28%,#b2571f 52%,#f0a066 74%,#8f4517 100%);
  box-shadow:0 2px 0 rgba(90,40,12,.85),0 5px 14px rgba(0,0,0,.45),
             inset 0 1px 0 rgba(255,226,196,.75),inset 0 -2px 4px rgba(90,40,12,.5);
  transition:filter .16s ease,transform .16s ease;
}
.motion-toggle:hover,.motion-toggle:focus-visible{filter:brightness(1.08) saturate(1.06);transform:translateY(-1px)}
.motion-toggle:active{transform:translateY(1px)}

/* OFF: deep blue lettering */
.motion-toggle.is-off{color:#0a2350;text-shadow:0 1px 0 rgba(255,222,190,.45)}
/* ON: glowing gold lettering */
.motion-toggle.is-on{
  color:#ffdd80;
  text-shadow:0 0 6px rgba(255,206,92,.95),0 0 16px rgba(255,176,40,.7),0 1px 0 rgba(70,30,8,.6);
}

/* two gold flashes, then the caller swaps to the destination state.
   0.9s for two peaks = well under the 3-flashes-per-second seizure
   threshold, and it only ever runs in response to a deliberate click. */
@keyframes motionFlicker{
  0%,100%{color:var(--mt-rest,#0a2350);text-shadow:0 1px 0 rgba(255,222,190,.45)}
  18%,58%{color:#ffe6a3;text-shadow:0 0 10px rgba(255,214,110,1),0 0 24px rgba(255,180,50,.85)}
  36%,76%{color:var(--mt-rest,#0a2350);text-shadow:0 1px 0 rgba(255,222,190,.45)}
}
.motion-toggle.flicker{animation:motionFlicker .9s ease-in-out 1}
.motion-toggle.is-on.flicker{--mt-rest:#ffdd80}

/* the control must animate even while the page is in reduced-motion mode:
   it is the thing that turns motion back on, and its feedback is the only
   confirmation the click registered. Deliberate, click-triggered exception. */
html.reduce-motion .motion-toggle{animation-name:motionFlicker !important}
html.reduce-motion .motion-toggle:not(.flicker){animation:none !important}
