@charset "UTF-8";
*,
*::after,
*::before {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

html {
  position: relative;
  height: 100%;
  overflow-x: hidden;   /* clip decorative sprites that extend past the frame */
}


details {
  background-image: url("images/backgrounds/bg21.png");
    font-family: basiic, sans-serif;
  margin-block: 1em;
  padding: 0.5em;
  border: 1px dotted;
  border-radius: 8px;
}
details ul li {
  margin-block: 0.5em;
}
details ul li a {
  font-size: 1.2rem;
  color: #667378;
}
details ul li a:hover {
  color: #2c45ff;
  text-decoration: wavy underline #002fff 1px;
}
details summary {
  font-family: basiic, sans-serif;
  font-size: 1em;
  background-color: #fff;
  border-radius: 8px;
  padding: 0.5em;
  border: 1px dotted;
    background: linear-gradient(
    300deg,
    #e8c0c8,
    #C7C4D9,
    #f6ebbe,
    #f2c0bf,
    #f5f4d6,
    #FFFFFA
  );
  background-size: 360% 360%;
  animation: gradient-animation 36s ease infinite;
  will-change: background-position;
}

@keyframes gradient-animation {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}  





@font-face {
  font-family: IBM; sans-serif;
  src: url(fonts/IBMModel3x.woff);
}

@font-face {
  font-family arcade; sans-serif;
  src: url(fonts/arcadei.ttf);
}

@font-face {
  font-family: basiic; sans-serif;
  src: url(fonts/basiic.ttf); format:("truetype");
}

@font-face {
  font-family: daydream; sans-serif;
  src: url(fonts/Daydream.ttf);
}

@font-face {
  font-family: freepixel; sans-serif;
  src: url(fonts/FreePixel.ttf);
}

@font-face {
  font-family: retrocomputer; sans-serif;
  src: url(fonts/Retrocomputer.ttf);
}

@font-face {
  font-family: SMB3; sans-serif;
  src: url(fonts/SMB3.ttf);
}

@font-face {
  font-family: nes; sans-serif;
  src: url(fonts/NES.ttf);
}

@font-face {
  font-family: LineLinker; sans-serif;
  src: url(fonts/LineLinker.ttf);
}

@font-face {
  font-family: Cherrybomb; sans-serif;
  src: url(fonts/Cherrybomb.ttf);
}

@font-face {
  font-family: shapebit; sans-serif;
  src: url(fonts/ShapeBit.otf);
}

@font-face {
  font-family: Minecraftbold; sans-serif;
  src: url(fonts/minecraftbold.otf);
}

@font-face {
  font-family: Minecraft; sans-serif;
  src: url(fonts/Minecraft.otf);
}

@font-face {
  font-family: nintendods; sans-serif;
  src: url(fonts/Nintendo-DS.ttf);
}

@font-face {
  font-family: rainyhearts; sans-serif;
  src: url(fonts/rainyhearts.ttf);
}

@font-face {
  font-family: mariokartds; sans-serif;
  src: url(fonts/Mario-Kart-DS.ttf);
}

@font-face {
  font-family: 04B; sans-serif;
  src: url(fonts/04B.ttf);
}

@font-face {
  font-family: grapesoda; sans-serif;
  src: url(fonts/GrapeSoda.ttf);
}

@font-face {
  font-family: clover; sans-serif;
  src: url(fonts/LilyClover.ttf);
}


:root {
  --grid-bg: #ffffee;
  --pink: #d81888;
  --purple: #5A3268;
  --teal: #0FA3A3;
  --yello: #ffffee;
}

.--home {
  letter-spacing: 1.3px;
  /* THE SITE POINTER — the global default, on everything.
     Was curs1.gif (32x32, 3 frames — and only frame 1 ever rendered,
     because browsers do not animate gif cursors). This is the pink arrow
     rebuilt from 223.gif with its tip anchored at the hotspot; see the
     CONTEXTUAL CURSORS block further down for why the original gif could
     not be used directly.
     image-set lets hi-dpi screens take the 2x. `auto` is the last-resort
     fallback if neither image loads. */
  cursor:
    -webkit-image-set(
      url(images/cursors/pointer.png) 1x,
      url(images/cursors/pointer@2x.png) 2x) 0 0,
    url(images/cursors/pointer.png) 0 0,
    auto !important;
  font-family: "Atkinson Hyperlegible", sans-serif;
  position: relative;
  background: url("images/backgrounds/bg84.gif"), linear-gradient(#cce4ff, rgba(250, 250, 250, 0));
  background-attachment: fixed;
  display: flex;
  flex-flow: column;
  animation: slide 200s linear infinite;
  align-items: center;
}

.container--home {
  background: transparent;
    align-items: center;
  display: grid;
  gap: 1em;
  grid-template-columns: auto auto;
  margin-block: 10px;
    margin-top: 10px;
  margin-inline: auto;
  width: 1370px;
  padding-top: 15px;
  padding-bottom: 10px;

  grid-auto-rows: minmax(min-content, auto);
  grid-template-areas: 

  "content content" 
  "content content" 
  "content content" 
  "content content" 
  "content content" 
  "content content" 
  "content content" 
;
  image-rendering: pixelated;
  grid-template-columns: var(--col-left) var(--col-cA) var(--col-cB) var(--col-right); 
}

/* Desktop-only: this offset used to live as an inline style on <main>
   (width:1280px; margin-left:400px). Moved here so the HTML is clean.
   Gated to >=900px so mobile stays full-width. */
/* ============================================================
   CABINET  —  fixed-width centered site frame (the template)
   ------------------------------------------------------------
   1200px composition, centered, wallpaper on the sides.
   Three columns by golden-ish ratio:
     left (fluff)  190px  |  center (canvas) 740px  |  right (nav) 230px
   Desktop-only; below 1250px it collapses to the mobile stack.
   ============================================================ */
@media (min-width: 1250px){
  .cabinet{
    width: 1240px;                  /* widened +40 to give the left column room */
    max-width: 1240px;
    margin: 0 auto;
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: flex-start;
    gap: 15px;
    box-sizing: border-box;
  }

  /* Left column — fluff gadgets (moon widget, stamps, astro widget) */
  .cabinet > .leftsidebox--home{
    position: static;
    width: 230px;                   /* wider so the astro.com widget fits */
    min-width: 230px;
    height: auto;
    margin: 0;
    padding: 0;
    top: auto; left: auto; right: auto;
    z-index: auto;
    order: 0;
    flex: 0 0 230px;
    overflow: hidden;               /* keep gadgets/marquee inside the column */
  }
  .cabinet > .leftsidebox--home .stamps-wrap,
  .cabinet > .leftsidebox--home .transits,
  .cabinet > .leftsidebox--home marquee{
    max-width: 230px;
    overflow: hidden;
    box-sizing: border-box;
  }
  .cabinet > .leftsidebox--home iframe,
  .cabinet > .leftsidebox--home img{
    max-width: 100%;
    height: auto;
  }

  /* Center column — the canvas */
  .cabinet > .container--home{
    width: 740px;
    min-width: 740px;
    margin: 0;
    padding: 0;
    order: 1;
    flex: 0 0 740px;
  }
  .cabinet > .container--home .content--home{
    width: 740px;
    max-width: 740px;
    padding: 0;
  }

  /* Right column — nav gadgets (menu, about, music) */
  .cabinet > .rightsidebox--home{
    position: static;
    width: 230px;
    min-width: 230px;
    height: auto;
    margin: 0;
    padding: 0;
    top: auto; right: auto; left: auto;
    z-index: auto;
    order: 2;
    flex: 0 0 230px;
  }

  /* ============================================================
     REFLOW  —  make old hardcoded box widths fit the new columns
     ------------------------------------------------------------
     Everything below only runs on the >=1250px desktop frame.
     Mobile (<1250px) keeps its own single-column rules untouched.
     Old widths were sized for the pre-cabinet 1080–1370px layout;
     here we pull them back into 190 / 740 / 230.
     ============================================================ */

  /* ---- CENTER COLUMN (740px canvas) ---- */

  /* content box: was 1080px -> fill the 740 canvas */
  .cabinet .content--home{
    width: 100%;
    max-width: 740px;
    box-sizing: border-box;
  }

  /* intro banner: was 1040px */
  .cabinet .introgradbox{
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    height: auto;
    min-height: 110px;
  }

  /* site title: inline was 2.6em -> shrink to fit one line in 740 */
  .cabinet .introgradbox h1{
    font-size: 1.7em !important;
    line-height: 1.15;
    margin: 6px 0;
    word-break: break-word;
  }

  /* webmaster's corner grid: was 650px */
  .cabinet .webmasterframe{
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    height: auto;
  }
  .cabinet .btns{
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
  }

  /* below-canvas fluff pieces: keep inside 740 */
  .cabinet .todos{
    width: auto;
    max-width: 100%;
    box-sizing: border-box;
  }
  .cabinet .--home .chatbox,
  .cabinet .socialsbox,
  .cabinet .buttonbox{
    max-width: 100%;
    box-sizing: border-box;
  }
  /* readingsbox already width:auto; just tame its side margins in the tighter column */
  .cabinet .readingsbox{
    margin-left: 10px;
    margin-right: 10px;
  }

  /* ---- LEFT COLUMN (230px fluff) ---- */

  /* transits panel: fill the wider column, grow tall enough for the
     astro.com widget to actually render (it needs real height) */
  .cabinet .leftsidebox--home .transits{
    width: 100%;
    max-width: 230px;
    height: auto;
    min-height: 900px;   /* room for moon + stamps + astro widget */
    box-sizing: border-box;
    margin-top: 0;
    padding: 10px;
  }
  /* moon widget + astro.com ppos iframes: use the full column width */
  .cabinet .leftsidebox--home iframe{
    width: 100%;
    max-width: 208px;   /* 230 minus transits padding/border */
    box-sizing: border-box;
  }
  /* the astro.com ppos iframe needs a taller box to show all planet rows */
  .cabinet .leftsidebox--home .planets iframe{
    min-height: 340px;
  }
  .cabinet .leftsidebox--home .planets,
  .cabinet .leftsidebox--home .planets > div{
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
  }

  /* ---- RIGHT COLUMN (230px nav) ---- */

  /* neutralize the old position:fixed / width:360px base rule */
  .cabinet .rightsidebox--home{
    position: static;
  }
  /* main menu: inline height/width 333px -> fit 230 */
  .cabinet #mainmenu{
    width: 100% !important;
    max-width: 230px;
    height: auto !important;
    box-sizing: border-box;
  }
  /* about box: was 305px */
  .cabinet .aboutbox{
    width: 100%;
    max-width: 230px;
    height: auto;
    box-sizing: border-box;
  }
  /* music player: was 260px, close enough but clamp it */
  .cabinet #musicplayer{
    width: 100%;
    max-width: 230px;
    box-sizing: border-box;
  }
}


h1.hl,
h2.hl,
h3.hl {
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  color: hsl(317, 90%, 53%);
  margin-bottom: 0.5em;
}
h1.hl::after,
h2.hl::after,
h3.hl::after {
  content: "";
  display: block;
  background: linear-gradient(90deg, hsl(317, 90%, 53%), rgba(255, 255, 255, 0));
  height: 3px;
}

.rightsidebox--home {
  font-family: daydream, serif;
    font-size: 1rem;
  gap: 4px;
  display: flex;
  flex-direction: column;
position: fixed;
top: 0;
right: 0;
width: 360px;
height: 800px;
padding: 0px;
margin-top: 100px;
margin-right: 45px;
z-index: 100;
  align-items: center;
   color:#5A3268;
}


/* THE SOLAR SYSTEM GIF (gf8.gif, above the gameboy).

   TWO BUGS FIXED HERE.

   1. This rule was DEAD. It said `boarder-radius` and `boarder` — both
      misspelled — and the missing semicolon after `boarder: dotted 1px`
      swallowed the `justify-content` line too. None of it was applying.

   2. The gif is 240x180 NATIVE and was being painted at 202x152. That is a
      0.84x FRACTIONAL DOWNSCALE on a 167-frame animation, which shimmers —
      exactly what the integer-scaling rule exists to prevent. The legal
      sizes are 240 and 120, and nothing in between.

      120 is too small to read. So instead of crushing the SPRITE, the
      FRAME gives way: the img renders at its native 240 and the container
      lets it sit at full size. (Match the frames, not the sprites.)

   The asset itself was also 1.30MB / 500 frames — the heaviest thing in
   this column. Now 281KB / 167 frames: every 3rd frame kept, delays
   tripled, so the 20-second loop runs at EXACTLY the same speed. 78%
   smaller, visually identical. */
.fluff{
  display: flex;
  justify-content: center;
  margin: 0 0 6px 0;
  width: 100%;
  overflow: visible;      /* let the oversized sprite hang out, do not clip it */
}
.fluff img{
  /* !important because a later global `img { max-width: 100% }` was winning
     on cascade order and crushing this to 202px — a 0.84x fractional
     downscale on a 167-frame animation, which shimmers. The integer rule
     says 240 or 120, nothing between. 120 is unreadably small, so it holds
     at NATIVE 240 and overhangs the 207px column slightly, centred by the
     negative margin below. Resize the frame, never the sprite. */
  width: 240px !important;
  height: 180px !important;
  max-width: none !important;
  box-sizing: content-box;   /* border-box would silently upscale it */
  image-rendering: pixelated;

  /* THE BORDER-CLIP BUG. This was `margin-left: -17px`, and a negative MARGIN
     still participates in layout — the 240px sprite in a 207px column dragged
     .transits wider than .leftsidebox--home, whose `overflow: hidden` then
     SCISSORED 2px off the left edge of the purple border down the whole
     column. Ashley spotted the chopped border.

     `position: relative` + `left` offsets the sprite WITHOUT feeding into
     layout. It can now overhang its column without MOVING its column. That is
     the whole difference between a sprite that hangs over an edge and a sprite
     that moves the edge. */
  position: relative;
  left: -17px;
  margin-left: 0;
  flex: 0 0 auto;
}


.planets {
  margin-top: -38px;
}

.planets .stroke {
  font-family: "daydream", serif;
  letter-spacing: 4px;
  color: #fff;
  text-shadow: 1px 0 #5A3268, 0 1px #5A3268, 1px 1px #5A3268, -1px -1px #5A3268, -1px 0 #5A3268, 0 -1px #5A3268, 2px 3px #5A3268;
  position: relative;
    margin-top: -20px;
  font-size: 1rem;}

.transits {
  border-radius: 6px;
  border: 4px double #a348c1;
  outline-offset: -9px;
  outline: 1px solid #000000;
  background-image: url("images/backgrounds/bg26.gif");
  width: 225px;
  height: 888px;
  padding: 5px;
      margin-top: 45px;
      margin-right: 5px;
}

:root {
        --titlebar-gradient: linear-gradient(90deg, #6a1535, #b23965, #f9a5b9);
        --dialog-blue: #6a1535;
        --dialog-accent: white; 
        --titlebar-color: white; 
        --player-image: url(images/pfp/shiny.gif);

        --surface: #c0c0c0;
        --button-highlight: #ffffff;
        --button-face: #dfdfdf;
        --button-shadow: #808080;
        --light-button-shadow: #a9a9a9;
        --window-frame: #0a0a0a;
        --border-field: inset -1px -1px var(--button-highlight),
            inset 1px 1px var(--button-shadow), inset -2px -2px var(--button-face),
            inset 2px 2px var(--window-frame);
    }

    @font-face {
  font-family: "Pixelated MS Sans Serif";
  src: url("https://files.catbox.moe/qgjjre.woff") format("woff");
  src: url("https://files.catbox.moe/8fwbkl.woff2") format("woff2");
    font-weight: normal;
    font-style: normal;
}

@font-face {
    font-family: "Pixelated MS Sans Serif";
    src: url("https://files.catbox.moe/z7csle.woff") format("woff");
    src: url("https://files.catbox.moe/moqhx6.woff2") format("woff2");
    font-weight: bold;
    font-style: normal;
    }

    #musicplayer {
  font-family: "Pixelated MS Sans Serif", Arial;
  -webkit-font-smoothing: none;
  font-size: 11px;
  color:var(--window-frame);
  width: fit-content;
  border:var(--window-frame) solid 1px;
    box-shadow: inset -1px -1px var(--window-frame), inset 1px 1px var(--button-face), inset -2px -2px var(--button-shadow), inset 2px 2px var(--button-highlight);
  background: var(--surface);
  padding: 3px;
  width: 260px;
  margin-top: 10px;
    }

    .title-color {  
  padding: 3px 2px 3px 3px;
  display: flex;
  justify-content: space-between;
  align-items: center;
    background: var(--titlebar-gradient);
    margin-bottom: 2px;
    font-weight: bold;
}

.actual-title {
    color:var(--titlebar-color);
  display: flex;
  gap: 5px;
}

.seeking {
    font-weight: normal;
    font-size: 11px;
  display: flex;
  justify-content: center;
  align-items: center;
    gap: 5px;
    margin: 12px 0;
}

.player-flex {
    display: flex;
    gap: 3px;
}

.player-main {
    width: calc(100% - 100px);
    margin-top: 1px;
}

.player-icon-holder {
  border: 2px solid var(--surface);
  width:95px;
  height:95px;
  padding: 1px;
  background-image:linear-gradient(-45deg, var(--button-face) 50%, var(--window-frame) 50%);
  background-size:cover;
  background-repeat:no-repeat;
  border-width:1px;
  border-style:solid;
  border-color: var(--button-shadow) var(--button-highlight) var(--button-highlight) var(--button-shadow);
  
  overflow: hidden;
  position:relative;
  z-index: 2;
}

.player-icon {
    background-image:var(--player-image);
    background-size: contain;
    background-repeat: no-repeat;
  width:95px;
  height:95px;
  display: display;
}

.track-select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  border: none;
  border-radius:0;
  box-sizing: border-box;
  outline:0;
  
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;

  position: relative;
  padding: 3px 4px;
  padding-right: 32px;
  background-image: url("https://loveberry.nekoweb.org/assets/musicplayer/button-down.svg");
  background-position: top 2px right 2px;
  background-repeat: no-repeat;
  width:100%;

  box-shadow: var(--border-field);
  background-color: var(--button-highlight);
  height: 21px;

  font-family: "Pixelated MS Sans Serif", Arial;
  -webkit-font-smoothing: none;
  font-size: 11px;
  font-weight: lighter;
}

.track-select:focus {
  color: var(--dialog-accent);
  background-color: var(--dialog-blue);

}
.track-select:focus option {
  color: var(--window-frame);
  background-color: var(--button-highlight);  
  outline: 1px;
  border: none;
}

.track-select:active {
  background-image: url("https://loveberry.nekoweb.org/assets/musicplayer/button-down-active.svg");
}

#musicplayer input[type="range"] {  
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  background: transparent;
}

#musicplayer input[type="range"]:focus {
  outline: none;
}

#musicplayer input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  height: 21px;
  width: 11px;
  background: url("https://loveberry.nekoweb.org/assets/musicplayer/indicator-horizontal.svg");
  transform: translateY(-8px);
  box-shadow: none;
  border: none;
}

#musicplayer input[type="range"].has-box-indicator::-webkit-slider-thumb {
  background: url("https://loveberry.nekoweb.org/assets/musicplayer/indicator-rectangle-horizontal.svg");
  transform: translateY(-10px);
}

#musicplayer input[type="range"]::-moz-range-thumb {
  height: 21px;
  width: 11px;
  border: 0;
  border-radius: 0;
  background: url("https://loveberry.nekoweb.org/assets/musicplayer/indicator-horizontal.svg");
  transform: translateY(2px);
}

#musicplayer input[type="range"].has-box-indicator::-moz-range-thumb {
  background: url("https://loveberry.nekoweb.org/assets/musicplayer/indicator-rectangle-horizontal.svg");
  transform: translateY(0px);
}

#musicplayer input[type="range"]::-webkit-slider-runnable-track {
  width: 100%;
  height: 2px;
  box-sizing: border-box;
  background: var(--window-frame);
  border-right: 1px solid var(--button-shadow);
  border-bottom: 1px solid var(--button-shadow);
  box-shadow: 1px 0 0 var(--button-highlight), 1px 1px 0 var(--button-highlight), 0 1px 0 var(--button-highlight), -1px 0 0 var(--light-button-shadow),
    -1px -1px 0 var(--light-button-shadow), 0 -1px 0 var(--light-button-shadow), -1px 1px 0 var(--button-highlight), 1px -1px var(--light-button-shadow);
}

#musicplayer input[type="range"]::-moz-range-track {
    width: 100%;
    height: 2px;
    box-sizing: border-box;
    background: var(--window-frame);
    border-right: 1px solid var(--button-shadow);
    border-bottom: 1px solid var(--button-shadow);
    box-shadow: 1px 0 0 var(--button-highlight), 1px 1px 0 var(--button-highlight), 0 1px 0 var(--button-highlight), -1px 0 0 var(--light-button-shadow),
    -1px -1px 0 var(--light-button-shadow), 0 -1px 0 var(--light-button-shadow), -1px 1px 0 var(--button-highlight), 1px -1px var(--light-button-shadow);
}

.window-button {
    background: var(--surface);
    border: none;
    border-radius: 0;
    box-shadow: inset -1px -1px var(--window-frame),inset 1px 1px var(--button-highlight),inset -2px -2px var(--button-shadow),inset 2px 2px var(--button-face);
    box-sizing: border-box;
    color: inherit;
    text-shadow: 0 0 var(--window-frame);
}

.window-button:active {
  box-shadow: inset -1px -1px var(--button-highlight), inset 1px 1px var(--window-frame), inset -2px -2px var(--button-face), inset 2px 2px var(--button-shadow);
  text-shadow: 1px 1px var(--window-frame);
}

.window-button:focus  {
  outline: 1px dotted #000;
  outline-offset: -4px;
}
.player-buttons {
    display: flex;
    gap: 10px;
    margin: 5px 5px 0 5px;
}
/* THE OVERFLOW BUG: these were `width: 33%`. Three buttons at 33% = 99% of
   the container, and THEN flex adds two 10px gaps on top of that, plus the
   10px of horizontal margin above. 99% + 30px will always overflow — the
   buttons were spilling ~6px out the right side of the window.

   The fix is to stop hardcoding a percentage and let flex do the division:
   `flex: 1` shares the space that is ACTUALLY left after the gaps, and
   `min-width: 0` lets a flex item shrink below its content width (without
   it, the padding + svg would hold the button open and it would overflow
   anyway). This is the correct way to split a flex row into equal parts. */
.player-buttons .window-button  {
    flex: 1 1 0;
    min-width: 0;
    width: auto;
    font-size: 10px;
    padding: 5px 4px;
}
.player-buttons .window-button svg {
   width: 15px;
   height: 15px;
}

.title-bar-controls {
    display: flex;
}
.close {
    margin-left: 2px;
    background-image: url("https://loveberry.nekoweb.org/assets/musicplayer/close.svg");
    background-repeat: no-repeat;
    background-position: top 3px left 4px;

    padding: 0;
    display: block;
    min-width: 16px;
    min-height: 14px;
}

.minimize {
  background-image: url("https://loveberry.nekoweb.org/assets/musicplayer/minimize.svg");
  background-repeat: no-repeat;
  background-position: bottom 3px left 4px;
    padding: 0;
    display: block;
    min-width: 16px;
    min-height: 14px;
}

.maximize {
  background-image: url("https://loveberry.nekoweb.org/assets/musicplayer/maximize.svg");
  background-repeat: no-repeat;
  background-position: top 2px left 3px;
    padding: 0;
    display: block;
    min-width: 16px;
    min-height: 14px;
}


.aboutbox {
  font-family: retrocomputer, serif;
  font-size: .8em;
  border-radius: 8px;
  border: 4px double #d81888;
  outline-offset: -3px;
  outline: 1px solid #d81888;
  background: #000000;
  width: 305px; 
 height:420px;
  color: #ffffff;
  padding-left: 15px;
  padding-right: 5px;
  margin-top: 20px;
}

.aboutbox .stroke {
  font-family: "nes", serif;
   font-size: 1.8em;
  letter-spacing: 4px;
  color: #ff51b7; 
  text-shadow: 1px 0 #5A3268, 0 1px #5A3268, 1px 1px #5A3268, -1px -1px #5A3268, -1px 0 #5A3268, 0 -1px #5A3268, 2px 3px #5A3268;
  position: relative;
  padding: 5px;
}

.aboutimage {
    border-image: 1px dashed #2eb3b8;
    margin-top: -15px; 
}

.webmasterframe {
   grid-area: frame; 
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-areas:
    "todos chat"
    "socials chat"
    "socials friends";
  gap: 2px;
  align-items: center; 
float: auto;
width: 650px;
margin-top: 5px;
height: 530px;
  color: #5A3268;
  background: url("images/backgrounds/bg87.gif");
border : 2px dashed #c6bbaa; 
}  

.wm-todos   { grid-area: todos; }

.wm-chatbox   { grid-area: chat; 
position: relative; }

.wm-socials   { 
  grid-area: socials; 
    position: relative;
  justify-self: center;
  align-self: start;
  padding: 10px;
}

.wm-friends { 
  grid-area: sticker;
  position: relative;
  justify-self: center;
  align-self: start;
}

.crystalstickerleft{
  position: absolute;
  width: 145px;
  top: -60px;
  left: -430px;
  z-index: 10;
  pointer-events: none;
  transform: rotate(5deg);
}

.crystalsticker-containerleft{
  position: relative;
  height: 0;
  margin-top: -60px;
  margin-left: 20px;
  z-index: 1000;
}

.crystalstickerright{
  position: absolute;
  width: 145px;
  top: -70px;
  left: 940px;
  z-index: 10;
  pointer-events: none;
  transform: rotate(5deg);
}

.crystalsticker-containerright{
  position: relative;
  height: 0;
  margin-top: -60px;
  margin-left: 20px;
  z-index: 1000;
}


@keyframes songscroll {
  0% {
    transform: translateX(240px);
  }
  100% {
    transform: translateX(-200px);
  }
}

.btns {
  grid-area: btns; 
  width: 650px;
      border-top: 3.5px double #000;
      border-left: 3.5px double #000;
      border-right: 3.5px double  #000;
      border-bottom: 3.5px double  #000;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

.btns marquee {
  background: url("images/backgrounds/bg26.gif");
  border-radius: 8px;
}


.content--home {
  width: 1080px;
  background: url("images/backgrounds/bg57.gif");
  border-radius: 8px;
  border: 4px double var(--pink);
  outline-offset: -3px;
  outline: 1px solid #fff;
  grid-area: content;
  padding: 15px;
}

.readingsbox {
  width: auto;
      height: auto; 
        overflow: visible; 
  border: 4px double var(--purple, #d81888);
  outline: 1px solid #fff;
  outline-offset: -3px;
  padding: 1em;
  font-family: basiic; 
  margin-left: 20px; 
  margin-right: 20px;
  margin-bottom:10px; 
  margin-top: 40px;
  background: linear-gradient(
    300deg,
    #e8c0c8,
    #C7C4D9,
    #f6ebbe,
    #f2c0bf,
    #f5f4d6,
    #FFFFFA
  );
  background-size: 360% 360%;
  animation: gradient-animation 36s ease infinite;
  will-change: background-position;
}

@keyframes gradient-animation {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}  

.readingsbox h3 {
  font-family: "cherrybomb", serif;
  font-size: 2.4em;
  letter-spacing: 4px;
  color: #fff;
  text-shadow: 1px 0 #5A3268, 0 1px #5A3268, 1px 1px #5A3268, -1px -1px #5A3268, -1px 0 #5A3268, 0 -1px #5A3268, 2px 3px #5A3268;
  position: absolute;
  margin-top: -40px;
}

.todos {
      overflow: scroll;
      overflow-x: hidden;
    background: url(images/backgrounds/bg24.gif); 
      height: 150px;
      width: 295px;
      margin: 18px;
      padding: 1rem;
      border-top: 3.5px double #000;
      border-left: 3.5px double #000;
      border-right: 3.5px double pink;
      border-bottom: 3.5px double pink;
        line-height: 1.35;
    }

.update-date {
  font-family: "grapesoda" , sans-serif;
  font-size: 2.5rem;
  font-weight: 600;
  color: #5A3268;
}

.buttonbox {
  width: 250px;
height: 100px;
margin-left: 50px;
  border-radius: 10px;
  border: 4px double var(--teal);
  outline-offset: -3px;
  outline: 1px solid #fff;
  background-color: var(--yello);
    background: url(images/backgrounds/bg81.jpg); 
}

.socialsbox {
  width: 290px;
height: 320px;
  border-radius: 10px;
  border: 4px double var(--teal);
  outline-offset: -3px;
  outline: 1px solid #fff;
  padding: 1em;
  background-color: var(--yello);
    background: url(images/backgrounds/bg58.png); 
}

.box-inner-2{
    background-image:url(https://foollovers.com/mat/t-frame/frame09-c/back01.png),url(https://foollovers.com/mat/t-frame/frame09-c/back01.png);
    background-repeat:no-repeat,no-repeat;
    background-position:left top,right bottom;
    margin-top:-12px;
    padding:10px;}

.time{
  float: left;
    background:#fff;
    border:1px dotted #999;
    border-radius:10px;
    text-align:center;
    width:170px;
    height: 60px;
    margin-right: 10px;
  }

.fireflysticker{
  position: absolute;
  z-index: 10;
  pointer-events: none;
}

.fireflysticker-container{
  float: left;
  position: relative;
  height: 120px;
  margin-left: 280px;
  margin-top: -135px;
  z-index: 1000;
}

.friendssticker {
  display: block;
  max-width: 100%;
  height: auto;
  margin-top: -10px;
  margin-left: 10px;
}

.coloredearthsticker {
  display: block;
  max-width: 100%;
  height: auto;
}

.messagesticker{
  position: absolute;
  z-index: 10;
  pointer-events: none;
}


.messagesticker-container{
  float: right;
  position: relative;
  z-index: 1000;
  margin-right: 80px;
}

.emailsticker{
  position: absolute;
  z-index: 10;
  pointer-events: none;
}


.emailsticker-container{
  position: relative;
margin-top: -40px;
  margin-left: 260px;
  z-index: 1001;
}    


ul.circle {
  list-style-type: circle;
  padding: 1rem;
}

.--home .entrybox {
  margin-top: 1em;
float: right;
  box-shadow: 1px 4px 1px rgba(255, 255, 255, 0.341);
}

.--home .chatbox {
height: 420px; 
width: 300px;
background: url("images/backgrounds/bg88.png");
border:3px solid #FFE4DB;
border-radius:10px;
box-shadow: 0px 0px 10px pink, 0px 0px 10px pink, 0px 0px 10px pink, 0px 0px 10px pink, 0px 0px 14px pink;
color:black;
overflow-y:auto;
padding: 10px;
margin-top: 15px;
}

  .siteButton {
    width: 200px; 
    background-color:black;
    color:#79DC77;  
  }


.disclaimer p{
  display: flow-root;
  margin: 10px;
  font-size: 1em;
  line-height: 1.35;
}

.disclaimer img{
  float: right;
  width: clamp(90px, 30%, 160px);
  margin: 0 0 6px 10px;
  display: block;
}


.entry h3 {
  font-family: "daydream", serif;
  font-size: 1.3em;
  letter-spacing: 4px;
  color: #fff;
  text-shadow: 1px 0 #5A3268, 0 1px #5A3268, 1px 1px #5A3268, -1px -1px #5A3268, -1px 0 #5A3268, 0 -1px #5A3268, 2px 3px #5A3268;
  position: absolute;
  margin-top: -33px;
}

.--home .entry a {
  color: #0FA3A3;
}
.--home .entry a:hover {
  color: #0FA3A3;
}

.entry h3,
.entry h2 {
  color: initial;
}

.site-title.bounce mark {
  display: inline-block;
  animation: bounce-anim 2.5s infinite;
  animation-timing-function: cubic-bezier(0.25, 0.85, 0.4, 1);
  background: none;
}

.site-title.bounce mark:nth-child(1) { animation-delay: 0s; }
.site-title.bounce mark:nth-child(2) { animation-delay: 0.2s; }
.site-title.bounce mark:nth-child(3) { animation-delay: 0.4s; }
.site-title.bounce mark:nth-child(4) { animation-delay: 0.6s; }
.site-title.bounce mark:nth-child(5) { animation-delay: 0.8s; }
.site-title.bounce mark:nth-child(6) { animation-delay: 1s; }
.site-title.bounce mark:nth-child(7) { animation-delay: 1.2s; }
.site-title.bounce mark:nth-child(8) { animation-delay: 1.4s; }
.site-title.bounce mark:nth-child(9) { animation-delay: 1.6s; }
.site-title.bounce mark:nth-child(10) { animation-delay: 1.8s; }
.site-title.bounce mark:nth-child(11) { animation-delay: 2s; }
.site-title.bounce mark:nth-child(12) { animation-delay: 2.2s; }
.site-title.bounce mark:nth-child(13) { animation-delay: 2.4s; }


.bounce mark {
  display: inline-block;
  animation: bounce-anim 2.5s infinite;
  animation-timing-function: cubic-bezier(0.25, 0.85, 0.4, 1);
  background: none;
}

@keyframes bounce-anim {
  0% {
    transform: scale(1) translateY(0);
  }
  20% {
    transform: scale(1.1, 0.9) translateY(0);
  }
  40% {
    transform: scale(0.9, 1.1) translateY(-10px);
  }
  60% {
    transform: scale(1.05, 0.95) translateY(0);
  }
  80% {
    transform: scale(1) translateY(-3px);
  }
  100% {
    transform: scale(1) translateY(0);
  }
}

.site-title.bounce-sequence {
  text-align: center;
  font-family: basiic, sans-serif;
  font-size: 1.3em;
  margin-top: 1em;
  color: #fff;
}

.site-title.bounce-sequence mark {
  display: inline-block;
  background: none;
  animation: bounceWave 10s ease-in-out infinite;
  animation-fill-mode: both;
  text-shadow: 0 0 0 transparent;
}

.site-title.bounce-sequence mark:nth-child(1) { animation-delay: 0s; }
.site-title.bounce-sequence mark:nth-child(2) { animation-delay: 0.2s; }
.site-title.bounce-sequence mark:nth-child(3) { animation-delay: 0.4s; }
.site-title.bounce-sequence mark:nth-child(4) { animation-delay: 0.6s; }
.site-title.bounce-sequence mark:nth-child(5) { animation-delay: 0.8s; }
.site-title.bounce-sequence mark:nth-child(6) { animation-delay: 1s; }
.site-title.bounce-sequence mark:nth-child(7) { animation-delay: 1.2s; }
.site-title.bounce-sequence mark:nth-child(8) { animation-delay: 1.4s; }
.site-title.bounce-sequence mark:nth-child(9) { animation-delay: 1.6s; }
.site-title.bounce-sequence mark:nth-child(10) { animation-delay: 1.8s; }
.site-title.bounce-sequence mark:nth-child(11) { animation-delay: 2s; }
.site-title.bounce-sequence mark:nth-child(12) { animation-delay: 2.2s; }
.site-title.bounce-sequence mark:nth-child(13) { animation-delay: 2.4s; }

@keyframes bounceWave {
  0% {
    transform: translateY(0);
    text-shadow: 0 0 0 transparent;
  }
  10% {
    transform: translateY(-12px);
  }
  20% {
    transform: translateY(0);
  }
  25% {
    transform: scale(1.05);
  }
  30% {
    transform: scale(1);
  }
  70% {
    text-shadow: 0 0 0 transparent;
  }
  80% {
    text-shadow: 0 0 6px #ffcba4, 0 0 12px #ffcba4;
  }
  90% {
    text-shadow: 0 0 3px #ffcba4;
  }
  100% {
    text-shadow: 0 0 0 transparent;
  }
}


/* THE CARRIAGE. Each link box is UNLIT until its turn comes, then it
   lights up at the exact moment its own text starts typing. That is what
   makes the menu read as a typewriter working DOWN the page rather than
   as a block of text that fades in.
   Visibility, not display: the row keeps its space, so nothing jumps. */
.sidelinks li.is-unlit > a{
  opacity: 0;
  transform: translateX(-6px);
}
.sidelinks li > a{
  opacity: 1;
  transform: translateX(0);
  transition: opacity .18s ease, transform .18s ease;
}
/* the title holds until the very end */
#bouncy-title.is-unlit{
  opacity: 0;
}
#bouncy-title{
  opacity: 1;
  transition: opacity .35s ease;
  /* the brand is one span per letter (inline-block). in a tight column
     the letters wrap and "PLANETS" drops to a second line - that is the
     squish in the screenshot. nowrap keeps it one line no matter what
     the column does or when navigator.js injects it. */
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce){
  .sidelinks li.is-unlit > a,
  #bouncy-title.is-unlit{ opacity: 1; transform: none; }
}

/* =====================================================================
   THE CRT POWER-ON — the menu box booting up.

   This is the EASTER EGG half of the menu animation. It does not run on
   every page load (see js/navigator.js): ~1 in 20 loads, on clicking the
   "Main Menu" label, on returning from feeding konpeibii, or on konami.

   Deliberately RESTRAINED. A little static, not a light show. The whole
   effect is ~640ms and most of that is the type-in. The point is the
   flash before the signal locks — the moment the machine turns on —
   not the noise itself.
   ===================================================================== */

/* 1. STATIC. A CSS-generated noise field, no asset needed: repeating
      hard-stop gradients at odd angles beat against each other and
      produce interference. Kept at low opacity — you should read it as
      "signal is finding itself", not "the screen is broken". */
/* THE TUBE IS DARK WHEN IT WAKES.

   THIS WAS THE BUG. The static was pale gradients at opacity .55 laid over
   the menu's own LIGHT background — technically running for a full 1.6
   seconds, and effectively invisible. Ashley found the egg and could not
   see what she had found.

   A CRT does not warm up over a white page. It warms up in the DARK. So
   the ::after now paints a near-black ground FIRST and the noise on top of
   that, at full opacity. Same 1.6s, but you can actually see it now. */
.sidebar--home.is-booting::after{
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  border-radius: 8px;
  opacity: 1;

  /* the dark tube, then the noise on top of it */
  background-color: #0a0710;
  background-image:
    /* bright scanline grain */
    repeating-linear-gradient(0deg,
      rgba(255,255,255,.55) 0px, rgba(255,255,255,.55) 1px,
      transparent 1px, transparent 3px),
    /* vertical interference */
    repeating-linear-gradient(97deg,
      rgba(199,164,255,.40) 0px, rgba(199,164,255,.40) 1px,
      transparent 1px, transparent 4px),
    /* the pink channel bleeding */
    repeating-linear-gradient(13deg,
      rgba(224,0,138,.45) 0px, rgba(224,0,138,.45) 2px,
      transparent 2px, transparent 6px),
    /* a soft glow in the middle, like the beam is finding itself */
    radial-gradient(ellipse at 50% 50%,
      rgba(159,245,200,.22) 0%, transparent 62%);
  background-size: 100% 3px, 4px 100%, 9px 100%, 100% 100%;
  animation: crt-noise .09s steps(3, end) infinite;
}
/* the noise jitters. steps() not ease — static does not EASE. */
@keyframes crt-noise{
  0%   { background-position: 0 0,    0 0,   0 0,  0 0; }
  33%  { background-position: 0 -2px, 3px 0, -4px 0, 0 0; }
  66%  { background-position: 0 1px,  -2px 0, 5px 0, 0 0; }
  100% { background-position: 0 2px,  4px 0, -2px 0, 0 0; }
}

/* the rolling scanline — a bright bar sweeping down the dark tube. Much
   more visible now that it has a dark ground to sweep across. */
.sidebar--home.is-booting::before{
  z-index: 4;
  background-image:
    linear-gradient(rgba(255,255,255,.85), rgba(255,255,255,0) 55%);
  background-repeat: no-repeat;
  background-size: 100% 34px;
  /* rolls through 3x across the 1600ms static */
  animation: crt-roll .32s linear 2;
}
@keyframes crt-roll{
  from { background-position: 0 -40px; }
  to   { background-position: 0 115%; }
}

/* 2. THE SIGNAL LOCKS. The classic CRT power-on snap: the picture
      collapses to a horizontal line, then springs open. This is the beat
      that actually sells it — more than the static does. */
.sidebar--home.is-locking{
  transform: scaleY(.02);
  filter: brightness(2.4);
  transition: none;
}
.sidebar--home{
  /* Springs back open when .is-locking is removed. Timed to SNAP_MS in
     navigator.js (420ms) — if you retune one, retune the other or the
     class will be removed before the spring has finished and the snap
     will look clipped. */
  transition: transform .42s cubic-bezier(.2, 1.5, .35, 1), filter .42s ease;
  transform-origin: center;
}

/* 3. THE SPINNER — loading.gif, 18x19 NATIVE, dead centre while the tube
      warms up. Ashley flagged this gif for exactly this: "if something is
      loading." It only exists for ~420ms. */
.sidebar--home .boot-spinner{
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 4;
  width: 18px !important;
  height: 19px !important;
  max-width: none !important;
  max-height: none !important;
  margin: -9px 0 0 -9px;          /* true centre */
  image-rendering: pixelated;
  pointer-events: none;
  opacity: 1 !important;          /* beats the sprite fade-in rule */
}

/* Respect the setting. A CRT boot is a flourish, not information — if
   someone has asked for less motion, they lose nothing by skipping it. */
@media (prefers-reduced-motion: reduce){
  .sidebar--home.is-booting::after,
  .sidebar--home.is-booting::before{ animation: none; }
  .sidebar--home.is-locking{ transform: none; filter: none; }
  .sidebar--home{ transition: none; }
}

/* The menu box.

   THE SHAVED TOP BUG: the frame below is a ::before with `inset: 0`, and
   it used to also carry `margin-top: -10px; margin-bottom: -7px`. Those
   negative margins dragged the frame OUTSIDE the box's own bounds — 10px
   above its top edge and 7px below its bottom. The frame then landed in
   the sticky right column, which has `overflow: hidden auto`, and got
   CLIPPED. That is why the top border vanished and "Main Menu" was sliced
   in half: the border was being drawn in space the parent refuses to show.
   (It is also where the phantom 7px of overflow came from —
   scrollHeight 215 vs clientHeight 208.)

   The fix is not to nudge the margins, it is to stop drawing outside the
   box at all. `inset: 0` with no negative margins keeps the frame inside
   its own bounds, and the padding here gives the label room to sit within
   the border instead of on top of it. */
.sidebar--home {
  position: relative;
  z-index: 1;
  background: none;
  color: #5A3268;
  box-sizing: border-box;
  padding: 14px 12px 12px 12px;   /* clearance so the frame never touches
                                     the "Main Menu" label */
}

.sidebar--home::before {
  content: "";
  position: absolute;
  inset: 0;                       /* stays INSIDE the box. no negative
                                     margins — that was the whole bug. */
  background-image: url("images/backgrounds/bg21.png");
  background-repeat: repeat;
  z-index: -1;
  border: 4px double #e0008a;
  border-radius: 8px;
}

.sidebar--home .stroke {
  font-family: "Daydream", serif;
     font-size: 1em;
  letter-spacing: 4px;
  color: #fff;
  text-shadow: 1px 0 #5A3268, 0 1px #5A3268, 1px 1px #5A3268, -1px -1px #5A3268, -1px 0 #5A3268, 0 -1px #5A3268, 2px 3px #5A3268;
  position: relative;
  margin-top: 30px;
}

/* MENU ICONS — a uniform 25x25 SLOT, with each gif at its own NATIVE size
   inside it.

   The icons are not all the same size (saturn is 25x25, the rest are
   20x20), so with no rule at all they rendered at native and the taller
   one made its row taller than the others. The lazy fix would be to force
   every icon to 20x20 — but that is a 0.8 FRACTIONAL downscale on 25px
   pixel art, i.e. shimmer, and it breaks the hard rule.

   So: fix the BOX, never the sprite. Every row gets an identical 25px
   slot; the 20px gifs sit centred inside with 2-3px of air. Rows are
   uniform, nothing is resized, and a future icon of any size up to 25px
   drops in without touching this.

   They start invisible and FADE in during the cascade (navigator.js,
   movement 2). The transition is what makes it a fade rather than a snap. */
#mainmenu .sidelinks a img,
.sidelinks a img{
  flex: 0 0 auto;
  box-sizing: content-box;      /* the global border-box rule inflates
                                   width:auto on images */
  width: auto;
  height: auto;
  max-width: 25px;
  max-height: 25px;
  image-rendering: pixelated;
  opacity: 0;
  transition: opacity .45s ease;
}
/* the slot itself: identical on every row, so row height cannot vary.
   35px = the 25px icon + 4px padding x2 + 2px of slack. Sized to the
   TALLEST icon, so the tall one no longer pushes its own row out. */
#mainmenu .sidelinks li a,
.sidelinks li a{
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 35px;
}

#mainmenu a,
.sidelinks a {
  font-family: "basiic", sans-serif;
  font-size: 1em;
 color: #0FA3A3;
  text-decoration: none;
    margin: 5px;
    padding: 4px;
    background-color: rgba(255, 255, 255, 0.6);
}

#btnbox {
  display: block;
  margin-inline: auto;
}



.leftsidebox--home {
  font-family: daydream, serif;
    font-size: 1em;
  gap: 4px;
  display: flex;
  flex-direction: column;
position: fixed;
top: 0;
left: 0;
width: 490px;
height: 900px;
z-index: 100;
  align-items: center;
   color:#5A3268;
   margin-top: 25px;
}

.sidebar--home ul {
  list-style: none;
  padding: 0;
}

.sidebar--home a {
  font-family: basiic, sans-serif;
  text-shadow: .1px
  font-size: 1.2rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 2px;
  color: #5A3268;
  padding: 2px 4px;
  border: 1px solid #5A3268;
  transition: padding-left 100ms, background-color 100ms;
}

.sidebar--home a:hover {
  background-color: #12B6B6;
  padding-left: 15px;
}


/* ------------------ stamps ------------------ */
@keyframes stampmarquee {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(calc(-100% - 1rem));
  }
}
.stampsbox {
  border-radius: 8px;
  background-color: rgb(170, 181, 255);
  background-image: url("images/backgrounds/yelo.png");
  border: 1px dotted #000000;
  margin-top: auto;
  padding: 0.3em;
  gap: 1rem;
  display: flex;
  overflow: hidden;
}





@keyframes slide {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: -564px -450px;
  }
}

.container--index {
  padding-block: 1em;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.3em;
  min-height: 100dvh;
  overflow: hidden;
}

.footer--index {
  background: url("images/backgrounds/29.png");
  text-align: center;
}


@keyframes scroll-left {
  0% {
    transform: translateX(100%);
  }
  100% {
    transform: translateX(-100%);
  }
}



/* navigator.js hardcodes a <br> between the link list and the title. It is
   in the JS, not the HTML, so the clean lever is here: collapse it and let
   .site-title's margin be the single source of truth for that gap. Two
   spacing mechanisms fighting is what shoved the title down in the first
   place. */
.sidelinks > br{
  display: none;
}

/* SITE TITLE bounce + glow */
.site-title {
  text-align: center;
  font-family: basiic, sans-serif;
  font-size: 1.3em;
  color: #5A3268;
  /* Was `margin-top: 1em` (20.8px) PLUS the <br> above (~15px), stacking
     to a 42px gap over the title while only 12px sat below — which shoved
     it down against the bottom of the box. With the <br> collapsed, this
     margin alone controls the gap. */
  margin-top: 0.9em;
}

.site-title.bounce-sequence mark {
  display: inline-block;
  background: none;
  animation: none;
  text-shadow: none;
  transition: transform 0.3s ease, text-shadow 0.3s ease;
}

@keyframes bounceLetter {
  0%   { transform: translateY(0); }
  30%  { transform: translateY(-14px); }
  60%  { transform: translateY(0); }
  100% { transform: translateY(0); }
}

.introgradbox {
  width: 1040px;  
  height: 110px;
 align-items: center;
  background-color: rgba(255, 255, 255, 0.9);
  background: url(images/backgrounds/bg54.jpeg);
  border: 4px double var(--purple, #5A3268);
  position: auto;
  overflow: hidden;
}


@media (max-width: 1249px){
  .intro-grid{
    grid-template-columns: 1fr;
  }
  .intro-right{
    padding-top: 0;
  }
}

.intro-flex1{
  display: flex;
  gap: 12px;
  padding-left: 10px;
  padding-right: 10px;
padding-top: 30px;
}



.intro-card-wrap{
  flex: 0 0 auto;
  height: 358px;
  margin-left: 35px;
  margin-top: -10px;
}

@media (max-width: 1249px){
  .introbar{ 
    flex-direction: column; 
  }
  
  .cam-wrap{ 
    align-self: flex-start; 
  }

}

.introbar{
  display: grid;
  grid-template-columns: 444px 1fr;
  gap: 12px;
  align-items: start;
}

.introbar .gradbox{ 
  flex: 1 1 0; 
}



@media (max-width: 1249px){
  .introbar{
    grid-template-columns: 1fr;
  }
}

.w98-card.small {

  --card-w: 280px;      /* matches the flair box below (shared column edge) */
  --card-h: 230px;
  --portrait-h: 128px;

  width: var(--card-w);
  height: var(--card-h);
}


.w98-card[data-theme="pp"]{
  --pp-rose:   #e8c0c8;
  --pp-lav:    #C7C4D9;
  --pp-gold:   #f6ebbe;
  --pp-blush:  #f2c0bf;
  --pp-cream:  #f5f4d6;
  --pp-paper:  #FFFFFA;

  --paper: var(--pp-paper);
  --ink:   #222;
  --chrome: var(--pp-lav);
  --bevel-hi: var(--pp-cream);
  --bevel-lo: var(--pp-rose);
  --accent: var(--pp-gold);
  --accent-2: var(--pp-blush);

  --grid-bg: linear-gradient(#C7C4D933 1px, transparent 1px),
             linear-gradient(90deg, #C7C4D933 1px, transparent 1px);
  --grid-size: 12px;

  width: var(--card-w);
  height: 100%;
  background: var(--paper);
  color: var(--ink);
  border: 2px solid var(--chrome);
  box-shadow: 2px 2px 0 rgba(0,0,0,.25);
  font-family: basiic, system-ui, sans-serif;
  line-height: 1.2;
  display:flex;
  flex-direction:column;
}

.w98-bevel{
  border: 2px solid var(--bevel-lo);
  box-shadow:
    inset 2px 2px 0 var(--bevel-hi),
    inset -2px -2px 0 var(--bevel-lo);
  background: var(--paper);
}


.w98-bevel-inset{
  border: 2px solid var(--bevel-hi);
  box-shadow:
    inset 2px 2px 0 var(--bevel-lo),
    inset -2px -2px 0 var(--bevel-hi);
  background: var(--paper);
}

.w98-titlebar{
  display:flex; justify-content:space-between; align-items:center;
  padding:4px 6px; font-size:12px;
  background: var(--accent);
  border-bottom:2px solid var(--bevel-lo);
}


.w98-title{ display:flex; align-items:center; gap:6px; font-weight:700; }


.w98-controls{ display:flex; gap:4px; }


.w98-ctl{
  font:inherit; line-height:1; padding:0 6px; cursor:default;
  border:2px solid var(--bevel-lo); background:var(--paper);
  box-shadow:inset 2px 2px 0 var(--bevel-hi), inset -2px -2px 0 var(--bevel-lo);
  font-size:12px;
}

/* Card body is now ONE unified stat panel — the square portrait is
   floated inside it and the stats wrap around it (character-sheet style). */
/* Card body: photo spans the FULL WIDTH on top; below it the meters and the
   Big-3 sign box sit SIDE BY SIDE. */
/* Card body: bg21 texture, no white wash over it. */
.w98-body {
  flex: 1;
  padding: 0;
  background-image: url("images/backgrounds/bg21.png");
  background-repeat: repeat;
  background-size: auto;
  background-color: var(--paper);
  display: block;
}

/* =====================================================================
   THE BIG 3 — the character sheet's identity line.

   This replaces the hover-only prose blurb. A stat sheet reads NAME first,
   then who the character IS, then their numbers — so the big 3 sit
   directly under the name and above the meters.

   Three columns per row: glyph | label | sign. The glyph and label columns
   are FIXED width, so the signs line up in a hard column. That alignment
   is what makes it read as a SHEET rather than as three sentences. It is
   the whole trick.
   ===================================================================== */
.w98-big3{
  list-style: none;
  margin: 6px 0 0 0;
  padding: 0;
  display: grid;
  gap: 3px;
}
.w98-big3 li{
  display: grid;
  /* glyph | label | sign. The label column was a hard 52px, which made
     the row (16 + 52 + 16 + two 6px gaps + two 6px pads = 108px) wider
     than the 102px ul that holds it - so every row overflowed ~8px to
     the right and the sign glyph's white box poked out the side of the
     big-3 box. minmax(0, 1fr) lets the label take exactly the space
     that's left instead of forcing 52px and overflowing. */
  grid-template-columns: 16px minmax(0, 1fr) 16px;
  align-items: center;
  gap: 6px;
  padding: 3px 6px;
  background: rgba(255,255,255,.45);
  border: 1px solid rgba(90,50,104,.22);
  font-family: "basiic", monospace;
  font-size: 12.5px;                      /* floor */
  line-height: 1.2;
}
/* THE ZODIAC GLYPH — 16x16 native, and it must STAY 16x16.

   box-sizing: content-box is NOT optional. Images are globally
   border-box on this site, which makes `width: auto` on an <img>
   resolve to natural + border and silently upscale the sprite. A
   16px pixel glyph upscaled by even 1px shimmers. This is the same
   bug that was quietly inflating safecat.gif by 2px.

   Native size only. 16 or 32. There is nothing in between. */
.w98-big3 .zglyph{
  width: 16px;
  height: 16px;
  box-sizing: content-box;
  image-rendering: pixelated;
  display: block;
  border: 0;
  cursor: help;              /* the title= tooltip names the sign on hover */
}
.w98-big3 .glyph{
  text-align: center;
  font-size: 13px;
  color: #d81888;
  text-shadow: 0 1px 0 rgba(255,255,255,.8);
}
.w98-big3 .lbl{
  color: #6E6278;
  letter-spacing: .3px;
}
.w98-big3 .sign{
  font-weight: bold;
  color: #5A3268;
}
/* the name sits tight above the big 3 now, not floating away from it */
.w98-stats-hd .w98-name{
  margin: 0 0 2px 0;
}

.w98-stats{
  padding: 0;
  font-size: 11px;
  position: relative;
  display: block;        /* photo sits on top as a plain block */
  box-sizing: border-box;
  /* transparent so the bg21 texture on .w98-body shows through */
  background: transparent;
  border: 0;
  box-shadow: none;
}
/* the two lower panels sit SIDE BY SIDE under the photo again.

   THE HISTORY, so nobody re-breaks this:
   Side by side ORIGINALLY did not fit. The card's content box is 264px and
   the two panels wanted 268px+, because the Big-3's sign column had to hold
   the WORD "Sagittarius" (84px at 12.5px basiic, and 12.5 is the floor —
   below it, lowercase i and l are indistinguishable, so there was no
   shrinking left to do). The grid resolved that overflow by starving the
   meters to an 87px unreadable sliver AND clipping the Big-3 via
   `overflow: hidden`, which is what was guillotining "Sagittarius".

   I briefly STACKED them to fix it. That worked, but the card grew ~55px
   and covered the counter box. Wrong trade.

   THE ACTUAL FIX (Ashley's): the sign is now a 16px GLYPH, not a word. The
   sign column went 154px -> 16px, which gave back ~138px, which is more
   than enough for side by side. The meaning did not disappear — it moved to
   the SYMBOL KEY under the astrodienst widget, which is where a key belongs.

   So: if you ever put the sign WORD back in this box, side by side will
   overflow again. The glyph is what pays for this layout. */
.w98-stats-lower{
  display: grid;
  /* THE SPLIT. The Big-3 panel was 167px and the stats box 92px, but the
     Big-3's rows only USE 16 (glyph) + 52 (label) + 16 (sign) + gaps = ~96px.
     The other ~50px was dead space trailing after the zodiac glyph, while the
     HP/MAG meters were squeezed down to a 43px stub.

     So the width moves across: Big-3 gets what it actually needs, and the
     stats box takes the rest. The meters roughly double. Nothing else in the
     card changes size. */
  grid-template-columns: 118px minmax(0, 1fr);
  column-gap: 5px;
  align-items: start;
  padding: 6px;
  box-sizing: border-box;
}

/* PORTRAIT — spans the whole top of the card, EDGE TO EDGE, square,
   and NOT cropped (the whole photo shows). */
/* PORTRAIT — shown at its NATIVE size (180x180) so it's always pin-sharp.
   Thin frame, centered. No hover pop-out; hovering only opens the bubble. */
.w98-portrait{
  float: none;
  margin: 10px auto 12px auto;   /* breathing room top AND bottom (not touching the boxes) */
  padding: 0;
  display: block;
  position: relative;
  width: 180px;
  height: 180px;
  /* NOTE: no overflow:hidden — it would clip the hover speech bubble */
}
.w98-portrait img{
  width: 180px;
  height: 180px;          /* native size — no scaling, no crop, no blur */
  display: block;
  position: absolute;
  top: 0; left: 0;
  border: 1px solid var(--accent-2);   /* thin frame */
  box-shadow: 2px 2px 0 rgba(90,50,104,.22);
  background: none;
  cursor: help;
  z-index: 1;
  box-sizing: border-box;
  transition: none;
}
/* A whisper of CRT: faint scanlines over the photo, and a soft glow that only
   comes up on hover. Deliberately subtle — the BOOKING button is where the
   eye is supposed to land, so this hints "hover me" without competing. */
.w98-portrait::after{
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(46,40,56,.055) 0px,
    rgba(46,40,56,.055) 1px,
    transparent 1px,
    transparent 3px
  );
  box-shadow: inset 0 0 18px rgba(90,50,104,.12);   /* gentle vignette */
  transition: box-shadow .2s ease;
}
/* on hover the glass "lights up" a touch */
.w98-portrait:hover::after{
  box-shadow: inset 0 0 22px rgba(122,110,150,.28);
}
.w98-portrait:hover img{
  border-color: #7A6E96;
}
@media (prefers-reduced-motion: reduce){
  .w98-portrait::after{ transition: none; }
}
/* No hover pop-out on the photo anymore — it's already at native size.
   Hovering just opens the speech bubble. */

/* The SPEECH BUBBLE, its tail, its ghost copy and its typing caret all
   used to live here. All gone with the blurb — see index.html. The
   @keyframes blink-caret it used has gone with them; nothing else drove
   it. */

/* MOBILE — there's no hover on a phone, so the blurb can't be an easter egg
   there or nobody would ever read it. Show it as a normal caption under the
   photo instead (static, always visible, no typing). */
@media (max-width: 1249px){
  .w98-portrait{
    float: none;
    width: 100%;
    height: auto;
    margin: 0 0 8px 0;
  }
  .w98-portrait img{
    position: static;
    width: 180px;      /* native size — sharp on mobile too */
    height: 180px;
  }
  .w98-portrait:hover img{
    width: 180px;
    height: 180px;
  }
  /* no speech-bubble tail on mobile — it's a plain caption there */
  /* no ghost needed on mobile (nothing is typing) — it would double the height */
  /* the card internals need to be bigger on a phone */
  .w98-stats .w98-kv{ font-size: 14px; }
  .w98-stats .w98-stats-hd ul{ font-size: 14px; }
  .w98-stats .w98-name{ font-size: 16px; }
  .visitor-left2 #typewriter{ font-size: 14px; }
}

/* METERS — its own panel, sits beside the Big-3 box */
/* STATS BOX — mirrors the Big-3 box beside it, but in the cool lavender
   from the palette (complements the warm gold of the Big-3 box). */
/* STATS — beside the Big 3, height-matched to it.

   WHY THE NUMBERS WERE BLURRY (I did this, and it is worth understanding):
   they were `font-weight: 700` in `basiic`. basiic is a BITMAP font with no
   bold cut, so the browser SYNTHESIZES bold by smearing each glyph sideways.
   On a pixel font that is not boldness, it is BLUR — and it is worst on
   digits, because "444 / 444" and "14059" are dense with vertical strokes
   that smear into each other. Synthetic bold and pixel fonts are enemies.

   Fixed two ways:
   1. font-weight: 400. Never synthesize bold on a bitmap face.
   2. rainyhearts, not basiic. basiic is a DISPLAY face (Ashley's rule);
      rainyhearts is the body pixel font and its digits/I/l/1 are distinct.
      A numeric readout is running data, not display text.

   HEIGHT: stretch to match the Big-3 box beside it (was 86px vs its 110px,
   which read as short and unbalanced). `align-self: stretch` from the
   parent grid + space-between distributes the four rows down the full
   height instead of bunching them at the top. */
.w98-kv{
  clear: none;
  min-width: 0;
  align-self: stretch;              /* match the Big-3 box's height */
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-content: space-between;     /* spread the 4 rows over the full height */
  gap: 5px 5px;
  padding: 6px 7px;
  background: #E8E3F0;              /* soft lavender — light enough to read on */
  border: 2px solid var(--bevel-lo);
  box-shadow:
    inset 2px 2px 0 rgba(255,255,255,.55),
    inset -2px -2px 0 var(--bevel-lo);
  box-sizing: border-box;
}
.w98-kv .k{
  font-family: "rainyhearts", monospace;
  font-weight: 400;                 /* NOT 700. see above. */
  font-size: 14px;                  /* rainyhearts reads smaller than basiic
                                       at the same px, so this is roughly the
                                       same optical size but sharp. */
  color: #6E6278;
  letter-spacing: 0;                /* was 1.3px, inherited — it was spacing
                                       the digits apart and making "444 / 444"
                                       harder to parse, not easier. */
  white-space: nowrap;
  align-self: center;
}
.w98-kv .v{
  min-width: 0;
  overflow: visible;
  white-space: nowrap;
  font-family: "rainyhearts", monospace;
  font-weight: 400;                 /* NOT 700. this is what was blurring
                                       the numbers. */
  font-size: 14px;
  letter-spacing: 0;
  color: #5A3268;
  display: flex;
  align-items: center;
}
/* stat bars fit the column instead of spilling out of the box */
.w98-kv .w98-meter{
  width: 100%;
  max-width: 100%;
}

/* NAME + BIG 3 — its own panel, full width under the photo */
.w98-stats-hd{
  /* `overflow: hidden` USED TO LIVE HERE and it is what was actually
     chopping "Sagittarius" in half. The panel's content was 196px wide
     inside a 168px box, and this rule quietly scissored off the 28px that
     did not fit. Now that the panel is full width the content fits, and
     hiding overflow would only mask a future bug. Left visible on purpose. */
  padding: 5px 6px;
  margin: 0;
  background: var(--accent);
  box-sizing: border-box;
  min-width: 0;
}
.w98-name{ margin:0; font-size:12.5px; }   /* basiic floor */
.w98-sub{ margin:0; font-size:12.5px; }
.w98-stats-hd ul{
  padding-left: 0;        /* was 12px, stealing width from the sign column */
  margin: 3px 0 0 0;
  font-size: 12.5px;      /* basiic floor. was 10px to survive the squeeze. */
  line-height: 1.4;
}
.w98-stats-hd li{ white-space: nowrap; }



.w98-meter{
  --score:7;
  height:12px; border:2px solid var(--bevel-lo);
  box-shadow:inset 2px 2px 0 var(--bevel-hi), inset -2px -2px 0 var(--bevel-lo);
  background-image:
    linear-gradient(to right,
      var(--accent-2) 0 calc(10% * var(--score)),
      var(--paper) calc(10% * var(--score)) 100%),
    repeating-linear-gradient(90deg,
      transparent 0, transparent 10%,
      rgba(0,0,0,.08) 10%, rgba(0,0,0,.08) 10.5%);
}

.clouds{

  --h: 41px;
  --tile: 320px;
  --speed: 90s;
  height: var(--h);
  margin-top: -7px;
  background-image: url("images/dividers/div2.gif");
  background-repeat: repeat-x;
  background-size: var(--tile) var(--h);
  animation: clouds-scroll var(--speed) linear infinite;
}

@keyframes clouds-scroll{
  from { background-position-x: 0; }
  to   { background-position-x: calc(-1 * var(--tile)); }
}


@media (prefers-reduced-motion: reduce){
  .clouds{ animation: none; }
}

    .booking {
    border-image:  url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAACpF6WWAAAAAXNSR0IArs4c6QAAAEBJREFUOE9jZICA/1CakQpsRpghIJpa4D81DYM5CmwoyOtUNZwWho56n1oJCWHOaOyPJv7RvD90ij5YQU2VugoAfv8oCQf/l3YAAAAASUVORK5CYII=') 7 /  7px / 0 round;
    border-width:  7px;
    border-style:  solid; 
  background-color: #5ECEC9;
  line-height: 1.35;     
  height: 360px;
  width: 536px;
padding: 10px; 
  margin-top: 20px;  
  display:flex;
  align-items:flex-start;
  gap:10px;
  flex-wrap:wrap;
  }

  
.bookinglinkbutton{
  text-align:center;
  background:#f8e8f3;
  padding:18px 14px;
  width: 280px;
  height: 140px;
  border:2px outset #ffc6ee;
  box-sizing: border-box;
  display: flex;
  align-items: center;
}

.bookinglinkbutton:hover{ border:2px inset #ffc6ee; }

.book-reading-link{
  white-space: normal !important;
  text-align: left;
  padding-left: 10px;
  line-height: 1.05;
  font-family: "MS Sans Serif", Tahoma, Verdana, Arial, sans-serif;
}

#bookLine1,
#bookLine2Wrap{ display:block; }

#bookLine1{
  color: #5A3268;
  font-size: 24px !important;
  font-weight: 900;
  text-shadow:
    0 1px 0 #ffffff,
    0 0 6px rgba(90,50,104,.25);
}

#bookLine2{
  color: #FFEA00;
  font-size: 26px !important;
  font-weight: 900;
  letter-spacing: 1px;
  text-shadow:
    -2px -2px 0 #5A3268,
     2px -2px 0 #5A3268,
    -2px  2px 0 #5A3268,
     2px  2px 0 #5A3268,
     0px  0px 10px #FFEA00;
}

#bookLine2Wrap.is-blinking{
  animation: cta-blink 3.8s steps(1, end) infinite;
}


@keyframes cta-blink{
  0%   { opacity: 1; }
  4%   { opacity: 0; }
  8%   { opacity: 1; }
  12%  { opacity: 0; }
  16%  { opacity: 1; }
  20%  { opacity: 0; }
  24%  { opacity: 1; }
  28%  { opacity: 0; }
  32%  { opacity: 1; }
  36%  { opacity: 0; }
  40%  { opacity: 1; }
  44%  { opacity: 0; }
  48%  { opacity: 1; }
  100% { opacity: 1; } /* long pause */
}


@media (prefers-reduced-motion: reduce){
  #bookLine2Wrap.is-blinking{ animation:none; }
}

@media (prefers-reduced-motion: reduce){
  #bookLine2Wrap.is-flashing{ animation:none; }
}

.bookinglinkbutton .book-cursor{
  color: #FFEA00 !important;
  text-shadow:
    -2px -2px 0 #5A3268,
     2px -2px 0 #5A3268,
    -2px  2px 0 #5A3268,
     2px  2px 0 #5A3268;
}

#bookTicker{
  display: inline-block;
  min-width: 18ch;
}

.book-cursor{
  margin-left: 2px;
  animation: book-blink 0.9s steps(2, start) infinite;


  color:#2bbfbe;
  text-shadow:
    -2px -2px 0 #fff4dc,
     2px -2px 0 #fff4dc,
    -2px  2px 0 #fff4dc,
     2px  2px 0 #fff4dc;
}

@keyframes book-blink{
  50%{ opacity:0; }
}

@media (prefers-reduced-motion: reduce){
  .book-cursor{ animation:none; }
}


.bookinglinkbutton a,
.bookinglinkbutton a:visited,
.bookinglinkbutton a:hover,
.bookinglinkbutton a:active{
   font-family: "Pixelated MS Sans Serif","MS Sans Serif",Tahoma,"Segoe UI",sans-serif !important;
  color:#ae0abd !important;
  font-size:26px !important;
  font-weight:800 !important;
  text-shadow:
    -2px -2px 0 #fff4dc,
     2px -2px 0 #fff4dc,
    -2px  2px 0 #fff4dc,
     2px  2px 0 #fff4dc,
     0px  2px 0 #fff4dc,
     2px  0px 0 #fff4dc,
     0px -2px 0 #fff4dc,
    -2px  0px 0 #fff4dc !important;
    
}

.bookinglinkbutton .book-cursor{
  color:#2bbfbe !important;
}




    .profile {
    border-image:  url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAACpF6WWAAAAAXNSR0IArs4c6QAAAEBJREFUOE9jZICA/1CakQpsRpghIJpa4D81DYM5CmwoyOtUNZwWho56n1oJCWHOaOyPJv7RvD90ij5YQU2VugoAfv8oCQf/l3YAAAAASUVORK5CYII=') 7 /  7px / 0 round;
    border-width:  1px;
    border-style:  solid; 
    background-color: rgba(255, 255, 255, 0.815);   
      background-clip: padding-box;
margin: 5px;
margin-top: -20px;
  }
  
.visitor-row{
  display:flex;
  align-items:flex-end;
  gap:10px;
  margin-top: -5px;
}

.visitor-wrap{
  display:flex;
  align-items:flex-start;
  gap:10px;
}

.visitor-main{
  display:flex;
  flex-direction:column;
}

.booking-split{
  display:flex;
  align-items:flex-start;
  gap:2px;
}

.booking-left{
  display:flex;
  flex-direction:column;
  gap:10px;
  margin-left: 40px;
}


.visitor-left1{
 display: flex;
  flex-direction:column;
}

.visitor-left2{
  display: flex;
  flex-direction: column;
  width: 280px;
  height: 70px;
  background-color: #F1EBE5;
padding: 10px;
padding-top: 20px;
  box-sizing: border-box;
  border-width: 1px;
  border-style: solid;
  margin-top: -20px;
  margin-bottom: 10px;
}





#typewriter{
  display:block;
  white-space: pre-line;
}

#visitorCounter{
  opacity: 0;
  transition: opacity 450ms ease;
}

#visitorCounter.is-visible{
  opacity: 1;
}

/* ---- INSIDE the counter box (the box itself is unchanged) ----
   The "Wow! You are visitor number:" line and the digits now sit as a tidy
   little readout instead of floating loose. */
.desktop-screen .visitor-left2 #typewriter{
  display: block;
  font-family: basiic, system-ui, sans-serif;
  font-size: 13.5px;      /* bigger */
  line-height: 1.25;
  color: #5A3268;
  letter-spacing: .2px;
}
.desktop-screen .visitor-left2 #visitorCounter{
  display: inline-block;
  margin-top: 5px;
  padding: 2px 7px;
  background: #FFFFFB;
  border: 1px solid #1a1a1a;      /* matches the box's thin line */
  box-shadow: 1px 1px 0 rgba(0,0,0,.18);
  line-height: 1;
}
.desktop-screen .visitor-left2 #visitorCounter img{
  display: block;
  image-rendering: pixelated;
}
/* kill any stray gaps inside the box */
.desktop-screen .visitor-left2 > div{ margin: 0; }
.desktop-screen .visitor-left2 br{ display: none; }

.visitor-right1{
  display:flex;
  flex-direction:row;
  align-items:flex-end;
  gap:4px;
margin-right: 85px;
}


.splashbuttons{
  display:block;
  align-items: center;
  width: 108px;
  height: 310px;
  background-color: #F1EBE5;
    border-image:  url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAACpF6WWAAAAAXNSR0IArs4c6QAAAEBJREFUOE9jZICA/1CakQpsRpghIJpa4D81DYM5CmwoyOtUNZwWho56n1oJCWHOaOyPJv7RvD90ij5YQU2VugoAfv8oCQf/l3YAAAAASUVORK5CYII=') 7 /  7px / 0 round;
padding: 10px;
margin-top: 5px;
margin-left: -15px;
}

.wm-corner{
  display: grid;
  grid-template-columns: 1fr 360px;
  grid-template-areas:
    "blog   widget"
    "btns   widget"
    "frame  gameboy";
  gap: 10px;
    column-gap: 10px;
  row-gap: 2px;   
  align-items: start;  
  row-gap: 0;
}

.wm-corner .zodiacblog{ 
  margin-bottom: 0; 
}

.wm-corner .wm-side{ 
  grid-area: widget; 
}

.wm-corner .btns{ 
  margin-top: -85px; 
}

.wm-corner .webmasterframe{ 
  grid-area: frame; 
    margin-top: -125px; 
}

.wm-side{
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.gameboy-float{
  margin-top: 0;
  margin-right: 0;
  width: 100%;
  text-align: center;
    float: none;
    padding-top: 10px;
    padding-right: 10px;
}

@media (max-width: 1249px){
  .wm-corner{
    grid-template-columns: 1fr;
    grid-template-areas:
      "blog"
      "widget"
      "btns"
      "frame";
  }
}

      .zodiacblog {
          grid-area: blog; 
      border-top: 3.5px double #000;
      border-left: 3.5px double #000;
      border-right: 3.5px double  #000;
      border-bottom: 3.5px double  #000;
    width: 650px;
    float: none;
height: 320px;
  padding: 1em;
    background: url(images/backgrounds/bg2.gif);
    margin: 5px;
    margin-left: 5px;
  }

  .updateblog {
  font-family: "ibm", sans-serif;
      height: 270px;
      width: 575px;
      margin: 10px;
      padding: 1rem;
      border-top: 3.5px double #000;
      border-left: 3.5px double #000;
      border-right: 3.5px double pink;
      border-bottom: 3.5px double pink;       
      overflow: scroll;
      overflow-x: hidden;
      color: #fff;
    background: hsla(0, 0%, 0%, 0.767);  
      align-items:center
    }

    .birthchart-widget-frame{
      grid-area: widget; 
width: 350px; 
height: 500px; 
float: right;
margin-right: 30px;
margin-top: 5px;
    border-image:  url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFQAAABUCAYAAAAcaxDBAAAHfUlEQVR4AeRcXZrbNgyU9j7JW3u7tl/T03X71h7IFSlIHBAACf7ZTuLPEklgOBiMdpXddeyPz2//PVqOfxx4gfmrrUaLnnfDfmyNj4cDLzAi4CCZAdlPEhrOxeJzs6GL9cylpwtJw1xug63P0K5L/sy2jG6fEK4a+uvvX3Y8opfgDebK86+Mp4zlNZ+JzT3Pa+f5sI6ehMlxVA09MOwJXrL4D71Ax3BOTaMnH0r+gCHkWL7jUxdOSotJwjQMaAfOIwUPfMQl1afhgKXZtuF8e59HFG7JKSatTZ1x7s/5LU/1w5DfMz6//ct+Ts3znSredtvd3x/nffwz+7n7ztO/LXkjp6F59PqqjOYHmwXgTQJR4Botom0RoLo8bhj6OC19hD0LRQf6oSMK7GYQnVGAhoxXj26nU9v1MAzdtjGpF/2Wlds2Gdj6HlZ/CpsFFT1SgAaFqR4ShjruGXVWAxEbG1GLvA08DVCscMz3Lb9n5v4cIPYUhrJsXEQb4qznhM3gvIeL7RGyRIDB+xaWYiu+bQ5D7c19IsOuPZzso5impJD1mHY3sYVdGdJwLe/xYRn6uCFrJjZ/lGqnDzl20s4c257y3KWh5z0j/d5du2dskx9dpsSrMFkI0eX9n/6cP6OGOcHugX3LL9R1F/RPLDVKXLkKCspfGpCtPMxQRRdQG1OqSIMB6glbaqw4r+FD8T3aqpWHGaoRVmNUkYYqfAgw/6pV5CgFlRCSMEMDtvWegWTj86CgwEJXrYIqELSmHvLn0D/562M5Ixj6mPbbUV7EvybHKhssFBqN8wrd1DQY+ioJ8/pBo3E+r0KdCQzlYLQX5xy1aKUUVELPK25VUkQJQ8PPVuH4Bf7e13q1lTqWJD2uFFRC+t7hqFIJGgre3MdvXyBzFhaGnuGxM0pKFdMssPNViCw+RgpiQxWZzNBizc5k0pJmQRNfhcjio6FgsdWKTGZosWZ/8pYwIvQmmTax1RRbrdRnhlawIm1LEtAYGBEaCaaeHkv+OmUY6rPqMbXBuWSeDlboB0ORHudzG30Wm7uDivOYxrnVBxhqw0VGBCz61fEJQirOYxrnVmdgqAVRXrDzMNt0xUybRWNC2moVZd9J09C2Ym3ou7oy8Vg0q5qnVpTYUNA0NBK5Tx5pDaoqdT3VKhTudFTdUNA01OKIBTxyBNDDKDZ5KlUxiTXNqpsIgKrFbhHY5GtKJ89+DsoZCyjpFHID05bsbo2JoXmSkmaB0O4yZOXBdx95EdgsQxXksX/N85m1eAcrKpvf8rx0y2o/wTSci0lni9OKTyrLaCq1wNBZ14t4aGBiHIuiXotTxIssDhUJIphErYQNMzBUbA35Jxy8bkWvU88clu34bd/NRG2AoduCB1UpMrslF1nGkpbOijbcRtDFhlKVsW4X7VbcaK2ktLfY0FaFz8QrbnSV5zzvaSh+8eC8q+H1m1Bin6HIsEIvXnScr6g1zLmz/8/QZ2hsklyloajLg4kEHqAHE8kaTvM4wdDo0i2iXoLwNNwbtYmFEUUsIJJ6MIj3zEc4+V4wlHfHYUkUR6V416yzSEFDl4yxTVwNGDpGO3W3ZTQVqaQJpQ+8fR0zEm021N3MauWdXbv1d/I3G4p1ip6tVo5CJsyLvTTwlw2tVHmpZxVtwoMKflYvZUNnVWntTuCVQKs2wld8VQploQpB2dCMazv++rJNeVB3CldFr7KjLWRXdvIggSK20VBkcwpohK2v0CioBFfENhpK7MqVUUIEdg7DBM46i2F9hiqilIuloLIQmthFkPGtWqJOpQam+wx95+aVhodDVr/kJKb7DB1WSASohEJtwz7tn8lYd49n/wn1015haP4+JT/7RCSJqzNOeStQKoMGpSibXf5cn+93J2mvMPQGvHJC4nQJbrfV7e7dFaAl8T0NVa24glYrV748une7gbzekKGVi8gr/SQrYej9Hhx6n9J1z7hG9KXzIh4U73EpPCpi3/D5p7k/RzPsKQxl2R984f6CUIDWxXiRoYrCkYtndTfCSXstaqsDMNSCEHPrYClp5fHgJ0v3lLQwYOjpQLxnwOe91e4ZFjF7bdUEPSNx9tVb6XrP6+VD7k/OC4aOXeYx2bmsmeuxvlqVnIZGN+JJ3W9nErwoWyFQQonsO56dhhbdmPAftRV+EVIcVkJvb/VpKMi87hXXWLtnwNaxqXB4woW0FIkrJQL3zrx/8uX+POkbSBNhKMXfbrBb7pCKFy8SY6CDD7Z8N4ZaLUc/oKHmqUXcTHRuWG5oa8NFvJKc7MfpysBZGFq+Z3zd8/xVG3vFeWvDRXwxeSkJIyoIa/+R95ffM898+lzqnFkYmgP42u4IMzjn+5+1GlHg2bubrxQ0GloxZM/zIpAD3nDt02zZPtdQUUUEyECfaALrQydF5zahweIBQ/Xmz3tG+ry3nDnPh3V8vQX+hhhi/Ej3IB5Pdarx7DPoqnj6+8TfNH6yz+dPekr9hb6uvO7Whu/13C/s8BiLxdMw1UIC7BfndklPS/AVahMty/j6KJT3tKhsH66rcFLotYZ2+kHaj6HTmc66nmr/AwAA///RWZacAAAABklEQVQDAGAuCb5CjNJFAAAAAElFTkSuQmCC') 28 /  28px / 0 round;
  color:black;
content: center;
padding: 10px;
background: url(images/backgrounds/bg3.gif);
}

#widget_container{
  margin-top: -57px;
  margin-left: 18px;
    font-family: "ibm", sans-serif;
      transform: scale(0.75);
  transform-origin: left;
  border: none;
}

        .lilspacebox {
      border-top: 3.5px double #000;
      border-left: 3.5px double #000;
      border-right: 3.5px double  #000;
      border-bottom: 3.5px double  #000;
    width: 1030px;
    float: left;
height: 136px;
  padding: 1em;
    background: url(images/backgrounds/bg3.gif);
    margin: 5px;
  }
  
.gameboy-float{
  grid-area: gameboy;
  float: none;
  margin: 0;
  margin-right: 20px;
  text-align: center;
}

.gameboy-float img{
  display: block;
  max-width: 100%;
  height: auto;
  margin: 0 auto;
}

.starcornerleft img{ 
    float: left;
  height: auto;
  margin: 0 0 6px 10px;
  display: block;  
}

.starcornerright img{ 
    float: right;
  height: auto;
  margin: 0 0 6px 10px;
  display: block;
}

  .miniblog {
  font-family: "ibm", sans-serif;
      height: 270px;
      width: 350px;
      margin: 10px;
      padding: 1rem;
      border-top: 3.5px double #000;
      border-left: 3.5px double #000;
      border-right: 3.5px double pink;
      border-bottom: 3.5px double pink;       
      overflow-x: hidden;
      color: #fff;
  background-image: url("images/backgrounds/bg6.gif");
      align-items:center
    }

/* ============================================================
   MOBILE LAYOUT  —  added during cleanup pass
   ------------------------------------------------------------
   Everything above this line is the ORIGINAL desktop design
   and is untouched. This block ONLY applies on screens
   narrower than 900px (phones + small tablets). On desktop
   it does nothing, so the desktop view is byte-for-byte the
   same as before.

   Mobile stacking order (via flex `order`):
     1. Right column  = navigation menu  (people need to get around)
     2. Ash card + offerings + booking    (the money path)
     3. Everything else (left column widgets, music, fluff)
   ============================================================ */

@media (max-width: 1249px){

  /* Let the page be a normal top-to-bottom scroll */
  html, body{
    overflow-x: hidden;
  }

  body.--home{
    display: flex;
    flex-direction: column;
    align-items: stretch;
    width: 100%;
    padding: 8px;
    animation: none;               /* stop the desktop bg drift on phones */
  }

  /* --- Cabinet becomes a vertical stack on mobile --- */
  .cabinet{
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 100%;
    margin: 0;
    gap: 0;
  }

  /* --- Release the two fixed side columns --- */
  .rightsidebox--home,
  .leftsidebox--home{
    position: static;              /* was: fixed */
    width: 100% !important;
    height: auto !important;
    max-width: 100%;
    margin: 0 0 12px 0 !important;
    padding: 0 !important;
    top: auto; left: auto; right: auto;
    z-index: auto;
  }

  /* --- Stacking order (hierarchy: nav+about first, canvas, fluff last) --- */
  .rightsidebox--home{ order: 1; }   /* menu + about + music: navigation first */
  .container--home   { order: 2; }   /* canvas: Ash + offerings + booking */
  .leftsidebox--home { order: 3; }   /* fluff widgets last */

  /* --- Kill the desktop hard widths + offset --- */
  .container--home{
    width: 100% !important;
    max-width: 100%;
    margin: 0 !important;
    padding: 0 !important;
    display: block;                /* drop the desktop grid */
    grid-template-columns: none;
  }

  .content--home{
    width: 100% !important;
    max-width: 100%;
    padding: 10px !important;
  }

  .introgradbox{
    width: 100% !important;
    height: auto !important;
    padding: 10px 0;
  }

  /* Site title: let the big NES headline wrap instead of clipping */
  .introgradbox h1{
    font-size: 1.5em !important;
    line-height: 1.25;
    word-break: break-word;
    margin-left: 0 !important;
    text-align: center;
  }

  /* --- Intro row: stack card over booking instead of side-by-side --- */
  .introbar{
    display: flex !important;
    flex-direction: column !important;
    grid-template-columns: none !important;
    align-items: center;
    gap: 16px;
    margin: 0 !important;
  }

  .intro-flex1{
    flex-direction: column;
    align-items: center;
    padding: 0;
    gap: 16px;
  }

  .intro-card-wrap{
    margin: 0 !important;
    height: auto !important;
    width: 100%;
    max-width: 360px;
  }

  /* Win98 Ash card: let it size to the phone */
  .w98-card.small,
  .w98-card[data-theme="pp"]{
    width: 100% !important;
    max-width: 360px;
    height: auto !important;
    margin: 0 auto;
  }
  .w98-body{ height: auto !important; }

  .booking-col{ width: 100%; }
  .booking-split{
    flex-direction: column;
    align-items: center;
    gap: 12px;
  }
  .booking-left{
    margin-left: 0 !important;
    align-items: center;
    width: 100%;
  }

  /* --- Offerings box: full width, comfy tap targets --- */
  .readingsbox{
    width: 100% !important;
    margin: 20px 0 10px 0 !important;
    padding: 14px !important;
  }
  .readingsbox h3{
    position: static !important;
    margin: 0 0 10px 0 !important;
    font-size: 1.8em !important;
    text-align: center;
  }
  .package summary{
    font-size: 1.05em;
    padding: 12px 8px;            /* bigger tap target */
  }

  /* --- Left column widgets: full width, centered, no fixed sizes --- */
  .transits{
    width: 100% !important;
    height: auto !important;
    margin: 0 0 12px 0 !important;
  }
  .transits iframe,
  .planets iframe{
    max-width: 100%;
  }

  /* --- Right column bits (nav / about / music) --- */
  .aboutbox,
  #musicplayer{
    width: 100% !important;
    max-width: 360px;
    margin: 0 auto 12px auto !important;
  }
  #mainmenu{
    width: 100% !important;
    max-width: 360px;
    height: auto !important;
    margin: 0 auto 12px auto !important;
  }

  /* --- Webmaster / fluff zone: let it wrap into one column --- */
  .wm-corner,
  .webmasterframe{
    display: block !important;
    width: 100% !important;
  }
  .webmasterframe > *{
    width: 100% !important;
    max-width: 100%;
    margin: 0 0 12px 0 !important;
    float: none !important;
  }

  /* Stop any wide decorative stickers from causing sideways scroll */
  .crystalstickerleft,
  .crystalstickerright,
  .gameboy-float{
    display: none !important;
  }

  /* Images never overflow the viewport */
  img, iframe{
    max-width: 100%;
    height: auto;
  }
}


/* ============================================================
   FAUX WIN98 DESKTOP SCREEN  —  center centerpiece
   ------------------------------------------------------------
   Wraps the Ash window, offerings menu, and booking/counter
   into one "screen" arranged on a grid. Desktop-only (>=1250px);
   below that the mobile block stacks these naturally.

   Grid zones:
     dz-ash       top-left    (Ash window)
     dz-offerings right col    (offerings, spans both rows)
     dz-booking   bottom-left  (booking window + gyaru/counter)
   Flair buttons (.splashbuttons) run along the bottom.
   ============================================================ */

@media (min-width: 1250px){

  .desktop-screen{
    /* the "screen" itself — teal CRT wallpaper.
       TEAL TRANSPARENCY: the last two hex digits are opacity.
       E6 ≈ 90%. Lower them (e.g. CC ≈ 80%) to calm it more,
       raise toward FF for more solid. Tweak to taste. */
    background-color: #5ECEC9E6;

    /* Thin recessed border — embedded-CRT / arcade-cabinet look.
       Deeper inset so the screen reads as sunk INTO the page.
       (Glass shine + CRT curvature bevel are TABLED for a later
       revision pass — revisit the "glass" idea then.) */
    border: 2px solid #5A3268;
    box-shadow:
      inset 3px 3px 7px rgba(0,0,0,.38),      /* deeper recessed shadow, top-left */
      inset -2px -2px 5px rgba(255,255,255,.4),
      inset 0 0 22px rgba(0,0,0,.14),          /* subtle inner vignette */
      0 2px 3px rgba(90,50,104,.3);
    padding: 18px;
    margin: 0 auto 10px auto;
    width: 100%;
    max-width: 740px;
    min-height: 620px;      /* taller ~4:3 CRT canvas for scattered windows */
    box-sizing: border-box;

    display: grid;
    grid-template-columns: 280px 1fr;   /* left column fits the flair box (buttons need native size) */
    grid-template-areas:
      "ash       offerings"
      "counter   booking"
      "flair     booking";           /* flair is now a widget under the counter */
    grid-template-rows: auto auto auto;
    gap: 16px;
    align-items: start;
    align-content: start;
  }

  /* the Ash zone sits ABOVE the offerings zone, so the hover speech bubble
     can pop out of the card and float over everything. (.readingsbox has
     will-change from its animated gradient, which makes its own stacking
     context — without this the bubble gets painted underneath it.) */
  .dz-ash      { grid-area: ash; position: relative; z-index: 5; }
  .dz-offerings{ grid-area: offerings; }
  .dz-booking  { grid-area: booking; }
  .dz-counter  { grid-area: counter; }

  /* FLIPPED: the counter now lives on the LEFT, so mirror the sprites
     horizontally — otherwise she'd be facing off the left edge of the
     screen. Mirrored, she looks back INWARD toward the center. */
  .desktop-screen .visitor-row img{
    transform: scaleX(-1);
  }

  /* --- Ash window: drop the old flex offsets, sit snug top-left --- */
  .desktop-screen .intro-flex1{
    display: block;
    /* Was `padding: 0`, which flushed ash.exe against the canvas edge at
       left:0 while the offerings box below it sits at left:10 — so the two
       did not share an edge and ash.exe read as "too far left."
       10px matches .readingsbox. The boxes down this column now line up. */
    padding: 0 10px;
  }
  .desktop-screen .intro-card-wrap{
    margin: 0;
    height: auto;
  }
  .desktop-screen .w98-card.small{
    width: 100%;
    max-width: 280px;   /* matches the flair box below */
    /* CENTRED in its 324px cell. It was sitting flush left, which left a
       10px gap on the left and a 34px gap on the right — 24px off, and
       Ashley's eye caught it before any measurement did. `margin-inline:
       auto` splits the slack evenly (22/22) without touching the cell, the
       counter box, or the 324px column floor. */
    margin-inline: auto;
  }

  /* Fix: the "hi! I practice" blurb had margin-top:-20px pulling it up
     over the photo. Sit it cleanly below the card instead. */
  .desktop-screen .profile{
    margin-top: 6px;
  }

  /* --- Offerings: keep gradient+font, give it room to breathe --- */
  .desktop-screen .readingsbox{
    margin: 0;
    width: 100%;
    height: auto;          /* item 1: natural height, don't stretch Ash */
    box-sizing: border-box;
    font-size: 0.92em;
    /* extra top padding makes room for the header now that it sits
       IN FLOW instead of being pulled up with a negative margin.

       SIDE PADDING TRIMMED 1.1em -> 0.6em. The package hooks ("the
       foundational one, start here") were wrapping to a second line and
       squishing the boxes. The width had to come from somewhere, and it
       could NOT come from the left column: .visitor-left2 is 324px wide
       and narrowing that column overflows the counter box — which is not
       to be touched. So it comes from this box's own padding instead.
       Same column, more usable width. */
    padding: 2.2em 0.6em 1.1em 0.6em;
  }
  /* header sits in normal flow — no absolute/negative-margin collision.
     centered banner across the top of the box, with real breathing room. */
  .desktop-screen .readingsbox h3.stroke{
    position: static;
    margin: 0 0 0.5em 0;
    font-size: 1.5em;
    line-height: 1.15;
    text-align: center;
    letter-spacing: 2px;
  }
  /* package rows: a touch more spacing so they don't crowd */
  .desktop-screen .readingsbox .package{
    margin-bottom: 8px;
  }
  /* offering titles + prices use basiic (the original font) — same family
     as the .exe box, which is what Ash had before. */
  .desktop-screen .readingsbox .package summary{
    font-family: basiic, system-ui, sans-serif;
    line-height: 1.25;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
  }
  /* ---- OFFERING ICONS ----
     Each package gets a tiny icon: coffee = Coffee Chat, ringed planet =
     Full Chart, clipboard = Solar Return. Semantic, not decorative — the
     icon says what the thing IS before the words do.

     18-20px NATIVE, never scaled: these are tiny pixel gifs and any
     resize at this size destroys them outright.

     The summary is `justify-content: space-between` with the price on the
     right. Adding the icon as a third child would let space-between push
     all three apart and float the icon away from its own title. So the
     icon is `margin-right: auto`-free and instead the TITLE takes the
     slack: icon and title stay glued, price still gets shoved right. */
  .desktop-screen .readingsbox .pkg-icon{
    flex: 0 0 auto;
    width: auto;
    height: auto;
    max-width: none !important;
    max-height: none !important;
    box-sizing: content-box;      /* global border-box would inflate it */
    image-rendering: pixelated;
    align-self: flex-start;
    margin-top: 2px;              /* optically level with the title text */
  }
  /* the title takes all the slack, so space-between cannot separate the
     icon from the words it belongs to. */
  .desktop-screen .readingsbox .pkg-title{
    display: flex;
    flex-direction: column;
    min-width: 0;
    margin-right: auto;
  }
  /* THE HOOKS GET THE FULL WIDTH.

     They were wrapping to a second line and squishing the boxes. The
     instinct is to shorten the copy until it fits — but that is letting a
     layout bully the writing, and "your year ahead, the birthday reading"
     is the line that actually sells a solar return.

     So the hook is allowed to run the full width of the summary instead:
     it sits BELOW the icon+title+price row rather than being squeezed into
     the same line as the price. Copy stays as written. The box stops
     wrapping. Nobody has to compromise. */
  .desktop-screen .readingsbox .pkg-hook{
    white-space: nowrap;
    overflow: visible;
  }
  /* the hook: tells them what it IS without needing to click */
  .desktop-screen .readingsbox .pkg-hook{
    font-size: 0.86em;   /* bumped for i/l legibility */
    font-style: italic;
    color: #6b5a7a;
    margin-top: 2px;
    letter-spacing: .2px;
  }
  /* price: quieter than the title, so they read WHAT it is first, price second */
  .desktop-screen .readingsbox .pkg-price{
    font-size: 0.95em;
    font-weight: 700;
    color: #5A3268;
    white-space: nowrap;
    opacity: .85;
    flex: 0 0 auto;
  }
  /* THE FEATURED OFFERING — one clear signal so the eye knows where to land.
     Brighter border + a subtle glow. No size change. */
  .desktop-screen .readingsbox .package-feature > summary{
    border: 2px solid #7A6E96;
    box-shadow: 0 0 0 1px rgba(122,110,150,.25), 2px 2px 0 rgba(90,50,104,.18);
  }
  .desktop-screen .readingsbox .package-feature > summary .pkg-hook{
    color: #7A6E96;
    font-weight: 700;
    font-style: normal;
  }
  /* keep the open-details body in the readable body font */
  .desktop-screen .readingsbox .package-body{
    font-family: basiic, system-ui, sans-serif;
  }
  /* CTA at the bottom of the offerings box — closes the loop:
     "here's what I offer" -> "here's how to get it". Replaces the old
     birth-data fine print, which now lives in the booking window. */
  .desktop-screen .readingsbox .readings-cta{
    margin: 14px 4px 0 4px;
    padding-top: 10px;
    border-top: 1px dashed rgba(90,50,104,.4);
    font-family: basiic, system-ui, sans-serif;
    font-size: 0.9em;
    line-height: 1.35;
    text-align: center;
    color: #5A3268;
  }
  .desktop-screen .readingsbox .readings-cta b{
    color: #7A6E96;
  }
  /* the arrow nudges toward the booking window (which sits to the right) */
  .desktop-screen .readingsbox .readings-cta-arrow{
    display: inline-block;
    color: #7A6E96;
    font-weight: 700;
    animation: cta-nudge 1.4s ease-in-out infinite;
  }
  @keyframes cta-nudge{
    0%,100% { transform: translateX(0); }
    50%     { transform: translateX(4px); }
  }
  @media (prefers-reduced-motion: reduce){
    .desktop-screen .readingsbox .readings-cta-arrow{ animation: none; }
  }

  /* --- Booking + counter zone: strip the old teal box, it's on the screen now --- */
  .desktop-screen .booking-col{
    width: 100%;
    padding-left: 0;
    padding-top: 0;
    margin-top: -6px;      /* fine vertical placement (golden line) */
    overflow: visible;
  }
  /* BOOKING WINDOW — bigger, and positioned on the golden-ratio crossing
     of the open desktop area (roughly the lower-left φ intersection),
     like a window that naturally opened there.
     margin-left = horizontal φ placement; the window is wider now that
     there's room. Nudge margin-left / margin-top to re-place. */
  .desktop-screen .booking-window{
    max-width: 400px;      /* wider still — now that flair moved out of the bottom row */
    margin-left: auto;     /* centered under the offerings box above it */
    margin-right: auto;
    margin-top: 16px;
  }
  /* scale the inner window contents up a touch to match the bigger frame */
  .desktop-screen .booking-window .booking-window-body{
    padding: 12px 14px 12px 14px;   /* tighter — the window was reading heavy */
  }
  .desktop-screen .booking-window .booking-blurb{
    font-size: 1.05em;
  }
  .desktop-screen .booking-window .booking-btn{
    font-size: 1.1em;
    padding: 7px 18px;      /* shorter — it was reading too tall */
    line-height: 1.25;
    letter-spacing: .4px;
  }
  .desktop-screen .booking{
    background: transparent;
    border-image: none;
    border: 0;
    box-shadow: none;
    height: auto;
    width: 100%;
    padding: 0;
    margin: 0;
    display: block;
  }
  .desktop-screen .booking-split{
    display: block;
  }
  .desktop-screen .booking-left{
    margin: 0;
    gap: 0;
  }

  /* --- Flair buttons: a compact WIDGET under the counter (no longer a
     full-width shelf). Buttons stack 3 across x 2 down. --- */
  .desktop-screen .splashbuttons{
    grid-area: flair;
    display: grid;
    grid-template-columns: repeat(3, auto);   /* 3 across, wraps to 2 rows */
    justify-content: center;
    align-content: start;
    gap: 3px;
    width: 280px;             /* wider than the column, but buttons need to be READABLE */
    justify-self: start;
    height: auto;
    box-sizing: border-box;
    /* same thin-line box language as the counter above it */
    background-color: #F1EBE5;
    border: 1px solid #1a1a1a;
    box-shadow: 2px 2px 0 rgba(0,0,0,.18);
    border-image: none;
    padding: 4px;             /* tight — the space belongs to the buttons */
    margin: -10px 0 0 0;   /* raised up closer to the counter */
  }
  .desktop-screen .splashbuttons > *{
    margin: 0;
    flex: 0 0 auto;
    min-width: 0;
  }
  /* buttons at their full NATIVE 88x31 so the text on them is readable */
  .desktop-screen .splashbuttons img{
    display: block;
    width: 88px;
    height: 31px;
    margin: 0;
    image-rendering: pixelated;
  }
}


/* ============================================================
   GYARU LEANS ON THE COUNTER  —  frame-break overlap effect
   ------------------------------------------------------------
   The computer + gyaru sprites sit ON TOP of the beige counter
   box (.visitor-left2), so her elbow/coffee/computer read as
   resting on the counter's edge instead of floating in a
   separate box. Relative positioning + z-index does the layering;
   the counter's negative margin tucks up under the sprites.
   ============================================================ */

@media (min-width: 1250px){

  .desktop-screen .visitor-wrap{
    display: block;
    margin-top: -73px;   /* tucked up closer under the Ash card */
    margin-left: 0;
  }
  .desktop-screen .visitor-main{
    display: flex;
    flex-direction: column;
    align-items: flex-start;    /* FLIPPED: pin the counter + sprites to the LEFT corner */
    position: relative;
  }

  /* Sprites layer: sits above the counter box.
     GYARU HEIGHT TWEAK: the bottom margin is how far the sprites drop onto
     the counter. MORE negative = she sinks lower onto it. Goal: the table
     line drawn in her gif sits flush with the counter box's top border. */
  .desktop-screen .visitor-row{
    position: relative;
    z-index: 2;                 /* draws over the box's top border */
    /* SITTING ON THE TABLE.
       The sprites have perspective bases (see .visitor-left2 above):
       the gyaru's seat is at sprite y~103 of 119, the computer's foot
       at y~78 of 95. So the bottom ~16px of each image is the FRONT
       FACE of the surface they're on — it must fall BELOW the box's
       top border or they look like they're hovering above it.
       -20px seats them: their bases land in the box's 48px top shelf,
       which is empty (the text was pushed down), so nothing collides. */
    margin: 0 0 -20px 0;
    align-items: flex-end;
    justify-content: flex-end;    /* with row-reverse this anchors them LEFT */
    flex-direction: row-reverse;  /* mirrored gyaru sits left, facing in toward the computer */
    gap: 0;                       /* computer pulled right up next to her */
    padding-left: 6px;
    padding-right: 0;
    pointer-events: none;
    /* NOTE: no scale() here — fractional scaling makes the pixel-art gifs
       shimmer/glitch. The counter is wide enough now that they fit natively. */
  }
  .desktop-screen .visitor-right1{
    margin-right: 0;
  }

  /* The counter GADGET: back to the original thin-line box. Only the
     CONTENTS are restyled (see below) — the box itself stays as it was. */
  .desktop-screen .visitor-left2{
    position: relative;
    z-index: 1;
    /* Pulled up to close the dead space under the gifs. Ashley's eye was
       right: the gap was a touch too big.

       This is the SAFE lever. The two gif offsets
       (.visitor-row margin-bottom / .visitor-left2 padding-top) are a
       HARD CONSTRAINT — "DO NOT MOVE THE GIFS ... finally correct after
       many attempts" — so this moves the READOUT BOX instead and leaves
       the gifs exactly where they are. If this needs tuning, tune THIS
       number and nothing else. */
    margin-top: -14px;
    width: 100%;
    max-width: 280px;
    min-width: 280px;      /* matches ash.exe + flair — the left column shares one edge */
    height: auto;
    min-height: 0;
    box-sizing: border-box;
    background-color: #F1EBE5;
    border: 1px solid #1a1a1a;    /* thin black table line */
    box-shadow: 2px 2px 0 rgba(0,0,0,.18);
    padding: 16px 10px 8px 10px;  /* tight — no wasted deadspace inside */
    color: #5A3268;
    font-size: 13px;
  }
}


/* ============================================================
   BOOKING WINDOW  —  refreshed CTA (replaces typewriter button)
   ------------------------------------------------------------
   A little win98 dialog with one bright, obvious button.
   To repoint booking later, change ONE line: the href on
   <a class="booking-btn"> in index.html.
   ============================================================ */

.booking-window{
  width: 100%;
  max-width: 250px;
  background: #ece9d8;
  border: 2px solid #5A3268;
  box-shadow: 3px 3px 0 rgba(90,50,104,.45);
  font-family: "MS Sans Serif", Tahoma, Verdana, Arial, sans-serif;
  margin-bottom: 12px;
}

.booking-titlebar{
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* matches the offerings background gradient family */
  background: linear-gradient(90deg, #e8c0c8, #C7C4D9, #f6ebbe, #f2c0bf);
  color: #5A3268;
  padding: 4px 6px;
  font-size: 13px;
  font-weight: bold;
  border-bottom: 1px solid #5A3268;
}
.booking-title{ letter-spacing: .3px; text-shadow: 0 1px 0 rgba(255,255,255,.5); }
.booking-ctls{ display: flex; gap: 3px; }
.booking-ctl{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px; height: 14px;
  background: #ece9d8;
  border: 1px solid #5A3268;
  color: #5A3268;
  font-size: 10px;
  line-height: 1;
}

.booking-window-body{
  padding: 12px 12px 14px;
  text-align: center;
}
.booking-blurb{
  color: #5A3268;
  font-size: 13px;
  margin: 0 0 10px;
  line-height: 1.3;
}
.booking-btn{
  display: block;
  background: #FFEA00;
  color: #5A3268 !important;
  font-weight: 800;
  font-size: 14px;
  text-decoration: none;
  padding: 7px 8px;
  border: 3px outset #ffd6f2;
  text-shadow: 0 1px 0 #fff;
  transition: transform .05s;
}
.booking-btn:hover{
  background: #fff36b;
  border-style: inset;
}
.booking-btn:active{ transform: translateY(1px); }

/* ============================================================
   BOOKING CTA — "notice me" treatment
   ------------------------------------------------------------
   The button felt hidden. This gives it a gentle pulsing glow +
   a slow sheen so the eye lands on it without it being obnoxious.
   Desktop-only; honors prefers-reduced-motion (falls back to a
   static glow, no motion).
   ============================================================ */
@media (min-width: 1250px){
  /* the whole window gets a soft breathing halo so it reads as "active" */
  .desktop-screen .booking-window{
    animation: booking-halo 2.6s ease-in-out infinite;
  }
  /* the button itself pulses a touch bigger + brighter */
  .desktop-screen .booking-btn{
    position: relative;
    overflow: hidden;
    animation: booking-pulse 1.8s ease-in-out infinite;
  }
  /* a slow diagonal sheen sweeps across the button */
  .desktop-screen .booking-btn::after{
    content: "";
    position: absolute;
    top: 0; left: -60%;
    width: 40%; height: 100%;
    background: linear-gradient(100deg,
      transparent, rgba(255,255,255,.75), transparent);
    transform: skewX(-20deg);
    animation: booking-sheen 3.4s ease-in-out infinite;
    pointer-events: none;
  }
}
@keyframes booking-halo{
  0%,100% { box-shadow: 3px 3px 0 rgba(90,50,104,.45), 0 0 0 rgba(255,190,80,0); }
  50%     { box-shadow: 3px 3px 0 rgba(90,50,104,.45), 0 0 16px 3px rgba(255,190,80,.55); }
}
@keyframes booking-pulse{
  0%,100% { transform: scale(1);    background:#FFEA00; }
  50%     { transform: scale(1.04); background:#fff36b; }
}
@keyframes booking-sheen{
  0%   { left: -60%; }
  55%  { left: 130%; }
  100% { left: 130%; }
}
@media (prefers-reduced-motion: reduce){
  .desktop-screen .booking-window{
    animation: none;
    box-shadow: 3px 3px 0 rgba(90,50,104,.45), 0 0 14px 2px rgba(255,190,80,.5);
  }
  .desktop-screen .booking-btn{ animation: none; }
  .desktop-screen .booking-btn::after{ animation: none; display:none; }
}
.booking-note{
  color: #7a5a86;
  font-size: 12px;
  margin: 5px 0 0;
}
/* birth-data prep note — moved here from the offerings box. This is the
   ACTION step, so it's where someone actually needs to know what to bring. */
.booking-prep{
  margin: 8px 0 0;
  padding-top: 7px;
  border-top: 1px dashed rgba(90,50,104,.35);
  font-size: 12.5px;      /* bumped for i/l legibility */
  line-height: 1.4;
  color: #5A3268;
  text-align: center;
}
.booking-prep b{ color: #7A6E96; }
.booking-prep img{
  vertical-align: middle;
  margin-left: 3px;
}


/* ============================================================
   OFFERINGS — FLOATING WIN98 POP-OPEN WINDOWS  (item 5)
   ------------------------------------------------------------
   Desktop only. Clicking an offering opens its details as a
   little win98 window that FLOATS over the desktop (absolute)
   instead of pushing the counter/booking down. One at a time
   (enforced by JS below). This keeps the offerings list compact
   with no dead space reserved underneath.
   ============================================================ */

@media (min-width: 1250px){

  .desktop-screen .readingsbox{
    /* compact: no reserved space, list sits tight */
    padding-bottom: 0.6em;
  }

  .desktop-screen .package{
    position: relative;      /* anchor for the floating body */
  }

  .desktop-screen .package summary{
    cursor: pointer;
    list-style: none;
  }
  .desktop-screen .package summary::-webkit-details-marker{ display:none; }

  /* The floating window: pop over, don't push */
  .desktop-screen .package[open] > .package-body{
    position: absolute;
    top: calc(100% - 2px);
    left: 0;
    right: 0;
    z-index: 60;
    margin: 0;
    /* bg4 texture with a translucent light wash over it so text stays crisp.
       The white layer sits ON TOP of the texture; raise its alpha (0.85 -> 0.92)
       if text is ever hard to read, lower it to let more texture through. */
    background:
      linear-gradient(rgba(255,255,255,0.85), rgba(255,255,255,0.85)),
      url("images/backgrounds/bg4.jpg");
    background-size: auto, cover;
    border: 2px solid #5A3268;
    box-shadow: 4px 4px 0 rgba(90,50,104,.4);
    padding: 10px 12px 12px !important;
    color: #3a2740;
  }
  .desktop-screen .package[open] > .package-body::before{
    content: "▸ details";
    display: block;
    margin: -10px -12px 8px -12px;
    padding: 3px 8px;
    background: linear-gradient(90deg, #e8c0c8, #C7C4D9, #f6ebbe);
    border-bottom: 1px solid #5A3268;
    color: #5A3268;
    font-weight: bold;
    font-size: 12px;
  }
}

/* ================================================================
   ============  SESSION: MOBILE-FIRST + STICKY + FIXES  ==========
   ================================================================
   Everything below is NEW and intentionally last in the file so it
   wins the cascade. Organised as:
     1. DESKTOP  — sticky right column
     2. DESKTOP  — center / stretch fixes (update box, marquee,
                   yellow box, starry box)
     3. SHARED   — how-to-chart box, footer
     4. MOBILE   — the real mobile design (<=1249px)
   ================================================================ */


/* ================================================================
   1. STICKY RIGHT COLUMN  (desktop only)
   ----------------------------------------------------------------
   The menu + about + music player ride along as you scroll.
   max-height:100vh + overflow-y:auto means that if the column is
   ever TALLER than the screen (short laptop, big zoom), it gets its
   own little scrollbar instead of having its bottom become
   permanently unreachable.
   The left column is deliberately NOT sticky — it scrolls away with
   the center, as requested.
   ================================================================ */
@media (min-width: 1250px){

  .cabinet > .rightsidebox--home{
    position: sticky;
    top: 12px;                    /* gap between viewport top and column */
    align-self: flex-start;       /* REQUIRED: a stretched flex item cannot stick */
    /* NO max-height / overflow-y here anymore. It was `max-height:100vh;
       overflow-y:auto` so a column taller than the screen could scroll in
       place - but on any viewport shorter than the column (~940px) that
       turned into a permanent, ugly scrollbar down the right edge, AND it
       intermittently flickered on at threshold heights (the "scroll is
       back" bug). Nothing in this column actually needs to scroll on its
       own; if it is ever taller than the viewport, the PAGE scroll reaches
       it fine. Dropping the clip removes the scrollbar for good. */
    overflow: visible;
    padding-left: 4px;
    padding-right: 4px;
    gap: 14px;
  }

  /* The column's `gap` is the single source of truth for spacing between
     these boxes. Their own margins fought it and produced uneven gutters. */
  .cabinet > .rightsidebox--home > *{
    margin-top: 0 !important;
    margin-bottom: 0 !important;
  }

  /* win98-flavoured scrollbar for the sticky column */
  .cabinet > .rightsidebox--home::-webkit-scrollbar{
    width: 10px;
  }
  .cabinet > .rightsidebox--home::-webkit-scrollbar-track{
    background: #ded9ee;
    border-left: 1px solid #8f8aa8;
  }
  .cabinet > .rightsidebox--home::-webkit-scrollbar-thumb{
    background: var(--pink);
    border: 2px solid #ded9ee;
  }

}

@keyframes float-crystal{
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-7px); }
}
@media (prefers-reduced-motion: reduce){
  .crystalstickerright,
  .footer-crystal { animation: none !important; }
}


/* ================================================================
   2. CENTER / STRETCH FIXES  (desktop)
   ================================================================ */
@media (min-width: 1250px){

  /* ---- 2a. UPDATE BOX centered in its window ---- */
  .cabinet .zodiacblog{
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    margin: 5px 0;
    display: flex;
    flex-direction: column;
    align-items: center;      /* centers the updateblog horizontally */
  }
  .cabinet .zodiacblog .updateblog{
    width: 100%;
    max-width: 92%;
    margin: 0 auto;
    text-align: left;         /* body copy stays readable, box is centered */
    box-sizing: border-box;
  }
  .cabinet .zodiacblog .updateblog h3.stroke{
    text-align: left;
  }

  /* ---- 2b. BUTTON MARQUEE centered + stretched ---- */
  .cabinet .btns{
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    margin: 8px 0;
    display: block;
    text-align: center;
  }
  .cabinet .btns marquee{
    width: 100%;
    display: block;
    margin: 0 auto;
  }
}

/* ---- 2c. THE BIG YELLOW BOX (todos / chat / time) ----
   Stretch it full width of its container, and give it the same
   diagonal upward-scrolling background as the page.
   NOTE: `slide` is the page's existing bg animation keyframe, so the
   motion matches the wallpaper exactly. */
.webmasterframe{
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  background-image: url("images/backgrounds/bg84.gif");
  background-repeat: repeat;
  background-attachment: local;    /* so it scrolls WITH the box, not fixed */
  animation: slide 200s linear infinite;
}

/* ---- 2d. THE STARRY SHITPOST / WEBRING BOX ----
   Stretch to full width + a little more height. */
.wm-corner .socialsbox,
.wm-socials{
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  min-height: 260px;               /* "a little more height" */
}

@media (prefers-reduced-motion: reduce){
  .webmasterframe{ animation: none; }
}


/* ================================================================
   3. SHARED COMPONENTS  —  how-to-chart box + footer
   ================================================================ */

/* ---- 3a. HOW TO GET YOUR WHOLE-SIGN CHART ----
   Replaces the old third-party birth chart widget. */
.howto-chart{
  grid-area: widget;
  box-sizing: border-box;
  width: 100%;
  margin: 5px 0;
  padding: 14px 16px;
  border: 3.5px double #000;
  background: var(--yello);
  font-family: basiic, monospace;
  font-size: 13px;                 /* >=12.5px: basiic i/l stay legible */
  line-height: 1.55;
  color: #2e2838;
}
.howto-chart h3.stroke{
  margin: 0 0 6px 0;
  font-size: 1.15em;
}
.howto-intro{
  margin: 0 0 10px 0;
  font-style: italic;
  color: #5A3268;
}
.howto-steps{
  margin: 0;
  padding-left: 1.35em;
  list-style: decimal;
}
.howto-steps li{
  margin-bottom: 5px;
}
.howto-steps b{
  color: var(--pink);
}
.howto-steps a{
  color: var(--teal);
  font-weight: bold;
}
.howto-note{
  margin: 10px 0 0 0;
  padding-top: 8px;
  border-top: 1px dashed #a99fc0;
  font-size: 0.95em;
}
.howto-note img{
  vertical-align: middle;
  margin-right: 4px;
}

/* ---- 3b. FOOTER (disclaimer lives here now) ---- */
.site-footer{
  position: relative;
  box-sizing: border-box;
  width: 100%;
  margin: 26px 0 10px 0;
  padding: 12px 16px;
  border-top: 3px double #5A3268;
  background: rgba(255,255,251,0.72);
  font-family: basiic, monospace;
  font-size: 12.5px;               /* the basiic floor, not below */
  line-height: 1.6;
  color: #5A3268;
  text-align: center;
}
.footer-inner{
  max-width: 560px;
  margin: 0 auto;
}
.footer-disclaimer{
  margin: 0 0 4px 0;
}

/* THE CRYSTAL — floats at the footer's bottom-LEFT corner.

   500x500 native, scaled to 130. It was 190 on the right, which crowded
   the disclaimer text and read as too heavy for a footer ornament.
   Smooth png (not pixel art), so a uniform downscale is safe at any size.
   Overhangs the corner slightly so it reads as a sticker stuck on rather
   than an image placed in a slot.
   pointer-events:none so it can never eat a click on the footer text. */
.footer-crystal{
  position: absolute;
  left: -14px;
  right: auto;
  bottom: -16px;                   /* overhangs the corner, like a sticker */
  width: 130px;
  height: auto;
  z-index: 4;
  pointer-events: none;
  filter: drop-shadow(2px 3px 4px rgba(20, 10, 45, .40));
  animation: float-crystal 4s ease-in-out infinite;
}


/* ================================================================
   4. MOBILE  —  THE REAL DESIGN  (<= 1249px)
   ----------------------------------------------------------------
   Most traffic arrives here from Facebook, on a phone.
   The job of this screen, in order:

        1. WHO I AM        Ash card: face, name, big-3
        2. WHAT I SELL     the three offerings, tappable
        3. HOW TO BOOK     one fat pink button
        4. ...everything else, sunk to the bottom.

   So the DOM order (nav, canvas, fluff) gets re-ordered with flex
   `order`, and the canvas itself is re-ordered internally.
   Design notes:
     - pixel gifs are NEVER fractionally scaled; we resize the
       CONTAINER and let the sprite sit at native size.
     - splashpfp.jpg is 180x180. It is never upscaled past 180.
     - body copy stays >= 12.5px so basiic's i/l don't collapse.
   ================================================================ */
@media (max-width: 1249px){

  /* ---------- 4.0 PAGE SHELL ---------- */
  body.--home{
    padding: 0 0 24px 0;           /* was 8px all round; gutters live on children now */
    background-attachment: scroll; /* fixed bg is janky + expensive on phones */
  }

  .cabinet{
    padding: 0 10px;               /* one consistent gutter for the whole stack */
    box-sizing: border-box;
  }

  /* ---------- 4.1 THE STACK ORDER ----------
     Overrides the old nav-first order. Canvas (who I am / what I
     sell / how to book) now comes FIRST. */
  .container--home   { order: 1 !important; }  /* the canvas */
  .rightsidebox--home{ order: 2 !important; }  /* menu + about + music */
  .leftsidebox--home { order: 3 !important; }  /* fluff widgets, last */

  /* ---------- 4.2 THE BANNER ----------
     Keep it, but make it short and punchy. It's the first thing a
     Facebook visitor sees, so it must not eat the whole screen. */
  .introgradbox{
    padding: 14px 10px !important;
    min-height: 0 !important;
    text-align: center;
  }
  .introgradbox > div[style]{        /* the inline margin-left:30px wrapper */
    margin-left: 0 !important;
  }
  .introgradbox h1{
    font-size: 1.05em !important;    /* NES font is wide; this fits ~360px */
    line-height: 1.35;
    letter-spacing: 0;
    margin: 0;
  }
  /* the drifting clouds are decorative + costly: bin them on phones */
  .introgradbox .clouds{ display: none; }

  /* ---------- 4.3 THE DESKTOP CANVAS BECOMES A STACK ----------
     .desktop-screen is a grid on desktop. On mobile it's just a
     column, and we re-order the drop-zones into the priority order. */
  .desktop-screen{
    display: flex !important;
    flex-direction: column !important;
    grid-template-columns: none !important;
    grid-template-areas: none !important;
    width: 100% !important;
    padding: 0 !important;
    gap: 14px;
    background: none;
    border: 0;
  }
  .dz{ width: 100% !important; margin: 0 !important; }

  .dz-ash      { order: 1; }   /* WHO I AM */
  .dz-offerings{ order: 2; }   /* WHAT I SELL */
  .dz-booking  { order: 3; }   /* HOW TO BOOK */
  .dz-counter  { order: 4; }   /* gyaru + counter: sinks below the fold */

  /* ---------- 4.4 WHO I AM — the Ash card ----------
     Rebuilt as a clean vertical card: photo on top at its NATIVE
     180px (never upscaled), name + big-3 beneath, meters last. */
  .intro-flex1{
    display: block !important;
    padding: 0 !important;
  }
  .intro-card-wrap{
    width: 100% !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
  }

  .w98-card.small,
  .w98-card[data-theme="pp"]{
    width: 100% !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
    box-sizing: border-box;
  }

  .w98-body{
    height: auto !important;
    padding: 8px !important;
  }

  /* the stat panel stops being a float-around-photo layout */
  .w98-stats{
    display: flex !important;
    flex-direction: column !important;
    align-items: center !important;
    width: 100% !important;
    height: auto !important;
    padding: 10px !important;
    box-sizing: border-box;
    gap: 12px;
  }

  /* PORTRAIT: native 180x180, dead centre, never scaled up */
  .w98-portrait{
    float: none !important;
    position: relative !important;
    width: 180px !important;
    height: 180px !important;
    max-width: 180px !important;
    margin: 0 auto !important;
    flex: 0 0 auto;
  }
  .w98-portrait img{
    width: 180px !important;
    height: 180px !important;
    max-width: 180px !important;
    display: block;
    image-rendering: auto;         /* it's a photo, not pixel art */
  }

  /* THE SPEECH BUBBLE — on desktop it pops out on hover. Phones have
     no hover, so the JS already renders it permanently. Here we stop
     it from being an absolutely-positioned overlay ON TOP of her face
     and let it sit UNDER the photo as a normal little bubble. */
  /* the desktop tail pointed at a floating bubble; irrelevant now */
  /* the invisible ghost sizer is a desktop-only trick; it would just
     add dead height here */

  /* name + big-3 + meters, stacked and centered under the photo */
  .w98-stats-lower{
    display: flex !important;
    flex-direction: column-reverse !important;  /* NAME above METERS */
    width: 100% !important;
    height: auto !important;
    gap: 10px;
    align-items: stretch;
  }
  .w98-stats-hd{
    width: 100% !important;
    padding: 10px !important;
    box-sizing: border-box;
    text-align: center;
  }
  .w98-name{
    font-size: 1.5em !important;
    margin: 0 0 6px 0 !important;
    text-align: center;
  }
  .w98-sub ul{
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 3px;
    align-items: center;
  }
  .w98-sub li{
    font-size: 13px;
    letter-spacing: 0.5px;
  }
  .w98-kv{
    width: 100% !important;
    box-sizing: border-box;
    font-size: 13px;
  }

  /* ---------- 4.5 WHAT I SELL — the offerings ----------
     Full width, big tap targets, price always visible. */
  .readingsbox{
    width: 100% !important;
    margin: 0 !important;
    padding: 16px 12px !important;
    box-sizing: border-box;
  }
  .readingsbox h3.stroke{
    position: static !important;
    margin: 0 0 4px 0 !important;
    font-size: 1.5em !important;
    text-align: center;
  }

  .package{
    margin-bottom: 10px !important;
  }
  .package summary{
    display: flex !important;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 13px 12px !important;  /* >=44px tall: a real thumb target */
    font-size: 13.5px !important;
    line-height: 1.4;
  }
  .pkg-title{
    display: flex;
    flex-direction: column;
    text-align: left;
    flex: 1 1 auto;
    min-width: 0;
  }
  .pkg-hook{
    font-size: 11.5px;              /* hook is a whisper; not body copy */
    opacity: 0.85;
    margin-top: 3px;
    line-height: 1.3;
  }
  .pkg-price{
    flex: 0 0 auto;
    font-size: 1.15em;
    font-weight: bold;
    white-space: nowrap;
  }
  .package-body{
    padding: 12px !important;
    font-size: 13px;
    line-height: 1.6;
  }
  .package-body ul{
    padding-left: 1.1em;
    margin: 6px 0 0 0;
  }
  .package-body li{ margin-bottom: 4px; }

  /* the "book over there ->" arrow makes no sense in a stack:
     on mobile booking is BELOW, so point down. */
  .readings-cta{
    text-align: center;
    font-size: 13px;
    margin-top: 12px !important;
  }
  .readings-cta-arrow{
    display: inline-block;
    transform: rotate(90deg);
  }

  /* ---------- 4.6 HOW TO BOOK — the money button ---------- */
  .booking-col,
  .booking{
    width: 100% !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    box-sizing: border-box;
  }
  /* THIS was the 536px box overflowing its 346px parent */
  .booking-split{
    display: flex !important;
    flex-direction: column !important;
    width: 100% !important;
    max-width: 100% !important;
    align-items: stretch !important;
    gap: 12px;
  }
  .booking-left,
  .booking-right{
    width: 100% !important;
    max-width: 100% !important;
    margin: 0 !important;
    float: none !important;
  }
  .booking-window{
    width: 100% !important;
    max-width: 100% !important;
    box-sizing: border-box;
    margin: 0 !important;
  }
  .booking-window-body{
    padding: 14px 12px !important;
    text-align: center;
  }
  .booking-blurb{
    font-size: 14px;
    margin: 0 0 12px 0;
  }
  /* the CTA: full width, unmissable, comfortably tappable */
  .booking-btn{
    display: block !important;
    width: 100% !important;
    box-sizing: border-box;
    padding: 16px 12px !important;
    font-size: 16px !important;
    text-align: center;
    margin: 0 auto !important;
  }
  .booking-prep{
    font-size: 12.5px;
    margin: 12px 0 0 0;
    line-height: 1.5;
  }
  .booking-prep img{ vertical-align: middle; }
  .booking-note{
    font-size: 11.5px;
    opacity: 0.8;
    margin: 6px 0 0 0;
  }

  /* ---------- 4.7 THE SINKING FLUFF ----------
     Gyaru, counter, flair buttons, stamps, webring: all still there,
     all still charming, all below the things that pay the bills. */
  .visitor-wrap{
    width: 100% !important;
    box-sizing: border-box;
  }
  .visitor-row{
    display: flex;
    justify-content: center;
    align-items: flex-end;
    gap: 8px;
    flex-wrap: wrap;
  }
  /* container resized, sprite left at native size (no shimmer) */
  .visitor-left1,
  .visitor-right1{
    width: auto !important;
    max-width: 48% !important;
    overflow: hidden;
  }
  .visitor-left1 img,
  .visitor-right1 img{
    max-width: 100%;
    height: auto;
    image-rendering: pixelated;
  }
  .visitor-left2{
    text-align: center;
    font-size: 12.5px;
    margin-top: 8px;
  }

  .splashbuttons{
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px;
    padding: 12px 0;
    width: 100%;
    box-sizing: border-box;
  }
  .splashbuttons img{ image-rendering: pixelated; }

  /* ---------- 4.8 WEBMASTER'S CORNER on mobile ---------- */
  .wm-corner{
    display: flex !important;
    flex-direction: column !important;
    width: 100% !important;
    gap: 12px;
  }
  .zodiacblog{
    width: 100% !important;
    max-width: 100% !important;
    height: auto !important;        /* was a hard 320px: let it breathe */
    max-height: 320px;
    overflow-y: auto;               /* long update log scrolls in place */
    margin: 0 !important;
    box-sizing: border-box;
    font-size: 12.5px;
    line-height: 1.6;
  }
  .updateblog{ width: 100% !important; }

  .webmasterframe{
    display: flex !important;
    flex-direction: column !important;
    width: 100% !important;
    gap: 12px;
    padding: 10px;
    box-sizing: border-box;
  }
  .webmasterframe > *{
    width: 100% !important;
    max-width: 100% !important;
    margin: 0 !important;
    float: none !important;
    box-sizing: border-box;
  }
  .todos{ font-size: 12.5px; }
  .wm-chatbox iframe{ width: 100% !important; }
  .wm-socials{ min-height: 0 !important; text-align: center; }
  .wm-socials .siteButton{ width: 100%; box-sizing: border-box; }

  /* ---------- 4.9 RIGHT COLUMN (nav / about / music) ----------
     NOT sticky on mobile — sticky in a single-column stack would
     just pin a box over the content. Plain flow. */
  .rightsidebox--home{
    position: static !important;
    max-height: none !important;
    overflow: visible !important;
  }
  .aboutbox,
  #musicplayer,
  #mainmenu{
    width: 100% !important;
    max-width: 100% !important;
    margin: 0 auto 12px auto !important;
    box-sizing: border-box;
  }
  #mainmenu{
    height: auto !important;
    min-height: 0 !important;
    padding-bottom: 10px;
  }
  /* the rainbow about-blurb is a wall of text on a 390px screen */
  .aboutbox{
    font-size: 12.5px;
    line-height: 1.65;
  }
  .aboutimage img{ max-width: 100%; height: auto; }

  /* music player: make the controls thumb-sized */
  #musicplayer .player-buttons .window-button{
    min-width: 44px;
    min-height: 36px;
  }
  #musicplayer .track-select{
    width: 100%;
    box-sizing: border-box;
    font-size: 12.5px;
  }

  /* ---------- 4.10 LEFT COLUMN (fluff widgets), dead last ---------- */
  .leftsidebox--home{
    order: 3 !important;
  }
  .transits{
    width: 100% !important;
    box-sizing: border-box;
    text-align: center;
  }
  .transits iframe,
  .planets iframe{
    max-width: 100%;
  }
  .stamps-wrap{ width: 100%; overflow: hidden; }

  /* ---------- 4.11 FOOTER on mobile ---------- */
  .site-footer{
    margin-top: 20px;
    padding: 14px 12px;
  }
  .footer-crystal{
    width: 58px;
    right: 4px;
  }

  /* ---------- 4.12 HOW-TO-CHART on mobile ---------- */
  .howto-chart{
    font-size: 13px;
    padding: 14px 13px;
  }

  /* ---------- 4.13 GLOBAL MOBILE SAFETY ---------- */
  img, iframe, marquee, textarea{
    max-width: 100%;
    box-sizing: border-box;
  }
  .gameboy-float,
  .crystalsticker-containerleft,
  .crystalsticker-containerright,
  .skybox{                         /* the WHOLE sky box (widget + key), not
                                      just .zodiac-key inside it — hiding only
                                      the child would leave an empty pink frame
                                      sitting on the phone.

                                      ADDED WITH the box itself, on purpose.
                                      Anything that lives OUTSIDE .lilspacebox
                                      escapes the mobile cut and defaults to
                                      `order: 0`, which sorts it to the TOP of
                                      the phone. THE TEN and DOORWAYS both did
                                      exactly this and buried the business card
                                      under a trophy case. Mobile = business
                                      card; a reference table is desktop reward. */
    display: none !important;       /* the footer crystal covers this now */
  }
}


/* ---------- 4.14 SMALL PHONES (<=380px) ----------
   iPhone SE / older Androids. Only nudge what actually breaks. */
@media (max-width: 380px){
  .introgradbox h1{ font-size: 0.92em !important; }
  .w98-portrait,
  .w98-portrait img{
    width: 150px !important;
    height: 150px !important;       /* DOWN-scaling a photo is fine */
  }
  .package summary{ font-size: 12.8px !important; }
  .pkg-price{ font-size: 1.05em; }
  .cabinet{ padding: 0 6px; }
}


/* ================================================================
   5. WEBMASTER'S CORNER — corrections after measuring the render
   ----------------------------------------------------------------
   Measured problems on the live page:
     a) .webmasterframe had a hardcoded width:650px inside a 732px
        container, so "full width" never happened.
     b) .wm-socials (the starry box) had justify-self:center +
        align-self:start, which shrink-wraps it. That's why it
        wouldn't stretch or grow.
     c) The diagonal bg animation was fighting the text underneath.
        Keep the animation (it was asked for) but lay a translucent
        scrim over it so copy stays readable.
     d) The button marquee spilled past the container's right edge.
   ================================================================ */
@media (min-width: 1250px){

  /* (a) THE BIG YELLOW BOX — actually full width now */
  .cabinet .webmasterframe{
    width: 100% !important;
    max-width: 100% !important;
    box-sizing: border-box;
    height: auto !important;
    min-height: 530px;
    padding: 12px;
    gap: 10px;
    align-items: stretch;       /* was center: let cells fill their row */
  }

  /* (b) THE STARRY BOX — stretch it, and give it a little more height */
  .cabinet .wm-socials{
    justify-self: stretch !important;   /* was center (shrink-wrapped) */
    align-self: stretch !important;     /* was start */
    width: 100% !important;
    max-width: 100% !important;
    box-sizing: border-box;
    min-height: 280px;                  /* "a little more height" */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
  }
  .cabinet .wm-chatbox{
    align-self: stretch !important;
    width: 100%;
    box-sizing: border-box;
  }
  .cabinet .todos{
    align-self: stretch !important;
    box-sizing: border-box;
  }

  /* (d) MARQUEE — clip it to the container so it can't bleed out */
  .cabinet .btns{
    overflow: hidden;
    box-sizing: border-box;
  }
  .cabinet .btns marquee{
    width: 100% !important;
    max-width: 100%;
    box-sizing: border-box;
  }
}

/* (c) READABILITY SCRIM over the animated diagonal background.
   The bg gif + drift stays; a soft wash sits between it and the text
   so the todos/chat/time copy doesn't have to fight the pattern.
   Applies on every screen size. */
.webmasterframe{
  position: relative;
  isolation: isolate;
}
.webmasterframe::before{
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 238, 0.62);   /* --yello, translucent */
  pointer-events: none;
  z-index: -1;                              /* behind content, over the bg */
}

/* The button marquee's inner wrapper divs are un-clipped, so the
   shuffled <img>s render past the viewport and create a horizontal
   scrollbar. Clip at the wrapper, not the sprite (never scale gifs). */
#random-button-marquee-wrapper,
#random-button-marquee,
#stamp-marquee{
  display: block;
  max-width: 100%;
}
/* the left column itself must clip, or the stamp marquee's sprites
   render past the cabinet and create a horizontal scrollbar */
.leftsidebox--home{
  overflow-x: hidden;
  box-sizing: border-box;
}

/* .transits was width:225px PLUS 5px padding PLUS 4px border with no
   box-sizing, so it measured ~243px inside a 230px column.
   Border-box makes the declared width the REAL width.

   THE CHOPPED BORDER. .transits is 230px wide inside a `display: flex` parent
   (.leftsidebox--home) that has `overflow: hidden`. A flex item that is wider
   than its line overflows, and it overflowed to the LEFT — putting .transits'
   left edge 2px OUTSIDE the clipping parent, which then scissored 2px off the
   purple border down the entire column. Ashley spotted it.

   `flex-shrink: 0` + `margin-right: 0` stops it being squeezed and pushed out.
   The border is whole again. */
.transits{
  box-sizing: border-box;
  max-width: 100%;
  flex-shrink: 0;
  margin-right: 0;
}
.transits > *,
.stamps-wrap,
.stamps-wrap marquee{
  max-width: 100%;
  box-sizing: border-box;
}
/* ...EXCEPT the sky box. .transits is 225px with 5px padding and a 4px
   double border, so its INNER width is only 207px — and the astrodienst
   iframe needs its native 210px or the widget's own content overflows and
   Astrodienst draws a horizontal SCROLLBAR through the middle of the box.
   A 3px shortfall was producing a scrollbar. The box gets to be as wide as
   the instrument inside it. */
.transits > .skybox{
  max-width: none;
}

/* Keep the marquees clipped to their boxes (defensive, cheap). */
.stamps-wrap,
.btns{
  contain: paint;
  overflow: hidden;
  max-width: 100%;
  box-sizing: border-box;
}

/* ============================================================
   ROOT CAUSE of the 25px horizontal scrollbar (found by hiding
   elements one by one until scrollWidth dropped):
   .crystalstickerright is position:absolute with left:940px and
   width:145px. Inside the 740px canvas that lands at 1085px, so it
   punched a hole 25px past the right edge of the page.
   It is NOT in the right column, so my sticky rule never touched it.
   The crystal now lives in the FOOTER (.footer-crystal), so this
   stray copy is retired.
   ============================================================ */
.crystalsticker-containerright,
.crystalstickerright{
  display: none !important;
}
.btns,
.btns marquee,
.stamps-wrap,
.stamps-wrap marquee{
  overflow: hidden !important;
  box-sizing: border-box;
  max-width: 100%;
}
/* Sideways-scroll containment.
   IMPORTANT: do NOT put overflow-x:hidden on <body>. It computes
   overflow-y to `auto`, which turns body into a scroll container and
   silently kills position:sticky on the right column. Clip on <html>
   only — that keeps the viewport as the sticky scrollport. */
html{
  overflow-x: hidden;
}
body{
  max-width: 100%;
}


/* ================================================================
   6. BELOW-THE-DESKTOP STACK  —  REBUILT
   ----------------------------------------------------------------
   The old .wm-corner was a 2-column grid:
       grid-template-columns: 1fr 360px;
       "blog widget" / "btns widget" / "frame gameboy"
   ...plus negative-margin hacks (.btns margin-top:-85px and
   .webmasterframe margin-top:-125px) that dragged boxes upward and
   made them overlap. That's why the marquee "disappeared": it was
   still in the DOM, just yanked up BEHIND the update box.

   Replaced with a simple, honest vertical stack, centered in the
   middle column:

        [ desktop canvas ]      (above, untouched)
        [ update box    ]       650 x 320, centered
        [ marquee       ]       650 wide, centered
        [ webmaster box ]       full width of the middle column
        [ how-to-chart  ]       parked here for now

   Nothing floats beside anything. Nothing overlaps.
   ================================================================ */

/* ---- 6.1 The stack itself ---- */
.wm-corner{
  display: flex !important;
  flex-direction: column !important;
  align-items: center !important;      /* centers each box in the column */
  grid-template-columns: none !important;
  grid-template-areas: none !important;
  gap: 18px !important;
  width: 100%;
  box-sizing: border-box;
}

/* Kill every negative-margin hack from the old grid. These were the
   cause of the overlapping. */
.wm-corner .zodiacblog,
.wm-corner .btns,
.wm-corner .webmasterframe,
.wm-corner .howto-chart{
  grid-area: auto !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  float: none !important;
}

/* ---- 6.2 UPDATE BOX — back to its original size, centered ---- */
.wm-corner .zodiacblog{
  width: 650px !important;             /* the original */
  max-width: 100%;
  height: 320px !important;            /* the original */
  box-sizing: border-box;
  margin-left: auto !important;
  margin-right: auto !important;
  overflow-y: auto;                    /* the log is long: scroll inside */
  display: block !important;           /* undo my earlier flex centering */
  padding: 1em;
}
/* the satsleep gif used to sit outside the box; keep it tucked inside */
.wm-corner .zodiacblog > img{
  display: block;
  margin: 6px auto 0 auto;
}

/* ---- 6.3 MARQUEE — restored, centered, original 650 width ----
   It was never deleted. margin-top:-85px was hiding it. */
.wm-corner .btns{
  width: 650px !important;             /* the original */
  max-width: 100%;
  box-sizing: border-box;
  margin-left: auto !important;
  margin-right: auto !important;
  display: flex !important;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  contain: paint;                      /* keep sprites from extending the page */
}
.wm-corner .btns marquee{
  width: 100% !important;
  display: block;
}

/* ---- 6.4 WEBMASTER BOX — stretches the middle column ----
   Contents (todos / chat / time+socials) keep their original
   arrangement: the 2-col grid INSIDE the box is preserved.
   Only the OUTER box changes: it now spans the column. */
.wm-corner .webmasterframe{
  width: 100% !important;
  max-width: 100% !important;
  box-sizing: border-box;
  height: auto !important;
  min-height: 530px;                   /* the original height, as a floor */

  /* restore the original inner grid (I had flattened it last round) */
  display: grid !important;
  grid-template-columns: 1fr 1fr !important;
  grid-template-areas:
    "todos   chat"
    "socials chat"
    "socials friends" !important;
  gap: 2px !important;
  align-items: center !important;
  padding: 10px;
}

/* ---- 6.5 The yellow box's background: slow vertical scroll, up-left ----
   Distinct from the page's diagonal drift. A gentle upward crawl with
   a slight leftward lean, slow. */
@keyframes wm-bg-upleft{
  from { background-position: 0 0; }
  to   { background-position: -120px -600px; }   /* mostly up, a little left */
}
.webmasterframe{
  position: relative;
  isolation: isolate;
  background-image: url("images/backgrounds/bg87.gif");
  background-repeat: repeat;
  animation: wm-bg-upleft 90s linear infinite;   /* slow */
}
/* readability scrim so the todos/chat copy isn't fighting the pattern */
.webmasterframe::before{
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 238, 0.55);
  pointer-events: none;
  z-index: -1;
}
@media (prefers-reduced-motion: reduce){
  .webmasterframe{ animation: none; }
}

/* (Round-2 align-self overrides removed here. They pinned the cells to
   center/start with !important and were fighting the stretch rules in
   section 9.3, which is why todos stayed 150px next to a 420px chat.) */

/* ---- 6.6 HOW-TO-CHART — parked at the bottom of the stack ---- */
.wm-corner .howto-chart{
  width: 650px !important;
  max-width: 100%;
  box-sizing: border-box;
  margin-left: auto !important;
  margin-right: auto !important;
}

/* ---- 6.7 GAMEBOY — new life in the LEFT column ---- */
.leftsidebox--home .gameboy-float{
  display: block !important;           /* my earlier mobile rule hid it */
  width: 100%;
  padding: 10px 0 0 0;
  margin: 0;
  text-align: center;
  float: none;
}
.leftsidebox--home .gameboy-float img{
  max-width: 100%;
  height: auto;
}


/* ================================================================
   7. MOBILE — the rebuilt stack must not keep its desktop widths
   ----------------------------------------------------------------
   The business order (Ash -> offerings -> booking) is unchanged and
   still comes first. This just stops the new 650px boxes from
   blowing out a 390px phone.
   ================================================================ */
@media (max-width: 1249px){

  .wm-corner{
    gap: 12px !important;
    align-items: stretch !important;
  }

  /* update box: no fixed 732x400 on a phone.
     The OUTER box never scrolls (that would clip Saturn); the INNER
     log does its own scrolling. */
  .wm-corner .zodiacblog{
    width: 100% !important;
    max-width: 100% !important;
    height: auto !important;
    overflow: visible !important;
    padding: 12px !important;
  }
  /* the narrowed 560px panel goes full-width on a phone */
  .wm-corner .zodiacblog .update-panel{
    width: 100% !important;
    max-width: 100% !important;
  }
  .wm-corner .zodiacblog .updateblog{
    width: 100% !important;
    height: auto !important;
    max-height: 280px;
    overflow-y: auto;
  }
  /* Saturn stays on the panel corner, tucked in so she cannot spill
     off a 390px screen. Sprite not upscaled. */
  .wm-corner .zodiacblog .saturn-flair{
    right: -6px;
    bottom: -10px;
    max-width: 90px;
    height: auto;
  }

  .wm-corner .btns,
  .wm-corner .howto-chart{
    width: 100% !important;
    max-width: 100% !important;
  }

  /* webmaster box: the inner 2-col grid is too tight for a phone,
     so it stacks — but the CONTENTS all stay. */
  .wm-corner .webmasterframe{
    display: flex !important;
    flex-direction: column !important;
    grid-template-columns: none !important;
    grid-template-areas: none !important;
    min-height: 0 !important;
    gap: 12px !important;
    padding: 10px !important;
  }
  .wm-corner .webmasterframe > *{
    width: 100% !important;
    max-width: 100% !important;
    margin: 0 !important;
  }
  .cabinet .wm-socials{
    justify-self: stretch !important;
    width: 100% !important;
    text-align: center;
  }

  /* gameboy rides along in the left column (which sits last on mobile) */
  .leftsidebox--home .gameboy-float{
    display: block !important;
    text-align: center;
  }
}


/* ================================================================
   8. THREE FIXES: update box scroll, Saturn flair, bg jitter
   ================================================================ */

/* ---- 8.1 OUTER BOX MUST NOT SCROLL ----
   .updateblog (the INNER box) already had `overflow: scroll` at
   270x575 — that was correct all along. Last round I wrongly added
   overflow-y:auto to .zodiacblog (the OUTER box) too, which gave the
   box a second scrollbar AND clipped Saturn.
   The outer box is now a plain, unscrolling frame. Only the inner
   log scrolls. Saturn can now hang outside the frame's edge. */
.wm-corner .zodiacblog{
  overflow: visible !important;      /* was auto: that was the bug */
  position: relative;                /* containing block for Saturn */
  display: flex !important;          /* let the inner log fill exactly */
  flex-direction: column;
}
.cabinet .zodiacblog{
  overflow: visible !important;
}

/* the inner log keeps the scrollbar, and keeps it to itself.
   flex:1 + min-height:0 makes it consume exactly the leftover space,
   so there is no percentage-height math left to overflow by a few px. */
.wm-corner .zodiacblog .updateblog{
  overflow-y: auto;
  overflow-x: hidden;
  flex: 1 1 auto;
  min-height: 0;                     /* REQUIRED or flex refuses to shrink */
  width: 100% !important;
  height: auto !important;
  max-width: 100%;
  margin: 0 !important;
  box-sizing: border-box;
}

/* ---- 8.2 SAILOR SATURN — z-layered flair ----
   She perches on the BOTTOM-RIGHT corner of the update box, half on
   the frame and half off it, like a sticker slapped on the edge.
   Sitting above the box (z-index) but below any real UI.
   Not scaled: rendered at her native size so she stays crisp. */
.wm-corner .zodiacblog .saturn-flair{
  position: absolute;
  right: -18px;                      /* hangs off the frame's edge */
  bottom: -14px;
  z-index: 5;                        /* the "z layered" bit */
  margin: 0;
  pointer-events: none;
  image-rendering: pixelated;
  filter: drop-shadow(2px 2px 0 rgba(46, 40, 56, 0.35));
  animation: saturn-bob 5s ease-in-out infinite;
}
/* she breathes a little, so she reads as flair not furniture */
@keyframes saturn-bob{
  0%, 100% { transform: translateY(0)    rotate(-3deg); }
  50%      { transform: translateY(-5px) rotate(-3deg); }
}
@media (prefers-reduced-motion: reduce){
  .saturn-flair{ animation: none !important; }
}

/* ---- 8.3 MARQUEE — span the full middle column ----
   Was 650px (matching the update box). Now matches the webmaster
   box: the full width of the column. */
.wm-corner .btns{
  width: 100% !important;
  max-width: 100% !important;
}

/* ---- 8.4 BACKGROUND SCROLL — fix the jitter, retune the angle ----

   THE JITTER: bg87.gif is a 100x100 tile. My old keyframe ended at
   `-120px -600px`. 120 is NOT a multiple of the 100px tile, so when
   the animation looped back to 0 the pattern was mid-tile and SNAPPED
   sideways. Every loop = a visible jump.

   THE FIX: end on an exact multiple of the tile in BOTH axes. Landing
   on (-100, -800) is visually identical to (0,0) because the tile has
   repeated a whole number of times — so the loop is seamless.

   THE ANGLE: you wanted more left, less up, and NOT a 45-degree
   diagonal. The tile is 100x100, so BOTH numbers must stay multiples
   of 100 to keep the loop seamless.
     -100 / -800  =  7 deg off vertical  (too subtle, barely leans)
     -200 / -600  = 18 deg off vertical  <- this one: clearly left,
                                            still clearly vertical
     -600 / -600  = 45 deg               (the diagonal you didn't want)

   Also using translate on a pseudo-element rather than animating
   background-position: compositor-driven, so it's smooth instead of
   repainting every frame (the other half of the jitter). */
@keyframes wm-bg-upleft{
  from { transform: translate(0, 0); }
  to   { transform: translate(-200px, -600px); }   /* exact tile multiples */
}

.webmasterframe{
  position: relative;
  isolation: isolate;
  overflow: hidden;                  /* clip the oversized moving layer */
  background-image: none;            /* the moving layer handles it now */
  animation: none;                   /* stop animating the element itself */
}

/* the moving wallpaper layer. It must be big enough to cover the box
   for the WHOLE journey: it travels 200px left and 600px up, so it is
   oversized by more than that in each direction. GPU-composited so it
   doesn't judder. */
.webmasterframe::after{
  content: "";
  position: absolute;
  top: -700px;                       /* > the 600px it travels up */
  left: -300px;                      /* > the 200px it travels left */
  width: calc(100% + 600px);
  height: calc(100% + 1400px);
  background-image: url("images/backgrounds/bg87.gif");
  background-repeat: repeat;
  animation: wm-bg-upleft 60s linear infinite;
  will-change: transform;            /* promote to its own layer */
  transform: translateZ(0);          /* force GPU compositing */
  backface-visibility: hidden;       /* kills sub-pixel shimmer */
  z-index: -2;
  pointer-events: none;
}

/* readability scrim sits BETWEEN the wallpaper and the content */
.webmasterframe::before{
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 238, 0.55);
  pointer-events: none;
  z-index: -1;
}

@media (prefers-reduced-motion: reduce){
  .webmasterframe::after{ animation: none; }
}


/* ================================================================
   9. POLISH PASS: update box width, marquee gap, webmaster interior,
      crystal sticker z-layer, star bracket box
   ================================================================ */

/* ---- 9.1 UPDATE BOX — match the marquee + webmaster box ----
   It was 650px while the marquee and webmaster box are the full 732px
   column, which is what made the marquee look like it was floating.
   Now all three share the same edges: one clean vertical line down
   both sides of the stack. */
.wm-corner .zodiacblog{
  width: 100% !important;
  max-width: 100% !important;
}
.wm-corner .howto-chart{
  width: 100% !important;
  max-width: 100% !important;
}

/* ---- 9.2 MARQUEE — sat too low ----
   The stack gap was a uniform 18px. The marquee is a thin 41px strip,
   so an equal gap above and below made it look like it was drifting.
   Pull it up snug under the update box; keep the normal gap below. */
.wm-corner .btns{
  margin-top: -8px !important;
  margin-bottom: 0 !important;
}

/* ---- 9.3 WEBMASTER BOX INTERIOR — comfortable proportions ----
   Was lopsided: todos 317x150 sitting next to a 353x420 chat box, with
   the time/socials cell shrink-wrapped. Give the cells room to breathe
   and let them fill their rows evenly. */
.cabinet .webmasterframe{
  grid-template-columns: 1fr 1fr !important;
  /* Explicit rows. Without these the rows shrink-wrap to content, which
     is why todos collapsed to 150px beside a 420px chat.
     The frame HEIGHT is fixed so the two rows divide it evenly rather
     than growing to fit the iframe's min-height. */
  grid-template-rows: 1fr 1fr !important;
  grid-template-areas:
    "todos   chat"
    "socials chat" !important;
  height: 560px !important;
  min-height: 0 !important;
  gap: 12px !important;
  align-items: stretch !important;
  padding: 14px !important;
}

/* Assign the grid areas by the classes that are ACTUALLY in the HTML.
   The old stylesheet targeted `.wm-todos`, but the element's class is
   just `todos` — so it never claimed its area and fell into auto-flow.
   That's the real reason it collapsed to 150px. */
.cabinet .webmasterframe > .todos       { grid-area: todos !important; }
.cabinet .webmasterframe > .wm-chatbox  { grid-area: chat !important; }
.cabinet .webmasterframe > .wm-socials  { grid-area: socials !important; }
/* the sticker shim is absolutely positioned; keep it out of the grid */
.cabinet .webmasterframe > .crystalsticker-containerleft{
  grid-area: unset !important;
}

/* todos: fills its cell instead of collapsing to its text height */
/* todos: fills its cell instead of collapsing to its text height.
   It had a hardcoded `height: 150px` (and width: 295px) from the old
   layout, which is what pinned it small next to the 420px chat box. */
.cabinet .webmasterframe .todos{
  align-self: stretch !important;
  width: 100% !important;
  height: auto !important;            /* was a hardcoded 150px */
  min-height: 0;
  box-sizing: border-box;
  margin: 0 !important;
  padding: 12px 14px !important;
  overflow-y: auto;                   /* keep the list scrollable if long */
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.cabinet .webmasterframe .todos ul{
  margin: 6px 0 0 0;
  padding-left: 1.2em;
}
.cabinet .webmasterframe .todos li{
  margin-bottom: 4px;
  line-height: 1.5;
}

/* chat: fills its column, no fixed float widths.
   Had a hardcoded height:420px / width:300px. */
.cabinet .webmasterframe .wm-chatbox{
  align-self: stretch !important;
  width: 100% !important;
  height: auto !important;            /* was a hardcoded 420px */
  box-sizing: border-box;
  margin: 0 !important;
  padding: 10px !important;
  display: flex;
  flex-direction: column;
}
.cabinet .webmasterframe .wm-chatbox iframe{
  width: 100% !important;
  flex: 1 1 0 !important;             /* basis 0 + grow = take the leftover */
  height: auto !important;
  min-height: 220px;                  /* never collapse to nothing */
  border: 0;
  display: block;
}
/* the kitty/comment sprites under the chat shouldn't eat the iframe's
   space; keep them compact on one line */
.cabinet .webmasterframe .wm-chatbox img{
  flex: 0 0 auto;
  max-height: 34px;
  width: auto;
}
.cabinet .webmasterframe .wm-chatbox br{ display: none; }

/* time + socials: fills its cell, contents centered and evenly spaced */
.cabinet .wm-socials{
  justify-self: stretch !important;
  align-self: stretch !important;
  width: 100% !important;
  max-width: 100% !important;
  height: auto !important;            /* was a hardcoded 320px */
  box-sizing: border-box;
  margin: 0 !important;
  padding: 12px !important;
  display: flex !important;
  flex-direction: column;
  align-items: center;
  justify-content: space-evenly;
  gap: 8px;
  min-height: 0 !important;
}
.cabinet .wm-socials img{
  max-width: 100%;
  height: auto;
}
.cabinet .wm-socials .siteButton{
  width: 100%;
  max-width: 240px;
  box-sizing: border-box;
}

/* ---- 9.4 CRYSTAL STICKER — up the z-axis, like a real sticker ----
   It was trapped inside the webmaster grid, so it could only ever sit
   INSIDE its cell. A sticker should sit ON TOP of the boxes, straddling
   their edges.
   The container is a 0-height shim, so we position the sticker off it
   and lift it above everything in the frame. */
.webmasterframe .crystalsticker-containerleft{
  position: absolute !important;
  top: 0;
  left: 0;
  width: 0 !important;
  height: 0 !important;
  margin: 0 !important;
  overflow: visible !important;
  z-index: 40;                        /* above the boxes it overlaps */
}
.webmasterframe .crystalstickerleft{
  position: absolute !important;
  /* straddles the seam between the todos box and the chat box */
  top: 152px;
  left: 296px;
  width: 130px;
  z-index: 40;
  pointer-events: none;
  transform: rotate(-8deg);
  filter: drop-shadow(3px 3px 0 rgba(46, 40, 56, 0.28));
}
/* the frame must not clip the sticker where it overhangs a box edge */
.webmasterframe{
  overflow: visible !important;
}
/* ...but the moving wallpaper must still be clipped to the frame, or it
   would spill across the page. `contain: paint` on the ::after layer
   clips the wallpaper to its own box WITHOUT clipping the sticker,
   which lives outside that layer. */
.webmasterframe::after{
  overflow: hidden;
  contain: paint;
}

/* ---- 9.5 STAR BRACKET BOX — fit the column, taller, above the footer ----
   Was a stale 1030px wide (from the pre-cabinet layout) and floated
   left, sitting BELOW the disclaimer. Now it fits the middle column,
   is taller, and sits above the footer. */
.lilspacebox{
  width: 100% !important;
  max-width: 100% !important;
  box-sizing: border-box;
  float: none !important;
  height: 200px !important;           /* taller */
  margin: 18px 0 0 0 !important;
  padding: 1em;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  position: relative;
}
.lilspacebox .starcornerleft,
.lilspacebox .starcornerright{
  flex: 0 0 auto;
}
.lilspacebox .starcornerleft img,
.lilspacebox .starcornerright img{
  float: none !important;
  margin: 0 !important;
  max-width: 100%;
  height: auto;
}

/* footer sits under the star box now */
.site-footer{
  margin-top: 14px;
}


/* ================================================================
   9. POLISH: crystal z-layer, webmaster internals, star box, footer
   ================================================================ */

/* ---- 9.1 CRYSTAL STICKER — actually on top now ----
   THE BUG (mine): I gave .webmasterframe `isolation: isolate` to make
   the background scrim work. That creates a STACKING CONTEXT, which
   traps every child inside it. So the crystal's z-index:40 was only
   competing with its siblings INSIDE the box, and could never rise
   above the box's own edges. It looked buried.

   FIX: drop `isolation` from the frame. The scrim doesn't need it —
   the ::before/::after already sit behind content via negative z-index
   on a positioned parent. With the trap gone, the crystal is free to
   layer over everything like a real sticker slapped on the page. */
.webmasterframe{
  isolation: auto !important;
  overflow: visible !important;   /* let the sticker hang off the edge */
}

/* the moving wallpaper must still not escape the box. Rather than
   clipping the frame (which would cut the sticker off), PAN THE TILE
   itself: the layer stays exactly the size of the frame and the
   pattern slides inside it. With a 100x100 tile and 100px-multiple
   travel this is seamless, and nothing can overflow. */
.webmasterframe::after{
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-image: url("images/backgrounds/bg87.gif");
  background-repeat: repeat;
  transform: none;
  animation: wm-bg-pan 60s linear infinite;
  will-change: background-position;
}

/* seamless because both values are exact multiples of the 100px tile.
   -200x / -600y = the 18deg leftward lean, mostly vertical. */
@keyframes wm-bg-pan{
  from { background-position:    0px    0px; }
  to   { background-position: -200px -600px; }
}

/* THE STICKER: lifted above the box, like it's stuck on the page */
.crystalsticker-containerleft{
  z-index: 400 !important;
}
.crystalstickerleft{
  z-index: 400 !important;
  filter: drop-shadow(3px 4px 3px rgba(46, 40, 56, 0.45));
  pointer-events: none;
}

/* ---- 9.2 WEBMASTER BOX INTERNALS — comfortable sizing ----
   The three cells were inheriting old fixed sizes and sitting at odd
   heights. Give them a calm, even rhythm: equal columns, matched
   heights, real padding, nothing crammed against an edge. */
.cabinet .webmasterframe{
  grid-template-columns: 1fr 1fr !important;
  grid-template-areas:
    "todos   chat"
    "socials chat" !important;
  gap: 14px !important;
  padding: 16px !important;
  align-items: stretch !important;
  min-height: 0 !important;
}

/* TODOS — top-left */
.cabinet .webmasterframe .todos{
  grid-area: todos;
  align-self: stretch !important;
  box-sizing: border-box;
  margin: 0 !important;
  padding: 12px 14px !important;
  width: 100% !important;
  font-size: 13px;
  line-height: 1.7;
  overflow: auto;
}
.cabinet .webmasterframe .todos h3.stroke{
  margin: 0 0 8px 0;
}
.cabinet .webmasterframe .todos ul{
  margin: 0;
  padding-left: 1.2em;
}
.cabinet .webmasterframe .todos > img{
  display: block;
  margin: 8px auto 0 auto;
  max-width: 100%;
}

/* CHAT — full-height right column */
.cabinet .webmasterframe .wm-chatbox{
  grid-area: chat;
  align-self: stretch !important;
  box-sizing: border-box;
  margin: 0 !important;
  padding: 12px !important;
  width: 100% !important;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.cabinet .webmasterframe .wm-chatbox iframe{
  width: 100% !important;
  flex: 1 1 auto;
  min-height: 250px;
  border: 0;
}
.cabinet .webmasterframe .wm-chatbox br{ display: none; }
.cabinet .webmasterframe .wm-chatbox img{
  margin-top: 8px;
  max-width: 100%;
}

/* SOCIALS / TIME — bottom-left */
.cabinet .webmasterframe .wm-socials{
  grid-area: socials;
  align-self: stretch !important;
  justify-self: stretch !important;
  box-sizing: border-box;
  margin: 0 !important;
  padding: 12px !important;
  width: 100% !important;
  min-height: 0 !important;
  display: flex !important;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
}
.cabinet .webmasterframe .wm-socials br{ display: none; }
.cabinet .webmasterframe .wm-socials img{ max-width: 100%; }
.cabinet .webmasterframe .wm-socials .time{ margin: 0; }
.cabinet .webmasterframe .wm-socials .siteButton{
  width: 100%;
  max-width: 240px;
  height: 60px;
  box-sizing: border-box;
  font-size: 11px;
}

/* ---- 9.3 STAR BRACKET BOX — fits the column, taller, above footer ---- */
.lilspacebox{
  width: 100% !important;
  max-width: 100% !important;
  box-sizing: border-box;
  height: 260px !important;         /* taller */
  margin: 18px 0 0 0 !important;
  padding: 1em;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  position: relative;
}

/* ---- 9.4 MARQUEE — tighten the gap under the update box ---- */
.wm-corner{
  gap: 12px !important;
}
.wm-corner .btns{
  margin-top: -4px !important;      /* pull it up snug under the update box */
}


/* ================================================================
   10. UPDATE BOX: narrow inner panel, filled bg, Saturn re-anchored
   ================================================================ */

/* ---- 10.1 BACKGROUND: fill, don't tile ----
   bg2.gif is 640x360 — an illustrated PANEL, not a seamless tile. In a
   732px box it repeated and you could see the seam. Stretch one copy to
   cover the whole frame instead. */
.wm-corner .zodiacblog{
  background-image: url("images/backgrounds/bg2.gif") !important;
  background-repeat: no-repeat !important;
  background-size: 100% 100% !important;   /* fill the frame exactly */
  background-position: center !important;

  /* HEIGHT: 320px only showed 53% of the log, cutting mid-sentence into
     the 2nd entry — which reads as broken, not as "scroll for more".
     The 1st entry alone is 187px. At 400 the panel gets ~352px: the
     whole first entry plus a clear slice of the next, so the scroll is
     legible as a scroll. 732x400 is also close to bg2.gif's native
     640x360 ratio, so stretching distorts the art less. */
  height: 400px !important;

  /* the inner panel is narrower than the frame, so center it and give
     the artwork room to breathe on both sides */
  align-items: center;                     /* it's a flex column */
  padding: 18px !important;
}

/* ---- 10.2 INNER TEXT PANEL: narrowed ----
   .update-panel wraps the scrolling log AND Saturn, so she travels with
   the panel instead of the outer frame. */
.wm-corner .zodiacblog .update-panel{
  position: relative;                      /* Saturn anchors to THIS */
  width: 560px !important;                 /* narrower than the 732 frame */
  max-width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  margin: 0 auto !important;
  display: flex;
  flex-direction: column;
}

.wm-corner .zodiacblog .updateblog{
  width: 100% !important;
  flex: 1 1 auto;
  min-height: 0;
  margin: 0 !important;
  box-sizing: border-box;
  overflow-y: auto;
  overflow-x: hidden;
}

/* ---- 10.3 SAILOR SATURN: re-anchored to the text panel ----
   She was pinned to the OUTER frame, whose right edge is flush with the
   middle column — so she hung 12px off the side of the page.
   Now she perches on the PANEL's corner, comfortably inside the column,
   and moves with it. */
.wm-corner .zodiacblog .saturn-flair{
  position: absolute;
  right: -14px;
  bottom: -12px;
  left: auto;
  top: auto;
  z-index: 5;
}


/* ================================================================
   11. LATEST POST CARD  —  blog.exe on the desktop canvas
   ----------------------------------------------------------------
   Placement rationale: it sits DIRECTLY UNDER the booking window, so
   the canvas reads as a funnel:

        who I am  ->  what I sell  ->  how to book  ->  read more of me

   That last step is the correct next action for someone who likes Ash
   but isn't ready to buy yet. It's a full-width row rather than a
   narrow column so the excerpt has room to breathe and the card reads
   as a piece of writing, not a sidebar widget.

   It's another .exe window, so it costs no new visual language.
   ================================================================ */

/* ---- 11.1 The new grid row (desktop) ----
   The `flair` row is retired: the flair buttons now live INSIDE the
   counter box (.visitor-left2), not as a separate grid item. */
@media (min-width: 1250px){
  .desktop-screen{
    /* NOTE, in case anyone tries to widen the offerings panel: you cannot
       do it by shrinking this left column. The COUNTER BOX carries
       `min-width: 324px` (.visitor-left2), and a grid track cannot shrink
       below its content's minimum — so the left column is pinned at 324 by
       the counter, not by ash.exe. Tried it; it does nothing. And that
       min-width is part of the counter's hard-won layout, so it stays. */
    grid-template-areas:
      "ash       offerings"
      "course    booking"
      "counter   blog"
      "flair     blog" !important;      /* flair tucks UNDER the counter in the
                                           left column (3x2 button grid); blog
                                           spans down the right to fill the row.
                                           Reversed from the full-width "flair
                                           flair" band per Ashley - the buttons
                                           belong under the counter box. */
    grid-template-rows: auto auto auto auto !important;
    /* row-gap bumped from the base 16px. The counter's gyaru gif rides ~14px
       up out of its own cell (the counter's padding-top:48px / margin
       -bottom:-20px offsets, which are hard-constrained), so a 16px row gap
       left it 2px off the course box and read as touching. 28px row-gap puts
       real air there. column-gap stays 16px so the two columns are unchanged. */
    row-gap: 16px !important;
    column-gap: 16px !important;
  }
  .dz-blog{
    grid-area: blog;
    width: 100%;
    box-sizing: border-box;
    align-self: stretch;
  }
  /* FILL THE TEAL — booking and blog STRETCH to fill their rows so there
     is no dead teal band above them. Course does NOT stretch: it stays at
     the top of its row with a real bottom margin, because it needs a gap
     UNDER it to clear the calendargyaru gif's head. */
  .dz-booking, .dz-blog{
    align-self: stretch;
  }
  .dz-course{
    align-self: start;
    margin-top: -68px;        /* pull course UP to sit right under ash.
                                 Row 2 starts at y=574 because offerings
                                 (424) is taller than ash (356) and drags
                                 the row down, leaving an 84px teal band
                                 under ash. This closes it - course sits
                                 ~16px below ash like a normal stack. */
  }
  .dz-course .course-card{
    min-height: 355px;        /* course now starts high (under ash) and
                                 grows DOWN to fill the column, stopping
                                 ~30px short of the gyaru head below. was
                                 313; the extra height eats the gap the
                                 pull-up opened. */
    margin-bottom: 30px;      /* the clearance gap under the box */
    display: flex;
    flex-direction: column;
    box-sizing: border-box;
  }
  .dz-booking .booking-window,
  .dz-booking .booking-col,
  .dz-booking > *{
    height: 100%;             /* booking fills up toward offerings */
  }
  .dz-blog .post-card{
    height: 100%;             /* blog fills up toward booking */
    min-height: 260px;        /* real presence without cascading the counter
                                 row so tall it re-opens a huge gap under the
                                 course box */
    display: flex;
    flex-direction: column;
  }
  /* THE FLAIR BUTTON ROW — full width, bottom of the canvas.

     These six buttons used to live INSIDE the counter box, which made it
     tall and bottom-heavy and unbalanced the whole canvas (Ashley's call,
     and she was right — it was the single heaviest object in the left
     column). Out here they read as what 88x31 buttons have always been:
     a band of badges across the foot of the page. */
  .dz-flair{
    grid-area: flair;
    width: 100%;
    box-sizing: border-box;
  }
}

/* ---- 11.2 The card itself ----
   Chrome copied EXACTLY from .booking-window / .booking-titlebar so it
   reads as the same operating system, not a different app:
     - same #ece9d8 window body
     - same 2px #5A3268 border + 3px offset shadow
     - same pastel gradient titlebar with #5A3268 text
   Only the width differs (this one spans the canvas). */
.post-card{
  width: 100%;
  box-sizing: border-box;
  background: #ece9d8;
  border: 2px solid #5A3268;
  box-shadow: 3px 3px 0 rgba(90,50,104,.45);
  font-family: "MS Sans Serif", Tahoma, Verdana, Arial, sans-serif;
  overflow: hidden;
}

.post-titlebar{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  background: linear-gradient(90deg, #e8c0c8, #C7C4D9, #f6ebbe, #f2c0bf);
  color: #5A3268;
  padding: 4px 6px;
  font-size: 13px;
  font-weight: bold;
  border-bottom: 1px solid #5A3268;
}
.post-titlebar-name{
  letter-spacing: .3px;
}
.post-ctls{ display: flex; gap: 3px; }
.post-ctl{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px; height: 14px;
  background: #ece9d8;
  border: 1px solid #5A3268;
  color: #5A3268;
  font-size: 10px;
  line-height: 1;
}

/* body */
.post-body{
  padding: 16px 18px;
  flex: 1 1 auto;           /* fill the card when it stretches to match the
                              counter's height (align-self: stretch) */
  display: flex;
  flex-direction: column;
}
.post-meta{ margin-top: auto; }   /* pin the tag/date row to the card foot */
.post-link{
  text-decoration: none;
}
.post-title{
  margin: 0 0 8px 0;
  font-size: 1.35em;
  line-height: 1.25;
  color: #5A3268;
}
.post-link:hover .post-title{
  color: var(--pink);
  text-decoration: underline;
}
.post-excerpt{
  margin: 0 0 12px 0;
  font-family: basiic, monospace;
  font-size: 13px;              /* above the 12.5px basiic floor */
  line-height: 1.65;
  color: #2e2838;

  /* clamp to 3 lines so a long post can't blow the card's height out */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* meta row: tag chip + date */
.post-meta{
  display: flex;
  align-items: center;
  gap: 10px;
  padding-top: 10px;
  border-top: 1px dashed #a99fc0;
  font-family: basiic, monospace;
  font-size: 12.5px;
}
.post-tag{
  display: inline-block;
  padding: 3px 9px;
  background: var(--yello);
  border: 1px solid #5A3268;
  box-shadow: 2px 2px 0 rgba(90,50,104,.35);
  color: #5A3268;
  text-decoration: none;
  /* NO font-weight. This row is set in basiic, and basiic has no bold
     cut — the browser SYNTHESISES bold by smearing the glyph sideways,
     which on a bitmap face is not weight, it is blur. It was worst on
     the digits. The chip already carries its emphasis with a yellow
     fill, a border and a drop shadow; it does not need weight too. */
  white-space: nowrap;
}
.post-tag:hover{
  background: var(--pink);
  color: #FFFFFB;
}
.post-date{
  color: #7a7091;
}
/* .post-more sits INSIDE .post-meta, so margin-left:auto pushes it to
   the right of the row. No negative-margin hacks. */
.post-more{
  margin-left: auto;
  color: var(--teal);
  text-decoration: none;
  /* NO font-weight — same synthetic-bold blur as .post-tag above.
     The teal already sets it apart from the grey date beside it. */
  white-space: nowrap;
}
.post-more:hover{
  color: var(--pink);
  text-decoration: underline;
}

/* ---- 11.3 Mobile: after booking, before the fluff ---- */
@media (max-width: 1249px){
  .dz-blog{
    order: 4;                  /* ash(1) offerings(2) booking(3) BLOG(4) */
    width: 100% !important;
    margin: 0 !important;
  }
  .dz-counter{ order: 5; }     /* gyaru/counter sinks below the post card */

  .post-body{ padding: 14px; }
  .post-title{ font-size: 1.2em; }
  .post-excerpt{ font-size: 13px; }

  /* on a narrow screen let the meta row wrap rather than squeeze */
  .post-meta{
    flex-wrap: wrap;
    gap: 8px;
  }
  .post-more{
    margin-left: 0;
    width: 100%;
  }
}




/* ================================================================
   12. COUNTER BOX + FLAIR  —  buttons live INSIDE the counter box
   ----------------------------------------------------------------
   How this box actually works (don't break it again):

     .visitor-row    the computer gif + the calendargyaru gif
     .visitor-left2  the beige counter box, 280px wide, with
                     `margin-top: -20px` — which pulls the box UP
                     underneath the sprites so the GYARU APPEARS TO BE
                     SITTING ON IT LIKE A TABLE. That negative margin
                     IS the joke. It must survive.

   So: do NOT wrap this in a new window. Instead let the SAME beige box
   extend downward, and put the flair buttons inside it, under the
   counter. The girl keeps sitting on the top edge; the box just gets
   taller beneath her.
   ================================================================ */

/* THE TABLE ILLUSION, properly layered.
   The box has z-index:1 and comes LATER in the DOM, so by default it
   paints OVER the gyaru's legs — she'd be buried in the table instead
   of sitting on it. Lift the sprite row above the box so her body is
   drawn on top of the surface, and only the box's top edge passes
   behind her. */
.visitor-row{
  position: relative;
  z-index: 2;              /* above .visitor-left2's z-index: 1 */
}

/* NOTE ON SPECIFICITY: `.desktop-screen .visitor-left2` (further up this
   file) hard-locks the box with max-width:280px / min-width:280px. That
   was sized for the OLD box, before the flair buttons moved inside it.
   Matching that selector's specificity here so the new 304px wins. */
.desktop-screen .visitor-left2,
.visitor-left2{
  /* WIDTH: the flair buttons are 88x31 native and must NEVER be scaled.
     3 across = (88*3) + (2 gaps * 6px) = 276px of content.
     + 12px padding each side + 2px borders = 302px.
     So the box is 304px, which fits 3-across cleanly with a little air. */
  width: 324px !important;
  min-width: 324px !important;
  max-width: 324px !important;

  /* was a hard height:70px — it now grows to fit the buttons */
  height: auto !important;
  min-height: 70px;

  box-sizing: border-box;
  /* TOP SHELF.
     The sprites have PERSPECTIVE BASES — they taper toward the bottom
     because you're seeing the near edge of the surface they rest on.
     (Measured: the computer's footprint is at sprite y~78 of 95; the
     gyaru's seat is at y~103 of 119.) So their true "resting line" is
     ABOVE the bottom of the image, and they must overlap the box by
     that much to look planted rather than hovering.
     A deep top padding gives them an empty shelf to land in, so they
     never fight the "Wow! You are visitor number:" text — which used
     to be pressed right up against the top border. */
  padding: 48px 12px 12px 12px;
  margin-top: 0;                  /* the ROW's margin does the seating */
  margin-bottom: 10px;
  background-color: #F1EBE5;
  border: 1px solid #000;

  display: flex;
  flex-direction: column;
  align-items: flex-start;        /* do NOT stretch children (see below) */
  gap: 6px;
}

/* THE COUNTER CHIP: it's `display:inline-block` so it shrink-wraps to
   its digits — but a flex column defaults to `align-items: stretch`,
   which would blow it out to the full width of the box. `flex-start`
   above lets it stay only as wide as the number it contains.
   (In a sandbox the third-party counter script is blocked, so it looks
   empty/stretched there — on the live site it wraps the digits.) */
.desktop-screen .visitor-left2 #visitorCounter{
  align-self: flex-start;
  width: auto;
  max-width: 100%;
}

/* the typewriter line should span the box, though */
.desktop-screen .visitor-left2 #typewriter,
.visitor-left2 > div:first-child{
  align-self: stretch;
}

/* the flair buttons: their own little framed shelf inside the counter box.
   3 across at native 88x31 — the box was widened to fit them unscaled.

   THE SQUISH (fixed): this used to be a bare `border-top` with no side or
   bottom padding, so the buttons sat 1px from the left edge, 1px from the
   right and 0px from the bottom — jammed against a lone dashed line rather
   than framed by it. Now it's a real dashed BOX with even padding all round,
   so the frame actually wraps them. */
.visitor-left2 .splashbuttons{
  align-self: stretch;            /* span the box, despite flex-start above */
  display: flex !important;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 6px;

  width: 100% !important;
  margin: 6px 0 0 0 !important;
  padding: 10px !important;       /* even breathing room on ALL four sides */
  box-sizing: border-box;

  border: 1px dashed #b9b0a8 !important;   /* a frame, not a lone line */
  background: rgba(255,255,255,.35) !important;
}

/* pixel art: 88x31 native, wrapped not scaled. Never set a width here. */
.visitor-left2 .splashbuttons img{
  image-rendering: pixelated;
  display: block;
  flex: 0 0 auto;                 /* never let flex squash them */
}

/* the wrapper must not clip the box now that it's taller */
.visitor-wrap,
.visitor-main{
  height: auto;
}

/* ---- mobile ---- */
@media (max-width: 1249px){
  /* SITTING ON THE TABLE (mobile). Same as desktop: their perspective
     bases overlap into the box's empty top shelf. */
  .visitor-row{
    position: relative;
    z-index: 2;                    /* draws over the box's top border */
    margin: 0 0 -20px 0 !important;
  }

  .desktop-screen .visitor-left2,
  .visitor-left2{
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin-top: 0 !important;
    padding-top: 48px !important;  /* the same top shelf for the sprites */
  }
  .visitor-left2 .splashbuttons{
    justify-content: center;
    gap: 6px;
  }
}

/* the grid's left column was 280px, sized for the OLD counter box.
   The box is now 304px (to fit 3 native-size buttons across), so the
   column must grow with it or the box will overflow its cell. */
@media (min-width: 1250px){
  .desktop-screen{
    /* 324px. DO NOT NARROW THIS.

       I tried 302 to give the offerings column more room, and it worked for
       the offerings — but .visitor-left2 in this column is 324px wide and
       it immediately OVERFLOWED ITS CELL. The counter box (and the gif
       offsets inside it, which took many attempts to get right) is not to
       be disturbed. Whatever the offerings need, they do not get it from
       here.

       If the offerings need more width, take it from the GAP or from
       .readingsbox's own padding — not from this column. */

    /* WHY minmax(0, 1fr) AND NOT PLAIN 1fr:
       A plain `1fr` track has an automatic MINIMUM of min-content. It will
       refuse to shrink below the widest thing inside it, and when its
       content is wider than the free space, the track GROWS PAST THE CELL
       and hangs over the canvas edge. That is exactly what happened: the
       right column rendered 369.7px inside a 352px slot and pushed the
       offerings / booking / latest_post boxes 18px (one padding unit) out
       past the screen's inner right edge.
       minmax(0, 1fr) sets that floor to 0, so the track is free to be
       whatever the free space actually is, and the content wraps instead
       of overflowing. Do not put it back to plain 1fr. */
    grid-template-columns: 324px minmax(0, 1fr) !important;
  }
}

/* RAISE THE WHOLE COUNTER UNIT.
   The sprites overhang UPWARD out of the counter cell, so they were
   floating in a 42px hole between ash.exe and the cell. Pulling the
   CELL up lifts the sprites AND the box together, keeping the elbow
   line intact — it moves the whole assembly, not just one piece.

   RETIRED. This -50px was written for an older grid where the counter
   cell sat lower and the sprites had a hole to climb out of. In the
   current grid the counter sits directly beneath ash.exe in the left
   column, so a -50px pull no longer closes a gap — it drags the counter
   UP INTO ash.exe and makes ash.exe read as if it is overhanging the
   counter box. That collision was the bug.

   The sprites still overhang upward on their own (they are supposed to,
   that is the elbow), and .dz-ash carries z-index: 5, so ash.exe
   correctly paints above them without any negative margin at all.

   NOW ALSO CLOSING THE COLUMN IMBALANCE. The right column (offerings 424px
   + booking 225px) is TALLER than the left (ash 356px + counter 146px), so
   booking hung 89px below the counter and the canvas looked lopsided.

   Note the cause: it is a HEIGHT difference, not a position one. Lowering
   the left column (Ashley's first instinct) would just move the gap rather
   than close it — the left would still run out of content first. What
   actually closes it is giving the left column's own boxes the slack, so
   the two columns end together.

   The counter cell stretches to fill the row it is in, and the visitor box
   inside it centres in that space. Nothing moves, nothing narrows: the
   gyaru still sits on the box's top edge, the computer still sits beside
   her, and the 324px floor is untouched. */
@media (min-width: 1250px){
  .dz-counter{
    /* was margin-top: 0 !important. The gyaru gif is pulled ~14px UP out
       of this cell by the counter's own padding-top:48px / margin-bottom
       :-20px offsets (hard-constrained, must not touch), so it rides right
       up against the course box above and reads as touching it. A real top
       margin HERE - this is the rule that actually wins, an earlier
       .dz-counter margin was being overridden by this !important - drops
       the whole counter, gyaru included, clear of the course box. */
    /* Ashley flagged the gyaru's HEAD touching course.exe. The gif rides
       ~73px up out of this cell (48px padding overhang + the sprite sitting
       on the box's top edge), so the margin-top has to cover that plus a
       little air. At 22px her head still overlapped course by ~35px; 62px
       drops her head to ~4px clear of course's bottom. justify flex-start
       keeps the counter hugging the top of the cell. */
    margin-top: 62px !important;
    align-self: start;               /* hug content at the top of the row */
    display: flex;
    flex-direction: column;
    justify-content: flex-start;     /* sit the counter at the TOP of its cell,
                                        pulled up snug under course.exe */
  }
}


/* ================================================================
   13. COUNTER TEXT + CHIP  —  bigger, centered on the shelf
   ----------------------------------------------------------------
   The "Wow! You are visitor number:" line was 13.5px and hugging the
   left edge, with the digit chip tucked under it also hard-left. In a
   324px box that reads as an afterthought.
   Now: bigger text, and the text + chip centered as a pair so they sit
   as a proper little unit under the gifs.
   (The GIFS ARE NOT TOUCHED — this is all below them.)
   ================================================================ */

/* the box centers its content now (was flex-start, which left-jammed
   everything). The flair frame still stretches: see below. */
.desktop-screen .visitor-left2,
.visitor-left2{
  align-items: center !important;
}

/* "Wow! You are visitor number:" — bigger and centered */
.desktop-screen .visitor-left2 #typewriter,
.visitor-left2 #typewriter{
  font-family: basiic, monospace;
  font-size: 17px !important;      /* was 13.5px */
  line-height: 1.3;
  letter-spacing: .3px;
  color: #5A3268;
  text-align: center;
  display: block;
  width: 100%;
}
/* its wrapper div must span the box so the centering has something to
   center within */
.visitor-left2 > div:first-child{
  align-self: stretch !important;
  width: 100%;
  text-align: center;
}

/* THE DIGIT CHIP: centered under the text, and it shrink-wraps to the
   number (it's inline-block). In the sandbox the counter script is
   blocked so it looks like an empty sliver — live, it wraps the digits. */
.desktop-screen .visitor-left2 #visitorCounter{
  align-self: center !important;
  display: inline-block;
  width: auto;
  margin: 8px auto 2px auto;
  padding: 4px 10px;               /* a little more room around the digits */
  background: #FFFFFB;
  border: 1px solid #5A3268;
  box-shadow: 2px 2px 0 rgba(90,50,104,.30);
  line-height: 1;
}
.desktop-screen .visitor-left2 #visitorCounter img{
  display: inline-block;           /* digits sit in a row, not stacked */
  vertical-align: middle;
  image-rendering: pixelated;
}

/* the flair frame must still span the full width despite the centering */
.visitor-left2 .splashbuttons{
  align-self: stretch !important;
}

@media (max-width: 1249px){
  .visitor-left2 #typewriter{
    font-size: 16px !important;
  }
}

/* =====================================================================
   WEBMASTER BOX — AUTHORITATIVE BLOCK
   (Legacy .webmasterframe rules still exist earlier in this file. This
   block hangs off the #wmbox ID so it outranks all of them. Edit HERE.)

   Layout: two boxes side by side in a 732px frame, square corners,
   NO frame background (bg87 retired from here, saved for reuse).

     [ CLOCK BOX 388px: clock+earth / friends / todos / button+code ]
     [ CHAT BOX  328px: cbox 300x380 ]

   Crystal sticker floats off the LOWER-LEFT of the CLOCK BOX. The
   button + code box are pushed RIGHT so the sticker never covers them.
   ===================================================================== */

#wmbox{
  /* kill the legacy 3-area grid */
  display: flex !important;
  flex-direction: row !important;
  grid-template-areas: none !important;
  grid-template-columns: none !important;
  grid-template-rows: none !important;
  align-items: stretch !important;
  gap: 14px !important;           /* space BETWEEN the two boxes */

  width: 732px !important;
  height: auto !important;
  /* breathing room: margin pushes the whole box away from its neighbours
     above/below; the 14px gap between the two boxes is the flex `gap`. */
  margin: 22px 0 26px 0 !important;
  /* inner breathing room so the boxes aren't jammed against the frame
     edge. 732 - 28 padding - 14 gap = 690 usable => 376 + 314. */
  padding: 0 14px !important;   /* 732 - 28 - 14 gap = 690 => 360 + 330 */
  box-sizing: border-box !important;

  position: relative;
  overflow: visible !important;   /* sticker must overhang freely */

  background: none !important;    /* bg87 removed. square, clean, quiet. */
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  color: var(--purple);
}
/* retire the old panning layers entirely */
#wmbox::before,
#wmbox::after{ content: none !important; display: none !important; }

#wmbox > *{
  position: relative;
  z-index: 1;
  grid-area: auto !important;
  margin: 0 !important;
  float: none !important;
}

/* ================= CLOCK BOX (left, 388px) ================= */
#wmbox .wm-socials{
  flex: 0 0 360px;
  width: 360px !important;
  height: auto !important;
  box-sizing: border-box;
  padding: 14px !important;
  display: flex !important;
  flex-direction: column;
  align-items: center;
  gap: 10px;

  /* the chrome she likes. rounded corners back on. */
  border: 4px double var(--teal);
  border-radius: 10px;
  outline: 1px solid #fff;
  outline-offset: -3px;
  background: url("images/backgrounds/bg58.png") !important;

  position: relative;             /* sticker anchors to THIS box */
  overflow: visible !important;
  justify-self: auto !important;
  align-self: auto !important;
}

/* clock + earth on one row */
#wmbox .wm-flair-row{
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 100%;
}
#wmbox .time{
  float: none !important;
  margin: 0 !important;
  width: 170px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  border: 1px dotted #999;
  border-radius: 10px;
}
/* THE CORNER STARS.

   They are two background images (top-left + bottom-right) and they were
   painted on .box-inner-2 — the INNER wrapper around the clock iframe.
   That box is only 131x46, while the white rounded box you actually SEE
   (.time) is 170x60, so the inner box floats 20px in from the left and 7px
   down from the top.

   The stars were anchored to their box's corners perfectly. It was just the
   wrong box: they were landing in the middle of the visible one, which is
   why they read as "floating inside" rather than pinning the corners.

   So the backgrounds move OUT to .time — the box with the visible border —
   and .box-inner-2 goes transparent. Same two images, same corners, now on
   the frame you can actually see. */
#wmbox .time{
  position: relative;
  background-color: #fff;
  background-image:
    url(https://foollovers.com/mat/t-frame/frame09-c/back01.png),
    url(https://foollovers.com/mat/t-frame/frame09-c/back01.png);
  background-repeat: no-repeat, no-repeat;
  background-position: left top, right bottom;
}
#wmbox .time .box-inner-2{
  margin-top: 0 !important;
  padding: 4px !important;
  background-image: none !important;   /* the stars live on .time now */
}

/* native sizes. never scale a pixel gif. */
#wmbox .coloredearthsticker{
  width: 70px !important; height: 70px !important;
  max-width: none !important; max-height: none !important;
  float: none !important; margin: 0 !important;
}
#wmbox .friendssticker{
  width: 198px !important; height: 111px !important;
  max-width: none !important; max-height: none !important;
  float: none !important; margin: 0 !important;
}

/* ---- TODOS: panel inside the clock box, scrolls internally ---- */
#wmbox .todos{
  width: 100% !important;
  height: 150px !important;
  margin: 0 !important;
  padding: 10px 12px !important;
  box-sizing: border-box;
  overflow-y: auto;
  overflow-x: hidden;
  background: url("images/backgrounds/bg24.gif");
  border-top: 3.5px double #000;
  border-left: 3.5px double #000;
  border-right: 3.5px double var(--pink);
  border-bottom: 3.5px double var(--pink);
  line-height: 1.35;
  text-align: left;
  font-size: 13px;                /* basiic floor is 12.5px */
}
#wmbox .todos h3.stroke{ margin: 0 0 6px 0; }
#wmbox .todos ul{ margin: 0; padding-left: 1.1em; }
#wmbox .todos li{ margin-bottom: 3px; }
#wmbox .todos li img{ vertical-align: middle; max-height: none !important; }

/* ---- BUTTON + CODE BOX: pushed RIGHT, clear of the sticker ----
   Sticker is 130px wide and hangs off the lower-left of this box, so
   this row keeps a 96px left gutter and hugs the right edge. */
#wmbox .wm-sitebutton{
  width: 100%;
  box-sizing: border-box;
  padding-left: 96px;             /* the sticker's lane. do not shrink. */
  display: flex;
  flex-direction: column;
  align-items: flex-end;          /* push right */
  gap: 5px;
}
#wmbox .wm-sitebutton > img{
  width: 88px !important; height: 31px !important;   /* native */
  max-width: none !important; max-height: none !important;
  image-rendering: pixelated;
  align-self: flex-end;
}
/* the copy-paste code box, in P&P colors instead of black/green */
#wmbox .siteButton{
  width: 100%;
  height: 46px;
  box-sizing: border-box;
  resize: none;
  background: var(--yello);       /* cream */
  color: var(--purple);
  border: 2px solid var(--teal);
  outline: 1px solid var(--pink);
  outline-offset: -3px;
  border-radius: 0;
  padding: 5px 7px;
  font-family: "basiic", monospace;
  font-size: 12.5px;              /* floor. never below. */
  line-height: 1.3;
}
#wmbox .siteButton:focus{
  outline-color: var(--purple);
  background: #fff;
}

/* ---- CRYSTAL STICKER: floats off the lower-left of the CLOCK BOX ----
   Static png, no animation, so there is nothing to shake. 500x500
   native, scaled uniformly to 130. */
/* 500x500 native. Was 130px and pinned to the LEFT edge of the clock
   box — which is also the far-left edge of the whole webmaster corner,
   so it was a tiny thing dangling off the outside of the page. Now 200px
   and hung on the RIGHT edge, where it tucks into the gutter between the
   clock column and the chat box instead of falling off the world.
   Smooth png, not pixel art, so a uniform downscale is safe. */
/* THE CRYSTAL on the clock box.

   IT KEPT COVERING THE TODO LIST. At 120px anchored to the box's inner
   bottom-left corner it sat straight on top of the list — measured: it
   covered 5 of the 7 todos, 70px deep into each. There is no free corner
   inside this box; the todo list occupies the whole lower area. Shrinking
   it further would only have made a smaller crystal sit on fewer words.

   So it hangs OUTSIDE the box entirely — the same trick as the mail slot
   and the chao on the chat box. There is ~268px of empty cabinet to the
   left of this column, so it has somewhere to go. Content is now
   physically unobstructable: the sticker is not over the box, it is
   beside it.

   pointer-events:none so it can never eat a click either way. */
/* A float that PRESERVES THE ROTATION.

   The clock crystal is rotated -8deg. `float-crystal` animates `transform`
   outright, so applying it here would have overwritten the rotate and the
   crystal would snap upright the moment the animation started. A keyframe
   that animates `transform` always wins over a static `transform` on the
   same element — they are the same property.

   So this keyframe carries the rotation THROUGH every step. Same 7px
   drift, same 4s, same feel as the footer crystal — it just keeps its
   tilt. */
@keyframes float-crystal-tilted{
  0%, 100% { transform: rotate(-8deg) translateY(0); }
  50%      { transform: rotate(-8deg) translateY(-7px); }
}

#wmbox .wm-socials .crystalstickerleft{
  position: absolute !important;
  width: 100px !important;
  height: 100px !important;
  max-width: none !important;
  max-height: none !important;
  min-width: 0 !important;
  min-height: 0 !important;
  object-fit: contain;
  top: auto !important;
  right: auto !important;
  /* Pulled back in and shrunk: at 130px/-104px it hung too far out into
     the empty cabinet and read as detached from the box it belongs to.
     100px/-70px keeps it clear of the todo list (the reason it moved out
     in the first place) while still reading as attached. */
  left: -70px !important;
  bottom: -10px !important;
  /* the keyframe carries the rotate, so no static transform here — a
     static one would just be overwritten by the animation anyway */
  z-index: 5;
  pointer-events: none;
  animation: float-crystal-tilted 4s ease-in-out infinite;
  filter: drop-shadow(2px 3px 3px rgba(90,50,104,.35));
}
@media (prefers-reduced-motion: reduce){
  #wmbox .wm-socials .crystalstickerleft{
    animation: none !important;
    transform: rotate(-8deg);      /* keep the tilt, lose the drift */
  }
}
#wmbox .crystalsticker-containerleft{ display: none !important; }

/* THE GUESTBOOK FUNNEL — a link under the chat box, not a second chat box.
   The chat is the small ask; this is the real one. Sits directly on the
   chat box's own background, no card, because it is an exit not a widget.
   Banner is 212x141 NATIVE. Never scaled. */
#wmbox .wm-chatbox .guestbook-funnel{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 100%;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 2px dotted rgba(90, 50, 104, .35);
  text-decoration: none;
}
#wmbox .wm-chatbox .guestbook-funnel img{
  display: block;
  width: 212px;
  height: 141px;
  max-width: none !important;
  max-height: none !important;
  image-rendering: pixelated;
  border: 2px outset #7a6ba8;
}
#wmbox .wm-chatbox .guestbook-funnel-cta{
  font-family: rainyhearts, monospace;
  font-size: 16px;
  color: var(--purple);
  letter-spacing: .3px;
}
#wmbox .wm-chatbox .guestbook-funnel:hover img{
  border-style: inset;
}
#wmbox .wm-chatbox .guestbook-funnel:hover .guestbook-funnel-cta{
  color: var(--pink);
  text-decoration: underline;
}

/* ================= CHAT BOX (right, 328px) ================= */
/* cbox iframe target: 300 x 380 */
#wmbox .wm-chatbox{
  /* 330 = 300px cbox iframe + 12px padding x2 + 3px border x2.
     Do NOT shrink below this or the iframe overflows its own box. */
  flex: 0 0 330px;
  width: 330px !important;
  height: auto !important;
  box-sizing: border-box;
  padding: 12px !important;
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
  overflow: visible;

  background: url("images/backgrounds/bg88.png");   /* unchanged */
  border: 3px solid #FFE4DB;
  border-radius: 10px;                               /* rounded again */
  box-shadow: 0 0 10px pink, 0 0 14px pink;
  color: #000;
}
#wmbox .wm-chatbox iframe{
  display: block;
  width: 300px !important;
  height: 380px !important;
  border: 0;
  flex: 0 0 auto;
}
#wmbox .wm-chatbox br{ display: none; }
#wmbox .wm-chat-flair{
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 10px;
}
/* legacy .wm-chatbox img forced max-height:34px and squashed these.
   Release the clamp, hold NATIVE size. kittyread 75x57, comment 127x20. */
#wmbox .wm-chat-flair img{
  max-height: none !important;
  max-width: none !important;
  margin-top: 0 !important;
  image-rendering: pixelated;
}

/* THE MAIL SLOT — email.gif, NATIVE 75x125, never scaled.
   Absolutely positioned off the chatbox's right edge so it adds no
   height to the column. pointer-events:none so it can never eat a
   click meant for the cbox iframe. */
#wmbox .wm-chatbox .chat-mailslot{
  position: absolute;
  right: -46px;
  bottom: 10px;
  width: 75px;
  height: 125px;
  max-width: none !important;
  max-height: none !important;
  margin: 0 !important;
  image-rendering: pixelated;
  pointer-events: none;
  z-index: 3;
}

/* THE CHAO — 45x45 NATIVE, never scaled. TOP-RIGHT corner of the chat
   box, perched on the frame. The paper airplane sits at the bottom of
   the same edge, so the two winged things bracket the box top-to-bottom:
   "message me!" read twice. He bobs, because he is flying. */
#wmbox .wm-chatbox .chat-chao{
  position: absolute;
  right: -22px;
  top: -20px;
  width: 45px;
  height: 45px;
  max-width: none !important;
  max-height: none !important;
  margin: 0 !important;
  image-rendering: pixelated;
  pointer-events: none;
  z-index: 3;
  animation: chao-bob 3.4s ease-in-out infinite;
}

/* ================= MOBILE ================= */
@media (max-width: 1249px){
  /* the mail slot hangs OUTSIDE the box — on a narrow viewport that is
     a horizontal-scroll bug waiting to happen. Cut it. */
  #wmbox .wm-chatbox .chat-mailslot,
  #wmbox .wm-chatbox .chat-chao{ display: none; }
  #wmbox{
    flex-direction: column !important;
    align-items: center !important;
    width: 100% !important;
    max-width: 100%;
    padding: 0 !important;
    gap: 16px !important;
  }
  #wmbox .wm-socials,
  #wmbox .wm-chatbox{
    flex: 0 0 auto;
    width: 100% !important;
    max-width: 340px;
  }
  #wmbox .wm-chatbox iframe{ width: 100% !important; }
  /* sticker tucks in tighter so it cannot cause horizontal scroll */
  #wmbox .wm-socials .crystalstickerleft{
    width: 104px !important; height: 104px !important;
    left: -18px !important;
    bottom: -22px !important;
  }
  #wmbox .wm-sitebutton{ padding-left: 74px; }
}


/* =====================================================================
   MOBILE = THE BUSINESS CARD
   =====================================================================
   Philosophy (Ashley's call, July 2026): the desktop arcade cabinet is
   the site. Mobile is not a punishment and not a port. It is a clean
   vertical zine that gets a phone user booked, then invites them to
   come see the real thing on a computer.

   ORDER ON MOBILE:
     1. welcome banner
     2. main menu
     3. about box (the fluff blurb, NOT ash.exe)
     4. booking
     5. offerings
     6. how to get your chart
     7. "best viewed on desktop" callout
     8. footer

   EVERYTHING ELSE IS CUT. Not hidden-but-loaded-and-scrolling: gone
   from layout. The gyaru, counter, flair buttons, chat, update panel,
   marquees, webring, junk-dump, stamps, astro iframe, gameboy —
   all desktop rewards.

   HOW IT WORKS: flexbox `order` does not care where an element sits in
   the HTML. So nothing was moved in index.html to achieve this. If you
   add a new block, it will land in source order unless you give it an
   `order` here.

   DESKTOP IS UNTOUCHED. Every rule below lives inside the media query.
   ===================================================================== */

@media (max-width: 1249px){

  /* ---- the page becomes one honest column ---- */
  .cabinet{
    display: flex !important;
    flex-direction: column !important;
    align-items: center !important;
    width: 100% !important;
    max-width: 100%;
    gap: 0 !important;
    padding: 0 10px 24px 10px !important;
    box-sizing: border-box;
  }
  .cabinet > *{ width: 100%; max-width: 460px; }

  /* FLATTEN. `order` only sorts SIBLINGS, and these blocks live in
     different parents (.content--home vs .rightsidebox--home). So the
     wrappers are dissolved with display:contents, which removes the box
     but keeps the children — hoisting them all up to be siblings of
     .cabinet. Now one `order` scale sorts the whole page. */
  .content--home,
  .desktop-screen,
  .rightsidebox--home,
  .sidebar--home,
  main{
    display: contents !important;
  }

  /* =========================================================
     THE CUT LIST. display:none, so it costs zero layout.
     To bring one back on mobile, delete its line here.
     ========================================================= */
  .leftsidebox--home,          /* whole left column: moon, stamps, astro */
  .transits,
  .stamps-wrap,
  .planets,
  .gameboy-float,
  .dz-ash,                     /* ash.exe — fluff, per Ashley */
  .dz-counter,                 /* gyaru + counter + flair buttons */
  .visitor-wrap,
  .splashbuttons,
  .dz-blog,
  .wm-corner,                  /* legacy name, kept in case of stragglers */
  .webmaster-corner,           /* update panel, marquee, webmaster, chat */
  .zodiacblog,                 /* legacy */
  .site-news,                  /* changelog: desktop reward */
  .update-panel,
  #wmbox,
  .webmasterframe,
  .lilspacebox,                /* junk dump / konpeibii */

  /* THE GRADUATED BOXES. These three used to live INSIDE .lilspacebox and
     were cut for free by the line above. When they graduated out into
     their own top-level boxes they escaped this cut list — and because
     they had no `order` value they defaulted to order:0 and sorted to the
     very TOP of mobile, shoving the welcome, menu, offerings and booking
     down the page.

     Mobile is the business card. All three are junk-dump flair: the
     Sailor Moon thesis, the linkback/stamps table, and the trophy case.
     They are the desktop reward. Cut.

     NOTE FOR FUTURE: anything else that graduates out of the junk dump
     must be added HERE at the same time, or it will do this again. */
  .ten-box,
  .doorways-box,
  .awards-box,

  #musicplayer,                /* CD player — desktop reward */
  .clouds,
  .crystalsticker-containerleft,
  .footer-crystal,
  .cabinet marquee,
  #random-button-marquee-wrapper{
    display: none !important;
  }

  /* =========================================================
     THE KEEP LIST, in order.
     ========================================================= */

  /* 1. WELCOME */
  .introgradbox{
    order: 1;
    width: 100% !important;
    margin: 12px 0 4px 0 !important;
    padding: 14px 10px !important;
    box-sizing: border-box;
    background: none !important;
    border: 0 !important;
    min-height: 0 !important;
    height: auto !important;
  }
  .introgradbox > div{ margin-left: 0 !important; text-align: center; }
  .introgradbox h1{
    font-size: 1.5em !important;   /* 2.6em overflows a phone */
    line-height: 1.25;
    margin: 0;
    word-break: break-word;
  }

  /* 2. MENU */
  .sidelinks{
    order: 2;
    width: 100% !important;
    margin: 0 0 16px 0 !important;
    padding: 12px !important;
    box-sizing: border-box;
    text-align: center;
  }
  .sidelinks ul{
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px 14px;
    padding: 0;
    margin: 0;
    list-style: none;
  }
  .sidelinks li{ margin: 0; }
  .sidelinks br{ display: none; }
  .sidelinks a{
    display: inline-flex;
    align-items: center;
    gap: 5px;
    min-height: 44px;              /* thumb-sized tap target */
    padding: 0 6px;
    font-size: 14px;
  }
  .sidelinks .sidecat{ display: block; margin-bottom: 8px; }
  #bouncy-title{ display: none !important; }

  /* 3. ABOUT (the fluff blurb) */
  .aboutbox{
    order: 3;
    width: 100% !important;
    margin: 0 0 18px 0 !important;
    box-sizing: border-box;
    height: auto !important;
    max-height: none !important;
    overflow: visible !important;
  }

  /* 5. BOOKING — comes AFTER offerings now. Sequence is
        what you get -> what it costs -> how to start.
        Warm leads skip straight here via the menu anchor. */
  .dz-booking{
    order: 5;
    width: 100% !important;
    position: static !important;
    inset: auto !important;
    transform: none !important;
    margin: 0 0 20px 0 !important;
  }
  .booking-split,
  .booking-col,
  .booking-left{
    display: block !important;
    width: 100% !important;
    float: none !important;
  }
  .booking-window{ width: 100% !important; box-sizing: border-box; }
  .booking-btn{
    display: block;
    width: 100%;
    box-sizing: border-box;
    min-height: 52px;              /* big, obvious, thumb-first */
    line-height: 52px;
    font-size: 16px;
    text-align: center;
  }

  /* 4. OFFERINGS — before booking. Answer the question before
        asking for the sale. */
  .dz-offerings{
    order: 4;
    width: 100% !important;
    position: static !important;
    inset: auto !important;
    transform: none !important;
    margin: 0 0 20px 0 !important;
  }
  .readingsbox{ width: 100% !important; box-sizing: border-box; }
  .package{ width: 100% !important; box-sizing: border-box; }
  .package summary{ min-height: 44px; display: flex; align-items: center; }
  .package-body{ padding: 0.85rem !important; }

  /* 7. HOW TO GET YOUR CHART — the funnel back into booking */
  .howto-chart{
    order: 7;
    display: block !important;
    width: 100% !important;
    margin: 0 0 20px 0 !important;
    box-sizing: border-box;
  }

  /* 8. DESKTOP CALLOUT (element is in the HTML, hidden on desktop) */
  .desktop-invite{
    order: 8;
    display: block !important;
    width: 100% !important;
    margin: 4px 0 20px 0 !important;
  }

  /* 8. FOOTER */
  .cabinet footer,
  footer{ order: 9; width: 100%; }

  /* ---- the .dz windows were absolutely positioned on the canvas ---- */
  .dz{
    position: static !important;
    inset: auto !important;
    left: auto !important; top: auto !important;
    right: auto !important; bottom: auto !important;
    transform: none !important;
    width: 100% !important;
    height: auto !important;
    margin: 0 0 18px 0 !important;
    box-sizing: border-box;
  }

  /* ---- global mobile hygiene ---- */
  .cabinet img{ max-width: 100%; height: auto; }
  .cabinet iframe{ max-width: 100%; }
}

/* =====================================================================
   DESKTOP INVITE CARD — only ever seen on a phone.
   Hidden by default so it never shows up on the real site.
   ===================================================================== */
.desktop-invite{ display: none; }

.desktop-invite .di-inner{
  box-sizing: border-box;
  padding: 16px 14px;
  text-align: center;
  color: var(--purple);
  background: var(--yello);
  border: 4px double var(--teal);
  outline: 1px solid #fff;
  outline-offset: -3px;
}
.desktop-invite .di-title{
  font-family: "nes", "Press Start 2P", monospace;
  font-size: 12px;
  line-height: 1.6;
  color: #ff51b7;
  text-shadow: 2px 0 var(--purple), 0 2px var(--purple), 2px 2px var(--purple),
               -1px -1px var(--purple), -1px 0 var(--purple), 0 -1px var(--purple);
  margin: 0 0 10px 0;
}
.desktop-invite p{
  font-family: "basiic", monospace;
  font-size: 13px;               /* floor is 12.5px */
  line-height: 1.55;
  margin: 0 0 8px 0;
}
.desktop-invite .di-sprites{
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}
.desktop-invite .di-sprites img{
  image-rendering: pixelated;     /* never soften a pixel gif */
  max-height: none !important;
}


/* =====================================================================
   MOBILE POLISH — about float, readable prose, slimmer boxes
   MOBILE ONLY. Everything below is inside the breakpoint. Desktop is
   not touched by any rule in this block.
   ===================================================================== */

@media (max-width: 1249px){

  /* ---------- ABOUT BOX ----------
     Photo floats LEFT, prose wraps around it.
     NOTE: the about text is a rainbow gradient built from one <span>
     per character with inline color styles. Do NOT restyle those spans
     or you kill the gradient. font-family/font-size INHERIT, so we set
     them on the container and the colors survive untouched. */
  .aboutbox{
    padding: 14px 16px !important;
    overflow: hidden;               /* contains the float */
    line-height: 1.6;
  }
  .aboutbox h3.stroke{
    margin: 0 0 10px 0;
    /* pixel font stays on the header. brand lives here. */
  }
  .aboutbox br{ display: none; }    /* the <br> stack fought the float */

  .aboutbox .aboutimage{
    float: left;
    width: 110px;
    margin: 2px 14px 8px 0;
    shape-outside: margin-box;
  }
  .aboutbox .aboutimage img{
    display: block;
    width: 110px !important;
    height: auto !important;
    max-width: none !important;
    margin-top: 0 !important;
  }

  /* the prose: readable face + size. Colors are inline, so they win. */
  .aboutbox > div{
    margin-top: 0 !important;
    font-family: "Atkinson Hyperlegible", sans-serif;
    font-size: 15px;
    line-height: 1.62;
    letter-spacing: 0;
    text-align: left;
  }
  .aboutbox > div span{
    font-family: inherit;
    font-size: inherit;
    letter-spacing: 0;
  }

  /* ---------- BOOKING: slimmer chrome, same punch ---------- */
  .booking-window{
    padding: 0 !important;
    box-shadow: none;
  }
  .booking-titlebar{
    padding: 5px 8px !important;
    font-size: 12.5px;              /* floor */
    min-height: 0;
  }
  .booking-window-body{
    padding: 12px 12px 14px 12px !important;
  }
  .booking-blurb{
    margin: 0 0 10px 0 !important;
    font-size: 14px;
    line-height: 1.4;
  }
  .booking-btn{
    min-height: 46px !important;    /* was 52. still thumb-sized. */
    height: auto !important;
    line-height: 1.35 !important;
    font-size: 14px !important;
    padding: 10px 8px !important;
    margin: 0 0 10px 0 !important;
    display: flex !important;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  .booking-prep{
    margin: 0 0 4px 0 !important;
    font-size: 12.5px;
    line-height: 1.45;
  }
  .booking-note{
    margin: 0 !important;
    font-size: 12.5px;
    opacity: .85;
  }

  /* ---------- OFFERINGS: three tidy tappable bars ---------- */
  .readingsbox{
    padding: 12px !important;
  }
  .readingsbox > h2,
  .readingsbox > h3{
    margin: 0 0 10px 0 !important;
    font-size: 15px;
  }
  .package{
    margin: 0 0 8px 0 !important;
    box-sizing: border-box;
  }
  .package summary{
    min-height: 44px;               /* tap target */
    padding: 8px 10px !important;
    font-size: 13.5px;
    line-height: 1.35;
    cursor: pointer;
  }
  .package-body{
    padding: 10px 12px !important;
    font-size: 13.5px;              /* was 13 */
    line-height: 1.6;
  }
  .package-body p{ margin: 0 0 8px 0; }
  .package-body ul{ margin: 0 0 8px 0; padding-left: 1.15em; }
  .package-body li{ margin-bottom: 4px; }

  /* ---------- HOW TO CHART ---------- */
  .howto-chart{
    padding: 14px 16px !important;
  }
  .howto-chart h3{ margin: 0 0 8px 0; }
  .howto-intro{ font-size: 13.5px; line-height: 1.55; margin: 0 0 10px 0; }
  .howto-steps{ margin: 0; padding-left: 1.25em; }
  .howto-steps li{
    font-size: 13.5px;
    line-height: 1.6;
    margin-bottom: 6px;
  }
}


/* =====================================================================
   MOBILE QUICK-BOOK BAR — hidden on desktop, always.
   Lets a warm lead jump to booking without scrolling the packages,
   while the natural read order stays offerings -> booking.
   ===================================================================== */
.mobile-book-bar{ display: none; }

@media (max-width: 1249px){
  .mobile-book-bar{
    display: block !important;
    order: 3;                       /* menu(2) -> THIS -> about(3.5) */
    width: 100% !important;
    max-width: 460px;
    margin: 0 0 14px 0 !important;
    box-sizing: border-box;
  }
  .mobile-book-bar .mbb-link{
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 48px;               /* thumb-sized */
    box-sizing: border-box;
    padding: 10px 12px;
    text-decoration: none;

    font-family: "basiic", monospace;
    font-size: 14px;
    letter-spacing: .5px;
    color: var(--purple);
    background: var(--yello);
    border: 4px double var(--teal);
    outline: 1px solid #fff;
    outline-offset: -3px;
  }
  .mobile-book-bar .mbb-link:active{
    background: #fff;
    outline-color: var(--pink);
  }
  .mobile-book-bar .mbb-arrow{ opacity: .7; }

  /* about box now sits after the quick-book bar */
  .aboutbox{ order: 3.5; }

  /* the anchor target needs breathing room under the sticky-free header */
  #book{ scroll-margin-top: 12px; }
}


/* =====================================================================
   GAMEBOY SCREEN — divination mirror placeholder
   The gameboy.png has a transparent hole where the screen is. The gif
   sits BEHIND the png and shows through it. Measured from the asset:
   hole is x39..310, y111..350 of 350x398 => 11.14% / 27.89%,
   77.71% wide, 60.30% tall. Percentages keep it registered at any size.

   Rhapsodomancy will replace .gb-screen later. The registration math
   below stays valid whatever goes in that slot.
   ===================================================================== */
.gb-shell{
  position: relative;
  display: block;
  width: 100%;
  line-height: 0;          /* kill inline-image descender gap */
}
.gb-shell .gb-body{
  position: relative;
  z-index: 2;
  display: block;
  width: 100%;
  height: auto;
  pointer-events: none;
}
.gb-shell .gb-screen{
  position: absolute;
  z-index: 1;              /* BEHIND the body, shows through the hole */
  left: 11.14%;
  top: 27.89%;
  width: 77.71%;
  height: 60.30%;
  object-fit: cover;       /* gif is pre-cropped to the screen aspect */
  display: block;
  max-width: none !important;
  max-height: none !important;

  /* the mirror: a soft sparkle glow bleeding out of the screen */
  filter: saturate(1.15) brightness(1.06);
}

/* the glow spilling past the bezel, so it reads as a lit mirror
   rather than a sticker sitting behind a hole */
.gb-shell::after{
  content: "";
  position: absolute;
  z-index: 3;
  left: 11.14%;
  top: 27.89%;
  width: 77.71%;
  height: 60.30%;
  pointer-events: none;
  box-shadow:
    0 0 12px 2px rgba(160, 220, 255, .55),
    0 0 26px 6px rgba(120, 190, 255, .30),
    inset 0 0 18px 3px rgba(200, 240, 255, .35);
  animation: gb-shimmer 3.2s ease-in-out infinite;
}
@keyframes gb-shimmer{
  0%, 100% { opacity: .75; }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce){
  .gb-shell::after{ animation: none; }
}

/* the old rule targeted `.gameboy-float img` directly and would now hit
   BOTH images. Scope it to the body only. */
.leftsidebox--home .gameboy-float .gb-screen{
  width: 77.71% !important;
  height: 60.30% !important;
}


/* =====================================================================
   WEBMASTER CORNER — 3 boxes
   Left column: clock box (top) + site news (below).
   Right column: chat, full height.

   Site news moved here FROM the prime slot under the desktop canvas.
   Rationale: a changelog is webmaster furniture, same cultural object as
   the counter and the guestbook. It belongs with its people, not in the
   best remaining real estate. That slot is now free for the monthly
   forecast wheel.

   Old class names retired: .zodiacblog -> .site-news,
   .updateblog -> .news-log, .wm-corner -> .webmaster-corner.
   ===================================================================== */

#wmbox{
  flex-wrap: wrap !important;
  align-items: flex-start !important;
}

/* left column wrapper: the clock box and site news stack */
#wmbox .wm-socials{
  flex: 0 0 360px !important;
}

#wmbox .site-news{
  flex: 0 0 360px;
  width: 360px !important;
  box-sizing: border-box;
  order: 3;                    /* below the clock box in the left column */
  margin-top: 14px !important;
  padding: 12px !important;
  position: relative;
  overflow: visible;

  /* matches the clock box chrome so the left column reads as one column */
  border: 4px double var(--teal);
  border-radius: 10px;
  outline: 1px solid #fff;
  outline-offset: -3px;
  background: url("images/backgrounds/bg2.gif") center/cover no-repeat;
  color: var(--purple);
}

/* ---- THE HEAD ----
   It used to be two bare gifs and NO WORDS, which is why it read as
   unfinished: a box with a flashing UPDATE banner tells you something
   CHANGED but never tells you what the box IS.

   So it gets a title. The box is a webmaster's changelog, so it is
   called what it is: SITE LOG. The clipboard icon says the same thing
   again in one glyph (a log is a thing you write on), which is the same
   logic as the icons on the offerings menu: semantic, not decorative.

   update.gif is DEMOTED, not deleted. It is no longer the headline; it
   is a status light sitting at the end of the row, and it earns its keep
   by being the only thing in the head that MOVES. Flashing "UPDATE" next
   to the words SITE LOG reads as "recently updated" — which is true. */
#wmbox .site-news .news-head{
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  padding-bottom: 6px;
  border-bottom: 2px dotted rgba(90, 50, 104, .35);
}
#wmbox .site-news .news-head-title{
  flex: 1 1 auto;               /* takes the slack, so the flash is pushed
                                   to the far right and the clipboard stays
                                   glued to the words */
  font-family: rainyhearts, monospace;
  font-size: 20px;
  letter-spacing: 2px;
  color: var(--purple);
  text-shadow: 1px 1px 0 rgba(255,255,255,.7);
}
/* clipboard 20x20 NATIVE, update 64x20 NATIVE. Never scaled — at this
   size a resize would destroy them outright. content-box because the
   global border-box rule inflates `width:auto` on images. */
#wmbox .site-news .news-head-icon,
#wmbox .site-news .news-head-flash{
  flex: 0 0 auto;
  box-sizing: content-box;
  width: auto;
  height: auto;
  max-width: none !important;
  max-height: none !important;
  image-rendering: pixelated;
}

/* THE LOG: fixed height, scrolls internally. Grows forever, box doesn't. */
#wmbox .news-log{
  height: 190px;
  overflow-y: auto;
  overflow-x: hidden;
  box-sizing: border-box;
  padding: 10px 12px;
  background: rgba(255, 255, 238, .90);   /* cream, so text stays legible
                                             over the zodiac wheel */
  border-top: 3.5px double #000;
  border-left: 3.5px double #000;
  border-right: 3.5px double var(--pink);
  border-bottom: 3.5px double var(--pink);
  text-align: left;
}

#wmbox .news-entry{
  margin: 0 0 12px 0;
  padding: 0 0 12px 0;
  border-bottom: 1px dashed rgba(90, 50, 104, .35);
}
#wmbox .news-entry:last-child{
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: 0;
}
/* lunap.gif is the SAVE icon from Sailor Moon: Another Story. It is
   14x21, which is almost exactly a pilcrow's proportions, so it works
   as a paragraph marker — but the reason it belongs HERE specifically
   is that a changelog entry IS a save point. Each dated entry is a
   moment the site got written to disk.
   Done in CSS on ::before so every future entry inherits it for free.
   NATIVE 14x21. Never scaled. */
/* rainyhearts, NOT basiic. basiic has a 12.5px floor precisely because its
   lowercase i and l are indistinguishable — it is a DISPLAY face being
   asked to do body work, and a changelog is body work. rainyhearts is a
   true pixel font whose I / l / 1 are actually distinct, so it can carry
   running text. Pixel fonts want whole pixel sizes: 16px, never 15.5. */
#wmbox .news-date{
  margin: 0 0 4px 0;
  display: flex;
  align-items: center;
  gap: 7px;
  font-family: rainyhearts, monospace;
  font-size: 17px;
  color: var(--pink);
  letter-spacing: .3px;
}
#wmbox .news-date::before{
  content: "";
  flex: 0 0 auto;
  width: 14px;
  height: 21px;
  background: url("images/junk/lunap.gif") no-repeat center / 14px 21px;
  image-rendering: pixelated;
}
/* The body was inheriting Atkinson at 12.5px — i.e. it was not a pixel
   font at all, it was just SMALL. 12.5px is the basiic floor, and
   applying a pixel-font floor to a non-pixel font gets you the worst of
   both: no pixel character, and hard to read. rainyhearts at 16px is a
   real pixel face at a real reading size. */
#wmbox .news-body{
  font-family: rainyhearts, monospace;
  font-size: 16px;
  line-height: 1.55;
  color: var(--purple);
}
#wmbox .news-body img{
  max-height: none !important;
  vertical-align: middle;
  image-rendering: pixelated;
}

/* Sailor Saturn keeps perching on the corner.
   24x42 NATIVE. Never scale her — she is a pixel gif and any fractional
   upscale makes her shimmer. Small and cute is the point. */
#wmbox .site-news .saturn-flair{
  position: absolute;
  right: -8px;
  bottom: -12px;
  width: 24px !important;
  height: 42px !important;
  max-width: none !important;
  max-height: none !important;
  z-index: 4;
  pointer-events: none;
  image-rendering: pixelated;
}

/* chat spans the full height of the left column (clock + news) */
#wmbox .wm-chatbox{
  order: 2;
  align-self: stretch !important;
}


/* --- webmaster corner: explicit 2-column so chat can span full height --- */
#wmbox{
  flex-wrap: nowrap !important;
  align-items: stretch !important;
}
#wmbox .wm-leftcol{
  flex: 0 0 360px;
  width: 360px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}
#wmbox .wm-leftcol .wm-socials,
#wmbox .wm-leftcol .site-news{
  flex: 0 0 auto;
  width: 100% !important;
  order: 0;
  margin: 0 !important;
}
#wmbox .wm-chatbox{
  order: 0;
  align-self: stretch !important;
  height: auto !important;
}
/* chat content sticks to the top; the box itself stretches */
#wmbox .wm-chatbox iframe{ flex: 0 0 auto; }


/* =====================================================================
   MONTHLY FORECAST PANEL  ·  side by side
   Mirrors the printed handout: same wheel, same dignity key, same fonts.

   The handout stacks wheel-over-key because paper is tall. A screen is
   wide. Stacking them here made the panel 1470px — bigger than the whole
   desktop canvas (921px). So: wheel LEFT, key dates RIGHT, dignity key
   as a full-width band underneath. build_forecast.py emits the wheel as
   a 648x648 square and the key as a 676x134 band precisely for this.

   Both are SVG. They scale cleanly and can never shimmer.
   ===================================================================== */

.forecast-panel{
  width: 732px;
  box-sizing: border-box;
  margin: 0 0 22px 0;
  padding: 16px 18px 18px 18px;

  background: #FFFFFB;                 /* the handout's paper */
  border: 4px double var(--teal);
  border-radius: 10px;
  outline: 1px solid #fff;
  outline-offset: -3px;
  color: var(--purple);
}

.forecast-panel .fc-head{
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 12px;
  margin-bottom: 12px;
}
.forecast-panel .fc-title{
  margin: 0;
  font-family: "nes", "Press Start 2P", monospace;
  font-size: 12px;
  line-height: 1.6;
  color: #ff51b7;
  text-shadow: 2px 0 var(--purple), 0 2px var(--purple), 2px 2px var(--purple),
               -1px -1px var(--purple), -1px 0 var(--purple), 0 -1px var(--purple);
}
.forecast-panel .fc-month{
  margin: 0;
  font-family: "basiic", monospace;
  font-size: 13px;
  letter-spacing: 1px;
  color: #6e6278;
}

/* ---- the two columns ---- */
.forecast-panel .fc-main{
  display: flex;
  align-items: flex-start;
  gap: 16px;
}

/* WHEEL: left. 648x648 square, so 360px wide = 360px tall. */
.forecast-panel .fc-wheel-wrap{
  flex: 0 0 360px;
  width: 360px;
  min-width: 0;
}
.forecast-panel .fc-wheel-img{
  display: block;
  width: 360px;
  height: 360px;                       /* 1:1, matches the viewBox */
  max-width: none !important;
  max-height: none !important;
}
.forecast-panel .fc-guide{
  margin: 6px 0 0 0;
  font-family: "Raleway", sans-serif;
  font-size: 12.5px;                   /* floor */
  line-height: 1.4;
  text-align: center;
  color: #6e6278;
}

/* KEY DATES: right column */
.forecast-panel .fc-dates-wrap{
  flex: 1 1 auto;
  min-width: 0;
}
.forecast-panel .fc-dates-title{
  margin: 0 0 8px 0;
  font-family: "basiic", monospace;
  font-size: 13px;
  color: var(--pink);
  letter-spacing: .5px;
}
.forecast-panel .fc-dates{
  list-style: none;
  margin: 0 0 10px 0;
  padding: 8px 10px;
  box-sizing: border-box;
  background: #f8f6fb;
  border: 1.5px solid #C7C4D9;
  border-radius: 8px;
}
.forecast-panel .fc-row{
  display: grid;
  grid-template-columns: 14px 50px 1fr 46px;
  align-items: center;
  gap: 6px;
  padding: 4px 2px;
  font-family: "Raleway", sans-serif;
  font-size: 12.5px;
  line-height: 1.35;
  border-bottom: 1px dashed rgba(90, 50, 104, .18);
}
.forecast-panel .fc-row:last-child{ border-bottom: 0; }
.forecast-panel .fc-icon{ text-align: center; font-size: 11px; }
.forecast-panel .fc-date{
  font-family: "basiic", monospace;
  font-size: 12.5px;
  color: var(--pink);
}
.forecast-panel .fc-event{ color: #2e2838; }
.forecast-panel .fc-sign{ text-align: right; color: #6e6278; }
.forecast-panel .fc-time{ display: none; }   /* the wheel is the star; time is noise here */

.forecast-panel .lunation-new  .fc-event,
.forecast-panel .lunation-full .fc-event{ font-weight: 600; color: var(--purple); }
.forecast-panel .planet-station .fc-event{ color: #c9604a; }
.forecast-panel .ingress-row    .fc-event{ color: #1d7a6a; }

.forecast-panel .fc-note{
  margin: 0;
  font-family: "Raleway", sans-serif;
  font-size: 12.5px;
  line-height: 1.5;
  color: #6e6278;
}

/* DIGNITY KEY: full-width band under both columns. 676x134 => 5.04:1 */
.forecast-panel .fc-key-wrap{
  width: 100%;
  margin: 14px 0 0 0;
}
.forecast-panel .fc-key-img{
  display: block;
  width: 100%;
  height: auto;
  max-width: none !important;
  max-height: none !important;
}

/* CTA */
.forecast-panel .fc-cta{
  text-align: center;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 2px dashed rgba(15, 163, 163, .4);
}
.forecast-panel .fc-cta-line{
  margin: 0 0 10px 0;
  font-family: "basiic", monospace;
  font-size: 13px;
  color: var(--purple);
}
.forecast-panel .fc-cta-btn{
  display: inline-block;
  min-width: 220px;
  padding: 11px 22px;
  box-sizing: border-box;
  font-family: "basiic", monospace;
  font-size: 14px;
  letter-spacing: .5px;
  text-decoration: none;
  color: var(--purple);
  background: var(--yello);
  border: 4px double var(--teal);
  outline: 1px solid #fff;
  outline-offset: -3px;
}
.forecast-panel .fc-cta-btn:hover,
.forecast-panel .fc-cta-btn:active{
  background: #fff;
  outline-color: var(--pink);
  color: var(--pink);
}

/* ---------- MOBILE: the forecast earns its place ----------
   Everything else webcore was cut, but this is astrology and it is the
   reason to come back next month. Columns stack; wheel stays square. */
@media (max-width: 1249px){
  .forecast-panel{
    display: block !important;
    order: 6;
    width: 100% !important;
    max-width: 460px;
    padding: 14px !important;
    margin: 0 0 20px 0 !important;
  }
  .forecast-panel .fc-head{ flex-direction: column; gap: 6px; }
  .forecast-panel .fc-title{ font-size: 11px; }
  .forecast-panel .fc-main{ flex-direction: column; gap: 14px; }
  .forecast-panel .fc-wheel-wrap{
    flex: 0 0 auto;
    width: 100%;
  }
  .forecast-panel .fc-wheel-img{
    width: 100%;
    height: auto;                      /* square, so height follows width */
    aspect-ratio: 1 / 1;
  }
  .forecast-panel .fc-cta-btn{
    display: block;
    width: 100%;
    min-height: 48px;
  }
}


/* =====================================================================
   FORECAST WHEEL — HOVER TO ENLARGE
   The wheel sits at 360px so the panel stays shorter than the desktop
   canvas. That is small for reading glyphs, so hovering pops out a ~2x
   copy.

   WHY A SECOND COPY, not transform:scale() on the small one: scaling an
   SVG element scales its stroke widths and font sizes proportionally,
   which is fine, but the popup wants MORE room around the wheel and its
   own drop shadow. A second inlined <svg> at its own size is cleaner and
   the text renders at its true size instead of being blown up.

   Desktop only. Phones have no hover, and mobile already shows the wheel
   full-bleed, so there is nothing to enlarge.
   ===================================================================== */

.fc-wheel-zoom{
  position: relative;
  display: block;
  width: 360px;
  cursor: zoom-in;
  outline: none;
}

/* THE GAMEBOY WAS APPEARING OVER THE POPUP — and it was not a z-index
   NUMBER problem, it was a STACKING CONTEXT problem.

   The popup carries z-index:200, but that only competes with siblings
   INSIDE its own parent's stacking context. The gameboy lives in a
   different column entirely (.leftsidebox--home) whose context sits above
   the forecast panel's — so no matter how high the popup's number goes, it
   loses. Raising 200 to 9999 would have changed nothing.

   The fix is to lift the popup's ANCESTOR: give the panel a stacking
   context of its own that outranks the left column, and only while the
   wheel is actually hovered (so it does not permanently sit on top of
   everything else for no reason). */
.forecast-panel:has(.fc-wheel-zoom:hover),
.forecast-panel:has(.fc-wheel-zoom:focus-visible){
  position: relative;
  z-index: 300;
}
/* the gameboy's column stays where it is; it just no longer outranks a
   popup that is deliberately trying to cover it */
.leftsidebox--home{
  position: relative;
  z-index: 1;
}

/* the small wheel in the panel */
.fc-wheel-zoom > svg.fc-wheel{
  display: block;
  width: 360px;
  height: 360px;
  transition: filter .18s ease;

  /* WHITE PLATE. The zoomed pop-out sits on its own white card, so the wheel
     is crisp there — but the small preview was drawing straight onto the
     panel's TAN ground, and thin black chart lines on tan have far less
     contrast than on white. At 360px (vs the pop-out's 632px) those lines
     are already at their thinnest, so the preview needs MORE contrast than
     the zoom, not less. It was getting less.

     A white plate under the small wheel fixes it, and it also makes the two
     views consistent: the wheel always sits on white, whatever size it is. */
  background: #FFFFFB;
  border-radius: 4px;
  padding: 6px;
  box-sizing: content-box;
}
.fc-wheel-zoom:hover > svg.fc-wheel,
.fc-wheel-zoom:focus-visible > svg.fc-wheel{
  filter: brightness(.97) saturate(1.05);
}

/* the affordance. Without it, nobody discovers the hover. */
.fc-wheel-zoom .fc-zoom-hint{
  position: absolute;
  right: 6px;
  bottom: 6px;
  z-index: 2;
  padding: 3px 7px;
  font-family: "basiic", monospace;
  font-size: 12.5px;                  /* floor */
  letter-spacing: .3px;
  color: var(--purple);
  background: rgba(255, 255, 238, .92);
  border: 2px solid var(--teal);
  pointer-events: none;
  transition: opacity .18s ease;
}
.fc-wheel-zoom:hover .fc-zoom-hint,
.fc-wheel-zoom:focus-visible .fc-zoom-hint{ opacity: 0; }

/* THE POPUP. Anchored to the wheel, escapes the panel (no clipping
   ancestors: verified). Not display:none -> it fades, and pointer-events
   stay off so it can never steal the hover from its own trigger. */
.fc-wheel-zoom .fc-zoom{
  position: absolute;
  z-index: 200;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(.96);
  transform-origin: center center;

  /* SIZED TO ITS CONTENT, not guessed.
     This was `width: 660px`, which after 14px padding x2 and a 4px double
     border x2 leaves only 624px of content box — but the wheel SVG inside
     is 632px. It was 8px too narrow, so the wheel's outer sign band (the
     "pizza crust") got clipped at the cardinal points, exactly where the
     wheel is widest.
     Let the box size itself to the wheel instead of hardcoding a number
     that has to be re-derived every time the padding changes. */
  width: max-content;
  box-sizing: border-box;
  padding: 18px;

  opacity: 0;
  visibility: hidden;
  pointer-events: none;               /* never intercepts the mouse */

  background: #FFFFFB;
  border: 4px double var(--teal);
  border-radius: 10px;
  outline: 1px solid #fff;
  outline-offset: -3px;
  box-shadow: 0 10px 34px rgba(46, 40, 56, .38);

  transition: opacity .18s ease, transform .18s ease, visibility 0s linear .18s;
}
.fc-wheel-zoom:hover .fc-zoom,
.fc-wheel-zoom:focus-visible .fc-zoom{
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, -50%) scale(1);
  transition: opacity .18s ease, transform .18s ease, visibility 0s linear 0s;
}

.fc-wheel-zoom .fc-zoom svg.fc-wheel-big{
  display: block;
  width: 632px;
  height: 632px;                      /* 1:1, the viewBox is square */
  max-width: none !important;
  max-height: none !important;
}

@media (prefers-reduced-motion: reduce){
  .fc-wheel-zoom .fc-zoom{ transition: opacity .01s, visibility 0s; }
}

/* ---------- MOBILE: no hover, no popup ---------- */
@media (max-width: 1249px){
  .fc-wheel-zoom{
    width: 100%;
    cursor: default;
  }
  .fc-wheel-zoom > svg.fc-wheel{
    width: 100%;
    height: auto;
    aspect-ratio: 1 / 1;
  }
  .fc-wheel-zoom .fc-zoom,
  .fc-wheel-zoom .fc-zoom-hint{ display: none !important; }
}


/* =====================================================================
   FORECAST PANEL — "where to get your chart" band
   Replaces the dignity key that used to sit here. The key was a lookup
   table: unchanging month to month, and meaningless to anyone who does
   not already know what a dignity is. It answered a question nobody on
   the homepage was asking.

   This answers the question the wheel actually creates. The panel now
   reads as one funnel:
       here is the sky  ->  here is how to see YOUR sky  ->  book
   ===================================================================== */

.forecast-panel .fc-howto{
  width: 100%;
  box-sizing: border-box;
  margin: 14px 0 0 0;
  padding: 14px 16px;

  background: #f8f6fb;
  border: 1.5px solid #C7C4D9;
  border-radius: 8px;
}

/* the block was styled for the bottom of the page; re-tune it for here */
.forecast-panel .fc-howto .howto-chart{
  display: block !important;
  width: 100% !important;
  margin: 0 !important;
  padding: 0 !important;
  background: none !important;
  border: 0 !important;
  box-shadow: none !important;
}

.forecast-panel .fc-howto h3{
  margin: 0 0 6px 0;
  font-family: "basiic", monospace;
  font-size: 13px;
  color: var(--pink);
  letter-spacing: .5px;
}
.forecast-panel .fc-howto .howto-intro{
  margin: 0 0 10px 0;
  font-family: "Raleway", sans-serif;
  font-size: 12.5px;              /* floor */
  line-height: 1.5;
  color: #6e6278;
}

/* the six steps run as two columns so the band stays short and wide */
.forecast-panel .fc-howto .howto-steps{
  margin: 0 0 10px 0;
  padding: 0 0 0 1.25em;
  columns: 2;
  column-gap: 28px;
  font-family: "Raleway", sans-serif;
  font-size: 12.5px;
  line-height: 1.55;
  color: #2e2838;
}
.forecast-panel .fc-howto .howto-steps li{
  margin-bottom: 5px;
  break-inside: avoid;            /* never split a step across columns */
}
.forecast-panel .fc-howto .howto-steps b{ color: var(--purple); }
.forecast-panel .fc-howto .howto-steps a{ color: var(--teal); }

.forecast-panel .fc-howto .howto-note{
  margin: 0;
  font-family: "Raleway", sans-serif;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--purple);
}
.forecast-panel .fc-howto .howto-note img{
  vertical-align: middle;
  max-height: none !important;
  image-rendering: pixelated;
}

/* the old standalone block is gone from the bottom of the stack; make
   sure no orphan rule reserves space for it there */
.wm-corner > .howto-chart,
.webmaster-corner > .howto-chart{ display: none !important; }

/* ---------- MOBILE: single column, still in the funnel ---------- */
@media (max-width: 1249px){
  .forecast-panel .fc-howto{
    padding: 12px 14px;
    margin-top: 12px;
  }
  .forecast-panel .fc-howto .howto-steps{
    columns: 1;                   /* two columns is unreadable at 390px */
    padding-left: 1.2em;
  }
  /* the standalone howto no longer exists at the bottom; drop its order */
  .howto-chart{ order: 7; }
}


/* =====================================================================
   FIX: the todo list had collapsed to a 31px sliver.

   CAUSE: the clock box was pinned to a fixed height (360px) so the left
   column would visually match the chat box. But its children need ~400px
   (flair row 70 + friends 111 + todos 150 + button 82, plus padding and
   gaps). Flex resolved the overflow by shrinking whatever could shrink,
   and .todos had min-height:0, so it absorbed the entire deficit and went
   150px -> 31px. All 7 items were still in the DOM, just crushed.

   FIX: never pin a box to a height its content cannot fit in. The clock
   box now grows to its content, and .todos is explicitly not allowed to
   shrink. Content decides the height; the layout adapts to it.
   ===================================================================== */

#wmbox .wm-leftcol .wm-socials{
  height: auto !important;
  min-height: 0 !important;
  flex: 0 0 auto !important;
}

/* the todo box holds its 150px, full stop */
#wmbox .todos{
  flex: 0 0 150px !important;
  height: 150px !important;
  min-height: 150px !important;
  flex-shrink: 0 !important;
  overflow-y: auto;
}

/* nothing else in the clock box may collapse either */
#wmbox .wm-socials .wm-flair-row,
#wmbox .wm-socials .friendssticker,
#wmbox .wm-socials .wm-sitebutton{
  flex-shrink: 0 !important;
}


/* =====================================================================
   LINK BACK SHRINE — junk dump
   ---------------------------------------------------------------------
   This corner speaks a DIFFERENT dialect than the rest of the site.

   The rest of Pixel & Planets is win98: teal double borders, recessed
   panels, .exe windows. That is desktop-OS nostalgia.

   This is old-WEB nostalgia: a starfield tile, a centered table, content
   sitting directly on the stars, no card and no chrome. It is the
   grammar of a 2001 Sailor Moon fan page, and it is a <table> on purpose.
   Do not "fix" it into a card. The lack of chrome IS the style.
   ===================================================================== */

.linkback{
  display: block;
  margin: 0 auto;
  text-align: center;
}

.linkback-table{
  margin: 0 auto;                    /* centered, like every table on those pages */
  border: 0;
  border-collapse: collapse;
  background: url("images/backgrounds/bg6.gif") center/cover;  /* the starfield */
}

.linkback-cell{
  padding: 14px 20px;
  text-align: center;
  vertical-align: middle;
  /* a single hairline, the way those pages framed a table cell.
     NOT a double teal border. */
  border: 2px ridge #7fd4e8;
}

.linkback-head{
  margin: 0 0 8px 0;
  font-family: "basiic", monospace;
  font-size: 13px;
  letter-spacing: .5px;
  color: #ffffff;
  /* the hard drop-shadow every one of those pages had on white-on-space text */
  text-shadow: 1px 1px 0 #14304a, 2px 2px 0 rgba(0,0,0,.45);
}

.linkback-btn{
  display: block;
  margin: 0 auto 8px auto;
  width: 88px !important;
  height: 31px !important;           /* NATIVE. never scale a pixel gif. */
  max-width: none !important;
  max-height: none !important;
  image-rendering: pixelated;
  border: 1px solid rgba(255,255,255,.55);
}

.linkback-sub{
  margin: 0 0 8px 0;
  font-family: "basiic", monospace;
  font-size: 12.5px;                 /* floor */
  line-height: 1.45;
  color: #d8f2fb;
  text-shadow: 1px 1px 0 #14304a;
}

.linkback-code{
  display: block;
  width: 300px;
  max-width: 100%;
  height: 54px;
  margin: 0 auto;
  box-sizing: border-box;
  padding: 5px 7px;
  resize: none;

  /* the black-and-green terminal look belongs HERE, not in the clock box.
     This is the corner where a code box is supposed to look like a code box. */
  background: #0b1626;
  color: #79DC77;
  border: 2px inset #6fa8bf;
  border-radius: 0;
  font-family: "basiic", monospace;
  font-size: 12.5px;
  line-height: 1.35;
  cursor: pointer;
}
.linkback-code:focus{
  outline: 1px solid #ffffff;
  outline-offset: -1px;
}

.linkback-hint{
  margin: 6px 0 0 0;
  font-family: "basiic", monospace;
  font-size: 12.5px;
  color: #a9d8ea;
  text-shadow: 1px 1px 0 #14304a;
}

/* the junk dump has to grow now that it holds something real */
.lilspacebox{
  height: auto !important;
  min-height: 260px;
  flex-wrap: wrap;
  gap: 18px;
  align-items: center !important;
  justify-content: space-around !important;
}

/* ---------- MOBILE: the whole junk dump is cut, so nothing to do ---------- */


/* =====================================================================
   LINKBACK — "take my button"
   Moved out of the clock box. It was 82px of the overflow that crushed
   the todo list, and it never belonged there anyway: clock + friends +
   todos says "a person is online right now." A button someone copies to
   link back to you is a different gesture entirely. It belongs down here
   with the webring and the flair, where people hunting for linkbacks
   actually look.

   DIALECT NOTE — this is deliberate, do not "fix" it:
   The rest of the site speaks win98 (teal double borders, bevels, rounded
   dialog cards). This corner does NOT. It speaks late-90s fan-page: a
   starfield, a centered <table>, content sitting straight on the stars,
   ALL CAPS headers, a marquee-adjacent sensibility. It's a <table> on
   purpose. Two different nostalgias, both honest. Ashley's north star for
   this zone is the first Sailor Moon shrine she ever saw, and that page
   had no cards, no chrome, no border-radius. Just stars and a table.
   ===================================================================== */

/* =====================================================================
   THE DOORWAYS — stamps + linkback fused into ONE table.

   They were two separate boxes doing the same job ("take a thing / go
   see a thing"), so they are now two <td>s of one <table>. This is the
   structure principle applied literally: the 90s web was not freeform,
   it was TABLES, and the chaos belongs INSIDE the cells rather than
   between them.

   Deliberately NOT win98: starfield background, outset border,
   border-radius 0, content sitting directly ON the stars.
   ===================================================================== */
/* =====================================================================
   THE DOORWAYS — its own box, between THE TEN and the awards case.

   Stamps + linkback fused into ONE table. Two <td>s, because the 90s web
   was not freeform, it was TABLES — the chaos lives INSIDE the cells, not
   between them.

   It was `flex: 1 1 100%` as a .junk-item, which is meaningless outside
   the flex wall. Now a real centred block, matched to the ten-box and
   awards-box so the three read as one stack.

   Downstairs dialect: ridge border, border-radius 0. No win98 bevels.
   ===================================================================== */
.doorways-box{
  box-sizing: border-box;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto 18px auto;
  padding: 14px;
  background: rgba(5, 3, 15, .72);
  border: 3px ridge #6a5a98;
  border-radius: 0;               /* downstairs. never rounded. */
}

.doorways-box .doorway-table{
  width: 100%;
  border-collapse: collapse;
  /* the starfield. content sits ON it, not in a card on top of it. */
  background: url("images/backgrounds/bg7.png") repeat, #0b0620;
  border: 2px outset #7a6ba8;     /* an outset border, like it's 1999 */
}
/* THREE cells now, equal width, with real rules between them. A table with
   visible dividers is the entire point of using a table.
   (Was `width: 50%` when there were two — with three cells that would have
   overflowed to 150%.) */
.doorways-box .doorway-cell{
  width: 33.333%;
  padding: 14px 12px;
  text-align: center;
  vertical-align: top;
}
.doorways-box .doorway-cell + .doorway-cell{
  border-left: 2px groove #7a6ba8;
}

.doorways-box .doorway-head{
  margin: 0 0 10px 0;
  font-family: "basiic", monospace;
  font-size: 12.5px;              /* floor */
  letter-spacing: 1px;
  text-transform: uppercase;
  color: #ffe9a8;
  text-shadow: 1px 1px 0 #2a1050, 0 0 6px rgba(255, 220, 140, .5);
}

.doorways-box .linkback-btn{
  display: block;
  margin: 0 auto 10px auto;
  width: 88px !important;
  height: 31px !important;        /* NATIVE. never scale a pixel gif. */
  max-width: none !important;
  max-height: none !important;
  image-rendering: pixelated;
  border: 1px solid #b9a8e0;
}

.doorways-box .doorway-sub{
  margin: 0 0 10px 0;
  font-family: "basiic", monospace;
  font-size: 12.5px;
  line-height: 1.5;
  color: #cfc4f0;
  text-shadow: 1px 1px 0 #1a0a38;
}

.doorways-box .linkback-code{
  display: block;
  width: 100%;
  height: 58px;
  box-sizing: border-box;
  resize: none;
  padding: 6px 7px;

  /* the classic: pale text on near-black, monospace, inset border */
  background: #12082c;
  color: #9ff5c8;
  border: 2px inset #6a5a98;
  border-radius: 0;               /* NO rounded corners in this dialect */
  font-family: "basiic", monospace;
  font-size: 12.5px;
  line-height: 1.35;
  cursor: text;
}
.doorways-box .linkback-code:focus{
  outline: 1px dotted #ffe9a8;
  outline-offset: 2px;
}

.doorways-box .doorway-hint{
  margin: 6px 0 0 0;
  font-family: "basiic", monospace;
  font-size: 12.5px;
  color: #9a8fc9;
}

/* the starcorners bracket the whole zone; keep them clear of the table */
.lilspacebox .starcornerleft,
.lilspacebox .starcornerright{
  flex: 0 0 auto;
}


/* =====================================================================
   JUNK DUMP — star brackets back in the corners
   They were flex CHILDREN, so they sat in the content flow and bunched
   up on the left next to the linkback. They are decorative corner flair,
   not content: pin them to the corners, take them out of the flow.

   This is the flair zone. Ashley is going to drop more stuff in here.
   The container is a wrapping flex row with room to grow — new junk lands
   in .lilspacebox and finds its own place. The brackets stay put.
   ===================================================================== */

.lilspacebox{
  position: relative;
  /* padding keeps content clear of the pinned brackets */
  padding: 14px 118px !important;
}

.lilspacebox .starcornerleft,
.lilspacebox .starcornerright{
  position: absolute !important;
  z-index: 1;
  flex: none !important;
  width: 102px;
  height: 101px;                  /* native. never scale a pixel asset. */
  pointer-events: none;
}
.lilspacebox .starcornerleft{  left: 8px;  top: 8px; }
.lilspacebox .starcornerright{ right: 8px; top: 8px; }

.lilspacebox .starcornerleft img,
.lilspacebox .starcornerright img{
  display: block;
  width: 102px !important;
  height: 101px !important;
  max-width: none !important;
  max-height: none !important;
  image-rendering: pixelated;
}

/* the actual junk sits above the brackets and flows freely */
.lilspacebox > *:not(.starcornerleft):not(.starcornerright){
  position: relative;
  z-index: 2;
}

/* on a narrow desktop the brackets would crowd the content: shrink the
   gutters rather than the brackets. */
@media (max-width: 1320px){
  .lilspacebox{ padding: 14px 14px !important; }
  .lilspacebox .starcornerleft,
  .lilspacebox .starcornerright{ opacity: .55; }
}


/* =====================================================================
   THE WALL — junk dump proper
   1999 fan-shrine dialect. Under-curation is the aesthetic: things
   accumulate and nothing is removed. Do NOT tidy this into a widget
   shelf. Wrapping flex row, so new junk lands and finds its own place.

   No border-radius. No teal double borders. No win98 bevels. That
   language lives upstairs on the desktop canvas. Down here it is stars,
   tables, outset borders and ALL CAPS.
   ===================================================================== */

.lilspacebox .junk-wall{
  flex: 1 1 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: 16px;
  width: 100%;
  margin: 4px 0 0 0;
}

.lilspacebox .junk-item{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  max-width: 100%;
}

.lilspacebox .junk-item img{
  display: block;
  max-width: none !important;
  max-height: none !important;
  image-rendering: pixelated;      /* never soften a pixel asset */
}

/* THE AWARD. The best object in the batch. Give it the pride of place a
   1997 fan award deserves: a gold frame, like a certificate. */
.lilspacebox .junk-award img{
  width: 241px;                    /* NATIVE 241x232. never scale. */
  height: 232px;
  border: 3px ridge #e0c060;
  background: #000;
}
.lilspacebox .junk-cap{
  margin: 0;
  max-width: 241px;
  text-align: center;
  font-family: "basiic", monospace;
  font-size: 12.5px;               /* floor */
  line-height: 1.4;
  color: #cfc4f0;
  text-shadow: 1px 1px 0 #1a0a38;
}

/* the ASCII prayer. peak old-web sincerity. it is already black, so it
   melts into the starfield: give it a thin frame so it reads as an object */
.lilspacebox .junk-angel img{
  width: 342px;                    /* NATIVE 342x215 */
  height: 215px;
  border: 2px outset #7a6ba8;
}

/* blinkies stack, like a guestbook sidebar */
.lilspacebox .junk-blinkies{
  gap: 10px;
}
/* .blinkie used to hard-pin 298x85 — which was safecat's NATIVE size,
   written as if it were the class's size. The moment a second blinkie
   arrived (goodwithcomputer, 129x38) it got stretched 2.3x and
   shimmered. A blinkie is a SHAPE (wide + short), not a dimension.
   Hold every one at its own native size. */
.lilspacebox .junk-blinkies .blinkie{
  /* box-sizing MUST be content-box here. A global border-box rule makes
     `width:auto` on an <img> resolve to natural+border, which quietly
     upscales the sprite by 2px — a fractional resize, i.e. shimmer.
     The border must sit OUTSIDE the pixels, not eat into them. */
  box-sizing: content-box;
  width: auto;
  height: auto;
  max-width: none !important;
  max-height: none !important;
  image-rendering: pixelated;
  border: 1px solid #b9a8e0;
}
/* .junk-cats REMOVED — the cats moved into .ten-panel (see .ten-cats).
   They were sitting in the blinkie stack, which is a row of banners, and
   a cat sprite in a banner stack is a stray. */




/* =====================================================================
   STICKER FLAIR — the little guys
   Too small for the wall, so they perch on the junk dump instead. All at
   NATIVE size. A pixel gif scaled by a fraction shimmers; these never are.
   Absolutely positioned so they never push the layout around, and
   pointer-events:none so they never eat a click.
   ===================================================================== */
.lilspacebox .junk-sticker{
  position: absolute;
  z-index: 3;
  pointer-events: none;
  image-rendering: pixelated;
  max-width: none !important;
  max-height: none !important;
}

/* Luna, 14x21. Tiny. She perches on the top edge like a real cat. */
.lilspacebox .sticker-luna{
  width: 14px; height: 21px;
  left: 126px; top: -14px;
}
/* Chao, 45x45, floating. Give him a gentle bob: he is FLYING. */

@keyframes chao-bob{
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-7px); }
}
/* .sticker-orb (item29.gif) REMOVED — it moved into the VOID cabinet
   title. See .void-eclipse. Do not re-add it here; a black gif on the
   starfield had no readable edges. */

/* ---------- MOBILE: the junk dump is cut entirely, nothing to do ---------- */

/* the wall wants to be DENSE. 118px gutters for the star brackets were
   squeezing everything into a single column, which reads as a tidy list,
   not a shrine. Pull the gutters in and let items pair up. The brackets
   are pinned and pointer-events:none, so items may pass under them. */
.lilspacebox{
  padding: 96px 16px 20px 16px !important;
}
.lilspacebox .junk-wall{
  /* REQUIRED: konpeibii is absolutely positioned against this box.
     Without it he escapes to the nearest positioned ancestor. */
  position: relative;
  gap: 14px 18px;
  align-items: flex-end;          /* bottoms line up: shelf, not grid */
}


/* =====================================================================
   THE GLYPH TILE — the site's outer background
   This was the original scrolling background of Pixel & Planets and it
   is coming back. It earns the outermost layer for three reasons:
     1. it tiles PERFECTLY (measured: 0.0 edge diff both axes)
     2. it is ACTUAL ASTROLOGY — planet glyphs, not webcore texture
     3. it is pale, so it never fights the content sitting on it
   The source was 1250x500 but the pattern repeats every 250x249, so it
   is cropped to a single tile: 41KB -> 24KB and it now tiles infinitely.

   This is the honest fix for "the site reads more webcore than zodiac."
   The astrology is now on every screen, behind everything, for free.
   ===================================================================== */
/* body.--home carries the background (a class, so it beats bare `body`).
   The glyph tile is swapped into that existing rule below, and the `slide`
   keyframes are corrected to travel in whole multiples of the 250x249 tile
   so the loop cannot visibly jump. */

/* =====================================================================
   MORE WALL. Under-curation is the aesthetic. Keep adding.
   All images at NATIVE size: never fractionally scale a pixel asset.
   ===================================================================== */

/* the Sailor Team. a shrine needs its centerpiece. */
.lilspacebox .junk-senshi img{
  width: 500px;                    /* NATIVE 500x444 */
  height: 444px;
  border: 3px ridge #8fd68f;       /* matches the gif's own green frame */
}

/* the ten planetary glyphs, halftone. this one is LITERALLY the job. */
.lilspacebox .junk-sailorglyphs img{
  width: 275px;                    /* NATIVE 550x550, halved cleanly = no shimmer
                                      (a clean 2:1 integer ratio, not a fraction) */
  height: 275px;
  border: 2px outset #7a6ba8;
}

/* THE TWO SPACY OBJECTS START AT THE SAME HEIGHT.

   The wall is `align-items: flex-end` — every item is aligned to its
   BOTTOM. That is right for loose junk of mixed sizes, but wrong for
   these two: their captions are different lengths (caterpie's wraps to
   three lines, the orbit's to two), so their BOXES are different heights
   (316 vs 281) and bottom-aligning them pushed the orbit's image down 35px.
   The frames matched but the pictures did not start level.

   align-self: flex-start pins these two to the TOP of their row, so the
   two framed images begin at the same height and read as a pair. The
   captions can be whatever length they like underneath.
   Only these two — the rest of the wall keeps its bottom alignment. */
.lilspacebox .junk-orbit,
.lilspacebox .junk-caterpie{
  align-self: flex-start;
}

/* the solar system, orbiting. 6MB -> 192KB, same loop.

   MATCHED TO CATERPIE. The two spacy objects at the top of the wall should
   be the same size, but they are 230x231 and 240x240 natively and BOTH are
   pixel art — so neither can be rescaled to meet the other without
   shimmering.

   So the FRAMES match instead of the sprites: the box is padded out to
   caterpie's 240x240 and the gif sits centred inside at native size. Fix
   the box, never the sprite. Same principle as the menu icon slots. */
.lilspacebox .junk-orbit img{
  box-sizing: border-box;
  width: 240px;                    /* frame matches caterpie */
  height: 240px;
  padding: 5px 5px 4px 5px;        /* 240-230=10, 240-231=9 */
  object-fit: none;                /* do NOT stretch the gif to fill */
  border: 2px outset #7a6ba8;
  background: #000;
}

/* green wireframe globe. peak 1997. */



/* sparkles sticker, 60x60 native */
.lilspacebox .sticker-sparkle{
  width: 60px; height: 60px;
  right: 24px; bottom: 18px;
  animation: chao-bob 4.6s ease-in-out infinite;
}

/* THE GLYPH TILE takes over the site background.
   .--home is a CLASS on <body>, so it outranks a bare `body` selector.
   Layered OVER the old bg84 gradient wash so the pale glyphs still read. */
body.--home{
  /* NOTE: longhand, NOT the `background` shorthand. The shorthand resets
     background-position to its initial value, and with !important it wins
     against the animation's computed value on every frame -> the keyframes
     run and nothing ever moves. Set each longhand separately and leave
     background-position for the animation to own. */
  background-image:
    url("images/backgrounds/glyphtile.png"),
    linear-gradient(#cce4ff, rgba(250, 250, 250, 0)) !important;
  background-repeat: repeat, no-repeat !important;
  background-color: transparent !important;

  /* NOT `fixed`. background-attachment:fixed anchors the layer to the
     VIEWPORT, which pins background-position and silently kills the
     animation. `scroll` lets the position actually animate. */
  background-attachment: scroll, scroll !important;

  animation: glyphslide 200s linear infinite !important;
}

/* TILE MATH: the tile is 250x249. Travel MUST be a whole multiple of it
   in BOTH axes or the loop visibly jumps at the wrap.

   DIRECTION: up and left, weighted more LEFT than up — that is the drift
   it always had. The original `slide` went -564px/-450px, roughly a 5:4
   lean. Both negative = up-left. But 564 and 450 are not multiples of
   250/249, so that loop was quietly jumping.
   Here: -1250px = 5 tiles left, -996px = 4 tiles up. Same 5:4 lean,
   exact multiples, no jump. */
@keyframes glyphslide{
  0%   { background-position: 0 0, 0 0; }
  100% { background-position: -1250px -996px, 0 0; }
}
@media (prefers-reduced-motion: reduce){
  body.--home{ animation: none !important; }
}

/* MOBILE: no drift. A fixed, animating background on a phone is a
   scroll-jank and battery cost for something nobody is looking at.
   The tile stays; it just holds still. */
@media (max-width: 1249px){
  body.--home{
    animation: none !important;
    background-attachment: scroll, scroll !important;
  }
}


/* =====================================================================
   THE VOID CABINET — the black-background objects, framed together
   =====================================================================
   Four objects all sit on black: the ASCII prayer, the orbiting solar
   system, the wireframe globe, the halftone planet glyphs. Scattered on
   the starfield they read as NOISE, because they are the same value as
   the background and the eye cannot find their edges. Grouped into one
   dark panel they become a structural block, and the starfield goes back
   to being background instead of competing with them.

   STRUCTURE PRINCIPLE (applies to the whole junk dump):
   The 90s web was not freeform, it was TABLES. Hard-edged cells on a
   grid. The chaos lives INSIDE the cells, not between them. That is why
   kelprot reads as coherent despite being visually insane: every object
   has a BOX. So: pile in as much stupid stuff as you want, but give it a
   frame and align it. More boxes, not fewer things.
   ===================================================================== */

/* =====================================================================
   THE TEN — the wall's one load-bearing object.
   Replaces the old .void-cabinet, which grouped things for a rendering
   reason rather than a curatorial one. This panel makes a CLAIM, so it
   is the only thing on the wall that gets a frame + header + footer.
   ===================================================================== */
/* =====================================================================
   THE TEN — its own box, ABOVE the junk dump. Graduated out of the wall.

   Stays in the DOWNSTAIRS dialect on purpose: starfield, ridge border,
   ALL CAPS, border-radius 0. It is a fan-shrine argument, not a win98
   utility, so it does not get the teal bevels. Sitting where it does, it
   becomes the hinge where the page turns from upstairs to downstairs.

   It was `flex: 1 1 100%` when it was a .junk-item, which is meaningless
   outside the flex wall. Now it is a real centred block that matches the
   junk dump's width so the two read as a stack.
   ===================================================================== */
.ten-box{
  box-sizing: border-box;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto 18px auto;
  padding: 14px;
  background: #05030f;
  border: 3px ridge #6a5a98;
  border-radius: 0;               /* downstairs. never rounded. */
}

.ten-box .ten-frame{
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 10px;
  background: #000;
  border: 1px solid #33285c;   /* the cell IS the frame */
}

/* the glyphs. 550x550 native — halved to 275, a clean 2:1 integer. */
.ten-box .ten-glyphs{
  display: block;
  width: 275px;
  height: 275px;
  max-width: none !important;
  max-height: none !important;
  flex: 0 0 auto;
  border: 0;
}

/* THE TEAM — 500x444 native, HALVED to 250x222. A clean 2:1 integer
   ratio, which is the only legal way to shrink pixel art.
   It was 310x275 to force its height to match the glyphs exactly. That
   is a 0.62 FRACTIONAL downscale, which resamples between pixels and is
   precisely why it looked crushed. Ashley called it. Layout convenience
   is never a good enough reason to break the integer rule: fix the box,
   never the sprite. 250 vs the glyphs' 275 still reads as balanced. */
.ten-box .ten-team{
  display: block;
  width: 250px;
  height: 222px;
  max-width: none !important;
  max-height: none !important;
  flex: 0 0 auto;
  image-rendering: pixelated;
  border: 0;
}

.ten-box .ten-cap{
  max-width: 520px;
  margin: 12px auto 0 auto;
  text-align: center;
  font-family: "basiic", monospace;
  font-size: 12.5px;              /* floor */
  line-height: 1.6;
  color: #cfc4f0;
}
/* LUNA, ARTEMIS AND DIANA — inside the panel, sitting under the caption
   like a footer. NATIVE 100x100, never scaled. They are the exposition
   characters in the source material, so having them sit at the bottom of
   the argument explaining it is the correct joke. */
.ten-box .ten-cats{
  display: block;
  width: 100px;
  height: 100px;
  max-width: none !important;
  max-height: none !important;
  margin: 10px auto 0 auto;
  image-rendering: pixelated;
  border: 0;
}

.ten-box .ten-foot{
  margin: 8px 0 0 0;
  text-align: center;
  font-family: "basiic", monospace;
  font-size: 12.5px;
  letter-spacing: 1px;
  color: #9ff5c8;
  text-shadow: 0 0 6px rgba(159, 245, 200, .45);
}

/* =====================================================================
   THE PRAYER SHRINE — pulled out of the void, given an altar.
   ===================================================================== */
.lilspacebox .prayer-shrine{
  flex: 1 1 100%;
  width: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 14px;
  /* TRANSLUCENT, not solid black — the starfield behind it bleeds through.
     It reads as a shrine sitting IN the night sky rather than a black
     rectangle punched out of it. Kept dark enough (78%) that the white
     ASCII stays crisp; go much lighter and the prayer starts to fight
     the stars for legibility. */
  background: rgba(0, 0, 0, .78);
  border: 3px ridge #6a5a98;
}
.lilspacebox .prayer-img{
  display: block;
  width: 342px;                   /* NATIVE 342x215 */
  height: 215px;
  max-width: none !important;
  max-height: none !important;
  image-rendering: pixelated;
  border: 0;
  /* The jpg has a SOLID BLACK background baked in, so making the box
     translucent alone would do nothing — the image would still be an
     opaque black rectangle sitting on top of it.
     `screen` blending drops pure black to fully transparent while leaving
     the white ASCII untouched, so the starfield genuinely shows through
     the prayer itself, not just around it. This is the whole trick. */
  mix-blend-mode: screen;
}
.lilspacebox .prayer-cap{
  margin: 0;
  font-family: "basiic", monospace;
  font-size: 12.5px;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: #9a8fc9;
}

/* The stale `.junk-orbit img { width: 230px }` rule that used to sit here
   is GONE. It dated from when the orbit was "demoted, loose junk, no
   frame", and it was silently overriding the newer rule above that sizes
   the orbit's FRAME to match caterpie. Two rules for one element, the
   older one winning on source order. */

/* The title is a FLEX ROW now, not centered text: it has to hold the
   two eclipse gifs on the same baseline as the words. The ▚ glyphs are
   gone — they were standing in for exactly this and the real thing
   arrived. */
.ten-box .ten-title{
  margin: 0 0 10px 0;
  display: flex;
  width: 100%;                    /* span the cabinet, so the two
                                     eclipses BRACKET the panel instead
                                     of huddling around the words */
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-family: "basiic", monospace;
  font-size: 12.5px;              /* floor */
  letter-spacing: 2px;
  text-transform: uppercase;
  color: #9ff5c8;
  text-shadow: 0 0 6px rgba(159, 245, 200, .45);
}
/* the words take the middle and stay optically centered between the
   two eclipses */
.ten-box .ten-title > span{
  flex: 1 1 auto;
  text-align: center;
}
/* the eclipse, 51x49 NATIVE. Never scaled. The right-hand one is
   mirrored so the pair reads as a symmetrical bracket rather than as
   the same object printed twice. */
.ten-box .ten-eclipse{
  display: block;
  width: 51px;
  height: 49px;
  max-width: none !important;
  max-height: none !important;
  flex: 0 0 auto;
  image-rendering: pixelated;
  border: 0;
}
.ten-box .ten-title .ten-eclipse:last-child{
  transform: scaleX(-1);
}

/* .void-grid / .void-cell REMOVED — the VOID cabinet is gone.
   Its four objects were redistributed: the glyphs became .ten-panel
   (the thesis), the ASCII prayer became .prayer-shrine (its own altar),
   and the globe + orbit gif were demoted to loose wall junk because
   they are vibes, not arguments. Do not resurrect the cabinet. */


/* =====================================================================
   THE STAMP WALL — its own page (stamps.html)
   Stamps are a COLLECTION, and collections want a gallery, not a
   sidebar. 76 of them crammed into the junk dump would destroy the
   structure. A dedicated page is also an extremely old-web thing to
   have: it is a whole genre.

   The junk dump gets ONE stamp as a teaser that links here.
   ===================================================================== */

.stamps-page .cabinet{
  max-width: 1000px;
  margin: 0 auto;
  padding: 24px 16px 40px 16px;
}

.stamps-wrap-page{
  box-sizing: border-box;
  padding: 20px;
  background: url("images/backgrounds/bg7.png") repeat, #0b0620;
  border: 3px ridge #6a5a98;
}

.stamps-title{
  margin: 0 0 10px 0;
  text-align: center;
  font-family: "nes", "Press Start 2P", monospace;
  font-size: 14px;
  line-height: 1.7;
  color: #ffe9a8;
  text-shadow: 2px 2px 0 #2a1050, 0 0 8px rgba(255, 220, 140, .5);
}
.stamps-blurb,
.stamps-foot{
  margin: 0 auto 18px auto;
  max-width: 640px;
  text-align: center;
  font-family: "basiic", monospace;
  font-size: 12.5px;              /* floor */
  line-height: 1.6;
  color: #cfc4f0;
}
.stamps-foot{ margin: 20px auto 0 auto; }
.stamps-blurb a,
.stamps-foot a{ color: #9ff5c8; }

/* the grid. stamps are a uniform 99x56, so this is exact: no scaling,
   no shimmer, every cell identical. */
.stamp-grid{
  display: grid;
  grid-template-columns: repeat(auto-fill, 99px);
  justify-content: center;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.stamp-grid li{
  margin: 0;
  padding: 0;
  line-height: 0;
}
.stamp-grid img{
  display: block;
  width: 99px;                    /* NATIVE. never scale a pixel stamp. */
  height: 56px;
  max-width: none !important;
  max-height: none !important;
  image-rendering: pixelated;
  border: 1px solid #33285c;
  transition: transform .1s ease, border-color .1s ease;
}
.stamp-grid img:hover{
  transform: scale(1.06);         /* a transform, NOT a resize: no reflow */
  border-color: #ffe9a8;
}

@media (max-width: 1249px){
  .stamps-page .cabinet{ padding: 14px 10px 30px 10px; }
  .stamps-wrap-page{ padding: 14px; }
  .stamp-grid{ gap: 6px; }
}

/* the stamp teaser: a doorway to stamps.html, not the collection itself */
/* .junk-stamps REMOVED — the stamp teaser is no longer a standalone wall
   item. It is the right-hand CELL of the doorway table now. */

/* The teaser sits DIRECTLY on the cell's starfield. It used to carry its
   own dark background and outset border, which inside a table cell would
   be a box drawn inside a box — the exact thing the fused table exists to
   stop. The CELL is the frame. */
.doorways-box .stamp-teaser{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 0;
  text-decoration: none;
  background: none;
  border: 0;
}
.doorways-box .stamp-teaser img{
  width: 99px;                    /* NATIVE */
  height: 56px;
  image-rendering: pixelated;
  border: 1px solid #33285c;
}
.doorways-box .stamp-teaser-cta{
  margin-top: 3px;
  font-family: "basiic", monospace;
  font-size: 12.5px;
  color: #9ff5c8;
  text-shadow: 1px 1px 0 #1a0a38;
}
.doorways-box .stamp-teaser:hover{
  border-style: inset;
  background: rgba(20, 10, 45, .85);
}
.doorways-box .stamp-teaser:hover .stamp-teaser-cta{ color: #ffe9a8; }


/* =====================================================================
   THE AWARDS CASE
   Old-web sites displayed awards TOGETHER, in a case, with a header.
   One award floating alone on the wall reads as a stray image. A CASE
   reads as a brag — which is the whole point of having an award.
   Wrapping row: drop in more <figure class="award"> and it absorbs them.
   ===================================================================== */
/* THE AWARDS CASE — its own box, above the junk dump, under THE TEN.
   Graduated out of the wall by Ashley's rule: if you would want people to
   see it, it is not junk. This is a brag, so it is content.

   It was `flex: 1 1 100%` as a .junk-item, which means nothing outside the
   flex wall. It is a real centred block now, matching the ten-box width so
   the two stack cleanly.

   Still DOWNSTAIRS dialect: ridge border, ALL CAPS, border-radius 0. It is
   fan-shrine furniture, not a win98 dialog.

   position:relative + overflow:visible are REQUIRED — the fairy sticker
   hangs off the edge and needs this box as her positioning context. */
.awards-box{
  box-sizing: border-box;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto 18px auto;
  padding: 14px;
  background: rgba(5, 3, 15, .72);
  border: 3px ridge #e0c060;      /* gold. it's a trophy case. */
  border-radius: 0;               /* downstairs. never rounded. */
  position: relative;
  overflow: visible;
}
.awards-box .awards-title{
  margin: 0 0 10px 0;
  text-align: center;
  font-family: "basiic", monospace;
  font-size: 12.5px;              /* floor */
  letter-spacing: 2px;
  text-transform: uppercase;
  color: #ffe9a8;
  text-shadow: 1px 1px 0 #2a1050, 0 0 6px rgba(255, 220, 140, .5);
}
.awards-box .awards-row{
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: 14px;
}
.awards-box .award{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  margin: 0;
  padding: 8px;
  background: #000;
  border: 1px solid #6a5a98;
}
/* Every award is a DIFFERENT size, because every award came from a
   different stranger's Geocities page in a different year. That is the
   point of a trophy case: mismatched objects, one shelf.

   Scaled to fit THREE per row instead of two. At native size (168-286px
   wide) only two fit in the 732px box and the case was mostly air.
   These are JPGs — scanned/photographic artwork, NOT pixel art — so a
   uniform downscale is safe. The integer rule exists to protect pixel
   gifs; applying it to a scanned 1997 award would be cargo-culting it.

   HEIGHT-capped, not width-capped. Capping width would let the tall
   skinny ones (99award, 168x211) tower over the wide flat ones
   (romancememe, 253x158) and break the shelf line. Capping height gives a
   level shelf with mismatched widths — which is what a real trophy case
   actually looks like.
   coolaward 241x232 / cataward 286x176 / annaaward 250x249
   99award 168x211 / cutesiteaward 229x182 / romancememe 253x158 */
.awards-box .award img{
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 155px !important;   /* the shelf line */
  image-rendering: auto;          /* NOT pixelated. these are photos. */
}
.awards-box .award{
  max-width: 220px;
}
/* .award figcaption REMOVED. Captions are gone from the case: an award
   wall is a wall of OBJECTS, not a narrated gallery. Explaining each
   award turns the brag into a bit and kills it. They also forced every
   award to a different total height, which broke the shelf line. */

/* CATERPIE + PIKACHU, 240x240 NATIVE. Never scaled.
   Framed to MATCH the orbit gif beside it (2px outset + black ground) so
   the two spacy objects at the top of the wall read as a pair rather than
   as one framed thing next to one loose thing. */
.lilspacebox .junk-caterpie img{
  box-sizing: border-box;
  width: 240px;                    /* NATIVE 240x240 */
  height: 240px;
  border: 2px outset #7a6ba8;
  background: #000;
}
.lilspacebox .junk-caterpie .junk-cap{ max-width: 240px; }

/* the demoted wireframe globe. NATIVE 200x202. */


/* =====================================================================
   KONPEIBII — the easter egg.
   Deliberately unstyled to near-invisibility: no label, no frame, no
   header. He is a small creature sitting on a junk wall. The whole gag
   is that he is clickable and nothing tells you so. Any chrome added
   here converts an easter egg back into a feature. Resist it.
   ===================================================================== */
/* =====================================================================
   KONPEIBII — a loose object on a wall of loose objects.

   He is FLAIR. Not a mascot, not a feature, not a guide — flair. Every
   previous placement failed because it tried to make him MEAN something:
   his own centred row read as a headline, a floating gutter sticker was
   pinned to hardcoded offsets and broke twice as the wall resized, and
   squishing him against a blinkie made him a caption to someone else's
   joke.

   As a plain .junk-item he sits in the flow at his own scale, the flex row
   absorbs him at whatever size he actually is (he is a remote hotlink from
   tamanotchi.world and cannot be measured from here), and no layout change
   can misplace him again.

   flex: 0 0 auto is doing real work: WITHOUT it he stretches to fill the
   row and claims a whole line to himself, which is the one thing an easter
   egg must never do.

   Still no label, no stats, no hint. The asymmetry IS the gag.
   ===================================================================== */
.lilspacebox .konpeibii-egg{
  display: block;
  /* flex: 0 0 auto — WITHOUT this he stretches to fill the row and claims a
     whole line to himself, which is the one thing an easter egg must never
     do. */
  flex: 0 0 auto;
  /* CAPPED. He is a remote hotlink (tamanotchi.world) so his true size
     cannot be measured from here — and if he turns out to be wide, he
     wraps onto a line of his own and becomes a headline again. The cap
     guarantees he fits beside the blinkie stack no matter what he is:
     300 (blinkies) + 18 (gap) + 180 = 498, inside the 694px wall. */
  max-width: 180px;
  line-height: 0;
  border: 0;
  background: none;
  opacity: .85;                   /* he is tucked away, not on show */
  transition: opacity .2s, transform .2s;
}
.lilspacebox .konpeibii-egg img{
  max-width: 100%;
  height: auto;
}
/* ---------------------------------------------------------------------
   THE CURSOR.

   Built from 223.gif, but NOT used as-is — and the reason matters.

   That gif animates the arrow's POSITION inside the frame: across its 9
   frames the tip wanders from (1,1) to (10,3). A CSS cursor's hotspot is
   fixed (top-left by default), so the arrow would drift away from the
   thing you are actually pointing at. It is a lovely gif and an unusable
   cursor. (Browsers also only ever take the FIRST FRAME of a gif cursor,
   so the animation was never going to survive anyway.)

   So: one frame was extracted, cropped to the arrow itself, and re-anchored
   with the TIP AT (0,0) — which is what makes it point at what you are
   pointing at. images/cursors/pointer.png, 11x18.

   The @2x is a clean 2:1 nearest-neighbour upscale for hi-dpi screens; the
   browser picks whichever fits. No fractional scaling, so no shimmer.
   --------------------------------------------------------------------- */

/* OVER SOMETHING CLICKABLE — the same arrow, plus a sparkle.

   The arrow is IDENTICAL and still anchored at (0,0), so the hotspot does
   not move when you cross onto a link and the pointer does not appear to
   jump. Only the sparkle is added. That is the whole signal: the cursor
   picks up a little shine when the thing under it does something.

   (A hover cursor that is the same as the default is pointless, and one
   that is a completely different SHAPE is disorienting. Same arrow, one
   extra flourish, is the version that actually communicates.) */
a:hover,
.package summary:hover,
.doorways-box .stamp-teaser:hover,
.guestbook-funnel:hover,
.konpeibii-egg:hover,
.sidecat:hover,
button:hover,
summary:hover,
[role="button"]:hover{
  cursor:
    -webkit-image-set(
      url(images/cursors/pointer-link.png) 1x,
      url(images/cursors/pointer-link@2x.png) 2x) 0 0,
    url(images/cursors/pointer-link.png) 0 0,
    pointer !important;
}

/* text fields keep a real text caret — a pixel arrow over an input is
   actively unhelpful */
input:hover, textarea:hover, input:focus, textarea:focus{
  cursor: text !important;
}

/* THE FORECAST WHEEL KEEPS ITS ZOOM CURSOR. It is an <a>, so the a:hover
   rule above would have overridden `cursor: zoom-in` with the sparkle —
   losing a genuinely useful signal (that thing enlarges, and the cursor is
   what tells you so). A meaningful cursor beats a decorative one. */
.fc-wheel-zoom:hover{
  cursor: zoom-in !important;
}

/* The old rule put the INCENSE BURNER (6151b6fd.gif) on the "Main Menu"
   power button and konpeibii as a "secret clickable" tell. It made no
   sense — an incense stick is not a pointer, and a cursor that is not
   arrow-shaped just reads as broken. Those two now get the same pointer as
   everything else; their secrecy comes from having no affordance at all,
   which was always the better version of the gag. */

/* THE HUNGER TELL. Once every 30-45s he bobs, ONCE. See navigator.js.
   A single hop, not a loop: a creature that bobs constantly is an animated
   gif; one that twitches when you are not expecting it is alive. */
/* RESCOPED TO THE ELEMENT, not its parent.

   This was `.lilspacebox .konpeibii-egg.is-hungry`, and when konpeibii moved
   out of the junk wall into .doorways-box the selector stopped matching — so
   navigator.js kept adding .is-hungry every 30-45s and NOTHING HAPPENED. The
   hunger tell was silently dead. The bug was invisible because the failure
   mode of a missing animation is just... stillness.

   Scoping it to the element itself means he keeps his bob wherever he lives.
   Do not re-scope this to a parent. */
.konpeibii-egg.is-hungry{
  animation: konpeibii-bob .7s ease-in-out 1;
}
@keyframes konpeibii-bob{
  0%, 100% { transform: translateY(0); }
  30%      { transform: translateY(-7px); }
  55%      { transform: translateY(0); }
  70%      { transform: translateY(-3px); }
}
@media (prefers-reduced-motion: reduce){
  .konpeibii-egg.is-hungry{ animation: none; }
}

.lilspacebox .konpeibii-egg:hover{
  opacity: 1;
  transform: translateY(-3px);    /* the only tell. you have to find it. */
}
.lilspacebox .konpeibii-egg img{
  display: block;
  max-width: none !important;
  max-height: none !important;
  image-rendering: pixelated;
  border: 0;
}
/* A MEMBERSHIP is not a TROPHY. Same shelf, different hardware:
   the case is gold, this one gets club-blue. It reads as "I joined a
   thing" rather than "someone judged me," which is the honest claim. */
.awards-box .award--member{
  border-color: #7fb3e8;
}
.awards-box .award--member figcaption{
  color: #a9d2f5;
}
.awards-box .awards-foot{
  margin: 10px 0 0 0;
  text-align: center;
  font-family: "basiic", monospace;
  font-size: 12.5px;
  color: #9a8fc9;
}

/* =====================================================================
   STICKER FLAIR
   ===================================================================== */

/* pixelspace: 657x483 native. Halved to 328x241 — a clean 2:1 integer
   ratio, so no fractional resampling and no shimmer. Drifts slowly. */



@media (prefers-reduced-motion: reduce){
  .chat-chao{ animation: none; }
}

/* TEAL STARS on the forecast panel. Same treatment as the crystal on the
   clock box: static png, corner perch, no animation, nothing to shimmer.
   316x316 native, scaled to 110 (uniform, and it's a smooth png not pixel
   art, so a clean downscale is safe). */
.forecast-panel{ position: relative; overflow: visible; }
.forecast-panel .fc-stars-flair{
  position: absolute;
  width: 110px;
  height: 110px;
  right: -34px;
  top: -30px;
  z-index: 5;
  pointer-events: none;
  max-width: none !important;
  max-height: none !important;
  transform: rotate(8deg);
  filter: drop-shadow(2px 3px 2px rgba(90, 50, 104, .3));
}
@media (max-width: 1249px){
  .forecast-panel .fc-stars-flair{
    width: 78px; height: 78px;
    right: -12px; top: -16px;
  }
}


/* =====================================================================
   FIX: the forecast panel was blinding white
   Its background was #FFFFFB — chosen to mirror the printed handout's
   paper. That worked when the site background was a pale wash. Against
   the glyph tile it reads as a blank sheet punched out of the page.
   Warm it and let a little of the tile show through, so it sits ON the
   site instead of on top of it.
   ===================================================================== */
.forecast-panel{
  background: rgba(255, 253, 246, .90) !important;   /* was flat #FFFFFB */
  backdrop-filter: blur(1px);
}
.forecast-panel .fc-dates,
.forecast-panel .fc-howto{
  background: rgba(248, 246, 251, .92) !important;
}

/* =====================================================================
   TEAL STARS — a fixed sticker on the whole site
   Bottom-left corner, riding the viewport. position:fixed, so it stays
   put while everything scrolls past it. pointer-events:none so it can
   never eat a click. 316x316 native, scaled to 120 (a smooth png, not
   pixel art, so a clean downscale is safe).
   ===================================================================== */
.site-sticker{
  position: fixed;
  z-index: 300;
  pointer-events: none;
  max-width: none !important;
  max-height: none !important;
}
/* BOTTOM-RIGHT. It was bottom-LEFT and never got moved — this is the
   fix. Bumped 120 -> 150: at 120 it was a smudge in the corner. It is a
   smooth png, not pixel art, so a clean uniform downscale from 316 is
   safe. Mirrored so the lean tips INTO the corner rather than out of it. */
.site-sticker--stars{
  width: 150px;
  height: 150px;
  right: 14px;
  bottom: 10px;
  transform: rotate(6deg) scaleX(-1);
  filter: drop-shadow(2px 3px 3px rgba(20, 10, 45, .35));
}
/* on a phone a fixed sticker sits ON the content. cut it. */
@media (max-width: 1249px){
  .site-sticker{ display: none !important; }
}

/* THE FAIRY — flair on the awards case, not an exhibit in it.
   Same pattern as the crystal on the clock box: absolutely positioned
   off the edge, pointer-events:none, costs zero layout height. She reads
   as the one standing there admiring the trophies.
   160x232 NATIVE. Never scaled — she is pixel art.
   Requires position:relative + overflow:visible on .awards-box — both are
   set in the main .awards-box rule above. */
.awards-box .awards-fairy{
  position: absolute;
  right: -52px;
  bottom: -14px;
  /* NATIVE 160x232. She is pixel art, so the ONLY legal sizes are integer
     ratios: 160x232 (1:1) or 80x116 (2:1). There is nothing in between —
     100x145 or similar would be a fractional resize and she would shimmer.
     80 was too small, so she goes back to native. (She was originally
     shrunk because the awards box was 826px tall; it is 441px now, so
     native no longer overwhelms it.) */
  width: 160px;
  height: 232px;
  max-width: none !important;
  max-height: none !important;
  image-rendering: pixelated;
  pointer-events: none;
  z-index: 4;
  /* FLIPPED. She is drawn facing right, so hung on the right edge she was
     looking away from the trophies and off the side of the page. Mirrored,
     she faces INTO the case: now she is the one admiring the awards, which
     is the whole reason to put her there. */
  transform: scaleX(-1);
  filter: drop-shadow(-2px 3px 3px rgba(20, 10, 45, .45));
}
/* =====================================================================
   THE EMOJI WALL  (emoji.html)  and  CREDITS  (credits.html)

   Both are DOWNSTAIRS dialect: starfield, ridge borders, ALL CAPS heads,
   border-radius 0. They are fan-shrine pages, not win98 dialogs.
   ===================================================================== */

/* =====================================================================
   THE HOME BUTTON  —  a RULE, not a one-off.

   Any page that does NOT carry the main menu (stamps, emoji, links,
   credits, and every future frameless page) MUST have this button. A
   menu-less page is a dead end without it: the visitor has no dashboard,
   no nav, no way back except the browser chrome.

   OLD-WEB PLACEMENT: it lives at the BOTTOM of the page, after the last
   content, the way it always did — "oh, you're finished here? here is the
   way home." Not a top nav; a send-off. One component, styled once, at the
   foot of every frameless page. New frameless page? Paste .home-return at
   the very bottom. That is the rule.
   ===================================================================== */
.home-return{
  display: block;
  width: fit-content;
  margin: 26px auto 4px auto;      /* centered, it is a destination */
  padding: 12px 24px 12px 20px;
  font-family: "basiic", monospace;
  font-size: 17px;                 /* bigger — it is the end-of-page button */
  letter-spacing: .5px;
  color: #5A3268;                  /* plum, downstairs */
  text-decoration: none;
  background: linear-gradient(90deg, #f2c0bf, #C7C4D9);
  border: 2px solid #5A3268;
  /* the win98 raised-button bevel, in the site's plum */
  box-shadow: inset -1px -1px 0 rgba(90,50,104,.5),
              inset  1px  1px 0 rgba(255,255,255,.7),
              3px 3px 0 rgba(90,50,104,.35);
  display: flex;
  align-items: center;
  gap: 10px;
  transition: filter .1s ease, transform .04s ease;
}
.home-return:hover{ filter: brightness(1.06) saturate(1.1); }
.home-return:active{
  transform: translate(1px,1px);
  box-shadow: inset  1px  1px 0 rgba(90,50,104,.5),
              inset -1px -1px 0 rgba(255,255,255,.5);
}
.home-return .home-planet{
  width: 26px;                     /* the 20x20 planet.gif, scaled up cleanly */
  height: 26px;
  image-rendering: auto;
}

.emo-wrap-page,
.cr-wrap-page{
  box-sizing: border-box;
  width: 100%;
  max-width: 1100px;
  margin: 0 auto;
  padding: 20px 16px 40px 16px;
  /* SOLID panel, same as the stamp wall. Before this, the wrap was
     transparent and every box floated at 72% opacity over the SCROLLING
     site background — two moving translucent surfaces stacked behind the
     text, which is what made it hard to read. Now everything sits on one
     stable bg7 surface and nothing shows the scroll through. */
  background: url("images/backgrounds/bg7.png") repeat, #0b0620;
  border: 3px ridge #6a5a98;
  margin-top: 20px;
  margin-bottom: 30px;
}

.emo-title,
.cr-title{
  margin: 0 0 10px 0;
  text-align: center;
  font-family: "basiic", monospace;
  font-size: 20px;
  letter-spacing: 2px;
  color: #9ff5c8;
  text-shadow: 0 0 8px rgba(159, 245, 200, .45);
}

/* =====================================================================
   MILKY HEADER BOX (credits + links).
   The title/blurb/legend sat loose on the bg7 grid, and the grid lines
   running behind the text made it harder to read. This lifts them onto a
   soft, semi-opaque white card — enough to calm the grid without hiding
   it — so the text pops without needing to be any bigger. Rounded-free,
   soft violet edge, a whisper of shadow so it reads as a raised card. */
.credits-page .cr-header{
  max-width: 720px;
  margin: 0 auto 24px auto;
  padding: 18px 26px 20px 26px;
  background: rgba(255, 253, 255, .82);   /* the milk */
  border: 2px solid #C7C4D9;
  box-shadow: 0 2px 0 rgba(122, 110, 150, .25);
}
/* inside the milk, the text already went dark last round — but the title
   glow was built for a dark page, so soften it here. */
.credits-page .cr-header .cr-title{
  text-shadow: 0 1px 0 rgba(255,255,255,.6);
}
.credits-page .cr-header .cr-blurb,
.credits-page .cr-header .lk-legend{
  margin-bottom: 0;
}
.credits-page .cr-header .lk-legend{
  margin-top: 14px;
}
.emo-blurb,
.cr-blurb{
  max-width: 640px;
  margin: 0 auto 22px auto;
  text-align: center;
  font-family: rainyhearts, monospace;
  font-size: 17px;
  line-height: 1.6;
  color: #cfc4f0;
}
.emo-blurb a, .cr-blurb a, .emo-foot a{ color: #9ff5c8; }

/* =====================================================================
   TOP-OF-PAGE TEXT on the pale wrap (credits + links).
   The title / blurb / legend were authored in light mint + lavender for a
   DARK page. Once .cr-wrap-page became a pale bg7 panel, that text turned
   light-on-light and vanished. These overrides pull the header text DARK so
   it reads on the pale grid. Scoped to .credits-page so the dark-bg emoji
   page (which shares .cr-title/.cr-blurb) is untouched. */
.credits-page .cr-title{
  color: #6a3d7a;                  /* deep plum */
  text-shadow: 0 1px 0 rgba(255,255,255,.4);
}
.credits-page .cr-blurb{
  color: #4a4258;                  /* dark ink-violet, readable at paragraph size */
}
.credits-page .cr-blurb a{
  color: #a8447a;                  /* rose link, dark enough on pale */
  font-weight: 600;
}
.credits-page .lk-legend{
  color: #4a4258;
}
.credits-page .lk-legend .lk-tag--ghost{
  color: #5a4a88; border-color: #7A6E96; background: rgba(122,110,150,.14);
}
.credits-page .lk-legend .lk-tag--asset{
  color: #8a6a10; border-color: #C7A84a; background: rgba(199,168,74,.16);
}
.credits-page .cr-foot{
  color: #6E6278;                  /* warm gray, reads on pale */
}
.credits-page .cr-foot a{ color: #a8447a; }

/* ---- SEARCH ---- */
.emo-search-row{
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin: 0 0 8px 0;
}
.emo-search{
  width: 100%;
  max-width: 340px;
  box-sizing: border-box;
  padding: 8px 10px;
  background: #12082c;
  color: #9ff5c8;
  border: 2px inset #6a5a98;
  border-radius: 0;                /* downstairs. never rounded. */
  font-family: rainyhearts, monospace;
  font-size: 16px;
}
.emo-search::placeholder{ color: #6a5a98; }
.emo-hits{
  font-family: "basiic", monospace;
  font-size: 12.5px;
  color: #9a8fc9;
}
.emo-copied{
  min-height: 20px;
  margin: 0 0 18px 0;
  text-align: center;
  font-family: "basiic", monospace;
  font-size: 12.5px;
  color: #9ff5c8;
}

/* ---- CATEGORIES ---- */
.emo-cat{
  margin: 0 0 22px 0;
  padding: 12px;
  background: rgba(5, 3, 15, .72);
  border: 3px ridge #6a5a98;
  border-radius: 0;
}
.emo-cat-hd{
  margin: 0 0 12px 0;
  text-align: center;
  font-family: "basiic", monospace;
  font-size: 12.5px;              /* floor */
  letter-spacing: 1px;
  color: #ffe9a8;
  text-shadow: 1px 1px 0 #2a1050;
}
.emo-count{ color: #9a8fc9; }

.emo-grid{
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
}

/* each emoji is a BUTTON, because clicking it does something (copies the
   tag). A div would be a lie about what it is. */
.emo{
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  width: 84px;
  padding: 8px 4px 6px 4px;
  background: #0b0620;
  border: 2px outset #7a6ba8;
  border-radius: 0;
  cursor: pointer;
  transition: background .12s;
}
.emo:hover{ background: #1a1040; }
.emo:active{ border-style: inset; }
.emo.is-copied{
  background: #1d4a35;
  border-color: #9ff5c8;
}

/* NATIVE SIZE, always. These are 14-32px pixel gifs; any scaling destroys
   them outright. The BOX is a fixed 84px, the sprite sits in it at its own
   size. Fix the box, never the sprite. */
.emo img{
  display: block;
  width: auto !important;
  height: auto !important;
  max-width: none !important;
  max-height: none !important;
  image-rendering: pixelated;
  border: 0;
}
.emo-name{
  font-family: "basiic", monospace;
  font-size: 12.5px;              /* floor */
  line-height: 1.2;
  color: #9a8fc9;
  word-break: break-all;
  text-align: center;
}

.emo-foot,
.cr-foot{
  margin: 26px auto 0 auto;
  max-width: 620px;
  text-align: center;
  font-family: rainyhearts, monospace;
  font-size: 16px;
  line-height: 1.6;
  color: #9a8fc9;
}

/* ---- CREDITS ---- */
.cr-box{
  margin: 0 0 20px 0;
  padding: 14px 16px;
  /* OPAQUE now (was rgba .72). It sits on the solid bg7 wrap, so a
     translucent box would just show the tile behind it and muddy the
     text. A near-black solid ground gives every link maximum contrast. */
  background: #0a0618;
  border: 3px ridge #6a5a98;
  border-radius: 0;
}
.cr-box--note{ border-color: #e0c060; }      /* read this first = gold */
.cr-box--unknown{ border-color: #7fb3e8; }   /* help wanted = blue */

.cr-hd{
  margin: 0 0 12px 0;
  text-align: center;
  font-family: "basiic", monospace;
  font-size: 12.5px;
  letter-spacing: 1px;
  color: #ffe9a8;
  text-shadow: 1px 1px 0 #2a1050;
}
/* =====================================================================
   COLLAPSIBLE SECTIONS  —  .cr-drop
   Each cr-box on credits.html / links.html is a <details> so a long page
   of shrines and attributions can be tidied section by section. They ship
   OPEN (the page reads top to bottom by default); the triangle is just an
   affordance to re-collapse a hall you have finished browsing.
   Reuses the .cr-box frame — same ridge border, same ground — so a dropdown
   and a plain box are the same object, one just folds.
   ===================================================================== */
.cr-drop{
  /* it already carries .cr-box, so frame/ground/margin come from there.
     kill the box's inner padding — the summary and body own it now. */
  padding: 0;
  overflow: hidden;
}
.cr-drop-hd{
  /* the old .cr-hd heading, now the clickable lid. Wears the site's soft
     pastel titlebar gradient (blush -> lavender -> cream -> blush), the same
     one on course.exe and the post cards, so every framed window on the site
     is unmistakably a sibling. Dark plum text, because the gradient is light. */
  margin: 0;
  padding: 15px 16px;
  text-align: center;
  font-family: "basiic", monospace;
  font-size: 16px;                  /* was 12.5 — categories were too small */
  letter-spacing: 1px;
  color: #5A3268;                   /* plum, reads on the light gradient */
  text-shadow: 0 1px 0 rgba(255,255,255,.35);
  background: linear-gradient(90deg, #e8c0c8, #C7C4D9, #f6ebbe, #f2c0bf);
  border-bottom: 1px solid #5A3268;
  cursor: pointer;
  list-style: none;               /* hide the default disclosure triangle */
  position: relative;
  user-select: none;
  transition: filter .12s ease;
}
.cr-drop-hd::-webkit-details-marker{ display: none; }
.cr-drop-hd:hover{ filter: brightness(1.06) saturate(1.1); }
.cr-drop-hd:focus-visible{
  outline: 2px solid #9ff5c8;
  outline-offset: -2px;
}
/* our own triangle, right-aligned, points down when open, right when shut */
.cr-drop-hd::after{
  content: "▾";
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 11px;
  color: #5A3268;                   /* plum, to match the lid text */
  opacity: .75;
  transition: transform .12s ease;
}
.cr-drop:not([open]) > .cr-drop-hd::after{
  content: "▸";
}
.cr-drop-body{
  padding: 4px 16px 16px 16px;
}

/* =====================================================================
   LINKS PAGE ONLY — open the drawer bodies onto the pale bg7 surface.
   This is the "how did stamps do it" answer: on the stamp wall, content
   sits DIRECTLY on the pale bg7 tile — there are no dark boxes. Here the
   dark boxes were covering the texture and reading as heavy. So on the
   links page the drawer LID stays dark (a label bar) but the BODY is
   transparent, letting bg7 show through behind the links exactly like the
   stamps. Credits keeps its dark boxes — its dense tables need the ground.
   ===================================================================== */
.links-page .cr-drop{
  background: transparent;         /* was #0a0618 — let bg7 show */
  border-color: #6a5a98;
}
.links-page .cr-drop-body{
  background: rgba(255, 250, 255, .82);  /* pale, matches bg7's tone */
  padding: 14px 16px 16px 16px;
}
/* on the pale body, link text must go DARK to stay readable */
.links-page .cr-drop-body .lk-list > li{ color: #2e2838; }        /* ink */
.links-page .cr-drop-body .lk-list > li::before{ color: #7A6E96; } /* violet star */
.links-page .cr-drop-body .lk-list a{
  color: #6a3d7a;                  /* deep plum, reads on pale */
  border-bottom-color: rgba(106, 61, 122, .45);
}
.links-page .cr-drop-body .lk-list a:hover{
  color: #a8447a;                  /* rose */
  border-bottom-color: #a8447a;
}
.links-page .cr-drop-body .lk-note{ color: #6E6278; }             /* warm gray */
.links-page .cr-drop-body .lk-subhd{
  color: #a8447a !important;       /* rose subheads on pale */
  text-shadow: none;
}
.links-page .cr-drop-body .lk-sublist{ border-left-color: #C7C4D9; }
.links-page .cr-drop-body .lk-sublist > li{ color: #2e2838; }

/* FILL THE SPACE — the single-column list left the right half empty.
   Two columns turns it into an old-web directory: denser, less dead air.
   A link + its caption stay together (break-inside) so nothing splits. */
.links-page .cr-drop-body .lk-list{
  column-count: 2;
  column-gap: 28px;
}
.links-page .cr-drop-body .lk-list > li{
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
}
/* nested sublists and the moon hall's sub-headers span both columns so
   they don't get orphaned mid-column */
.links-page .cr-drop-body .lk-subhd{
  column-span: all;
  -webkit-column-span: all;
}

.cr-box p{
  margin: 0 0 10px 0;
  font-family: rainyhearts, monospace;
  font-size: 17px;
  line-height: 1.6;
  color: #cfc4f0;
}
.cr-box p:last-child{ margin-bottom: 0; }
.cr-sub{ color: #9a8fc9 !important; }
.cr-fill{
  margin-top: 12px !important;
  font-size: 15px !important;
  color: #9a8fc9 !important;
}

/* a real <table>, because this is downstairs and the 90s web was TABLES */
.cr-table{
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 6px 0;
}
.cr-table th,
.cr-table td{
  padding: 7px 9px;
  border: 1px solid #33285c;
  text-align: left;
  vertical-align: top;
  font-family: rainyhearts, monospace;
  font-size: 16px;
  line-height: 1.45;
  color: #cfc4f0;
}
.cr-table th{
  background: #1a1040;
  font-family: "basiic", monospace;
  font-size: 12.5px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: #ffe9a8;
}
.cr-table td a{ color: #9ff5c8; }
.cr-table tr:nth-child(even) td{ background: rgba(26, 16, 64, .35); }

/* inline emoji, for when they are used in actual posts */
.emo-inline{
  display: inline-block;
  vertical-align: -3px;
  margin: 0 2px;
  image-rendering: pixelated;
  border: 0;
}

/* =====================================================================
   LINKS PAGE  (links.html)  —  sibling of credits.html, same dialect.
   Reuses .cr-wrap-page / .cr-box / .cr-hd / .cr-blurb / .cr-foot. Only
   the list format is new. Downstairs: no rounding, ridge borders, ALL
   CAPS heads. A links page is a shrine, and its personality is the point.
   ===================================================================== */

/* the legend row explaining the two tags */
.lk-legend{
  max-width: 640px;
  margin: 0 auto 22px auto;
  text-align: center;
  font-family: "basiic", monospace;
  font-size: 12.5px;
  color: #b3a8d6;                   /* lifted for contrast */
}

/* the link lists. flat, dense, old-web. no bullets — a middot-free plain
   list where each row is a name + a caption in Ashley's voice. */
.lk-list{
  list-style: none;
  margin: 0;
  padding: 0;
}
.lk-list > li{
  margin: 0 0 11px 0;
  padding: 0 0 0 16px;
  position: relative;
  font-family: rainyhearts, monospace;
  font-size: 17px;
  line-height: 1.5;
  color: #cfc4f0;
}
/* a tiny pixel star instead of a bullet, downstairs style */
.lk-list > li::before{
  content: "\2726";                /* ✦ */
  position: absolute;
  left: 0;
  top: 0;
  color: #7a6ba8;
  font-size: 12px;
}
.lk-list a{
  /* link NAMES in basiic — the pixel font reads as a "label" and makes the
     clickable thing pop out of the caption sentence under it. */
  font-family: "basiic", monospace;
  font-size: 14px;
  color: #b6ffe0;                   /* brighter mint than before, for contrast */
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px dotted rgba(159, 245, 200, .5);
}
.lk-list a:hover{
  color: #fff;
  border-bottom-color: #9ff5c8;
}

/* the caption — Ashley's voice. quieter than the link, on its own line
   so a long list still scans. kept proportional (rainyhearts) so full
   sentences stay easy to read; brightened for contrast on the dark box. */
.lk-note{
  display: block;
  margin-top: 3px;
  font-family: rainyhearts, monospace;
  font-size: 15px;
  line-height: 1.45;
  color: #b3a8d6;                   /* was #9a8fc9 — lifted for readability */
}

/* nested lists (a site + its sub-pages, e.g. Sailor Soapbox) */
.lk-sublist{
  list-style: none;
  margin: 6px 0 0 0;
  padding: 0 0 0 16px;
  border-left: 2px solid #33285c;
}
.lk-sublist > li{
  margin: 0 0 6px 0;
  font-family: rainyhearts, monospace;
  font-size: 16px;
  line-height: 1.45;
  color: #cfc4f0;
}

/* the sub-headers inside the Sailor Moon hall (living / ghosts) */
.cr-box .lk-subhd{
  margin: 16px 0 10px 0;
  font-family: "basiic", monospace;
  font-size: 12.5px;
  letter-spacing: 1px;
  color: #f2c0bf;                  /* blush, so the moon hall feels special */
  text-shadow: 1px 1px 0 #2a1050;
}
.lk-subhd:first-of-type{ margin-top: 4px; }

/* the two little tags: ghost (archive) and asset (we pull art) */
.lk-tag{
  display: inline-block;
  margin-left: 4px;
  padding: 1px 6px 2px 6px;
  font-family: "basiic", monospace;
  font-size: 10.5px;
  letter-spacing: .5px;
  text-transform: uppercase;
  vertical-align: 1px;
  border: 1px solid;
  border-radius: 0;
}
.lk-tag--ghost{
  color: #b9a8e0;
  border-color: #6a5a98;
  background: rgba(60, 40, 100, .3);
}
.lk-tag--asset{
  color: #ffe9a8;
  border-color: #c7a44a;
  background: rgba(90, 70, 20, .3);
}

/* the Sailor Moon box gets a blush-tinted ridge, so the shrine hall reads
   as its own special room without leaving the downstairs dialect. */
.lk-box--moon{
  border-color: #D4849A;
}

/* ghost/asset tags on the pale links-page body: darken so they read */
.links-page .cr-drop-body .lk-tag--ghost{
  color: #5a4a88;
  border-color: #7A6E96;
  background: rgba(122, 110, 150, .14);
}
.links-page .cr-drop-body .lk-tag--asset{
  color: #8a6a10;
  border-color: #C7A84a;
  background: rgba(199, 168, 74, .16);
}

@media (max-width: 1249px){
  .emo-grid{ gap: 6px; }
  .emo{ width: 74px; }
  .cr-table th, .cr-table td{ font-size: 14px; padding: 5px 6px; }
}


/* THE EMOJI TEASER — third doorway. Same gesture as the stamps: go see a
   thing, take a thing. Four of them, NATIVE size, in a row. */
.doorways-box .emoji-teaser{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-decoration: none;
}
.doorways-box .emoji-teaser-row{
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 10px;
  background: #0b0620;
  border: 2px inset #6a5a98;
}
.doorways-box .emoji-teaser-row img{
  display: block;
  width: auto !important;
  height: auto !important;
  max-width: none !important;
  max-height: none !important;
  image-rendering: pixelated;   /* NATIVE. these are 20px sprites. */
  border: 0;
}
.doorways-box .emoji-teaser:hover .emoji-teaser-row{
  border-style: outset;
}


/* =====================================================================
   SKY BOX  —  the astrodienst widget + the symbol key, in ONE frame

   They are one instrument: the widget shows the sky right now, the key
   tells you how to read it. The seam between them is a hairline, not a
   border, so the eye reads a single object with two panels rather than
   two stacked widgets. Frame matches .aboutbox in the right column
   (4px double #d81888, inset outline, black ground) so the two framed
   objects on the page rhyme.

   DESKTOP ONLY — on the mobile cut list. See the mobile section.
   ===================================================================== */
.skybox{
  /* WIDTH: the iframe inside is scaled to .92, so it PAINTS at 193px. Add a
     real black gutter around everything and the box comes to:
       193 painted iframe + 2x1px mat border + 2x7px padding + 2x3px border = 215

     PADDING IS 7px, NOT 3px, ON PURPOSE. At 3px the mat was 195px wide inside
     a 194px space — it ate the padding entirely and the contents ran flush
     against the pink border with ZERO black gutter on any side. Ashley caught
     it: the box felt tight because there was no breathing room in it at all.
     The black ground needs to actually be VISIBLE for this to read as a
     framed instrument rather than a sticker jammed in a frame.

     FRAME WEIGHT: this was once 4px double + a 1px inset outline + 4px
     padding, which is THREE concentric rings and read as a slab swallowing
     the column. One 3px double border. The frame holds the instrument, it
     does not upstage it. */
  width: 207px;              /* EXACTLY .transits' inner width. It was 215px and
                                overflowing, and the -8px margin used to hide
                                that was clipping the column's own border. Now
                                the box genuinely fits, so nothing has to be
                                fudged. The widget scale (.88) is what pays for
                                this — see .skybox iframe. */
  box-sizing: border-box;
  margin: 6px 0 10px 0;      /* NO NEGATIVE LEFT MARGIN.

                                It used to be -8px, to pull this slightly-too-
                                wide box back onto the column's axis. That
                                worked visually but it pushed .transits itself
                                2px to the LEFT of .leftsidebox--home, which has
                                `overflow: hidden` — so it SCISSORED 2px off the
                                left edge of the purple border down the entire
                                column. Ashley spotted the chopped border.

                                A negative margin inside an overflow:hidden
                                ancestor is a trap. The box is centred by
                                `margin-inline: auto` on the width instead. */
  margin-inline: auto;
  padding: 7px;
  background: #000000;                 /* same black ground as .aboutbox */
  border-radius: 6px;
  border: 3px double #d81888;
}
/* THE MAT — a HAIRLINE surround under the white widget panel, not a slab.

   It exists to seat the white rectangle on the black ground (without it the
   widget reads as a screenshot taped inside the box). But it only needs to
   be a LINE. My first pass gave it 3px of padding plus a border plus a
   radius and it became a fat pink shelf that competed with the frame
   itself. One pink line is enough — the job is "this panel is mounted,"
   not "look at this mount." */
.skybox .planets.grid{
  /* THE CROP — the one real styling lever on a widget you cannot style.

     You cannot touch anything INSIDE a cross-origin iframe. But you can
     decide how much of it the world sees. Astrodienst's panel ends with
     ~75px of chrome we do not need:
       - "Explanations of the symbols" (a link) — REDUNDANT NOW. The symbol
         key directly below this box does that job, better, without a click.
       - "Powered by ASTRODIENST / www.astro.com" — their branding block.

     Making this box SHORTER than the iframe inside it and clipping the
     overflow cuts both off. What is left is the part with the actual
     information: the planet table. The instrument, not the packaging.

     (Attribution is not lost — astro.com is credited on credits.html,
     which is where credits belong.)

     Height maths: the iframe is 315px tall and painted at .92 = 290px.
     Cropping ~72px of that leaves 218px, which lands just under the last
     table row (Chiron) and above the "Explanations" link.

     The explicit size is also required because the iframe is TRANSFORMED —
     a transform does not change the layout box, so the iframe still
     reserves its full 210x315 in flow while painting smaller. Without a
     fixed size here the mat would draw a large empty pink-bordered area
     around a small widget. */
  width: 185px;    /* the PAINTED width of the .88-scaled iframe (210 x .88).
                      Must match, or the mat draws a pink box around empty
                      space (a transform does not shrink the layout box, so the
                      iframe still RESERVES 210px while painting 185). */
  height: 200px;   /* THE CROP, retuned for the .88 scale.

                      History, so nobody re-breaks it:
                        - at .92 scale: 200px cut Chiron, 216px let the
                          "Explanations of the symbols" link bleed through,
                          206px still clipped Chiron's DESCENDER (the bottom
                          of the glyph, which Ashley caught in a screenshot).
                        - the widget is now scaled .88, not .92, so the whole
                          table is ~4% shorter and these numbers all shift.

                      200px at .88 clears Chiron's full glyph and still lands
                      above the Explanations link.

                      CHIRON IS THE CANARY. He is the last row. If you retune
                      this, check that his glyph is whole — not just present,
                      WHOLE. A clipped descender is the failure mode here and
                      it is easy to miss. */
  margin: 0 auto 6px auto;             /* CENTRED. it was flush left, which read
                                          as the widget sitting left-of-centre
                                          inside the black frame. */
  padding: 0;
  display: block;
  overflow: hidden;                    /* this is what does the cropping */
  line-height: 0;                      /* kills the inline-element gap under
                                          the iframe that would otherwise put
                                          a white sliver above the seam */
  background: transparent;
  border: 1px solid rgba(216, 24, 136, .5);
  border-radius: 3px;
  font-size: 0;                        /* belt and braces on the inline gap */
}
.skybox iframe{
  display: block;
  /* THE 3px PROBLEM, and why this is a transform and not a width.

     .transits' inner width is 207px. Astrodienst needs its native 210px or
     its own content overflows and it draws a horizontal SCROLLBAR through
     the middle of the box. 207 < 210, so there is no width I can set here
     that both fits the column and avoids the scrollbar. Shrinking the
     iframe re-triggers the scrollbar; widening the box spills it out of
     the column (it was hanging 18px past the right edge).

     A TRANSFORM breaks the deadlock. The iframe still LAYS OUT internally
     at its full 210x315 — so astrodienst is happy and draws no scrollbar —
     but it PAINTS at 92%, which fits. Layout size and painted size are
     different things, and only the painted one has to fit the column.

     This is legal here because the iframe is TEXT, not pixel art. The
     integer-scaling rule exists to stop sprites shimmering; there are no
     sprites in this box. Do not copy this trick onto a gif. */
  width: 210px;
  min-width: 210px;
  max-width: none;
  height: 315px;
  transform: scale(.88);   /* 210 x .88 = 185px painted.
                              185 + 2 (mat border) + 14 (skybox padding)
                                  + 6 (skybox border) = 207 = the column. Fits.
                              Was .92, which needed a 215px box that did not
                              fit and had to be fudged with a negative margin —
                              and that margin clipped the column's border. */
  transform-origin: top left;
  border: 0;
  border-radius: 3px;
  overflow: hidden;

  /* ---- DO NOT INVERT THIS IFRAME ----
     The obvious trick for dark-moding a cross-origin iframe you cannot
     style is `filter: invert(1) hue-rotate(180deg)`. I had it staged here
     and Ashley correctly killed it:

     Astrodienst COLOUR-CODES its zodiac glyphs BY ELEMENT. Aries red
     (fire), Cancer blue (water), Taurus green (earth), Gemini yellow
     (air). invert() does not respect meaning — it just flips channels.
     Red fire comes out cyan. Blue water comes out orange. hue-rotate does
     not rescue it, it only drags them somewhere else wrong.

     That would scramble the one part of this widget that is actually
     TEACHING something, on a page whose whole feature is a key explaining
     how to read it. The widget would be lying.

     The white panel stays white. It is a readout; readouts are legible.
     Do not "fix" it. ---------------------------------------------- */
}
/* the hairline where the two panels meet. NOT a border — a seam. It should
   suggest one object with a fold in it, not two objects touching. */
.skybox-seam{
  height: 1px;
  margin: 8px 2px 7px 2px;
  background: rgba(216, 24, 136, .38);
}

/* =====================================================================
   ZODIAC SYMBOL KEY  —  the lower panel of the sky box

   The decoder ring. astro.com's widget is a wall of unlabeled glyphs and
   ash.exe's Big 3 is three more; this is where both get their meaning.
   Symbols in the table, explanation underneath — the astro.com pattern,
   except the key is right here instead of behind a click.

   NOTE: it has NO frame of its own. It is a panel inside .skybox, not a
   box beside it. Giving it a border would undo the whole point.

   Constraints:
   - Four columns in a ~218px inner width. That is why the long names are
     abbreviated ("Sagitt.", "Capric.", "Aquar.") — the full words do not
     fit and shrinking below the pixel-font floor is not allowed.
   - Glyphs are 16x16 NATIVE. content-box, no scaling, no shimmer.
   - It DOES NOT ANIMATE. Everything else here moves; a reference table is
     the one thing that should hold still. You consult a key, you do not
     watch it.
   ===================================================================== */
.zodiac-key{
  width: 100%;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  background: transparent;             /* sits on the skybox's black */
  border: 0;
  box-shadow: none;
}
.zodiac-key-hd{
  margin: 0 0 7px 0;
  font-family: "basiic", monospace;
  font-size: 12.5px;                   /* basiic floor */
  letter-spacing: .5px;
  text-align: center;
  color: #ff51b7;                      /* matches .aboutbox .stroke */
  font-weight: 400;                    /* no synthetic bold on a pixel font */
}
.zodiac-key-grid{
  /* 3 ACROSS, 4 DOWN. Was 4x3, which gave each cell ~48px and forced the
     long names to be abbreviated ("Sagitt.", "Capric.") — and even then
     "Cancer" and "Scorpio" were clipping. Three columns gives each cell
     ~65px, which fits every sign name in the zodiac WHOLE. The
     abbreviations are gone. */
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 9px 2px;
}
/* one cell: glyph on top, name under it. Stacked, not side by side —
   side by side leaves ~30px for the word, which fits nothing. */
/* .zk is the HOVER HOOK — it marks "this thing pops an info card."
   js/zodiackey.js binds to it. It is used in TWO places now:
     - the symbol key cells (below)
     - the three zodiac glyphs in ash.exe's Big 3
   so the same hover behaviour works in both, from one script.

   The CELL LAYOUT below is scoped to .zodiac-key-grid on purpose. Without
   that scope, ash.exe's .zk would inherit `flex-direction: column` and blow
   its Big-3 rows apart. The hook is shared; the layout is not. */
.zodiac-key-grid .zk{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  min-width: 0;
  padding: 2px 0;
  border-radius: 3px;
  transition: background .12s;
}
.zk{
  position: relative;
  cursor: help;              /* the affordance: the cursor says "ask me" */
}
.zodiac-key-grid .zk:hover{
  background: rgba(216, 24, 136, .22);
}
/* the glyph lifts slightly on hover — a small, cheap "I am clickable" tell.

   SCOPED TO THE KEY GRID ONLY. It used to be a bare `.zk img`, which also
   matched ash.exe's Big-3 glyphs — and there `scale(1.12)` painted the sprite
   OUTSIDE its row, so the little white boxes appeared to fall out of the right
   side of the card. Ashley caught it in a screenshot.

   The key's cells have room around them for a glyph to grow into. The Big-3's
   rows are a tight 16px grid column and do not. Same hook, different space, so
   the tell has to differ. */
.zodiac-key-grid .zk img{
  transition: transform .12s ease-out;
}
.zodiac-key-grid .zk:hover img{
  transform: translateY(-2px) scale(1.12);
}

/* ash.exe's Big-3 glyphs get a CONTAINED tell instead: they brighten and lift
   1px, but they do NOT scale. A 16px cell cannot hold a 17.9px sprite, and a
   fractional scale on pixel art shimmers anyway. */
.w98-big3 .zglyph{
  transition: transform .12s ease-out, filter .12s ease-out;
}
.w98-big3 .zk:hover .zglyph{
  transform: translateY(-1px);
  filter: brightness(1.25);
}
/* SCOPED to the key grid. As a bare `.zk img` this also matched ash.exe's
   Big-3 glyphs and, being later in the file, quietly overrode
   `.w98-big3 .zglyph`. Two rules sizing the same sprite is exactly the
   duplicate-selector trap that costs an hour every time. */
.zodiac-key-grid .zk img{
  width: 16px;
  height: 16px;
  box-sizing: content-box;   /* NOT border-box. see .w98-big3 .zglyph. */
  image-rendering: pixelated;
  display: block;
  border: 0;
}
.zk b{
  font-family: "rainyhearts", monospace;  /* rainyhearts, not basiic: this is
                                             running text at a small size, and
                                             basiic is a DISPLAY face. rainyhearts
                                             has distinct I/l/1. */
  font-size: 12px;
  font-weight: 400;
  color: #ffffff;                         /* white on the black ground */
  line-height: 1;
  white-space: nowrap;
  letter-spacing: -.3px;                  /* "Cancer" and "Scorpio" were
                                             CLIPPING out of their 4-col cells.
                                             A hair of negative tracking buys
                                             the few px they needed without
                                             dropping below the font's floor. */
}

/* ---------------------------------------------------------------------
   THE HOVER POPUP  —  the key teaches, on demand

   Hovering a sign pops its Hellenistic thumbnail:

       ARIES
       cardinal | fire
       ruled by Mars

   Modality + element + TRADITIONAL RULER. The ruler is the part almost no
   modern site bothers with, and it is the doorway to everything Ashley
   actually teaches — rulerships, dignity, the whole traditional frame. A
   key that only said "cardinal fire" would be a label; this one is a first
   lesson. All twelve rulers are the CLASSICAL scheme (no outers), which is
   the system the rest of the site runs on.

   WHY THIS IS A REAL ELEMENT AND NOT A ::after — it was, and that hit
   three walls at once:

   1. CLIPPING, NOT STACKING. Both .transits AND .leftsidebox--home have
      `overflow: hidden`. An absolutely-positioned popup inside them gets
      SCISSORED at their edges, and z-index cannot rescue it: raising 60 to
      9999 changes nothing, because this is a clip, not a stack.
   2. `position: fixed` escaped the clip but pins to the VIEWPORT, so the
      popup always appeared in the SAME spot near the top of the box no
      matter which sign you hovered. Which is exactly what Ashley saw.
   3. As an `absolute` child it also fed its own width into the cell's
      layout, blowing the grid out to 299px inside a 212px box — which is
      what was clipping "Cancer" and "Scorpio". The popup was CAUSING the
      clipping.

   One real <div> appended to <body> dodges all three: outside every
   clipping ancestor, out of the grid's flow entirely, and JS can put it
   exactly where the cursor is. See js/zodiackey.js.
   --------------------------------------------------------------------- */
#zk-pop{
  position: fixed;                    /* placed by JS at the cursor */
  z-index: 9999;
  pointer-events: none;               /* never eat a click, never trap the cursor */

  font-family: "rainyhearts", monospace;
  font-size: 17px;                    /* was 13px, too small to read comfortably */
  line-height: 1.65;
  text-align: left;
  white-space: nowrap;
  color: #ffffff;
  background: #1a0d16;
  border: 2px solid #d81888;
  border-radius: 4px;
  padding: 6px 9px;
  box-shadow: 0 3px 0 rgba(0,0,0,.55);

  opacity: 0;
  transition: opacity .12s ease-out;
}
#zk-pop.on{ opacity: 1; }

/* the popup is a GLYPH beside its TEXT */
#zk-pop{
  display: flex;
  align-items: center;
  gap: 10px;
}
/* THE BIG GLYPH — the same 16px pink sprite from the key, at a clean 2x.

   Ashley wanted the symbol featured larger here, and 32 is the ONLY legal
   size: 16x2. Not 24 (1.5x), not 28. Fractional scaling on pixel art
   shimmers, and there is no larger source art — the rainbow set is also
   ~16px, just ragged.

   Reusing the SAME sprite (rather than a different set) is also the right
   call for a different reason: the popup is teaching you to recognise the
   glyph you just hovered. Showing a second, different-looking version of
   it would undercut the whole point. */
#zk-pop .zp-glyph{
  width: 32px;                        /* 16 x 2. integer. */
  height: 32px;
  box-sizing: content-box;            /* border-box would silently upscale it */
  image-rendering: pixelated;
  display: block;
  flex: 0 0 auto;
  border: 0;
  max-width: none;                    /* defeat the global img clamp */
}
#zk-pop .zp-text{
  display: block;
}
#zk-pop .zp-sign{
  display: block;
  color: #ff51b7;                     /* the sign name, in the site pink */
  letter-spacing: .5px;
}
#zk-pop .zp-line{
  display: block;
}
#zk-pop .zp-ruler{
  display: block;
  color: #C7C4D9;                     /* the ruler, quieter than the sign */
}

@media (prefers-reduced-motion: reduce){
  #zk-pop{ transition: none; }
}


/* =====================================================================
   ash.exe — LIFE SIGNS

   The 90s-web rule Ashley named, and it is the correct one: the blinkies
   lived in your PERIPHERY. The margins flashed; the thing you were
   actually reading held still. That contract is why a wall of them was
   survivable. So nothing here blinks while you read it.

   Three signs of life instead, all of them RARE (same principle as
   konpeibii's 30-45s bob — a thing that always happens is a feature, a
   thing that sometimes happens is a secret):

   1. a slow specular sweep across the portrait, every ~14s
   2. a very faint breath in the meter bars — peripheral, ignorable
   3. a hover reward on the title-bar star

   All three respect prefers-reduced-motion at the bottom of this block.
   ===================================================================== */

/* ---- 1. THE GLINT — REMOVED.

   There was a specular sweep crossing the portrait every 22s. It was a nice
   effect and Ashley gave it a fair trial, but she called it: too much on her
   own face. Taken out at her request.

   The .w98-portrait rule below keeps `position: relative` (the layout needs
   it) but the ::after that drew the sweep is GONE. Do not put it back
   without asking. The card's remaining signs of life are the meter breath
   and the title-bar star on hover, both of which are peripheral. */
.w98-portrait{
  position: relative;
}

/* ---- 2. THE BREATH — the meters are alive, barely.
   Opacity only, and only 6% of it. You should not be able to point at
   this and say what it is doing; you should only notice the card is not
   dead. If you can consciously see it, it is too strong. */
.w98-kv .w98-meter{
  animation: ashBreath 4.5s ease-in-out infinite;
}
.w98-kv .k + .v .w98-meter{ animation-delay: .9s; }   /* MAG lags HP slightly,
                                                         so they breathe out of
                                                         phase — in sync reads
                                                         as one blinking object */
@keyframes ashBreath{
  0%, 100% { opacity: 1;   }
  50%      { opacity: .94; }
}

/* ---- 3. THE HOVER REWARD — the title-bar star turns, but only for you.
   Costs nothing when ignored. This is the "desktop is the reward" rule
   applied at the smallest possible scale. */
.tb-star{
  display: inline-block;
  transition: transform .5s cubic-bezier(.34,1.56,.64,1);   /* a little overshoot */
}
.w98-card.small:hover .tb-star{
  transform: rotate(180deg) scale(1.25);
}

/* ---- ACCESSIBILITY: if the OS says no motion, all of it stops. */
@media (prefers-reduced-motion: reduce){
  .w98-kv .w98-meter{ animation: none; }
  .tb-star{ transition: none; }
}


/* =====================================================================
   MOON PHASE WIDGET (dimden.dev) — tightened

   It was declaring width="210" / height="140" but the global
   `img, iframe { max-width: 100% }` was squeezing it to 202px, and three
   stray <br/> tags around it were adding ~66px of dead air. Same two
   diseases as the astrodienst widget had.

   Now: centred, no stray breaks, and its own margin is the only thing
   setting the space around it.
   ===================================================================== */
.moonbox{
  display: block;
  width: 210px;
  height: 150px;
  max-width: none;         /* defeat the global iframe clamp that squeezed
                              this to 202px and cropped its right edge */
  margin: 2px auto 8px auto;   /* centred in the column */
  border: 0;
  transform: scale(.95);       /* the column's inner width is 207px; scale the
                                  PAINT so it fits without the widget's own
                                  content reflowing. Same trick as the astro
                                  iframe — legal because it is text, not a
                                  sprite. */
  transform-origin: top center;
}

/* =====================================================================
   BOOKING — a little more air under the offerings box

   book_a_reading.exe was sitting 16px under the offerings panel, which
   read as crowded (they are two separate .exe windows and should not
   look welded together). 26px gives them their own space without moving
   anything else in the grid — the row gap is untouched, this is the
   booking card's own margin.
   ===================================================================== */
@media (min-width: 1250px){
  .dz-booking{
    margin-top: 10px;
  }
}

/* the global `.planets { margin-top: -38px }` was written for the OLD bare
   iframe and yanks the widget upward inside its new frame. Neutralised
   INSIDE the skybox only, so anything else still using .planets is
   unaffected. */
.skybox .planets{
  margin-top: 0;
}


/* =====================================================================
   FLAIR BUTTON ROW  —  full-width strip under latest_post.exe

   The six 88x31 buttons used to live INSIDE the counter box. That made the
   counter tall and bottom-heavy — the single heaviest object in the left
   column — and it dragged the canvas's whole balance down with it. Ashley
   called it, and moving them out is the fix.

   Out here they finally do the job 88x31 buttons were invented for: a band
   of badges across the foot of the page. That is the correct 90s form; they
   were never meant to be stacked in a sidebar.

   This rule is LAST in the file on purpose — several older `.splashbuttons`
   and `.visitor-left2 .splashbuttons` rules still exist above (they sized it
   as a 108x310 vertical block inside the counter) and this has to beat them.
   Those older rules are now dead and are on the CSS-cleanup list.
   ===================================================================== */
.dz-flair .splashbuttons{
  display: grid !important;
  grid-template-columns: repeat(3, auto) !important;  /* 3 across... */
  grid-auto-rows: auto !important;                    /* ...2 down = 6 buttons */
  justify-content: center;
  align-items: center;
  justify-items: center;
  gap: 8px 10px;

  width: 100% !important;
  height: auto !important;
  margin: 0 !important;
  padding: 10px 12px !important;
  box-sizing: border-box;

  background-color: #F1EBE5;
  border-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAACpF6WWAAAAAXNSR0IArs4c6QAAAEBJREFUOE9jZICA/1CakQpsRpghIJpa4D81DYM5CmwoyOtUNZwWho56n1oJCWHOaOyPJv7RvD90ij5YQU2VugoAfv8oCQf/l3YAAAAASUVORK5CYII=') 7 / 7px / 0 round;
}
/* the buttons themselves: NATIVE SIZE, always.
   88x31 is the canonical web-button dimension and these are pixel art —
   any scaling at all shimmers. Native or nothing. */
.dz-flair .splashbuttons img{
  width: auto !important;
  height: auto !important;
  max-width: none !important;
  box-sizing: content-box;    /* border-box would silently upscale them */
  image-rendering: pixelated;
  display: block;
  margin: 0 !important;
}

/* MOBILE: the flair row is CUT, like everything else that is not the
   business card. Added at the same time the row was created — anything
   living outside .lilspacebox escapes the mobile cut and defaults to
   `order: 0`, which would sort it to the TOP of the phone. THE TEN and
   DOORWAYS both did exactly that and buried the business card. */
@media (max-width: 1249px){
  .dz-flair{ display: none !important; }
}


/* the hover hint under the SYMBOL KEY header.

   THE AFFORDANCE PROBLEM: the key pops an info card on hover, but nothing
   told anyone that. An interaction nobody discovers may as well not exist.

   The 90s answer is also the best one: SAY SO. A tiny line of text is
   period-correct, costs nothing, and is unambiguous in a way that a subtle
   visual cue never is. (astro.com does the same thing right above this box:
   "Explanations of the symbols" — a plain-language label on a wall of
   glyphs.)

   Backed up by two cheap non-verbal tells: `cursor: help` on each cell, and
   the glyph lifting 2px on hover. */
.zodiac-key-hint{
  margin: -3px 0 7px 0;
  font-family: "rainyhearts", monospace;
  font-size: 12px;
  text-align: center;
  color: #C7C4D9;
  letter-spacing: .3px;
  opacity: .85;
}


/* =====================================================================
   THE FAIRY  —  footer, bottom right, opposite the crystal

   She used to hang off the awards case, admiring the trophies. Down here
   she bookends the page instead: crystal bottom-left, fairy bottom-right,
   disclaimer between them. The page closes with a sticker in each corner.

   NATIVE 160x232. She is pixel art, so the only legal sizes are 160x232
   (1:1) or 80x116 (2:1). Nothing between — a fractional resize shimmers.
   `box-sizing: content-box` is mandatory: images are globally border-box
   here, which makes `width:auto` resolve to natural + border and silently
   upscale the sprite.
   ===================================================================== */
.site-footer{
  position: relative;      /* the fairy anchors to this */
  overflow: visible;       /* she hangs past the footer's edge on purpose */
}
.site-footer .footer-fairy{
  position: absolute;
  right: -1px;             /* was 9px; nudged another 10px toward the corner */
  bottom: 0;

  width: 160px;            /* NATIVE. never scale. */
  height: 232px;
  max-width: none !important;
  max-height: none !important;
  box-sizing: content-box; /* border-box would silently upscale her */
  image-rendering: pixelated;

  pointer-events: none;    /* zero layout cost, never eats a click */
  z-index: 3;
  /* she is drawn facing right; mirrored so she faces INTO the page
     (toward the disclaimer) rather than off the edge of it — same reason
     she was flipped on the awards box. */
  transform: scaleX(-1);
}

/* MOBILE: cut. She is decoration, and mobile is the business card. Added at
   the same time she was placed — anything outside .lilspacebox escapes the
   mobile cut and defaults to `order: 0`, which sorts it to the TOP of the
   phone. THE TEN and DOORWAYS both did that and buried the business card. */
@media (max-width: 1249px){
  .site-footer .footer-fairy{ display: none !important; }
}


/* THE ASTRODIENST CREDIT.

   The widget's own "Powered by ASTRODIENST" block is cropped off (see
   .skybox .planets.grid — the crop removes their branding AND the
   "Explanations of the symbols" link, which the symbol key below has made
   redundant). Taking their data and cutting their name off it is scuzzy,
   and Ashley said so.

   A HOVER credit would not have fixed that. A credit you only see if you
   happen to mouse over it is a credit you can pretend is not there. It is
   the same dodge in a nicer coat. So this is plain text, always visible,
   and it links to them.

   Quiet enough not to compete with the instrument. Loud enough to count. */
.skybox-credit{
  margin: 5px 0 0 0;
  font-family: "rainyhearts", monospace;
  font-size: 11px;
  text-align: center;
  color: #8A7F96;
  letter-spacing: .2px;
  line-height: 1.2;
}
.skybox-credit a{
  color: #C7A8B8;
  text-decoration: none;
  border-bottom: 1px dotted rgba(199,168,184,.5);
}
.skybox-credit a:hover{
  color: #ff51b7;
  border-bottom-color: #ff51b7;
}


/* KONPEIBII — under the doorways, below "my emoji".

   Moved out of the junk wall at Ashley's request. He works better here: on
   the junk wall he was one object among many (a fine place to HIDE a thing,
   a poor place to FIND one). At the foot of the doorways box — the row of
   things that go somewhere — he reads as one more door, except the door is
   a creature and it is hungry.

   No label, no stats, no hint. Spot him, click him, feed him. Miss him and
   he is decor.

   HE IS A REMOTE HOTLINK (tamanotchi.world) and cannot be measured from
   here. Do NOT try to size him — no width, no height. If he looks wrong you
   are almost certainly measuring a broken-image ALT BOX and not the image
   itself; that mistake has been made three times on this site. Let the
   remote image be whatever size it is and let the box absorb it. */
.doorway-cell .konpeibii-egg{
  display: block;
  width: fit-content;
  margin: 10px auto 2px auto;   /* centred under the doorway cells */
  line-height: 0;               /* no inline-gap under the image */
  text-decoration: none;
  border: 0;
}
.doorway-cell .konpeibii-egg img{
  display: block;
  border: 0;
  /* deliberately NO width/height. See above. */
  image-rendering: pixelated;
}
/* he bobs ONCE every 30-45s as a hunger tell. Rarity is the feature — a
   thing that always moves is a feature, a thing that sometimes moves is a
   secret. */
.doorway-cell .konpeibii-egg:hover img{
  transform: translateY(-3px);
  transition: transform .15s ease-out;
}


/* ================================================================
   18. THE POST TEMPLATE  -  body.--post
   ----------------------------------------------------------------
   A post has NO LEFT COLUMN, on purpose.

   The left column is a DASHBOARD: live astrodienst transits, the moon
   phase, the symbol key, the solar system gif. That is the instrument
   panel of the HOMEPAGE, and it is great there. Next to an 800-word
   essay it competes for the exact attention the essay needs, and the
   symbol key is loudly answering a question the post never asked.

   The reader's job on a post is to read one idea. So: menu stays (they
   need to get out), header/footer/background/fonts stay (unmistakably
   the same site), dashboard goes, text column widens.

   Scoped to `body.--post` so it CANNOT leak onto the homepage. That is
   also why it can sit at the bottom of the file without fighting
   anything above it.
   ================================================================ */

@media (min-width: 1250px){
  body.--post .cabinet > .container--post{
    width: 740px;
    min-width: 0;         /* NOT auto. a flex child with min-width:auto
                             refuses to shrink below min-content and blows
                             out of the row. */
    flex: 1 1 740px;
    max-width: 860px;     /* prose has a readable ceiling. ~970px is 120
                             characters and nobody finishes a line that
                             long. */
    margin: 0;
    padding: 0;
    order: 1;
  }
  body.--post .cabinet{ justify-content: center; }
}

/* ---- 18.1 The post shell ---- */
body.--post .post{
  background: #FFFFFB;
  border: 2px solid #5A3268;
  box-shadow: 3px 3px 0 rgba(90,50,104,.45);
  margin: 0 0 20px 0;
  overflow: hidden;
}
body.--post .post-head{
  padding: 22px 26px 16px 26px;
  border-bottom: 1px dashed #a99fc0;
  background: linear-gradient(180deg, #f7f2f6, #FFFFFB);
}
body.--post .post-h1{
  margin: 0 0 10px 0;
  font-size: 2em;
  line-height: 1.2;
  color: #5A3268;
}
body.--post .post-head-meta{
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-family: basiic, monospace;
  font-size: 12.5px;      /* the basiic floor. never under. */
}

/* ---- 18.2 Body copy ----
   rainyhearts, NOT basiic. basiic is a DISPLAY face and this is the most
   running text on the site. rainyhearts is the body pixel font with
   distinct I/l/1, which matters in a paragraph. */
body.--post .post-content{
  padding: 24px 26px 28px 26px;
  font-family: rainyhearts, monospace;
  font-size: 17px;
  line-height: 1.75;
  color: #2e2838;
}
body.--post .post-content p{ margin: 0 0 1.15em 0; }
body.--post .post-content .post-lede{
  font-size: 19px;
  line-height: 1.65;
  color: #5A3268;
  padding-bottom: .5em;
}
body.--post .post-content h2{
  margin: 1.6em 0 .6em 0;
  font-size: 1.35em;
  line-height: 1.3;
  color: #5A3268;
  padding-bottom: 6px;
  border-bottom: 1px dashed #C7C4D9;
}

/* NO font-weight / font-style on emphasis. rainyhearts is a pixel font
   with no bold cut, so `bold` makes the browser SYNTHESISE it by smearing
   the glyph sideways - not weight, BLUR. Same bug that made the ash.exe
   stat numbers unreadable. Emphasis is COLOUR. */
body.--post .post-content b{ font-weight: normal; color: var(--pink); }
body.--post .post-content i{ font-style: normal; color: var(--teal); }

body.--post .post-content a{ color: var(--teal); text-decoration: underline; }
body.--post .post-content a:hover{ color: var(--pink); }

body.--post .post-content ul{ margin: 0 0 1.15em 0; padding-left: 1.4em; }
body.--post .post-content li{ margin-bottom: .5em; }

/* ---- 18.3 The CTA - the middle step ----
   "Book a reading" is too big an ask for someone who arrived googling
   "why is sailor mars so angry". The sect question costs nothing: you
   either know your birth time or you don't, and either way there's a
   next step. */
body.--post .post-cta{
  margin: 2em 0 0 0;
  padding: 18px 20px;
  background: #f7f2f6;
  border: 2px solid #5A3268;
  box-shadow: 3px 3px 0 rgba(90,50,104,.25);
}
body.--post .post-cta-line{
  margin: 0 0 .6em 0;
  font-family: basiic, monospace;
  font-size: 15px;
  line-height: 1.5;
  color: var(--pink);
}
body.--post .post-cta-sub{ margin: 0 0 .5em 0; font-size: 16px; line-height: 1.6; }
body.--post .post-cta-sub:last-child{ margin-bottom: 0; }

/* ---- 18.4 The FLAT tag chip ----
   Like .post-tag but a <span>, not an <a>, and it does NOT light up on
   hover - because it does not go anywhere. Tags are not links yet: a chip
   that 404s is a broken promise. At ~10 posts flip TAGS_ARE_LINKS in
   build_pages.py and these become real links. Vocabulary is recorded
   correctly from post one, so that day costs nothing. */
.post-tag--flat{ cursor: default; }
.post-tag--flat:hover{ background: var(--yello); color: #5A3268; }

/* ---- 18.5 Mobile ---- */
@media (max-width: 1249px){
  body.--post .post-head{ padding: 16px; }
  body.--post .post-content{ padding: 16px; font-size: 16px; }
  body.--post .post-h1{ font-size: 1.5em; }
  body.--post .post-content .post-lede{ font-size: 17px; }
  body.--post .post-cta{ padding: 14px; }
}

/* ================================================================
   17. course.exe  —  compact info box, WIN98 dialect
   ----------------------------------------------------------------
   REWRITTEN to match .post-card / .booking-window exactly. The prior
   version used the PIXEL fonts (basiic + rainyhearts), which read as a
   different design language from the other desktop windows - and that
   is the "not consistent with the other boxes" Ashley called out. The
   .exe windows on this canvas are win98 DIALOGS: MS Sans Serif body,
   #ece9d8 face, the pink->lavender->cream titlebar gradient. This box
   now speaks that same language.

   Still no banner (a 480px animation that crawls at any scale, and the
   source of the height/deadspace/glitch). Still compact, still column 1
   above the counter. Chrome copied from .post-titlebar so the two
   windows are unmistakably siblings.
   ================================================================ */

.course-card{
  background: #ece9d8;
  border: 2px solid #5A3268;
  box-shadow: 3px 3px 0 rgba(90,50,104,.45);
  /* THE win98 system stack - same as .post-card. this is the whole
     point of the rewrite. */
  font-family: "MS Sans Serif", Tahoma, Verdana, Arial, sans-serif;
  overflow: hidden;
  box-sizing: border-box;
}

/* titlebar copied from .post-titlebar (same gradient direction, same
   13px, same padding) so course.exe and latest_post.exe are siblings. */
.course-titlebar{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  background: linear-gradient(90deg, #e8c0c8, #C7C4D9, #f6ebbe, #f2c0bf);
  color: #5A3268;
  padding: 4px 6px;
  font-size: 13px;
  font-weight: bold;
  border-bottom: 1px solid #5A3268;
}
.course-titlebar-name{ letter-spacing: .3px; }
.course-ctls{ display: flex; gap: 3px; }
.course-ctl{
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 14px;
  background: #ece9d8; border: 1px solid #5A3268; color: #5A3268;
  font-size: 10px; line-height: 1;
}

.course-body{
  padding: 13px 15px;
  flex: 1 1 auto;            /* fill the taller card (min-height 300) and
                               let the note/space settle to the bottom */
  display: flex;
  flex-direction: column;
}

/* the hook uses the height gained by growing the box; without it the
   taller card would just be more empty tan. one line, sits under the
   subtitle, sets up the facts. */
.course-hook{
  margin: 0 0 11px 0;
  font-size: 12px;
  line-height: 1.45;
  color: #5a5270;
}

/* ---- header ---- */
.course-kicker{
  margin: 0 0 7px 0;
  font-size: 11px;
  font-weight: bold;
  letter-spacing: .4px;
  color: var(--pink);
  text-transform: uppercase;
}
.course-title{
  margin: 0 0 3px 0;
  font-size: 1.35em;        /* matches .post-title */
  line-height: 1.2;
  color: #5A3268;
}
.course-sub{
  margin: 0 0 11px 0;
  font-size: 12px;
  color: #7a7091;
}

/* ---- facts: stacked, tight ---- */
.course-facts{
  list-style: none;
  margin: 0 0 10px 0;
  padding: 9px 0 0 0;
  border-top: 1px dashed #a99fc0;
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 12px;
}
.course-facts li{
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.course-fact-k{
  flex: 0 0 42px;
  font-weight: bold;
  color: var(--teal);
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: .3px;
}
.course-fact-v{ color: #2e2838; min-width: 0; }

.course-note{
  margin: 0;
  margin-top: auto;         /* sink to the foot of the taller card so the
                               gained height opens above it, not below */
  padding-top: 9px;
  border-top: 1px dashed #a99fc0;
  font-size: 11px;
  font-style: italic;
  color: #7a7091;
}

/* CTA - commented out in HTML, styled and ready. */
.course-cta{
  display: inline-block;
  margin-top: 9px;
  padding: 5px 12px;
  background: var(--pink);
  border: 1px solid #5A3268;
  box-shadow: 2px 2px 0 rgba(90,50,104,.35);
  color: #FFFFFB;
  font-size: 12px;
  font-weight: bold;
  text-decoration: none;
  white-space: nowrap;
}
.course-cta:hover{ background: var(--teal); }
.course-cta:active{ transform: translate(2px,2px); box-shadow: none; }

/* ---- mobile: slot 6, after booking ---- */
@media (max-width: 1249px){
  .dz-course{
    order: 6 !important;
    width: 100% !important;
    margin: 0 0 20px 0 !important;
    display: block !important;
  }
  .course-body{ padding: 13px; }
}

/* ================================================================
   18.6 THE STARTING LINEUP  —  day/night team roster cards
   ----------------------------------------------------------------
   Ashley's jumbotron metaphor: two roster cards, captain up top,
   players below, in team colours. DAY = warm gold, NIGHT = cool
   violet. Built to match .post-cta (hard 2px border + offset
   shadow, the win98 card feel). Appended at END so it wins clean
   over any earlier duplicate. rainyhearts body = NO bold (smears);
   emphasis is colour + the basiic captain line.
   ================================================================ */
body.--post .post-emoji{
  /* NEVER SET A FIXED HEIGHT HERE.
     ------------------------------------------------------------------
     This rule used to hardcode `height: 15px` (sunrise's native height),
     which silently CRUSHED every other icon in the library. The favicon
     wall is 675 icons and only 608 are 20x20 -- the other 67 range from
     7x7 to 56x56. Any fixed height fractionally scales most of them,
     which breaks the integer-only pixel rule and looks visibly mushy.

     Instead: no height at all. The <img> renders at its OWN native pixel
     size by default, which is always 1x and always integer. `max-height`
     only kicks in for the handful of genuinely oversized icons (oddish
     is 56px, frigginnintendo is 50px) so one of those can't tower over a
     23px heading. `width:auto` keeps aspect ratio intact when it does.

     RESULT: a new icon from ANY category just works. No CSS edit, no
     per-file exception list, nothing to forget. Drop the tag in and the
     icon is correct at its native size. */
  max-height: 24px;         /* ceiling only. most icons never reach it. */
  width: auto;
  vertical-align: baseline;
  margin-left: 7px;
  box-sizing: content-box;  /* bordered/pixel gif rule: don't let global border-box upscale */
  image-rendering: pixelated;
  position: relative;
  top: 3px;                 /* nudge onto the text baseline */
}

/* ================================================================
   THE STARTING LINEUP  —  redesigned as a real roster board
   ----------------------------------------------------------------
   Three columns in one row: TEAM DAY | mercury (switch) | TEAM NIGHT.
   mercury is NOT a footnote under the cards anymore... it STANDS
   BETWEEN the two dugouts, which is exactly what a switch-hitter is.
   The gold->violet gradient runs down its spine, physically bridging
   the warm team and the cool team.

   Each dugout now has real structure instead of flat text:
     - a HEADER BAND (the jersey strip) so team identity hits first
     - a CAPTAIN block with a kicker label, set on its own tint panel
       so the hero of the card actually reads as the hero
     - a labelled BENCH of chips so the roster reads as teammates
   All pixel-font safe: basiic for display, no bold, colour = emphasis.
   ================================================================ */
body.--post .lineup{
  display: flex;
  align-items: stretch;      /* all three columns full height so the mercury
                                seam runs edge to edge... the dead-space fix is
                                at the CONTENT level (below), not here. */
  gap: 0;
  margin: 1.5em 0;
  border: 2px solid #8a7a9a;
  box-shadow: 4px 4px 0 rgba(90,50,104,.22);
  background: #fffdf9;
  overflow: hidden;
}

/* ---- a dugout ---- */
body.--post .team{
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  text-align: center;
  min-width: 0;
}
body.--post .team-day{  background: #fdf6e3; }
body.--post .team-night{ background: #efecf6; }

/* HEADER BAND — the jersey strip. bold, full-bleed, team colour. */
body.--post .team-name{
  margin: 0;
  padding: 7px 8px;
  font-family: basiic, monospace;
  font-size: 12px;
  letter-spacing: 1.5px;
  line-height: 1;
  color: #fffdf9;            /* light type ON the colour band */
}
body.--post .team-day .team-name{  background: #c9931f; }
body.--post .team-night .team-name{ background: #6b5f92; }

/* the body of the dugout. content is a COMPACT block, centered
   vertically... any extra height from matching the mercury column
   becomes even breathing room top and bottom, NOT stretched gaps
   between the captain and the bench. this is the real "big box
   holding small words" fix. */
body.--post .team-body{
  padding: 10px 12px 12px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 8px;
  flex: 1;
}

/* CAPTAIN — the hero. its own tinted panel with a kicker over the name. */
body.--post .team-cap{
  margin: 0;
  padding: 7px 6px;
  border: 1px solid;
  border-radius: 3px;
}
body.--post .team-day .team-cap{  border-color: #e6c266; background: rgba(224,176,58,.16); }
body.--post .team-night .team-cap{ border-color: #b3a9dc; background: rgba(154,143,201,.18); }
body.--post .team-cap .cap-kicker{
  display: block;
  font-family: basiic, monospace;
  font-size: 9px;
  letter-spacing: 2px;
  line-height: 1;
  margin-bottom: 5px;
}
body.--post .team-cap .cap-name{
  display: block;
  font-family: basiic, monospace;
  font-size: 17px;           /* the hero of the card, but not blown up */
  line-height: 1.15;
}
body.--post .team-day .cap-kicker{ color: #b57e12; }
body.--post .team-day .cap-name{   color: #a9750f; }
body.--post .team-night .cap-kicker{ color: #5a4e86; }
body.--post .team-night .cap-name{   color: #544883; }

/* BENCH label over the chips */
body.--post .team-bench-label{
  font-family: basiic, monospace;
  font-size: 9px;
  letter-spacing: 2px;
  line-height: 1;
  margin: 0;
}
body.--post .team-day .team-bench-label{  color: #b57e12; }
body.--post .team-night .team-bench-label{ color: #5a4e86; }

/* players: inline compact chips */
body.--post .team-roster{
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
}
body.--post .team-roster li{
  margin: 0;
  padding: 3px 11px;
  font-size: 15px;
  line-height: 1.3;
  border: 1px solid;
  border-radius: 2px;
}
body.--post .team-day .team-roster li{
  border-color: #e0b03a;
  background: rgba(224,176,58,.14);
  color: #8a5e0a;
}
body.--post .team-night .team-roster li{
  border-color: #9a8fc9;
  background: rgba(154,143,201,.16);
  color: #473c72;
}

/* ---- MERCURY: the switch-hitter, standing BETWEEN the dugouts ----
   a narrow center column with the gold->violet gradient running
   VERTICALLY down its spine, so it literally bridges warm team to
   cool team. no longer a dashed footnote. */
body.--post .team-switch{
  flex: 0 0 138px;           /* a touch wider so the note wraps shorter */
  margin: 0;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  text-align: center;
  color: #4a3f63;
  background: linear-gradient(180deg, rgba(224,176,58,.22), rgba(154,143,201,.26));
  border-left: 2px solid #c9931f;
  border-right: 2px solid #6b5f92;
}
body.--post .team-switch .switch-glyph{
  font-family: basiic, monospace;
  font-size: 22px;
  line-height: 1;
  color: #5A3268;
}
/* the mercury SYMBOL for the switch-hitter. NOT basiic... basiic is a pixel
   display face with no astrological glyphs (it would tofu), so this uses a
   font that actually has the astro block. sized to headline the seam column.
   it is also a .gloss-glyph, so hovering it pops mercury's definition. */
body.--post .team-switch .switch-planet{
  font-family: "Raleway", sans-serif;
  font-size: 26px;
  line-height: 1;
  color: #5A3268;
}
body.--post .team-switch .switch-name{
  font-family: basiic, monospace;
  font-size: 13px;
  letter-spacing: 1px;
  line-height: 1;
  color: #5A3268;
}
body.--post .team-switch .switch-role{
  font-family: basiic, monospace;
  font-size: 8px;
  letter-spacing: 1.5px;
  line-height: 1;
  color: #7a6a8f;
}
body.--post .team-switch .switch-note{
  font-size: 13px;
  line-height: 1.4;
  color: #4a3f63;
}

/* ---- MOBILE: stack the board. mercury drops to the SEAM between
   the two stacked dugouts, gradient turns horizontal again so it
   still reads as the bridge. ---- */
@media (max-width: 1249px){
  body.--post .lineup{ flex-direction: column; }
  body.--post .team-switch{
    flex: 1 1 auto;
    background: linear-gradient(90deg, rgba(224,176,58,.22), rgba(154,143,201,.26));
    border-left: 0;
    border-right: 0;
    border-top: 2px solid #c9931f;
    border-bottom: 2px solid #6b5f92;
    padding: 12px 14px;
  }
  body.--post .team-switch .switch-note{ font-size: 14px; }
}


/* ================================================================
   18.7 BLOG GLOSSARY  —  hover-to-define terms in post copy
   ----------------------------------------------------------------
   Sibling of the zodiac key popup (#zk-pop / js/zodiackey.js).
   Two halves:

     .gloss      the inline MARK on a defined term. present on
                 EVERY screen size... it is just a dotted underline
                 that says "there is more here." on mobile that is
                 all it is (no hover, by design).
     #gloss-pop  the popup itself. desktop only, placed at the
                 cursor by js/glossary.js. mirrors #zk-pop so the
                 two teaching popups feel like one system.

   Appended at the very END so it wins clean over the duplicate
   swamp above. No bold on the pixel body font anywhere here.
   ================================================================ */

/* THE INLINE MARK.
   a defined term reads in site PINK with a matching dotted underline,
   so it looks like a live, hoverable astrology word... the same pink
   the glyphs and the popup use. one emphasis colour across the whole
   glossary system. the dotted underline still says "hoverable" the
   way a dictionary word does. */
.post-content .gloss{
  color: #d81888;                      /* site pink, at rest */
  border-bottom: 1px dotted #e58bbd;   /* soft pink underline to match */
  cursor: help;                        /* desktop affordance; harmless on touch */
  text-decoration: none;
}
/* on the desktop, a hovered term brightens to confirm the pop. */
@media (hover: hover){
  .post-content .gloss:hover{
    color: #ff51b7;
    border-bottom-color: #ff51b7;
  }
}
/* MOBILE / touch: keep the mark, kill the interactivity.
   js/glossary.js already bails below 1250 + no-hover, so nothing
   is wired up... this just makes the cursor honest. */
@media (max-width: 1249px){
  .post-content .gloss{ cursor: text; }
}

/* THE PLANET GLYPH MARK.
   a bare astrology symbol inline in the copy (sun/moon/mercury...),
   rendered as the site's usual unicode glyph, in site PINK everywhere
   it appears... including inside the roster. the team boxes carry the
   day/night meaning (gold/violet bands + borders); the GLYPHS stay
   pink so they read as one consistent "hoverable astrology mark"
   across the whole site. no bold... these are unicode, not the pixel
   font, so weight is safe, and pink is the site's emphasis language. */
.post-content .gloss-glyph{
  color: #d81888;                      /* site pink, everywhere */
  cursor: help;
  font-style: normal;
  padding: 0 1px;
  font-size: 1.08em;
  line-height: 1;
}
@media (hover: hover){
  .post-content .gloss-glyph:hover{ color: #ff51b7; }
}
@media (max-width: 1249px){
  .post-content .gloss-glyph{ cursor: text; }
}

/* THE POPUP.
   built to match #zk-pop so the site has ONE popup language, not
   two. same dark ground, same pink border, same rainyhearts, same
   fixed/pointer-events-none/opacity-fade behaviour. the ONE
   difference: this one wraps (definitions are sentences, the
   zodiac popup was three short lines), so it has a max-width and
   drops the white-space:nowrap. */
#gloss-pop{
  position: fixed;                    /* placed by JS at the cursor */
  z-index: 9999;
  pointer-events: none;               /* never eat a click, never trap the cursor */

  font-family: "rainyhearts", monospace;
  font-size: 17px;                    /* matches #zk-pop, comfortable to read */
  line-height: 1.55;
  text-align: left;
  color: #ffffff;
  background: #1a0d16;
  border: 2px solid #d81888;
  border-radius: 4px;
  padding: 8px 11px;
  box-shadow: 0 3px 0 rgba(0,0,0,.55);

  max-width: 300px;                   /* a definition is a sentence... let it wrap */
  white-space: normal;

  opacity: 0;
  transition: opacity .12s ease-out;
}
#gloss-pop.on{ opacity: 1; }

/* the hovered word, echoed as the popup's heading, in site pink.
   same role as .zp-sign in the zodiac popup. */
#gloss-pop .gp-term{
  display: block;
  color: #ff51b7;
  letter-spacing: .5px;
  margin-bottom: 2px;
}
/* the definition line, quieter than the heading. mirrors .zp-ruler. */
#gloss-pop .gp-def{
  display: block;
  color: #C7C4D9;
}

/* ---- PLANET VARIANT: glyph beside text ----
   when a PLANET is hovered, js adds .has-glyph and the popup becomes
   a flex row: big glyph on the left, name/def/keywords on the right.
   this mirrors #zk-pop exactly (a sprite beside its text), so the two
   teaching popups are one visual system. */
#gloss-pop.has-glyph{
  display: flex;
  align-items: center;
  gap: 11px;
  max-width: 320px;                   /* a hair wider to fit the glyph column */
}
#gloss-pop .gp-glyph{
  flex: 0 0 auto;
  display: block;
  color: #ff51b7;                     /* the featured glyph, in site pink */
  line-height: 1;
  text-align: center;
}
/* the TEXT-CHAR glyph (today's default, no sprite yet).
   unicode astrology symbol, shown big. no pixel font here so scaling
   the SIZE is safe... this is a vector glyph, not sprite art. */
#gloss-pop .gp-glyph-char{
  font-family: "Raleway", "rainyhearts", monospace; /* a face with real astro glyphs */
  font-size: 30px;
  width: 34px;
}
/* the SPRITE glyph (drops in when Ashley's pixel art exists).
   32 = a clean 2x on a 16px sprite... integer, no shimmer. same rule
   the zodiac popup uses for its .zp-glyph. */
#gloss-pop .gp-glyph-sprite{
  width: 32px;
  height: 32px;
  box-sizing: content-box;            /* border-box would silently upscale it */
  image-rendering: pixelated;
  max-width: none;                    /* defeat the global img clamp */
  border: 0;
}
#gloss-pop .gp-text{ display: block; }
/* the Greek god name, a quiet italic subtitle under the planet name.
   Hellenistic flavour... Hermes under Mercury, Zeus under Jupiter. */
#gloss-pop .gp-greek{
  display: block;
  margin: -1px 0 3px 0;
  color: #e58bbd;
  font-style: italic;
  font-size: 14px;
  letter-spacing: .3px;
}
/* the keyword trio under the def, quietest line. mirrors .zp-line. */
#gloss-pop .gp-keys{
  display: block;
  margin-top: 3px;
  color: #9a90b5;
  font-size: 14px;
  letter-spacing: .3px;
}

@media (prefers-reduced-motion: reduce){
  #gloss-pop{ transition: none; }
}

/* =====================================================================
   THE JUNK-DUMP GAME BOY  —  playable easter egg          (gameboy.js)
   =====================================================================
   NOT the same object as the left-column .gb-shell "divination mirror".
   That one is decor: a gif showing through a screen hole, pointer-events
   off. THIS one is the real toy — a tiny GBC on the junk wall that boots
   a PLAYABLE pokemon battle when clicked. Different asset, different
   prefix (.gbc-* not .gb-*), no shared rules. They do not collide.

   Downstairs dialect: no win98 chrome, no teal double borders, no
   rounded corners. It is a loose object on a loose wall — the icon has
   no frame and no caption. Spot it, click it. Miss it, it's decor.

   Screen registration measured from the frame asset (486x805):
     hole x100..382, y71..306  =>  left 20.6%  top 8.8%
                                   width 58.0%  height 29.2%
   Percentages keep the game locked in the bezel at any console size.
   ===================================================================== */

/* the icon as it sits on the wall — a plain .junk-item, no chrome. The
   only tell that it is special is the cursor and a tiny lean on hover. */
.junk-item.gbc-egg{
  cursor: pointer;
}
.junk-item.gbc-egg img{
  image-rendering: pixelated;   /* it is a 50x50 pixel gif — never soften */
  transition: transform .12s ease;
}
.junk-item.gbc-egg:hover img{
  transform: translateY(-2px) rotate(-3deg);
}

/* ---- MAXWELL: the cat easter egg ----
   At rest he is a small, calm loaf on the wall (a photo, easy to miss).
   Click and he takes over the screen, spinning, because that is what
   Maxwell does. Click him / the backdrop / Esc and he loafs again. */
.junk-item.maxwell-egg{
  cursor: pointer;
  line-height: 0;
}
.junk-item.maxwell-egg img{
  width: 66px;                  /* small on the wall — a napping cat */
  height: auto;
  transition: transform .12s ease;
  filter: saturate(.95);
}
.junk-item.maxwell-egg:hover img{
  transform: translateY(-2px) scale(1.04);
}

/* the spotlight room he spins up in — same grammar as the gameboy overlay */
.maxwell-overlay{
  position: fixed;
  inset: 0;
  z-index: 4000;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 24px;
  /* the console is a fixed 635x1164 and is never scaled, so on short
     screens the overlay scrolls rather than squashing the console. */
  overflow: auto;
  background: rgba(10, 4, 28, .82);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  cursor: pointer;
}
.maxwell-overlay.is-on{ display: flex; }
.maxwell-overlay img{
  width: min(60vw, 420px);
  height: auto;
  image-rendering: auto;
  filter: drop-shadow(0 8px 24px rgba(0,0,0,.5));
  animation: maxwell-pop .3s cubic-bezier(.2,1.3,.5,1) both;
}
@keyframes maxwell-pop{
  0%   { transform: scale(.4); opacity: 0; }
  100% { transform: scale(1);  opacity: 1; }
}
.maxwell-hint{
  position: absolute;
  bottom: 6%;
  left: 0; right: 0;
  text-align: center;
  font-family: "basiic", monospace;
  font-size: 12px;
  letter-spacing: 1px;
  color: #f2c0bf;
  text-shadow: 1px 1px 0 #2a1050;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce){
  .maxwell-overlay img{ animation: none; }
}

/* ---- THE OVERLAY: the dim room the console powers on in ---- */
.gbc-overlay{
  position: fixed;
  inset: 0;
  z-index: 4000;                /* above everything; it is a takeover */
  display: none;                /* .is-on flips this */
  align-items: center;
  justify-content: center;
  padding: 24px;
  /* the console is a fixed 635x1164 and is never scaled, so on short
     screens the overlay scrolls rather than squashing the console. */
  overflow: auto;
  background: rgba(10, 4, 28, .82);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
.gbc-overlay.is-on{ display: flex; }

/* body scroll lock while the console is on */
body.gbc-locked{ overflow: hidden; }

/* ---- THE CONSOLE ----
   FIXED PIXEL SIZE, NEVER SCALED.
   The frame art (635x1164) is sized so its own screen window is exactly
   320x288 — the game's native board. Because the game is a fixed 320x288
   block of CSS pixels, the console must NOT be scaled: shrinking the shell
   while the game stays 320x288 is what made the game overflow the bezel.
   On short viewports we let the overlay scroll instead of resizing. */
.gbc-console{
  position: relative;
  width: 635px;
  height: 1164px;
  flex: 0 0 auto;
  animation: gbc-poweron .28s cubic-bezier(.2, 1.3, .5, 1) both;
}
@keyframes gbc-poweron{
  0%   { transform: scale(.35) translateY(20px); opacity: 0; }
  100% { transform: scale(1)   translateY(0);    opacity: 1; }
}

/* the console body png sits on top; the game shows through its hole */
.gbc-console .gbc-body{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 2;
  pointer-events: none;         /* clicks fall through to the game */
  image-rendering: pixelated;
}

/* THE SCREEN — the cutout the live game renders inside */
.gbc-screen{
  position: absolute;
  z-index: 1;
  /* the frame's window, measured: x162..481, y176..463 on a 635x1164 art.
     At the console's fixed size these percentages resolve to exactly
     320x288 CSS px, so the game sits in it 1:1 with no scaling at all. */
  left: 25.5118%;
  top: 15.1203%;
  width: 50.3937%;
  height: 24.7423%;
  overflow: hidden;
  background: #bbac58;
  display: block;
}

/* ---- ISOLATE THE VENDORED GAME FROM THE SITE'S GLOBAL STYLES ----
   VERIFIED against the vanilla pokemon-js source running standalone.
   Two site-wide rules leak in and are the whole cause of the "crushed"
   menu and level number:

   1. box-sizing:border-box (the reset at the top of this file).
      pokemon.css is authored for content-box. The battle menu is
      width:7.4em + 0.8em padding. Under content-box that is 192x92 (what
      vanilla renders); under border-box the padding is crammed INSIDE,
      giving 148x54 — so FIGHT and PKMN no longer fit on one line and PKMN
      collapses to zero width and prints on top of FIGHT. Same collapse
      squashes the level-wrap and the HP row.

   2. letter-spacing (set site-wide). It widens every word in the game
      (FIGHT 80px -> 87px) and pushes text out of its fixed-width boxes.

   Restoring the browser defaults for everything inside #pkmn-game makes
   the game render pixel-identical to the standalone original. */
.gbc-screen #pkmn-game,
.gbc-screen #pkmn-game *,
.gbc-screen #pkmn-game *::before,
.gbc-screen #pkmn-game *::after{
  box-sizing: content-box;
  letter-spacing: normal;
  word-spacing: normal;
  text-transform: none;
  text-indent: 0;
}

/* TASTE TWEAK (Ashley): nudge the battle menu text up 15px.
   The menu box is anchored by `bottom`, so touching its padding resizes
   the box and drags the ornate border with it. Instead leave the box
   byte-identical to vanilla and offset ONLY the button text. */
.gbc-screen #pkmn-game .windows .window.menu .button{
  position: relative;
  top: -8px;
}

/* THE GAME RUNS AT NATIVE SIZE — NOTHING IS RESIZED.
   pokemon.css is tuned for a 320x288 board at font-size:20px: the 7.4em
   menu, the 3.2em text box, the level offsets all assume it. Squeezing the
   board into a smaller hole is what crushed the menu and the level numbers.
   So the FRAME's hole was cut to 320x288 instead, and the game is left
   completely alone — same code, same CSS, same size it was built for.

   The one thing that must be neutralised is inherited TEXT metrics: the
   site sets letter-spacing:1.3px, which inherits in and widens every word
   (FIGHT 80px -> 87px), overflowing the menu and text boxes. Reset the
   text properties the game never set for itself. */
.gbc-screen #pkmn-game,
.gbc-screen #pkmn-game *{
  letter-spacing: normal;
  word-spacing: normal;
  text-transform: none;
  text-indent: 0;
}
.gbc-screen #pkmn-game{
  width: 100%;
  height: 100%;
  background: #bbac58;
  overflow: hidden;
  font-size: 20px;              /* the game's authored root. do not change. */
}
.gbc-screen #pkmn-game #pokemon{
  margin: 0;                    /* it is exactly 16em x 14.4em = 320x288 */
}

/* booting shimmer over the screen before the game paints */
.gbc-screen.is-booting::after{
  content: "";
  position: absolute;
  inset: 0;
  background:
    repeating-linear-gradient(
      0deg,
      rgba(0,0,0,.10) 0 2px,
      transparent 2px 4px
    );
  animation: gbc-scan .6s linear infinite;
}
@keyframes gbc-scan{
  from{ background-position: 0 0; }
  to  { background-position: 0 4px; }
}

/* if the cartridge won't read */
.gbc-oops{
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-family: "Press Start 2P", monospace;
  font-size: 8px;
  line-height: 1.8;
  color: #2f3622;
  background: #9bbc0f;
}

/* ---- THE POWER LIGHT: the off switch, sitting over the real one ----
   On the frame the POWER dot is up the left side. A small hotspot there
   reads as "press power to turn it off" without a foreign close button. */
.gbc-power{
  position: absolute;
  z-index: 3;
  /* Centred on the DMG's BATTERY led, measured from gbc_frame.png
     (635x1164): the red dot occupies x79..99, y258..280 — centre (89,269).
     The glow is drawn ~1.9x the led so the halo spills past it naturally.
     left = (89 - 20)/635, top = (269 - 21.5)/1164 */
  left: 10.874%;
  top: 21.233%;
  width: 6.283%;
  height: 3.754%;
  border-radius: 50%;
  cursor: pointer;
  background: radial-gradient(circle at 50% 40%,
              rgba(255,120,120,.95), rgba(200,30,30,.5) 60%, transparent 72%);
  box-shadow: 0 0 6px 2px rgba(255,70,70,.6);
  animation: gbc-blink 1.6s ease-in-out infinite;
}
@keyframes gbc-blink{
  0%, 100% { opacity: .55; }
  50%      { opacity: 1; }
}
.gbc-power:hover{ transform: scale(1.15); }

/* a whisper-quiet hint under the console, so nobody is trapped */
.gbc-hint{
  position: absolute;
  left: 0; right: 0;
  bottom: -26px;
  text-align: center;
  font-family: "Press Start 2P", monospace;
  font-size: 7px;
  letter-spacing: .5px;
  color: #cbb8f0;
  opacity: .7;
}

@media (prefers-reduced-motion: reduce){
  .gbc-console{ animation: none; }
  .gbc-power{ animation: none; }
  .gbc-screen.is-booting::after{ animation: none; }
}

/* ---- MOBILE: the junk dump is cut on mobile, so the icon never shows.
   Nothing to do here. If the overlay is ever opened on a narrow screen
   (it won't be, from the wall), width-led sizing keeps it on-screen. --- */
@media (max-width: 640px){
  .gbc-console{ height: auto; width: min(92vw, 400px); }
}

/* ================================================================
   19. POST PAGE ADDITIONS  -  july 19, 2026
   ----------------------------------------------------------------
   Appended at the END of the file on purpose. This sheet has
   pervasive duplicate selectors; anything added mid-file loses to
   a later copy of itself. Bottom of the file always wins.
   ================================================================ */

/* ---- 19.1 The glyph background on post pages ----
   body.--post was never given the tiled background the other pages
   carry, so a post rendered on flat browser white. Everything else
   about the post template was deliberate; this was just an omission.

   bg7 over #0b0620, SCROLLING, exactly as the frameless pages declare
   it (.emo-wrap-page / .cr-wrap-page / links). Not background-attachment
   fixed: a pinned tile would make posts the one page type on the site
   where the background behaves differently, and the .post card is
   solid #FFFFFB anyway, so nothing shows through behind the text. The
   readability rule that made those wraps opaque is already satisfied
   by the card; it does not also demand a still background. */
body.--post{
  background: url("images/backgrounds/bg7.png") repeat, #0b0620;
}

/* ---- 19.2 The "last updated" line ----
   A PILLAR post is a living document: it gets revised as the teaching
   sharpens, and a reader deserves to know the difference between a
   2026 post last touched in 2026 and one last touched three years
   later. The published date stays put forever; this line moves.

   RULE: only substantive edits earn a bump. Typo fixes do not.
   Journal / diary / thought-dump posts get NO updated line at all —
   those are a timestamp of a moment, not a document under revision.

   Set in basiic at the 12.5px floor like the meta row above it, but
   in the grey of .post-date so it reads as secondary to the chips. */
body.--post .post-updated-row{
  margin-top: 6px;
}
body.--post .post-updated{
  color: #7a7091;
  font-style: normal;   /* rainyhearts and basiic have no italic cut —
                           a synthetic slant on a bitmap face is a smear,
                           the same reason nothing here is bolded. */
}

/* ---- 19.3 home-return on a post ----
   The button is already styled once, globally, up in section 16 — this
   only handles the fact that on a post it sits under the white .post
   card rather than on a wrap panel, so it needs its own breathing room
   above and a little more below before the page ends. */
body.--post .home-return{
  margin: 4px auto 30px auto;
}

/* ================================================================
   20. POST READABILITY PASS  -  july 19, 2026
   ----------------------------------------------------------------
   Appended at the END, as always: this sheet has duplicate selectors
   throughout and only the last copy wins.

   The problem this solves is NOT that the post has too much dead
   space. It is that the space is UNDIFFERENTIATED — paragraph gaps,
   heading gaps and section gaps were all within a hair of each
   other, so the page read as one long undivided column and the eye
   had nothing to grab. Accessible reading needs a visible HIERARCHY
   of gaps, not uniformly smaller ones.
   ================================================================ */

/* ---- 20.1 Measure ----
   17px rainyhearts across the full 740px column runs ~95 characters.
   The comfortable range for sustained reading is 45-75. This is the
   single biggest readability win available on the page and it costs
   one line: cap the TEXT, not the card. Figures, the lineup and the
   CTA deliberately stay full-width, so the wide elements still use
   the whole column and the page keeps its rhythm. */
body.--post .post-content > p,
body.--post .post-content > ul,
body.--post .post-content > h2{
  max-width: 680px;   /* px, not ch: `ch` measures the "0" glyph, and
                         rainyhearts is a narrow pixel face, so 62ch
                         collapsed to ~430px — far too tight. A hard px
                         cap is stable no matter which font loads. */
}

/* ---- 20.2 A real gap hierarchy ----
   was: p 1.15em / h2 1.6em above. Those are too close to read as
   different kinds of break. Now a heading opens a clearly new
   section, and the dashed rule it already carries gets room to
   actually separate rather than crowding the line above it. */
body.--post .post-content h2{
  margin: 2.4em 0 .75em 0;
}
/* the first heading should not shove off the lede */
body.--post .post-content h2:first-of-type{ margin-top: 1.8em; }

/* ---- 20.3 The lede gets air under it ----
   It is the hook. It should sit alone for a beat before the body
   starts, the way a pull-quote does. */
body.--post .post-content .post-lede{
  margin-bottom: 1.6em;
  padding-bottom: .75em;
  border-bottom: 1px dashed #e2dcec;
}

/* ---- 20.4 The diagram had NO styling at all ----
   .post-figure and .post-diagram were used in the markup of both
   posts but never defined anywhere in this sheet, so the SVG
   rendered at its intrinsic size with browser-default figure
   margins (40px left AND right, indenting it out of alignment with
   the text). This is the actual dead-space bug on the page. */
body.--post .post-figure{
  margin: 2em 0;
  padding: 0;
}
body.--post .post-diagram{
  display: block;
  width: 100%;
  max-width: 560px;
  height: auto;
  margin: 0 auto;
  image-rendering: auto;   /* it is an SVG, not pixel art — let it smooth */
}
body.--post .post-figure figcaption{
  max-width: 560px;
  margin: .9em auto 0 auto;
  font-size: 15px;
  line-height: 1.55;
  color: #6E6278;
  text-align: center;
}

/* ---- 20.5 Let the big blocks breathe ----
   The lineup and the CTA are the two visual anchors of the post.
   They were sitting at the same 1.15em rhythm as an ordinary
   paragraph, which flattened them into the text flow. */
body.--post .lineup{ margin: 2.2em 0; }
body.--post .post-cta{ margin-top: 2.6em; }

/* ---- 20.6 Mobile ----
   ch-capping is a no-op on a narrow screen (the viewport is already
   the limit), but the vertical rhythm should tighten: on a phone,
   generous section gaps turn into scrolling past nothing. */
@media (max-width: 1249px){
  body.--post .post-content h2{ margin: 1.9em 0 .6em 0; }
  body.--post .post-figure{ margin: 1.6em 0; }
  body.--post .lineup{ margin: 1.7em 0; }
  body.--post .post-cta{ margin-top: 2em; }
  body.--post .post-content .post-lede{ margin-bottom: 1.3em; }
}


/* =====================================================================
   POST PAGES — DRIFTING BG7 BACKGROUND
   ---------------------------------------------------------------------
   Same treatment as the homepage glyph tile (see body.--home), but with
   bg7.png and scoped to body.--post so it cannot touch the homepage.

   Same two rules that made the homepage version actually move:
     - LONGHAND only, never the `background` shorthand. The shorthand
       resets background-position every frame and the animation never
       wins.
     - background-attachment MUST be `scroll`, not `fixed`. `fixed`
       anchors the layer to the viewport and pins the position dead.

   TILE MATH: bg7.png is 304 x 312. Travel has to be a whole multiple of
   the tile in BOTH axes or the loop visibly jumps at the wrap.
     -1520px = 5 tiles left, -1248px = 4 tiles up.
   Same up-and-left 5:4 lean as the homepage. Exact multiples, no jump.
   ===================================================================== */
body.--post{
  background-image:
    url("images/backgrounds/bg7.png"),
    linear-gradient(#cce4ff, rgba(250, 250, 250, 0)) !important;
  background-repeat: repeat, no-repeat !important;
  background-color: transparent !important;
  background-attachment: scroll, scroll !important;
  animation: bg7slide 200s linear infinite !important;
}

@keyframes bg7slide{
  0%   { background-position: 0 0, 0 0; }
  100% { background-position: -1520px -1248px, 0 0; }
}

@media (prefers-reduced-motion: reduce){
  body.--post{ animation: none !important; }
}

/* MOBILE: tile stays, drift stops. An animating background on a phone is
   scroll-jank and battery cost for something nobody is looking at. */
@media (max-width: 1249px){
  body.--post{
    animation: none !important;
    background-attachment: scroll, scroll !important;
  }
}


/* =====================================================================
   HOMEPAGE LATEST-POSTS LIST  —  divider between entries
   ---------------------------------------------------------------------
   The blog window on the canvas holds TWO posts now instead of one
   placeholder. It stays ONE .post-card (that box is height:100% and
   fills its grid slot; stacking two cards would make them fight over
   the same space and push the canvas taller). So the posts are rows
   inside the single window, and this is the rule between them.

   Appended at END per the house rule on duplicate selectors.
   ===================================================================== */
.dz-blog .post-sep{
  border: 0;
  border-top: 1px dashed var(--rule, #ccc4dc);
  margin: 14px 0;
  opacity: .8;
}

/* The two entries need to share the fixed height of the window without
   spilling. Let the body distribute the space and scroll only if it ever
   genuinely overflows, rather than pushing the card taller. */
.dz-blog .post-body{
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0;
  min-height: 0;
  overflow: auto;
}

/* the tag chips on the homepage list are NOT links yet (there is no tag
   archive until there are ~10 posts). they are <span> here, so give them
   the chip look without the pointer/underline affordance of an <a>. */
.dz-blog .post-meta .post-tag{
  cursor: default;
  text-decoration: none;
}
/* ================================================================
   CANVAS RIGHT COLUMN — tighten book-a-reading -> latest posts
   ----------------------------------------------------------------
   Ashley: "close the gap between book a reading and latest posts,
   move latest posts up."

   The booking window was set to align-self:stretch so it filled the
   teal UNDER offerings. Side effect: it also stretched to the FULL
   height of row 2 (which is tall because course.exe on the left is
   tall), leaving a dead teal band INSIDE the booking window, under
   the yellow button, before latest_posts.exe began a row below.

   Fix, appended at END so it wins over the stretch rules above:
     - booking hugs its own content (no more internal teal band)
     - latest_posts (blog) pulls UP with a negative top margin to sit
       snug under the booking window instead of a full row lower.
   Left column (course/counter/flair) is untouched by this.
   ================================================================ */
@media (min-width: 1250px){
  .dz-booking{
    align-self: start !important;   /* stop filling the tall course row;
                                       end right under the book button */
  }
  .dz-booking .booking-window,
  .dz-booking .booking-col,
  .dz-booking > *{
    height: auto !important;        /* let the window size to its content */
  }
  .dz-blog{
    align-self: start !important;   /* hug content at the top of its row... */
    margin-top: -92px;              /* ...then rise into the teal the booking
                                       box just gave back, sitting ~16px under
                                       book_a_reading.exe like a clean stack.
                                       (row 3 started ~92px below booking's
                                       real content bottom; this closes it.) */
  }
  .dz-blog .post-card{
    height: auto !important;        /* card sizes to its posts, no forced fill */
    min-height: 0 !important;
  }
}