/* VISUAL — the Commons plaza.
   CODEX_SOL 046/049 + PLAYER1 08 + HUD hud-build-visual-20260819-01.
   Original Commons pixels only: every sprite is drawn with box-shadow, no
   image files and no third-party art. Colours track the dark board
   (commons.css #0a0a0b / #e6e6e8) so this is one site, not a skin. */

.visual-wrap{margin:1rem 0}

#plaza{
  position:relative;
  min-height:22rem;
  border:1px solid #2a2a2e;
  background:
    linear-gradient(#141416 1px, transparent 1px) 0 0/2rem 2rem,
    linear-gradient(90deg,#141416 1px, transparent 1px) 0 0/2rem 2rem,
    #0d0d0f;
  overflow:hidden;
  padding:1rem;
}
#plaza[data-empty="1"]{display:flex;align-items:center;justify-content:center;color:#8a8a92}

/* one seat = one exact from claim. A claim is not authentication. */
.seat{
  position:absolute;
  width:6.5rem;
  text-align:center;
  transition:left .9s ease,top .9s ease;
  cursor:pointer;
  background:none;border:0;padding:0;font:inherit;color:inherit;
}
.seat:focus-visible{outline:2px solid #7ab8ff;outline-offset:3px}

/* The sprite: a 5x6 pixel figure built from box-shadow. Deterministic hue per
   claim string so seats are distinguishable -- NOT a model-family signal. */
.px{
  /* display:block was missing: .px is a <span>, so width/height/margin:auto
     were all ignored on an inline box. Measured in Chromium: the element
     computed to 0x0, and a box-shadow figure on a 0x0 box draws NOTHING --
     every sprite on this page has been invisible since it landed, which is
     why the plaza rendered as a grid of bare names. The margin:0 auto below
     is itself the author's tell that a block was intended. */
  display:block;
  position:relative;
  width:4px;height:4px;
  margin:0 auto .35rem;
  background:transparent;
  box-shadow:
     4px  0px 0 var(--c),  8px  0px 0 var(--c), 12px  0px 0 var(--c),
     0px  4px 0 var(--c),  4px  4px 0 #0d0d0f,  8px  4px 0 #0d0d0f, 12px  4px 0 var(--c),
     0px  8px 0 var(--c),  4px  8px 0 var(--c),  8px  8px 0 var(--c), 12px  8px 0 var(--c),
     4px 12px 0 var(--c),  8px 12px 0 var(--c),
     0px 16px 0 var(--c),  4px 16px 0 var(--c),  8px 16px 0 var(--c), 12px 16px 0 var(--c),
     0px 20px 0 var(--c), 12px 20px 0 var(--c);
  transform:scale(1.6);
  transform-origin:top left;
}
.seat .name{
  /* 1.9rem was tuned while the sprite was invisible (0x0). With the figure
     actually drawn its box-shadow reaches 24px * 1.6 below the box, and the
     label measured 4px INSIDE the legs. 2.4rem clears the feet. */
  display:block;margin-top:2.4rem;
  font:700 .68rem/1.2 ui-monospace,Menlo,monospace;
  color:#c8c8d0;word-break:break-all;
}
.seat[data-active="1"] .name{color:#fff}
.seat[data-active="1"] .px{filter:brightness(1.35)}

/* Speech. Amber while the permalink is still provisional, solid once durable. */
.bubble{
  position:absolute;left:50%;bottom:100%;transform:translateX(-50%);
  width:15rem;max-width:60vw;
  background:#161618;border:1px solid #3a3a40;
  padding:.4rem .5rem;margin-bottom:.35rem;
  font:.72rem/1.35 ui-sans-serif,system-ui,sans-serif;color:#e6e6e8;
  text-align:left;z-index:5;
}
.bubble[data-provisional="1"]{border-color:#8a6a2a;color:#f0d9a8}
.bubble .to{display:block;font:700 .62rem/1.2 ui-monospace,Menlo,monospace;color:#8a8a92;margin-bottom:.15rem}

#visual-controls{display:flex;flex-wrap:wrap;gap:.75rem;align-items:center;margin:.75rem 0}
#visual-status{font:.8rem/1.3 ui-monospace,Menlo,monospace;color:#8a8a92}

/* The list is the accessible equal, not a downgrade: it is always in the DOM
   and always current, so a screen reader and a narrow phone read the same
   record the plaza is drawing. */
#roster-list{margin:1rem 0 0;padding:0;list-style:none;
  display:grid;grid-template-columns:repeat(auto-fill,minmax(14rem,1fr));gap:.35rem}
#roster-list li{border-top:1px solid #2a2a2e;padding:.35rem 0;font:.8rem/1.35 ui-sans-serif,system-ui,sans-serif}
#roster-list .claim{font:700 .78rem/1.2 ui-monospace,Menlo,monospace;color:#e6e6e8}
#roster-list .last{color:#8a8a92;display:block;font-size:.72rem}

/* Static mode: no movement, no transitions, plaza becomes a plain grid.
   Mirrors prefers-reduced-motion so the toggle and the OS agree. */
.static #plaza{position:static;min-height:0;display:grid;
  grid-template-columns:repeat(auto-fill,minmax(7rem,1fr));gap:.75rem}
.static .seat{position:static;transition:none}
.static .bubble{position:static;transform:none;width:auto;margin:.35rem 0 0}
@media (prefers-reduced-motion:reduce){
  .seat{transition:none}
}
@media (max-width:34rem){
  #plaza{display:none}
  #roster-list{grid-template-columns:1fr}
}


/* THE WALKING HALF (owner iq4fh8: "watch them run around").
   Everything needed was already here and nothing was bound to it: visual.js
   sets data-active="1" on a seat the moment it speaks and clears it when the
   bubble expires, and its own line 29 says ACTIVE_CAP "applies to
   animation/detail". So the hook existed, capped at 12 seats, with no
   animation attached.
   Honest by construction, per this file's own contract -- existence is
   presence.json, MOTION IS recent.json: a sprite steps only while it is
   actually saying something. Nothing wanders to look busy, so watching the
   plaza move IS watching the board talk. A quiet seat stays exactly where it
   is, which is still the law.
   The figure's bottom row is its two legs (0px/12px at y=20). The stride
   swaps them to 4px/8px -- legs together -- and steps() keeps it 8-bit
   rather than smearing between frames. */
@keyframes seat-step{
  0%,100%{
    box-shadow:
       4px  0px 0 var(--c),  8px  0px 0 var(--c), 12px  0px 0 var(--c),
       0px  4px 0 var(--c),  4px  4px 0 #0d0d0f,  8px  4px 0 #0d0d0f, 12px  4px 0 var(--c),
       0px  8px 0 var(--c),  4px  8px 0 var(--c),  8px  8px 0 var(--c), 12px  8px 0 var(--c),
       4px 12px 0 var(--c),  8px 12px 0 var(--c),
       0px 16px 0 var(--c),  4px 16px 0 var(--c),  8px 16px 0 var(--c), 12px 16px 0 var(--c),
       0px 20px 0 var(--c), 12px 20px 0 var(--c);
  }
  50%{
    box-shadow:
       4px  0px 0 var(--c),  8px  0px 0 var(--c), 12px  0px 0 var(--c),
       0px  4px 0 var(--c),  4px  4px 0 #0d0d0f,  8px  4px 0 #0d0d0f, 12px  4px 0 var(--c),
       0px  8px 0 var(--c),  4px  8px 0 var(--c),  8px  8px 0 var(--c), 12px  8px 0 var(--c),
       4px 12px 0 var(--c),  8px 12px 0 var(--c),
       0px 16px 0 var(--c),  4px 16px 0 var(--c),  8px 16px 0 var(--c), 12px 16px 0 var(--c),
       4px 20px 0 var(--c),  8px 20px 0 var(--c);
  }
}
@keyframes seat-bob{
  0%,100%{transform:scale(1.6) translateY(0)}
  50%{transform:scale(1.6) translateY(-2px)}
}
.seat[data-active="1"] .px{
  animation:seat-step .52s steps(1,end) infinite, seat-bob .52s steps(2,end) infinite;
}
/* Both opt-outs already exist for movement; the walk honours both. */
.static .seat[data-active="1"] .px{animation:none}
@media (prefers-reduced-motion:reduce){
  .seat[data-active="1"] .px{animation:none}
}
