/* Herald — Dice FAB cluster + roller panel positioning */
/*
 * Token note: #dice-fab-splat carries its own [data-splat] attribute set by
 * dice-widget.js. The tokens.css [data-splat="X"] rules match that element
 * directly, so var(--accent) resolves to the correct splat color on the button
 * itself without any per-splat overrides here. The main FAB (#dice-fab-main)
 * inherits --accent from document.body.dataset.splat (set by dice-widget.js
 * when a character context is active), falling back to the tarnished-gold shell
 * default on generic pages.
 */
/*
 * Reduced-motion note: this file has no @media (prefers-reduced-motion:
 * reduce) block of its own, and doesn't need one — tokens.css's global
 * blanket (`*, *::before, *::after { animation-duration: 0.01ms !important;
 * transition-duration: 0.01ms !important; ... }`, see its §09 MOTION TOKENS)
 * already applies to every element on the page, hex fan included, since
 * tokens.css and this file load together on every template (see base.html).
 * The trace-draw transition, count-bounce animation, and hover/scale
 * transforms below are all ordinary transition-duration/animation-duration,
 * so that blanket already neutralizes them — nothing bespoke here escapes it.
 */

#dice-fab-cluster {
  position: fixed;
  /* Lifted off the very bottom edge (was --space-xl/24px) so the stat
     picker has somewhere to sit below the button without being clipped
     by the viewport edge — see .hex-below-stack below. */
  bottom: 72px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: var(--space-md);            /* 12px */
  z-index: 6000; /* persistent floating chrome tier — see z-index scale doc in sheet-layout.css */
  font-family: 'Special Elite', Courier, monospace;
}

/* Main dice button */
#dice-fab-main {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 38%, var(--parchment-700) 0%, var(--parchment-800) 100%);
  border: 2px solid var(--accent);
  color: var(--accent);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-e3);
  transition: transform var(--motion-fast) var(--ease-standard),
              box-shadow var(--motion-base) var(--ease-standard),
              border-color var(--motion-base) var(--ease-standard);
}
#dice-fab-main:hover {
  transform: translateY(-2px);
  border-color: var(--accent-hover);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.7), 0 0 0 4px color-mix(in oklch, var(--accent), transparent 82%);
}
#dice-fab-main.is-open {
  border-color: var(--accent-hover);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.7), 0 0 0 4px color-mix(in oklch, var(--accent), transparent 72%);
}
/* :not(.hex-border) — the die-icon svg is fixed-size, but the hex-mode
   border overlay (below) needs to fill the whole button; an ID selector
   would otherwise always beat that .hex-border { width: 100% } rule
   regardless of source order. */
#dice-fab-main svg:not(.hex-border) { width: 26px; height: 26px; }

/* Splat satellite button (smaller, left of main) */
/*
 * [data-splat] on this element triggers the tokens.css splat rules directly,
 * so var(--accent) and var(--border) resolve to the correct splat color.
 */
#dice-fab-splat {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: var(--surface);
  border: 1.5px solid var(--border);
  color: var(--text-muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  font-weight: 700;
  box-shadow: var(--shadow-e2);
  transition: transform var(--motion-fast) var(--ease-standard),
              border-color var(--motion-base) var(--ease-standard),
              color var(--motion-base) var(--ease-standard);
}
#dice-fab-splat:hover { transform: translateY(-2px); }

/* When a splat is selected the element has [data-splat="X"], which triggers
   tokens.css to swap --accent. These rules consume that derived --accent. */
#dice-fab-splat[data-splat] { color: var(--accent); border-color: var(--accent); }

#dice-fab-splat.hidden { display: none; }

/* Splat fan popover (opens above the splat button) */
#dice-fab-fan {
  position: absolute;
  bottom: 70px;
  left: 0;
  display: none;
  flex-direction: column;
  gap: var(--space-sm);            /* 8px → closest is sm=8px */
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md); /* 6px */
  padding: var(--space-sm);        /* 8px */
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.7);
}
#dice-fab-fan.open { display: flex; }

/* Fan option buttons carry their own [data-splat] so var(--accent) shows
   each option's correct color — active option = current splat. */
.dice-fab-fan-opt {
  display: flex;
  align-items: center;
  gap: var(--space-sm);            /* 8px */
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm); /* 3px */
  color: var(--text-muted);
  cursor: pointer;
  padding: var(--space-sm) var(--space-lg); /* 8px 16px */
  font-family: 'Special Elite', Courier, monospace;
  font-size: 12px;
  letter-spacing: 1px;
  white-space: nowrap;
  transition: border-color var(--motion-fast) var(--ease-standard),
              color var(--motion-fast) var(--ease-standard);
}
.dice-fab-fan-opt:hover { border-color: var(--border); color: var(--accent-hover); }
.dice-fab-fan-opt[data-splat] { color: var(--accent); }
.dice-fab-fan-opt.active { border-color: var(--accent); color: var(--accent); }
.dice-fab-fan-opt.disabled {
  opacity: .45;
  cursor: not-allowed;
  color: var(--border);
}
.dice-fab-fan-opt .glyph { font-weight: 700; width: 16px; text-align: center; }

/* ===============================================================
   BRANCHING HEX FAN (VTM / HTR / WTA)
   The FAB's only mode as of 2026-07-19 — see DiceWidget._hexConfig().
   A hex "Roll" trigger and a PCB-trace branch menu fan out above the
   same FAB position. Node identity (Vampire Die vs Regular Die, etc.)
   is splat-driven in JS (HEX_SPLAT_CONFIG) — this CSS only knows about
   POSITION (left/center/right), so it's shared across all three splats
   unmodified.

   Coordinate system: #dice-hex-branch is a fixed 280 x 273.5 overlay
   box whose bottom edge and horizontal center line up with the hex
   main button's own footprint (84 x 97), anchored inside
   #dice-fab-cluster. See branch history for the vertex math if these
   numbers ever need to move — they are not arbitrary.

   Horizontal anchor is `right`, not `left: 50%` of the cluster: the hex
   main button is always the cluster's last flex child, so its right edge
   is always flush with the cluster's own right edge — true whether the
   free-mode splat satellite button (#dice-fab-splat) is hidden (locked
   splat, e.g. a character sheet) or visible alongside it (free mode,
   e.g. the dashboard — see _hexConfig() in dice-widget.js). `left: 50%`
   of the cluster is NOT equivalent: the splat button adds 42px + 12px
   gap to the cluster's own width, shifting the cluster's midpoint away
   from the hex button's actual center by that same 27px whenever both
   are present. -98px == -(280 - 84) / 2, centering the 280px branch over
   the 84px button from its right edge. */

#dice-fab-main.hex-mode {
  position: relative;
  width: 84px;
  height: 97px;
  border-radius: 0;
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
  background: color-mix(in oklch, var(--parchment-800), transparent 32%);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: none;
  /* Soft outer glow so the frosted-glass edge reads as lit, matching the
     PCB trace glow below. The actual edge line is the .hex-border SVG
     polygon (see below) — box-shadow can't trace a clip-path hexagon. */
  box-shadow: 0 0 10px 1px color-mix(in oklch, var(--accent), transparent 55%),
              0 4px 14px color-mix(in srgb, var(--ink-900) 55%, transparent);
  transition: box-shadow 0.15s ease;
}
#dice-fab-main.hex-mode:hover,
#dice-fab-main.hex-mode.is-open {
  transform: none;
  box-shadow: 0 0 16px 2px color-mix(in oklch, var(--accent), transparent 35%),
              0 4px 18px color-mix(in srgb, var(--ink-900) 65%, transparent);
}
#dice-fab-main .hex-label {
  font-family: 'Special Elite', Courier, monospace;
  font-size: 13px;
  letter-spacing: 1.5px;
  color: var(--accent);
  text-transform: uppercase;
  pointer-events: none;
}

#dice-hex-branch {
  position: absolute;
  right: -98px;
  bottom: 0;
  width: 280px;
  height: 273.5px;
  pointer-events: none;
  z-index: 2;
}

.hex-connectors {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}
.hex-connectors path {
  fill: none;
  stroke-width: 2;
  stroke-linecap: square;
  stroke-linejoin: miter;
  transition: stroke-dashoffset 0.5s ease-out;
}
/* Glowing traces — the same "circuit lit up" language as the hex outlines
   below, so the whole fan reads as one continuous PCB rather than buttons
   with unrelated lines between them. */
.hex-connectors .trace-pos-left,
.hex-connectors .trace-pos-center {
  stroke: color-mix(in oklch, var(--accent), white 10%);
  filter: drop-shadow(0 0 3px color-mix(in oklch, var(--accent), transparent 25%));
}
.hex-connectors .trace-pos-right {
  stroke: var(--danger);
  filter: drop-shadow(0 0 3px color-mix(in oklch, var(--danger), transparent 15%));
}
.hex-connectors .hex-via {
  opacity: 0;
  transition: opacity 0.25s ease;
}
circle.hex-via[fill="var(--accent)"] { filter: drop-shadow(0 0 2px var(--accent)); }
circle.hex-via[fill="var(--danger)"] { filter: drop-shadow(0 0 2px var(--danger)); }
.hex-via-bend1 { transition-delay: 0.16s; }
.hex-via-bend2 { transition-delay: 0.32s; }
#dice-fab-cluster.is-open .hex-connectors .hex-via { opacity: 1; }

.hex-btn {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  margin: 0;
  padding: 0;
  cursor: pointer;
  background: color-mix(in oklch, var(--parchment-800), transparent 32%);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
  /* Soft outer glow only — the edge line itself is the .hex-border SVG
     polygon, which (unlike box-shadow) can actually trace a clip-path
     hexagon evenly on every side. */
  box-shadow: 0 0 10px 1px color-mix(in oklch, var(--accent), transparent 55%),
              0 4px 14px color-mix(in srgb, var(--ink-900) 55%, transparent);
  transition: box-shadow 0.15s ease, background 0.15s ease;
}
.hex-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.hex-btn:hover {
  box-shadow: 0 0 16px 2px color-mix(in oklch, var(--accent), transparent 35%),
              0 4px 18px color-mix(in srgb, var(--ink-900) 65%, transparent);
}

/* The true hex outline — a polygon sharing the buttons' exact clip-path
   vertices, stretched to fill the button (preserveAspectRatio="none") and
   held to a constant on-screen stroke width (non-scaling-stroke) despite
   that non-uniform stretch. Default/hover/position/state colors mirror
   what the old box-shadow inset used to carry. */
.hex-border {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.hex-border polygon {
  fill: none;
  stroke-width: 3;
  vector-effect: non-scaling-stroke;
  transition: stroke 0.15s ease, filter 0.15s ease;
}
#dice-fab-main.hex-mode .hex-border polygon,
.hex-btn .hex-border polygon {
  stroke: color-mix(in oklch, var(--accent), transparent 15%);
}
#dice-fab-main.hex-mode:hover .hex-border polygon,
#dice-fab-main.hex-mode.is-open .hex-border polygon,
.hex-btn:hover .hex-border polygon {
  stroke: var(--accent);
  filter: drop-shadow(0 0 3px color-mix(in oklch, var(--accent), transparent 35%));
}
.hex-btn-pos-right .hex-border polygon {
  stroke: color-mix(in oklch, var(--danger), transparent 10%);
}
.hex-btn-pos-right:hover .hex-border polygon {
  stroke: var(--danger);
  filter: drop-shadow(0 0 3px color-mix(in oklch, var(--danger), transparent 25%));
}
.hex-btn-pos-right.is-action-success .hex-border polygon {
  stroke: var(--success-bright);
  filter: drop-shadow(0 0 3px color-mix(in oklch, var(--success-bright), transparent 35%));
}
.hex-btn-pos-right.is-action-failure .hex-border polygon {
  stroke: var(--danger);
  filter: drop-shadow(0 0 3px color-mix(in oklch, var(--danger), transparent 25%));
}

.hex-btn-child {
  width: 60px;
  height: 69px;
  pointer-events: none;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.18s ease, transform 0.18s ease;
}
#dice-fab-cluster.is-open .hex-btn-child {
  pointer-events: auto;
  opacity: 1;
  transform: scale(1);
}

/* Action node (e.g. WTA's Rage Check) with no owned character to fire on —
   same dimmed/disabled treatment as .dice-fab-fan-opt.disabled, so it reads
   as unavailable instead of looking live and silently doing nothing. */
#dice-fab-cluster.is-open .hex-btn-child.is-unavailable {
  opacity: .45;
  cursor: not-allowed;
}

.hex-btn-pos-left   { left: 30px;  top: 75.5px; transition-delay: 0.05s; }
.hex-btn-pos-center { left: 110px; top: 57.5px; transition-delay: 0.02s; }
.hex-btn-pos-right  { left: 190px; top: 75.5px; transition-delay: 0.08s; }

.hex-btn-pos-left,
.hex-btn-pos-center {
  box-shadow: 0 0 8px 1px color-mix(in oklch, var(--accent), transparent 55%),
              0 4px 14px color-mix(in srgb, var(--ink-900) 55%, transparent);
}
.hex-btn-pos-left:hover,
.hex-btn-pos-center:hover {
  box-shadow: 0 0 14px 2px color-mix(in oklch, var(--accent), transparent 35%),
              0 4px 18px color-mix(in srgb, var(--ink-900) 65%, transparent);
}

/* Right hex — blood-red/danger treatment, distinct from left/center's
   neutral accent styling. Shared across VTM Rouse Check / WTA Rage Check /
   HTR Assist. */
.hex-btn-pos-right {
  box-shadow: 0 0 8px 1px color-mix(in oklch, var(--danger), transparent 45%),
              0 4px 14px color-mix(in srgb, var(--ink-900) 55%, transparent);
}
.hex-btn-pos-right:hover {
  box-shadow: 0 0 14px 2px color-mix(in oklch, var(--danger), transparent 25%),
              0 4px 18px color-mix(in srgb, var(--ink-900) 65%, transparent);
}
/* WTA's Rage Check (an action node, not tap-driven) briefly flashes its
   outcome after firing — success/failure tokens, not a hardcoded hex. */
.hex-btn-pos-right.is-action-success {
  box-shadow: 0 0 14px 2px color-mix(in oklch, var(--success-bright), transparent 35%),
              0 4px 14px color-mix(in srgb, var(--ink-900) 55%, transparent);
}
.hex-btn-pos-right.is-action-failure {
  box-shadow: 0 0 14px 2px color-mix(in oklch, var(--danger), transparent 25%),
              0 4px 14px color-mix(in srgb, var(--ink-900) 55%, transparent);
}

.hex-count {
  font-family: 'Special Elite', Courier, monospace;
  font-weight: 700;
  font-size: 22px;
  color: var(--accent);
  pointer-events: none;
}
.hex-btn-pos-right .hex-count { color: var(--danger); }
.hex-count.is-bouncing { animation: hex-count-bounce 0.38s cubic-bezier(0.34, 1.56, 0.64, 1); }

@keyframes hex-count-bounce {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.55); }
  60%  { transform: scale(0.88); }
  100% { transform: scale(1); }
}

/* Hover tooltip — delayed via JS (500ms), CSS only handles the fade */
.hex-tooltip {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  transform: translateX(-50%);
  white-space: nowrap;
  padding: 4px 9px;
  border-radius: 4px;
  background: var(--ink-900);
  border: 1px solid var(--border);
  color: var(--parchment-300);
  font-family: 'Special Elite', Courier, monospace;
  font-size: 11px;
  letter-spacing: 0.5px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
  z-index: 4;
}
.hex-tooltip.is-visible { opacity: 1; }

/* Overlay stack — NPC name / confirm bar, stacked vertically ABOVE the
   branch overlay (the fan's own nodes open upward from the button, so
   these live in the same direction). The stat picker lives separately,
   BELOW the button — see .hex-below-stack. Each row toggles its own
   visibility independently (NPC name is context-dependent, see
   _hexShowNpcName; the confirm bar depends only on active tap counts). */
.hex-overlay-stack {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 10px);
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  pointer-events: auto;
  z-index: 5;
}

/* Stat picker's own stack, BELOW the button rather than above with the
   rest of the overlay — #dice-fab-cluster's bottom offset (72px) leaves
   just enough clearance here for one pill row before the viewport edge.
   Deliberately tight: with 30+ options in the second select, that little
   room below is what makes the browser's native <select> popup flip and
   open upward instead of down off the bottom of the screen — there's no
   CSS property that controls a native select's popup direction, so this
   layout is what actually satisfies "drops up, not down."
   Hidden until the fan is actually open — .hex-stat-picker-row's own
   display:none/flex (see _hexRenderContextRows) only tracks whether the
   *context* supports a stat pick (right character, right splat), not
   whether the roller has pressed Roll, so without this the box would
   float below the FAB permanently. Faded + slid in on #dice-fab-cluster
   .is-open, same opacity/pointer-events gating hex-btn-child uses for
   the fan nodes above the button. */
.hex-below-stack {
  position: absolute;
  left: 50%;
  top: calc(100% + 10px);
  transform: translate(-50%, 6px);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  opacity: 0;
  pointer-events: none;
  z-index: 5;
  transition: opacity 0.18s ease, transform 0.18s ease;
}
#dice-fab-cluster.is-open .hex-below-stack {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, 0);
}
.hex-pill-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px 8px 14px;
  border-radius: 14px;
  background: color-mix(in oklch, var(--parchment-800), transparent 15%);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid var(--border);
  white-space: nowrap;
}

/* Stat picker + confirm bar get the main Roll button's own frosted-glass +
   splat-accent treatment (same background/blur recipe as
   #dice-fab-main.hex-mode and its glow, accent border instead of the
   neutral --border the NPC-name row still uses) — both read as part of
   the same lit "device" as the button, not a plain overlay row. */
.hex-stat-picker-row,
.hex-confirm-bar {
  background: color-mix(in oklch, var(--parchment-800), transparent 32%);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--accent);
  box-shadow: 0 0 10px 1px color-mix(in oklch, var(--accent), transparent 55%),
              0 4px 14px color-mix(in srgb, var(--ink-900) 55%, transparent);
}

/* Stat picker (Attribute, then Attribute-or-Skill) — shown whenever
   rolling against a character with known stats (your own, or an ST
   viewing someone else's), letting any Attribute+Attribute or
   Attribute+Skill test auto-fill the pool. NPC name — shown for a
   chronicle-attached roll with no character at all. Both port
   free-mode-panel-only features into hex mode; see
   _hexShowStatPicker/_hexShowNpcName and
   reports/hex-fan-full-replacement-followup-2026-07-19.md. */
.hex-attr-select,
.hex-stat2-select {
  flex: 1;
  min-width: 0;
  background: var(--parchment-900);
  border: 1px solid var(--border);
  border-radius: 3px;
  color: var(--text);
  font-family: 'Special Elite', Courier, monospace;
  font-size: 11px;
  padding: 3px 6px;
  cursor: pointer;
  outline: none;
}
.hex-npc-input {
  flex: 1;
  min-width: 0;
  box-sizing: border-box;
  background: var(--parchment-900);
  border: 1px solid var(--border);
  border-radius: 3px;
  color: var(--text);
  font-family: 'Special Elite', Courier, monospace;
  font-size: 11px;
  padding: 4px 6px;
  outline: none;
}
.hex-npc-input:focus,
.hex-attr-select:focus,
.hex-stat2-select:focus { border-color: var(--accent); }
.hex-confirm-bar {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  padding: 8px 10px 8px 14px;
  border-radius: 14px;
  white-space: nowrap;
}
.hex-confirm-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.hex-confirm-text {
  font-family: 'Special Elite', Courier, monospace;
  font-size: 11.5px;
  color: var(--parchment-300);
  letter-spacing: 0.3px;
}
.hex-confirm-roll-btn {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: none;
  background: var(--accent);
  color: var(--ink-900);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.1s ease;
}
.hex-confirm-roll-btn:hover:not(:disabled) { transform: scale(1.08); }
.hex-confirm-roll-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.hex-confirm-roll-btn svg { width: 14px; height: 14px; }

/* Difficulty stepper — second row of the confirm bar. Threads a real
   difficulty into HEX_SPLAT_CONFIG's buildBody/localCalc instead of the
   difficulty:2 that used to be hardcoded there (see
   reports/hex-fan-full-replacement-followup-2026-07-19.md). Persists
   across rolls like the free-mode panel's own stepper — not reset by
   close()'s tap-count abandon behavior. */
.hex-diff-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}
.hex-diff-label {
  font-family: 'Special Elite', Courier, monospace;
  font-size: 10px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-right: 2px;
}
.hex-diff-btn {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border-radius: 3px;
  background: var(--parchment-700);
  border: 1px solid var(--border);
  color: var(--text);
  cursor: pointer;
  font-size: 13px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  line-height: 1;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.hex-diff-btn:hover { border-color: var(--accent); color: var(--accent); }
.hex-diff-val {
  width: 20px;
  text-align: center;
  color: var(--accent);
  font-size: 13px;
  font-weight: 700;
  font-family: 'Special Elite', Courier, monospace;
}

.hex-row-sep {
  width: 1px;
  height: 14px;
  background: var(--border);
  flex-shrink: 0;
}

/* Visibility toggle — click cycles public/ST-only, same field as the
   free-mode panel's dw-vis select. Persists across rolls, same as
   difficulty; NPC name (the panel's other roll-config field) has no hex
   equivalent — see the comment at its JS binding for why. */
.hex-vis-toggle {
  flex-shrink: 0;
  font-family: 'Special Elite', Courier, monospace;
  font-size: 10px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 10px;
  background: var(--parchment-700);
  border: 1px solid var(--border);
  color: var(--text-muted);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.hex-vis-toggle:hover { border-color: var(--accent); color: var(--accent); }
.hex-vis-toggle[data-vis="private_st"] {
  border-color: var(--accent);
  color: var(--accent);
}
