/* christmas.css - december reskin, index.html only. every rule is scoped under
   html.gc-december, which script/season.js adds during december (or whenever
   localStorage gc_force_season=december). loaded unconditionally, so in other
   months nothing matches and the cost is one cached file (the icicle strip below
   only downloads when the class is actually present, since css background images
   are fetched lazily per matching element).
   palette is snow blasted, not candy: frost text, pale ice for the ratings and
   glows, saturated ice cyan for anything live (hover, focus, hints, the lcd
   readouts). one hue family, separated by saturation, which is what keeps the
   link hierarchy readable when the whole page is cold. the lcd goes cold too:
   the perf hud, the warp display and the keygen scope all read as frost light,
   the same call october makes swapping green to amber. the only warm thing left
   on the page is the santa hat in the logo art. */
html.gc-december {
  --gc-white:  #e6f1ff;
  --gc-yellow: #7fd8ff;
  --gc-gold:   #a9e4ff;
  --gc-lcd:    #8ce8ff;
  --gc-lcd-glow:        rgba(140,232,255,0.5);
  --gc-lcd-glow-bright: rgba(140,232,255,0.55);
  --gc-lcd-glow-dim:    rgba(140,232,255,0.45);
}

/* room list hint hover glow was hardcoded gold rgba, now follows the ice.
   the silver variant is left alone, same call october makes. */
html.gc-december #roomlist .gc-hintnum:hover {
  text-shadow: 0 0 2px var(--gc-gold), 0 0 5px var(--gc-gold), 0 0 10px rgba(127,216,255,0.55);
}

/* the logo wears its christmas variant for the season. the art is 311x201
   against the 311x173 layout box (the santa hat sits on top of the same logo),
   so a plain content swap would squash it 14%. instead the art is stretched into
   the box by the default object-fit and then scaled back up about its bottom
   edge: 201/173. transform does not affect layout, so the box, the alt text, the
   gc-slam animation and everything below the logo stay exactly where they were,
   and the 28px of hat overflows upward into the empty gap above the logo instead
   of pushing the page down. the header sits 100px down and nothing clips these
   (index's #gc-stage containment is layout only, on purpose), so the overflow
   paints. engines without content on img just show the year round logo. */
html.gc-december .logo-img {
  content: url(image/christmas.png);
  transform-origin: 50% 100%;
  /* scale first (bottom-aligned), then lift 4px: the christmas art draws its planet
     4px lower against the canvas edge than logo.png does, measured by an alignment
     search over the shared art, so the -4 puts the orb back exactly where the year
     round logo has it. */
  transform: translateY(-4px) scaleY(1.16185);
}

/* the tap wiggle (script/logotap.js) is keyframes on transform, and a running
   animation REPLACES the resting transform above rather than composing with it. so
   for the length of the wiggle the 1.162 unsquash disappeared and the christmas art
   snapped back to the squashed 311x173 fit: it looked like the tap broke the logo
   instead of tipping it. these december copies of the keyframes carry the same
   translate and scale as the resting state and rotate on top of it. same three
   stops, same 0.2s ease-out as index.html, only the transform is composed. */
@keyframes gc-tip-a-dec {
  0%   { transform: translateY(-4px) scaleY(1.16185) rotate(0deg); }
  30%  { transform: translateY(-4px) scaleY(1.16185) rotate(6deg); }
  70%  { transform: translateY(-4px) scaleY(1.16185) rotate(-1.5deg); }
  100% { transform: translateY(-4px) scaleY(1.16185) rotate(0deg); }
}
@keyframes gc-tip-b-dec {
  0%   { transform: translateY(-4px) scaleY(1.16185) rotate(0deg); }
  30%  { transform: translateY(-4px) scaleY(1.16185) rotate(6deg); }
  70%  { transform: translateY(-4px) scaleY(1.16185) rotate(-1.5deg); }
  100% { transform: translateY(-4px) scaleY(1.16185) rotate(0deg); }
}
html.gc-december .logo-img.gc-tip-a { animation-name: gc-tip-a-dec; }
html.gc-december .logo-img.gc-tip-b { animation-name: gc-tip-b-dec; }

/* the slam is a 311x173 sheet that basketball.js stamps in as an inline style,
   which outranks the content rule above. a 173 sheet under the 1.162 correction
   renders 28px too tall, so the slam plays at its own ratio. */
html.gc-december .logo-img.slammed,
html.gc-december .logo-img.slammed-b { transform: none; }

/* december cursor: the animated arrow gives way to the candy cane. the png is
   the one frame of the owner's cur_christmas.gif, so nothing animates it; it is
   static all season. hotspot 1 0 because the cane's shaft tip is the first solid
   pixel. animation:none is load bearing, a running css animation wins the
   cascade over any cursor declaration, and body carries ani-cur. */
html.gc-december body { animation: none; cursor: url(image/cursor/cur_xmas.png) 1 0, default; }

/* the same pointer contexts october claims, so the cane stands in for the hand
   over links, the warp nav and every stylesheet declared cursor:pointer toy on
   index. no tap cycle: a cane has no fingertip to plant, and october's cycle is
   built around handgen.ps1 rotating one. the inline styled stragglers (gd1, gd2,
   gd3, the shortcircuit img, bh-canvas, the coinflip disc, the timemachine span)
   keep the system pointer, an inline style beats any selector. grab contexts keep
   grab: ball, warp key, joystick canvas. the keygen player sits out, same reason
   as october. */
html.gc-december a:hover,
html.gc-december .wbtn:hover,
html.gc-december #magic8 .m8-ball:hover,
html.gc-december #crt-wrap:hover,
html.gc-december #crt-btn:hover,
html.gc-december #slot-canvas:hover,
html.gc-december #slotwrap .slot-disc:hover,
html.gc-december #gravity-btn:hover,
html.gc-december #coin:hover,
html.gc-december #warp-nav .warp-disc:hover,
html.gc-december #radio-control:hover,
html.gc-december #scope-img:hover,
html.gc-december #lights-wrap:hover,
html.gc-december #lights-btn:hover,
html.gc-december #joystickwrap .js-disc:hover,
html.gc-december .gc-logowrap:hover,
html.gc-december .chart-node.lit:hover {
  cursor: url(image/cursor/cur_xmas.png) 1 0, default;
}

/* ---- icicles (static) -------------------------------------------------------
   december counterpart of the october blood drip, same placement, deliberately
   no animation: ice does not drip and it does not wobble, and stepping a drip
   cycle at 10fps made the strip read as jelly. so the strip is held at one frame
   forever, which also means no keyframes and no compositor work at all.
   one sheet, image/sprite/icicles.png, TWO static frames of 400x18 side by side
   (generated by _tools/iciclegen.py, local only). two frames, not one, because
   the tagline and the address line sit 18px apart with nothing between them: a
   single strip would show the identical pattern twice and read as a repeating
   texture. they frost from their own glyph bottoms: inline-block shrink-wraps
   each line to its text width, overflow clips the 400px ::after strip to that,
   and a 16px padding zone under the text holds it. the padding is given straight
   back with a negative margin, so line pitch and page layout never change and the
   ice just hangs over whatever sits below (for the tagline that is the address
   line, which frosts too; for the address line it is the gap above the room
   list). the strip sits 2px up inside the line box so the ice hugs the glyph
   bottoms. the two logo lines' margins are stylesheet-only (index style block),
   so the negative margin wins cleanly. the splash enter line does not frost: the
   splash is not edited. */
html.gc-december .logo h1,
html.gc-december .logo div p:last-child {
  display: inline-block;
  position: relative;
  overflow: hidden;
  padding-bottom: 16px;
}

html.gc-december .logo h1,
html.gc-december .logo div p:last-child { margin-bottom: -16px; }

html.gc-december .logo h1::after,
html.gc-december .logo div p:last-child::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 2px;
  width: 400px;
  height: 18px;
  background: url(image/sprite/icicles.png) 0 0 no-repeat;
}

/* the address line takes the second frame so the two lines never match */
html.gc-december .logo div p:last-child::after { background-position: -400px 0; }

/* ---- snow settled at the base of the room list -----------------------------
   snowpiles.js (december only) drops drifts at the base of a seeded scatter of
   room list rows. a row is a contiguous 21px line box with no vertical gap, so
   the drift is pinned 16px below the row, which puts the bank in the gap under
   the words, and it paints at z-index -1 inside the row's own stacking context,
   so the glyphs draw over it instead of the snow sitting on the letters.
   pointer-events:none keeps the link under it clickable, and it is an <i>, so
   the gravity drop ignores it. one 144x10 sheet holds three drifts, weighted
   small, so rows do not all carry the same size. */
/* the drift layer: snowpiles.js inserts one div as the first child of #roomlist
   and every pile lives in it. it is a plain absolutely positioned layer stacked
   below the list content, so the snow is behind the text by construction and no
   row has to be made positioned (which was what kept painting the drifts over the
   rows below them). */
html.gc-december #roomlist { position: relative; }
html.gc-december #roomlist > .gc-snowlayer {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
/* the front layer sits ABOVE the list, and every drift is repeated in it clipped to
   its bottom two rows. those two rows spill over the feet of the letters, so the
   text looks like it is standing in the drift rather than lying on top of it. the
   clip is what makes it cheap: same element geometry, same transform, same
   background position, just the visible slice changed. */
html.gc-december #roomlist > .gc-snowlayer-front {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}
/* the front layer only shows the BOTTOM of the cell, so the drift spills over the
   feet of the letter and the text reads as standing in the snow. showing the last 3
   rows is 1.8px at the 60 percent drift size: a thin lip of snow on the bottom of the
   glyphs. fewer rows and it disappears, more and it reads as a flat bar across the
   word. */
html.gc-december i.gc-snowpile-front { clip-path: inset(7px 0 0 0); }

html.gc-december #roomlist > h2,
html.gc-december #roomlist > ol {
  position: relative;
  z-index: 1;
}

/* the row text is lifted explicitly above the drift, so the pile can never paint
   over a glyph no matter how the rows end up stacked.
   the shadow is the trick for "only where the snow is": it is a single 1px black
   shadow under each glyph, which is invisible against the black page and only
   shows up where a pale drift is behind the text. that reads as the letters
   casting a shadow onto the snow, without needing to clip anything. */
/* the halo goes on every room list anchor AND on the list markers. a drift can sit
   behind a row number as easily as behind a word, and the marker is a separate box
   (::marker) that does not inherit the anchor's shadow. chromium does apply
   text-shadow to ::marker even though the spec leaves it off the allowed list, so
   one shared declaration covers both. eight 1px offsets, no 2px tiers and no partial
   alpha, which is what keeps it hard pixels instead of a glow. */
html.gc-december #roomlist > ol a {
  position: relative;
  z-index: 1;
}
html.gc-december #roomlist > ol a,
html.gc-december #roomlist > ol > li::marker {
  text-shadow:
    1px 0 0 rgba(0,0,0,0.5),
    -1px 0 0 rgba(0,0,0,0.5),
    0 1px 0 rgba(0,0,0,0.5),
    0 -1px 0 rgba(0,0,0,0.5),
    1px 1px 0 rgba(0,0,0,0.5),
    -1px 1px 0 rgba(0,0,0,0.5),
    1px -1px 0 rgba(0,0,0,0.5),
    -1px -1px 0 rgba(0,0,0,0.5);
}

html.gc-december i.gc-snowpile {
  position: absolute;
  width: 48px;
  height: 10px;
  background-image: url(image/sprite/snowpile.png);
  background-repeat: no-repeat;
  image-rendering: pixelated;
  pointer-events: none;
  z-index: -1;
  /* 60 percent, anchored at the bottom-left so the base stays planted on the line.
     full size covered half the letters; at this size the drift reads as a pile at the
     base of the word. pixelated rendering keeps the downscale hard-edged. */
  transform: scale(0.6);
  transform-origin: 0 100%;
}
