/* halloween.css - october reskin, index.html only. every rule is scoped under
   html.gc-october, which script/season.js adds during october (or whenever
   localStorage gc_force_october=1). loaded unconditionally, so in other months
   nothing matches and the cost is one cached 2kb file (the drip sheet below only
   downloads when the class is actually present, since css background images are
   fetched lazily per matching element).
   the var overrides carry most of the page: link hovers, focus rings, widget
   hints, room list, gold glow, warp lcd, crt glow. the two hardcoded exceptions
   from index's inline style block are overridden here at higher specificity
   (html.gc-october prefix), so the inline block loading later does not matter.
   palette is jack o lantern, not horror: bone text, pumpkin, candle amber,
   amber lcd. bone keeps body text off the amber accents' hue so the link
   hierarchy survives the swap. */

html.gc-october {
  --gc-white:  #e8e2d2;
  --gc-yellow: #ffa62b;
  --gc-gold:   #ff7518;
  --gc-lcd:    #ffb000;
  --gc-lcd-glow:        rgba(255,176,0,0.5);
  --gc-lcd-glow-bright: rgba(255,176,0,0.55);
  --gc-lcd-glow-dim:    rgba(255,176,0,0.45);
}

/* the logo wears its halloween variant for the season: a content swap keeps
   the element, alt text and 311x173 layout box identical (the two pngs share
   dims), and the year round png keeps loading (fetchpriority high) but only
   renders in other months. engines that ignore content on img just show the
   regular logo, harmless. */
html.gc-october .logo-img { content: url(image/logo_halloween.png); }

/* room list hint hover glow was hardcoded gold rgba, now follows the pumpkin */
html.gc-october #roomlist .gc-hintnum:hover {
  text-shadow: 0 0 2px var(--gc-gold), 0 0 5px var(--gc-gold), 0 0 10px rgba(255,117,24,0.55);
}

/* october cursor: the animated arrow gives way to the static halloween one.
   cur_halloween.png is the 32x32 frame carved straight out of the owner's
   cur_halloween.cur (its entries were already png compressed; the png is the
   optimization). arrow tip sits at 0,0, same implicit hotspot as the house
   cursors, so no coords. animation:none is load bearing: a running css
   animation wins the cascade over any cursor value, so it has to stop first. */
html.gc-october body { animation: none; cursor: url(image/cursor/cur_halloween.png), default; }

/* ---- october tap cursor -----------------------------------------------------
   the hand answers rollover: on any link, button or pointer toy it runs a tap
   cycle around the planted fingertip, and holds the pressed frame while the
   mouse button is down. same discrete cursor swap mechanic as the year round
   ani-cur sheet in shared.css: cursor is discrete animatable, browsers flip
   between stop midpoints, so the stops below hold rest > press > rest > lift
   > rest and linear keeps those midpoints exact (no steps, no easing). frames
   come from handgen.ps1 (local only): the source hand rotated, hotspot
   compensated so the tip stays planted. rotation moves the tip off 0,0, so
   lift and press carry explicit hotspot coords scanned out of each raw
   frame; the tip must stay glued to the pointer or the tap reads as
   sliding. the rest frame is the untouched 32x32 source. scope is every
   stylesheet declared cursor:pointer on index: links,
   buttons and the toys. the six inline styled stragglers (gd1,
   gd2, the shortcircuit img, bh-canvas, the coinflip disc, the timemachine
   span) keep the system pointer, an inline style beats any selector and
   moving those declarations is the drip margin chore if it ever matters.
   grab contexts keep grab: ball, warp key, joystick canvas. the keygen
   player sits out entirely, its tiny buttons make the per frame hotspot
   jumps read as glitching and the pointer is honest affordance there, so
   the button coverage is narrowed to the warp nav .wbtn and the player's
   own cursor:pointer declarations stand.
   animation none on :active is load bearing, a running animation beats any
   cursor declaration. */
@keyframes gc-handtap {
  0%   { cursor: url(image/cursor/cur_halloween.png) 0 0, default; }
  30%  { cursor: url(image/cursor/cur_halloween_press.png) 6 0, default; }
  60%  { cursor: url(image/cursor/cur_halloween.png) 0 0, default; }
  80%  { cursor: url(image/cursor/cur_halloween_lift.png) 0 3, default; }
  100% { cursor: url(image/cursor/cur_halloween.png) 0 0, default; }
}

html.gc-october a:hover,
html.gc-october .wbtn:hover,
html.gc-october #magic8 .m8-ball:hover,
html.gc-october #crt-wrap:hover,
html.gc-october #crt-btn:hover,
html.gc-october #slot-canvas:hover,
html.gc-october #slotwrap .slot-disc:hover,
html.gc-october #gravity-btn:hover,
html.gc-october #coin:hover,
html.gc-october #warp-nav .warp-disc:hover,
html.gc-october #radio-control:hover,
html.gc-october #scope-img:hover,
html.gc-october #lights-wrap:hover,
html.gc-october #lights-btn:hover,
html.gc-october #joystickwrap .js-disc:hover,
html.gc-october .chart-node.lit:hover {
  cursor: url(image/cursor/cur_halloween.png) 0 0, default;
  animation: gc-handtap 0.78125s linear infinite;
}

html.gc-october a:active,
html.gc-october .wbtn:active,
html.gc-october #magic8 .m8-ball:active,
html.gc-october #crt-wrap:active,
html.gc-october #crt-btn:active,
html.gc-october #slot-canvas:active,
html.gc-october #slotwrap .slot-disc:active,
html.gc-october #gravity-btn:active,
html.gc-october #coin:active,
html.gc-october #warp-nav .warp-disc:active,
html.gc-october #radio-control:active,
html.gc-october #scope-img:active,
html.gc-october #lights-wrap:active,
html.gc-october #lights-btn:active,
html.gc-october #joystickwrap .js-disc:active,
html.gc-october .chart-node.lit:active {
  animation: none;
  cursor: url(image/cursor/cur_halloween_press.png) 6 0, default;
}

/* ---- animated blood drip ---------------------------------------------------
   one sheet, image/sprite/drips.png: 16 frames of 400x18 laid out horizontally
   (generated by dripgen.ps1, local only). every logo text line drips from its
   own glyph bottoms: inline-block shrink-wraps each line to its text width,
   overflow clips the 6400px ::after sheet to that, and a 16px padding zone
   under the text holds the strip. the padding is given straight back with a
   negative margin, so line pitch and page layout never change and the drips
   just run over whatever sits below (for the tagline that is the address
   line, which drips too; for the address line it is the gap above room list).
   collars sit 2px up inside the line box so they hug the glyph bottoms.
   transform only, compositor driven, like the gc-spr sheets in shared.css.
   16 steps over 1.6s = 10fps. the two logo lines' margins moved from inline
   styles into the index style block so the negative margin here wins cleanly.
   the splash enter line does not drip: the splash is not edited. */
@keyframes gc-drip { from { transform: translateX(0); } to { transform: translateX(-100%); } }

html.gc-october .logo h1,
html.gc-october .logo div p:last-child {
  display: inline-block;
  position: relative;
  overflow: hidden;
  padding-bottom: 16px;
}

html.gc-october .logo h1,
html.gc-october .logo div p:last-child { margin-bottom: -16px; }

html.gc-october .logo h1::after,
html.gc-october .logo div p:last-child::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 2px;
  width: 6400px;
  height: 18px;
  background: url(image/sprite/drips.png) 0 0 no-repeat;
  animation: gc-drip 1.6s steps(16) infinite;
}

/* ---- jack o lantern rating bars --------------------------------------------
   pumpkins.js (october only) swaps each room list star glyph for a real 16px
   pumpkin element (the whole ani frame art, arrows erased), one per star, exact
   by construction - no tiling, so no glyph advance or span width clipping games.
   the slot is 16px: the art's ink is 13 wide, which leaves 3px of gap between
   pumpkins and 3px before the room name. pumpkins.js also strips the 6px space
   after the bars to pay for the slot: measured budget is 340px of line (420 box
   minus 40 ol margin minus 40 ua list padding), and the widest line is 16x5 +
   256px name = 336, fits; the same line with the space kept was 342, which is
   the wrap the first 16px attempt hit. 21px tall so the lines stay at the 21px
   the star font metrics used to force. gold (filled star) is the lit pumpkin,
   silver (unfilled) dims to gray via filter, no second asset. hearts and
   diamonds keep their glyphs: the med ones fall out through :not(.gc-med), the
   one tall heart line carries a gc-nostar marker. the pumpkins are <i>s, not
   imgs, so the gravity drop ignores them. */
html.gc-october i.gc-pump {
  display: inline-block;
  width: 16px;
  height: 21px;
  background-image: url(image/sprite/pumpkin.png);
  background-repeat: no-repeat;
  background-position: 0 100%;
  image-rendering: pixelated;
  vertical-align: -3px;
}

html.gc-october i.gc-pump-dim { filter: grayscale(1) opacity(0.55); }

/* ---- october cursor bats -----------------------------------------------------
   bats.js (october only) spawns a bat at the cursor every 700ms while you
   move: each flies straight left or right on its own wavy path until it exits
   the viewport and its element is removed when the flight ends. all movement
   is compositor driven: the outer owns the horizontal flight, the bob layer
   owns the wave (randomized amplitude, period and phase per bat), the middle
   element is the frame sized clip window the flap strip slides inside. the
   handler only gates spawns: no frame loop, no timers, no layout reads. the
   art carries a dark blood red outline (batgen.ps1 pads 2px, dilates the alpha
   1px and composites a #8b0000 ring under the bat) so it reads against the
   dark background. the sheet is referenced here alone, so it only downloads in
   october. batgen.ps1 regenerates it. */
.gc-bats {
  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 900;
}

html.gc-october .gc-bat {
  position: absolute;
  width: 0;
  height: 0;
  animation: bat-fly 2s linear forwards;
}

html.gc-october .gc-bat-b {
  animation: bat-bob 1s ease-in-out infinite;
}

html.gc-october .gc-bat-w {
  position: absolute;
  left: -18px;
  top: -17px;
  width: 36px;
  height: 34px;
  overflow: hidden;
}

/* leftward bats: the window mirrors in place so the art faces the way it
   flies. the flap cycle runs backwards under the mirror, imperceptible */
html.gc-october .gc-bat-flip {
  transform: scaleX(-1);
}

html.gc-october .gc-bat-w::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 144px;
  height: 34px;
  background-image: url(image/sprite/bats.png);
  background-repeat: no-repeat;
  background-position: 0 0;
  image-rendering: pixelated;
  animation: bat-flap 0.36s steps(4) infinite;
}

@keyframes bat-fly {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(var(--dx), 0, 0); }
}

@keyframes bat-bob {
  from { transform: translateY(calc(-1 * var(--amp))); }
  50% { transform: translateY(var(--amp)); }
  to { transform: translateY(calc(-1 * var(--amp))); }
}

@keyframes bat-flap {
  from { transform: translateX(0); }
  to { transform: translateX(-100%); }
}

/* ---- october cameo ghost -----------------------------------------------------
   ghost.js (october only) fades one ghost in and out at a random spot in the
   viewport every 19-58s: each visit is 3-5s (duration goes inline), fades in
   over the first 12%, holds, fades out over the last 22%, and animationend
   removes the element. fade only, no drift (owner pick). the flap is
   ghost.png, the whole gif tight cropped to its 90x121 motion union, 9 frames
   laid out horizontally: steps(9) over 0.95s = the gif's native 10fps with its
   slightly longer last frame evened out (same uniform timing compromise as the
   skull sheet). the fade is opacity and the flap is transform, both compositor
   driven, like every other sheet. the ghost is a fixed <i>, so the gravity
   drop selector never matches it. the sheet is referenced here alone, so it
   only downloads in october. ghostgen.ps1 regenerates it. */
html.gc-october .gc-ghost {
  position: fixed;
  width: 90px;
  height: 121px;
  overflow: hidden;
  pointer-events: none;
  z-index: 880;
  animation: gc-ghost-visit 4s linear forwards;
}

html.gc-october .gc-ghost::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 810px;
  height: 121px;
  background-image: url(image/sprite/ghost.png);
  background-repeat: no-repeat;
  background-position: 0 0;
  image-rendering: pixelated;
  animation: gc-ghost-flap 0.95s steps(9) infinite;
}

@keyframes gc-ghost-flap {
  from { transform: translateX(0); }
  to { transform: translateX(-100%); }
}

@keyframes gc-ghost-visit {
  0%   { opacity: 0; }
  12%  { opacity: 0.85; }
  78%  { opacity: 0.85; }
  100% { opacity: 0; }
}

/* ---- october link planets -> skull ------------------------------------------
   the link grid sprites (spr-planet1..12) become the skull gif for october.
   skull.png is the whole gif tight cropped to the union of the art's motion:
   the canvas is 100x100 but the art only ever occupies 69x53 (+6+25), so the
   crop wastes nothing and no scaling is needed. 24 frames, steps(24) over
   1.872s = the gif's native 1.2s slowed 30%, another 50%, then 20% faster.
   every slot is exactly one frame wide (69px): a wider window showed the next
   frame's left edge, which read as the gif repeating. the sheet is referenced
   here alone, so it only downloads in october. skullgen.ps1 regenerates it. */
html.gc-october .spr-planet1,
html.gc-october .spr-planet2,
html.gc-october .spr-planet3,
html.gc-october .spr-planet4,
html.gc-october .spr-planet5,
html.gc-october .spr-planet6,
html.gc-october .spr-planet7,
html.gc-october .spr-planet8,
html.gc-october .spr-planet9,
html.gc-october .spr-planet10,
html.gc-october .spr-planet11,
html.gc-october .spr-planet12 {
  width: 69px;
  height: 53px;
}

html.gc-october .spr-planet1::before,
html.gc-october .spr-planet2::before,
html.gc-october .spr-planet3::before,
html.gc-october .spr-planet4::before,
html.gc-october .spr-planet5::before,
html.gc-october .spr-planet6::before,
html.gc-october .spr-planet7::before,
html.gc-october .spr-planet8::before,
html.gc-october .spr-planet9::before,
html.gc-october .spr-planet10::before,
html.gc-october .spr-planet11::before,
html.gc-october .spr-planet12::before {
  width: 1656px;
  height: 53px;
  background-image: url(image/sprite/skull.png);
  background-size: 1656px 53px;
  animation-duration: 1.872s;
  animation-timing-function: steps(24);
}
