/* ══ THE BUTTON LANGUAGE — "B · Moulded" ═══════════════════════════════════
   Owner, 2026-08-16, after judging the five treatments in the button lab on
   his phone: *"I like B. apply this type of reation on click, and this style
   of buttons everywhere."*

   B is a solid key: a lit top face, a hard base under it, and a press that
   physically pushes the key down into that base. It is the Coop TD / Brawl
   Stars shape he named as the bar to clear.

   ── THIS IS NOT A NEW IDEA IN THIS GAME ──────────────────────────────────
   The PLAY button has been exactly this since docs/125 ("01 Fireant lift"),
   and the Tower's CLIMB button is the same element. What the owner picked was
   therefore not a new drawing but a decision to make PLAY's drawing the
   LANGUAGE. Everything below generalises the three rules that button already
   proved, and they carry over unchanged:

     1. THE LIP IS NEVER A GENERIC BLACK. It is the button's own face shaded
        down, so the key belongs to the surface it sits on and, for the
        primary, to the world it is in — brown under gold, navy under ice.
        Here that is mechanical: every lip is the same colour-mix of its own
        face, so a new tone cannot get it wrong, because a tone does not get
        to name its lip at all.
     2. NO HOVER STATE, ANYWHERE. A touch screen fires hover on tap and leaves
        it stuck until you touch something else, so a hover state on a phone is
        a stuck state pretending to be feedback. There is not one in this file
        and none may be added. (Said in prose deliberately — a checker cannot
        tell a rule being broken from a rule being quoted, and the smoke asks
        the browser rather than grepping for the pseudo-class.)
     3. TRANSFORMS, NEVER SHADOWS, FOR THE MOVEMENT. Only `transform` is
        transitioned; the lip's box-shadow SNAPS at press and at release, which
        is two paints per tap rather than a shadow repainted every frame
        (the docs/77 perf lesson).

   ── ONE PAINTER ──────────────────────────────────────────────────────────
   The paint and the press for a button live in THIS FILE and nowhere else.
   game.html and fight.html keep only layout — width, flex, where the thing
   sits — and may set the language's own dials (`--pfb-face`, `--pfb-ink`,
   `--pfb-depth`). If you find yourself writing a `background` or a
   `box-shadow` for a button in a template, you are recreating the docs/133
   last-writer-wins trap: add a tone here instead.

   ⚠️ This sheet is linked BEFORE each template's inline `<style>`, so any
   paint left behind in a template WINS and that control silently keeps its
   old look while appearing adopted. That is the one failure mode of this
   file, it is invisible, and `node scripts/button_language_smoke.mjs` is what
   catches it.

   ── HOW TO USE IT ────────────────────────────────────────────────────────
     <button class="pfb pfb-pri">CLIMB</button>     primary, world accent
     <button class="pfb">Cancel</button>            secondary steel (default)
     <button class="pfb pfb-pri pfb-sm">Buy</button>
     <button class="pfb pfb-danger">Delete</button>

   Size and tone are INDEPENDENT on purpose: the Shop's Buy button is small
   and primary at once, and bundling them (as the lab's three families did)
   makes that combination unexpressible.

   ── THE ALIAS LIST ───────────────────────────────────────────────────────
   New markup writes `.pfb` and needs nothing here. The long selector lists
   below also name the buttons that already existed across game.html and
   fight.html, so ~200 of them became the language in one edit rather than two
   hundred. The lists repeat; the DECLARATIONS never do — there is exactly one
   copy of every property in this file, which is the part that has to stay
   true. Adding a button to the language is adding its name to those lists.

   ⚠️ Add a name only once its old paint is DELETED from the template.

   ⛔ WHAT IS DELIBERATELY NOT A KEY, and why. A moulded key says "press me
   and something happens". These are not keys and must not become them:
     · the bottom nav slots — a destination you are already standing in
     · the top-bar coins and gears — icons over art, not a toolbar
     · `.set-row` and the support rows — DOORS (glyph, name, chevron), docs/164
     · `.coll-med`, `.inst-tile`, `.pd-cell` — pictures of a thing you own
     · the skill dial's seats — SVG arcs, with their own press (docs/126)
     · `.sc-top` — the whole shop row, whose job is to be a row
     · `.tut-skip-btn`, `.aux-link` — quiet text, on purpose
     · `.hub-cta` (PLAY / CLIMB) — the original, and structurally different:
       its lip is a real ELEMENT, not a shadow, which is what lets it be that
       thick without the ground shadow going muddy. It is the one exception,
       it is where this all came from, and it stays as it is.
   Every one of those still answers a finger; it just answers in its own
   register. Making them keys too turns a screen into a wall of plastic, which
   is how this style fails everywhere it is done badly.                      */

:root {
  /* The press spec, docs/122, unchanged: down fast, release with a back-out
     overshoot (the >1 control point IS the overshoot). */
  --pfb-press-in: 60ms;
  --pfb-press-out: 220ms;
  --pfb-ease-out: cubic-bezier(.34, 1.56, .64, 1);
  --pfb-steel: #2b3d4f;
  --pfb-slate: #1e2b39;
}

/* ── THE KEY ────────────────────────────────────────────────────────────────
   Every declaration shared by every button in the game. A tone sets two
   colours; a size sets a height, a depth and a type scale. Nothing else about
   a button is a decision anyone has to make again.                          */
.pfb,
.skdet-btn, .sc-btn, .skills-action-btn, .skilltree-buy-btn, .fightbar-remove,
.trade-action-btn, .listing-card button, .auc-btn, .auction-toggle button,
.ml-act, .pf-sheet .pfs-btn, #boot-gate .bg-btn, .tut-next-btn, .claim-btn,
.coll-roadmap-btn, .inst-back, .fight-finished-btn, .fc-swap, .btn-primary, .btn-demo {
  /* defaults = the steel secondary, so a bare `.pfb` is already correct */
  --pfb-face: var(--pfb-steel, #2b3d4f);
  --pfb-ink: var(--text, #e5eef6);
  --pfb-depth: 4px;
  --pfb-radius: 13px;

  /* THE LIP, derived and not chosen — rule 1, enforced right here. */
  --pfb-lip: color-mix(in srgb, var(--pfb-face) 52%, #140b05);

  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 46px;
  padding: 0 16px;
  border: 0;
  border-radius: var(--pfb-radius);
  cursor: pointer;
  font: inherit;
  font-weight: 800;
  font-size: .82rem;
  letter-spacing: .04em;
  color: var(--pfb-ink);
  /* Two of the end-of-fight keys are `<a href>` (docs/142: leaving a fight
     REPLACES rather than pushes, so they have to be real links). Without this
     they wear the browser's underline and nothing else in the game does. */
  text-decoration: none;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--pfb-face) 84%, #fff),
    var(--pfb-face)
  );
  /* lit top face · shaded bottom face · the hard base · the ground shadow */
  box-shadow:
    inset 0 2px 0 rgba(255, 255, 255, .34),
    inset 0 -3px 0 rgba(0, 0, 0, .24),
    0 var(--pfb-depth) 0 var(--pfb-lip),
    0 calc(var(--pfb-depth) + 2px) 10px rgba(0, 0, 0, .40);
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
}

/* THE PRESS. The key travels down by its own depth less 1px — the last pixel
   of base is deliberate, so the key never looks like it fell off its lip. */
.pfb:active,
.skdet-btn:active, .sc-btn:active, .skills-action-btn:active,
.skilltree-buy-btn:active, .fightbar-remove:active, .trade-action-btn:active,
.listing-card button:active, .auc-btn:active, .auction-toggle button:active,
.ml-act:active, .pf-sheet .pfs-btn:active, #boot-gate .bg-btn:active,
.tut-next-btn:active, .claim-btn:active, .coll-roadmap-btn:active,
.inst-back:active, .fight-finished-btn:active, .fc-swap:active, .btn-primary:active,
.btn-demo:active {
  transform: translateY(calc(var(--pfb-depth) - 1px));
  box-shadow:
    inset 0 2px 0 rgba(255, 255, 255, .28),
    inset 0 -2px 0 rgba(0, 0, 0, .26),
    0 1px 0 var(--pfb-lip),
    0 2px 5px rgba(0, 0, 0, .38);
}

@media (prefers-reduced-motion: no-preference) {
  .pfb,
  .skdet-btn, .sc-btn, .skills-action-btn, .skilltree-buy-btn, .fightbar-remove,
  .trade-action-btn, .listing-card button, .auc-btn, .auction-toggle button,
  .ml-act, .pf-sheet .pfs-btn, #boot-gate .bg-btn, .tut-next-btn, .claim-btn,
  .coll-roadmap-btn, .inst-back, .fight-finished-btn, .fc-swap, .btn-primary, .btn-demo {
    transition: transform var(--pfb-press-out) var(--pfb-ease-out);
  }
  .pfb:active,
  .skdet-btn:active, .sc-btn:active, .skills-action-btn:active,
  .skilltree-buy-btn:active, .fightbar-remove:active, .trade-action-btn:active,
  .listing-card button:active, .auc-btn:active, .auction-toggle button:active,
  .ml-act:active, .pf-sheet .pfs-btn:active, #boot-gate .bg-btn:active,
  .tut-next-btn:active, .claim-btn:active, .coll-roadmap-btn:active,
  .inst-back:active, .fight-finished-btn:active, .fc-swap:active, .btn-primary:active,
  .btn-demo:active {
    transition-duration: var(--pfb-press-in);
    transition-timing-function: ease-out;
  }
}

/* Under reduced motion the key does not MOVE — but it must still answer the
   finger, or the button reads as broken for exactly the people who asked for
   less movement. The lip collapsing and the face darkening are paint, not
   motion, and they say "pressed" just as clearly. */
@media (prefers-reduced-motion: reduce) {
  .pfb,
  .skdet-btn, .sc-btn, .skills-action-btn, .skilltree-buy-btn, .fightbar-remove,
  .trade-action-btn, .listing-card button, .auc-btn, .auction-toggle button,
  .ml-act, .pf-sheet .pfs-btn, #boot-gate .bg-btn, .tut-next-btn, .claim-btn,
  .coll-roadmap-btn, .inst-back, .fight-finished-btn, .fc-swap, .btn-primary, .btn-demo {
    transition: none;
  }
  .pfb:active,
  .skdet-btn:active, .sc-btn:active, .skills-action-btn:active,
  .skilltree-buy-btn:active, .fightbar-remove:active, .trade-action-btn:active,
  .listing-card button:active, .auc-btn:active, .auction-toggle button:active,
  .ml-act:active, .pf-sheet .pfs-btn:active, #boot-gate .bg-btn:active,
  .tut-next-btn:active, .claim-btn:active, .coll-roadmap-btn:active,
  .inst-back:active, .fight-finished-btn:active, .fc-swap:active, .btn-primary:active,
  .btn-demo:active {
    transform: none;
    filter: brightness(.88);
  }
}

/* A dead key has no thickness to push into. */
.pfb:disabled, .pfb[aria-disabled="true"],
.skdet-btn:disabled, .sc-btn:disabled, .skills-action-btn:disabled,
.skilltree-buy-btn:disabled, .trade-action-btn:disabled, .auc-btn:disabled,
.ml-act:disabled, .pf-sheet .pfs-btn:disabled, #boot-gate .bg-btn:disabled,
.tut-next-btn:disabled, .claim-btn:disabled, .fight-finished-btn:disabled,
.btn-primary:disabled, .skills-action-btn.is-locked {
  opacity: .45;
  cursor: not-allowed;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12);
  transform: none;
  filter: none;
}

/* Keyboard only — a focus ring on a touch tap would be the stuck-hover
   problem wearing a different hat. */
.pfb:focus-visible, .skdet-btn:focus-visible, .sc-btn:focus-visible,
.trade-action-btn:focus-visible, .fight-finished-btn:focus-visible,
.btn-primary:focus-visible, .tut-next-btn:focus-visible {
  outline: 2px solid var(--pfb-focus, #ffe1a8);
  outline-offset: 2px;
}


/* ── TONES — two colours each, and never a lip ──────────────────────────── */

/* The primary belongs to its world (rule 1): under Playfair warm brown gold,
   under Arctic ice blue, with no edit here. */
.pfb-pri,
.skdet-btn.eq, .sc-btn, .skills-action-btn.primary, .skilltree-buy-btn.primary,
.trade-action-btn.primary, .listing-card button.primary,
.shop-card button.primary, .auc-btn.primary, .auction-toggle button.active,
.ml-act.primary, .pf-sheet .pfs-btn.primary, #boot-gate .bg-btn.primary,
.tut-next-btn, .claim-btn, .fight-finished-btn.primary, .btn-primary {
  --pfb-face: var(--world-accent, var(--accent, #de7b2b));
  --pfb-ink: #2a1608;
  text-shadow: 0 1px 0 rgba(255, 255, 255, .28);
}

/* The secondary is the default; named so markup can say what it means. */
.pfb-sec {
  --pfb-face: var(--pfb-steel, #2b3d4f);
  --pfb-ink: var(--text, #e5eef6);
}

/* Quiet: present, pressable, not asking for the tap. */
.pfb-quiet,
.inst-back, .coll-roadmap-btn, .btn-demo {
  --pfb-face: var(--pfb-slate, #1e2b39);
  --pfb-ink: var(--muted, #8ea3b5);
}

/* Danger is for things that destroy something. Red is HP everywhere else in
   this game (docs/63), which is exactly why it reads as a warning here. */
.pfb-danger {
  --pfb-face: var(--err, #e05050);
  --pfb-ink: #2a0808;
}


/* ── SIZES — a height, a depth, a type scale ────────────────────────────────
   Depth scales with the key: a 4px base under a small button looks like a
   toy, and a 3px base under the primary looks like a sticker.              */

.pfb-lg {
  --pfb-depth: 5px;
  --pfb-radius: 16px;
  min-height: 54px;
  padding: 0 22px;
  font-size: 1rem;
  letter-spacing: .05em;
}

/* ⚠️ NOT the lab's 34px chip. 44px is the floor for a TAPPABLE area
   (docs/122) and this file must never be the thing that breaks it — a small
   button gets tighter padding and smaller type, never a smaller target. A
   34px CHIP in this game is a label, and a label is not a button. (A surface
   MAY paint smaller than 44px if it carries its own pad — `.inst-back` does,
   and that is the docs/122 painted-versus-tappable rule, not an exception to
   the floor.) */
.pfb-sm,
.sc-btn, .ml-act, .coll-roadmap-btn {
  --pfb-depth: 3px;
  --pfb-radius: 11px;
  min-height: 44px;
  padding: 0 12px;
  font-size: .74rem;
}

/* Fills its container — the common case for a card's one action. */
.pfb-block { display: flex; width: 100%; }

/* An icon-only key: square, so a glyph sits in the middle of its own key
   rather than in a stretched pill. Still 44px. */
.pfb-ico {
  width: 46px;
  min-width: 46px;
  padding: 0;
  gap: 0;
}
.pfb-ico.pfb-sm { width: 44px; min-width: 44px; }
