/* Modern Claudefare — cheat codes: the in-game badge and the OPTIONS band.
 *
 * WHY THIS IS ITS OWN STYLESHEET.
 * `style.css` is being rewritten by another session in this fleet, and two agents
 * appending to one 3,600-line file is how a rule lands inside somebody else's media
 * query. `final-killcam.css` and `voice-hud.css` already established the pattern. This
 * file owns exactly two subjects — `#cheat-badge` in the HUD and `.cheat-band` in the
 * OPTIONS sheet — plus one derived stamp on the two result boards. index.html carries one
 * added <link>, after style.css, so every token below resolves.
 *
 * THE REGION THIS FEATURE CLAIMS: the HUD's TOP-RIGHT CORNER.
 * Stated out loud because a concurrent session is adding a speaking indicator to the same
 * HUD. Measured occupancy of every other region, from style.css:
 *
 *   #minimap        left 20,  top 18      clamp(150px, 13.5vw, 214px) square
 *   #score-header   top 12,   centred     three clamped columns, max-width 100vw - 32px
 *   #feed-stack     left 20,  bottom 22   grows UP, max-width min(46vw, 520px)
 *   #hp-readout     bottom 20, centred    clamp(132px, 13vw, 186px)
 *   #weapon-block   right 20, bottom 18   min-width 200px
 *   #streak         right 20, bottom 155  right-aligned, max-width 46vw
 *   #death-panel    right 20, vertically centred, min(360px, 42vw)
 *   #voice-hud      left rail, between the MEASURED bottom of #minimap and the MEASURED
 *                   top of #feed-stack (voice-hud.css --vh-top / --vh-max)
 *   #crosshair / #hitmarker / #banner   centre
 *
 * Nothing is anchored to the top-right. That is also where the reference games put a
 * playlist or modifier tag, so the corner is right on its own terms and not merely free.
 *
 * EVERY LAYER IS A RELATIONSHIP, NOT A NUMBER.
 * `AGENTS.md`: *"Express a layering requirement as a relationship, not a literal ...
 * writing it as a number is precisely how `--z-fx` came to equal `--z-modal` and paint the
 * damage vignette over the live endboard."* So the badge is `calc(var(--z-fx) + 1)` — one
 * above the damage vignette, because a modifier you are playing under must survive being
 * shot at — and it is a LOCAL index, since `#hud` carries `z-index: var(--z-hud)` and
 * therefore its own stacking context. It cannot reach past the HUD into a modal however
 * the scale moves.
 *
 * NO ACCENT BARS AND NO --gold. Gold is the primary action and the current selection and
 * nothing else. A live cheat is a WARNING about the state of the match, so it reads on
 * `--red`, the token already meaning damage and danger.
 */

/* ==========================================================================
   The badge
   ========================================================================== */

/* #preset-badge shares every rule below with #cheat-badge deliberately. They are the
 * same kind of signal — "this match is not the default one" — and giving the second one
 * its own geometry, surface and type scale would make two things to look at where the
 * player should see one column. Only the accent differs, and only because a cheat is a
 * WARNING (--red, already meaning danger) while a preset is a RULE everyone is playing
 * under, which is information rather than alarm. No new --gold and no new accent
 * surface: validate-ui-slop caps those and an accent on everything is an accent on
 * nothing. */
#cheat-badge,
#preset-badge {
  position: absolute;
  /* Mirrors #minimap's 20/18 inset on the opposite corner, so the two top corners are
   * optically level rather than each picking their own number. */
  right: 20px;
  top: 18px;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 4px 9px;
  max-width: min(280px, 26vw);
  background: rgba(8, 10, 12, 0.62);
  box-shadow: inset 0 0 0 1px rgba(228, 89, 108, 0.34);
  font-family: var(--ui);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-shadow: var(--hud-shadow);
  pointer-events: none;
  /* One above the damage vignette, inside #hud's own stacking context. */
  z-index: calc(var(--z-fx) + 1);
}

.cb-dot {
  width: 6px;
  height: 6px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--red);
}

.cb-title { color: var(--red); }

/* THE PRESET BADGE IS INFORMATION, NOT A WARNING, so it drops the red entirely and sits
 * on the HUD's own ink ramp. Overriding after the shared block rather than forking it. */
#preset-badge { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14); }
.pb-dot { background: var(--ink-2); }
.pb-title { color: var(--ink-1, var(--ink-2)); }

/* STACKED, NOT OVERLAPPING. Written as a relationship — *"while the cheat badge is up, I
 * am below it"* — rather than as two hardcoded insets, so moving one moves the pair. The
 * offset is the badge's own line box (11px type + 8px padding) plus the 6px gutter the
 * HUD uses between stacked elements. */
#hud:has(> #cheat-badge:not(.hidden)) #preset-badge { top: calc(18px + 27px + 6px); }

/* A full-screen board owns the screen, exactly as for the cheat badge above. */
#hud:has(> #scoreboard:not(.hidden)) #preset-badge,
#hud:has(> #endboard:not(.hidden)) #preset-badge { display: none; }

.cb-list {
  color: var(--ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A GUEST'S BADGE SAYS WHOSE SETTING IT IS.
 * Cheats are host controlled, so on a machine that joined somebody else's lobby the badge
 * names the owner. Written from the same `data-role` js/cheats.js puts on <html>, so the
 * badge and the OPTIONS band cannot disagree about the role. */
#cheat-badge[data-role="guest"] .cb-title::after {
  content: ' · HOST';
  color: var(--ink-3);
}

/* THE MID-MATCH ANNOUNCE.
 * A host can tick a box while everyone is playing, and a guest who has just joined
 * receives the host's set with their first frame. Both arrive through one `notify()`, so
 * both pulse here. Being told beats discovering it by noticing you cannot die. It settles
 * to the quiet resting state on its own — an indicator that pulses forever is one players
 * stop seeing, and the resting badge is already permanent. */
#cheat-badge.just-changed {
  animation: cheatAnnounce 4s var(--ease) 1;
}

@keyframes cheatAnnounce {
  0%   { background: rgba(96, 18, 26, 0.92); box-shadow: inset 0 0 0 1px rgba(228, 89, 108, 0.95); }
  70%  { background: rgba(96, 18, 26, 0.92); box-shadow: inset 0 0 0 1px rgba(228, 89, 108, 0.95); }
  100% { background: rgba(8, 10, 12, 0.62); box-shadow: inset 0 0 0 1px rgba(228, 89, 108, 0.34); }
}

@media (prefers-reduced-motion: reduce) {
  /* The announce is the only motion here and it is information, so it is not removed —
   * it stops animating and holds the loud state for the same window instead. */
  #cheat-badge.just-changed {
    animation: none;
    background: rgba(96, 18, 26, 0.92);
    box-shadow: inset 0 0 0 1px rgba(228, 89, 108, 0.95);
  }
}

/* A FULL-SCREEN BOARD OWNS THE SCREEN, and the badge steps out of its way.
 *
 * The same relationship voice-hud.css uses, and for the same reason: written as *"while a
 * board is showing, I am not"* rather than as a z-index or an offset, so it survives any
 * of those elements moving or changing corner. Only the two boards that actually cover the
 * top-right are listed — the killcam, the respawn counter, the spawn-in card and the death
 * panel do not reach it, and the badge deliberately STAYS UP through those, because the
 * seconds after you die immortal are exactly when a player should be told why.
 *
 * `js/hud.js` already toggles `.hidden` on both as the single expression of "is this board
 * up", so this reads state that exists instead of inventing a second copy. */
#hud:has(> #scoreboard:not(.hidden)) #cheat-badge,
#hud:has(> #endboard:not(.hidden)) #cheat-badge { display: none; }

/* ==========================================================================
   The consequence: the result boards say the match was played with cheats on
   ==========================================================================
   DECIDED AND STATED: cheats do NOT zero the scoreboard and do NOT stop the match
   ending. Silently voiding a match somebody just played is a bigger surprise than the
   cheat itself, and there is no persistent stat store in this build for a cheated score
   to poison. What they do instead is MARK the result, so a score achieved under a
   modifier can never be read as one that was not.

   Done in CSS off `html[data-cheats]` rather than in js/hud.js, deliberately: hud.js is
   held by the final-killcam session, and this needs no state hud.js does not already
   have. `::after` on the title survives hud.js writing `textContent`, which a JS append
   into the same node would not. */
html[data-cheats] #scoreboard-title::after,
html[data-cheats] #endboard-title::after {
  content: ' · CHEATS ON';
  color: var(--red);
  font-family: var(--ui);
  font-size: 0.5em;
  font-weight: 600;
  letter-spacing: 0.2em;
  vertical-align: middle;
  margin-left: 0.6em;
}

/* ==========================================================================
   The OPTIONS band
   ========================================================================== */

.cheat-band {
  margin-top: 26px;
  padding-top: 18px;
  border-top: 1px solid var(--rule-strong);
  display: grid;
  gap: 14px;
}

.cheat-band-head { display: grid; gap: 6px; }

.cheat-band-sub {
  margin: 0;
  max-width: 70ch;
  font-family: var(--ui);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-3);
}

.cheat-list {
  display: grid;
  /* Two abreast on a wide sheet, one on a narrow one, without a media query — the sheet
   * is already responsive and a breakpoint here would be a second opinion about its
   * width. */
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 12px;
}

.cheat-row {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: 10px;
  padding: 11px 13px;
  background: var(--s2);
  cursor: pointer;
}

.cheat-row:hover { background: var(--s3); }

.cheat-copy { display: grid; gap: 4px; }

.cheat-name {
  font-family: var(--ui);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.16em;
  color: var(--ink);
}

.cheat-note {
  font-family: var(--ui);
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-3);
}

/* A LIVE CHEAT LOOKS LIVE FROM ACROSS THE ROOM.
 * `:has()` on the row rather than a class toggled from JS, so the state cannot drift from
 * the checkbox that owns it — there is one source of truth and it is the input. */
.cheat-row:has(input:checked) {
  background: rgba(96, 18, 26, 0.28);
  box-shadow: inset 0 0 0 1px rgba(228, 89, 108, 0.4);
}

.cheat-row:has(input:checked) .cheat-name { color: var(--red); }

/* READ-ONLY MUST LOOK DELIBERATE AND SAY WHY.
 * A guest sees every cheat and its state and cannot change it — the same treatment the
 * lobby gives map, mode, preset and bot count. The cursor and the dimming say "not
 * yours"; the line below says who it belongs to. Never a control that accepts a click and
 * does nothing. */
.cheat-band[data-role="guest"] .cheat-row {
  cursor: not-allowed;
  opacity: 0.62;
}

.cheat-band[data-role="guest"] .cheat-band-sub::after {
  content: ' The host owns these for this match.';
  color: var(--red);
}

.cheat-band #btn-cheats-off { justify-self: start; }
