/*
 * Herald — Hunter's Journal Theme
 * Worn leather journal meets occult researcher's notes.
 * Medium grunge: visible character without being unusable.
 *
 * LOAD-BEARING, not a decorative overlay: this is Herald's global design-
 * system layer, loaded after main.css on every page (see base.html /
 * base_public.html's <link> order). main.css relies on it — e.g. it
 * deliberately dropped its own .specialty-input-field declarations because
 * this file's shared input[type="text"] rule already supplies them (see
 * main.css's removal comment on that class). Treat changes here as
 * changes to the app's baseline styling, not as an optional skin; there is
 * no "revert to base.html" rollback.
 */

/* -------------------------------------------------------------------------
   Google Fonts are loaded via <link> in base.html / base_public.html
   IM Fell English (headings), Crimson Text (body), Special Elite (typewriter)
   ------------------------------------------------------------------------- */

/* =========================================================================
   §04 — LOCKED TYPE SCALE  (base: 17px = 1rem)
   Three fonts, each in its own lane — never mix outside it:
     IM Fell English  → display / headings
     Crimson Text     → body copy; italic = field labels & hints
     Special Elite    → typewriter: eyebrows, nav labels, stat readouts, dice

   Usage: add class "type-display", "type-h1", … "type-caption" to any
   element that needs an explicit step instead of inheriting the element's
   default. Components should pick a step name, never a raw px value.
   ========================================================================= */

.type-display {
  font-family: 'IM Fell English', Georgia, serif;
  font-size: 3.059rem;    /* 52px @ 17px base */
  line-height: 1.05;
  letter-spacing: 0.04em;
  color: var(--heading);
}

.type-h1 {
  font-family: 'IM Fell English', Georgia, serif;
  font-size: 2.353rem;    /* 40px */
  line-height: 1.1;
  color: var(--heading);
}

.type-h2 {
  font-family: 'IM Fell English', Georgia, serif;
  font-size: 1.647rem;    /* 28px */
  line-height: 1.15;
  color: var(--heading);
}

.type-h3 {
  font-family: 'IM Fell English', Georgia, serif;
  font-size: 1.235rem;    /* 21px */
  line-height: 1.2;
  color: var(--heading);
}

.type-body-lg {
  font-family: 'Crimson Text', Georgia, serif;
  font-size: 1.118rem;    /* 19px */
  line-height: 1.6;
  color: var(--text);
}

.type-body {
  font-family: 'Crimson Text', Georgia, serif;
  font-size: 1rem;        /* 17px */
  line-height: 1.6;
  color: var(--text);
}

.type-small {
  font-family: 'Crimson Text', Georgia, serif;
  font-size: 0.824rem;    /* 14px */
  line-height: 1.5;
  color: var(--text-muted);
}

.type-caption {
  font-family: 'Special Elite', 'Courier New', monospace;
  font-size: 0.647rem;    /* 11px */
  line-height: 1.4;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* -------------------------------------------------------------------------
   BASE — leather ground layer & typography
   ------------------------------------------------------------------------- */
html {
  background-color: var(--bg);
}

body {
  background: transparent;
  color: var(--text);
  font-family: 'Crimson Text', Georgia, serif;
  font-size: 1rem;         /* §04 body = 17px = 1rem */
  line-height: 1.6;
}

/* Fixed leather ground — sits behind all page content, including cards */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  background: var(--texture-leather) center / cover no-repeat;
  opacity: var(--texture-leather-opacity);
  pointer-events: none;
  z-index: -1;
}

/* Typography hierarchy */
h1, h2, h3, h4 {
  font-family: 'IM Fell English', Georgia, serif;
  color: var(--heading);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
  letter-spacing: 0.05em;
}

/* Handwritten labels */
label, .label-style {
  font-family: 'Crimson Text', serif;
  font-style: italic;
  color: var(--text-muted);
  font-size: 0.824rem;     /* §04 small = 14px */
}

/* Typewriter elements */
.font-mono, code, .invite-code {
  font-family: 'Special Elite', 'Courier New', monospace;
  letter-spacing: 0.05em;
}

/* -------------------------------------------------------------------------
   NAVIGATION — leather spine / binding
   ------------------------------------------------------------------------- */
nav {
  background: linear-gradient(
    to bottom,
    var(--ink-800) 0%,
    var(--parchment-800) 50%,
    var(--ink-800) 100%
  );
  border-bottom: 2px solid var(--danger);
  box-shadow:
    inset 0 1px 0 rgba(107, 103, 98, 0.2),
    0 2px 10px rgba(0, 0, 0, 0.8);
}

/* Nav utility buttons (ROLL, dice settings) */
.btn-nav {
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-xs) var(--space-sm);
  cursor: pointer;
  color: var(--text-muted);
  font-family: 'Special Elite', Courier, monospace;
  font-size: 11px;
  letter-spacing: 1px;
  text-decoration: none;
  transition: border-color var(--motion-fast) var(--ease-standard),
              color var(--motion-fast) var(--ease-standard);
  display: inline-flex;
  align-items: center;
}

.btn-nav:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* Brand title in nav */
nav a.brand-title {
  font-family: 'IM Fell English', Georgia, serif;
  letter-spacing: 0.12em;
  text-shadow: 0 0 12px color-mix(in srgb, var(--accent) 40%, transparent);
}

/* -------------------------------------------------------------------------
   CARDS — aged paper with worn edges
   ------------------------------------------------------------------------- */
.card-journal {
  background-color: var(--parchment-800);
  /* Layer order (top → bottom): parchment grain, base gradient. The
     --texture-splat grid overlay that used to sit on top was removed —
     it read as visual noise rather than texture and was applied
     inconsistently across the app anyway. */
  background-image:
    var(--texture-parchment),
    linear-gradient(
      135deg,
      var(--parchment-800) 0%,
      var(--parchment-700) 50%,
      var(--parchment-800) 100%
    );
  border: 1px solid var(--border);
  box-shadow:
    inset 0 1px 0 rgba(191, 176, 154, 0.1),
    0 2px 8px rgba(0, 0, 0, 0.6),
    0 0 0 1px rgba(26, 22, 18, 0.8);
  position: relative;
  overflow: hidden;
}

/* Worn edge vignette */
.card-journal::before {
  content: '';
  position: absolute;
  top: -1px; right: -1px; bottom: -1px; left: -1px;
  background: linear-gradient(
    to bottom,
    rgba(61, 50, 36, 0.3) 0%,
    transparent 10%,
    transparent 90%,
    rgba(26, 22, 18, 0.4) 100%
  );
  pointer-events: none;
  border-radius: inherit;
  z-index: 0;
}

/* Ensure card content sits above the pseudo-element */
.card-journal > * {
  position: relative;
  z-index: 1;
}

/* Subtle coffee stain accent on select cards */
.stained::after {
  content: '';
  position: absolute;
  width: 60px;
  height: 60px;
  background: radial-gradient(
    circle,
    rgba(93, 64, 55, 0.15) 0%,
    transparent 70%
  );
  top: 10%;
  right: 15%;
  pointer-events: none;
  border-radius: 50% 40% 50% 40%;
  transform: rotate(-25deg);
  z-index: 0;
}

/* Ink accent mark (very subtle) */
.ink-accent {
  position: relative;
}

.ink-accent::before {
  content: '•';
  position: absolute;
  color: rgba(28, 25, 23, 0.4);
  font-size: 4px;
  top: 8px;
  left: 12px;
}

/* -------------------------------------------------------------------------
   DOT TRACKER — hand-drawn circles
   ------------------------------------------------------------------------- */
.dot-tracker span {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 1.5px solid var(--accent);
  background: transparent;
  position: relative;
  cursor: pointer;
  transition: background var(--motion-base) var(--ease-standard),
              border-color var(--motion-base) var(--ease-standard);
  box-shadow:
    inset 0 0 2px color-mix(in oklch, var(--accent), transparent 70%),
    0 1px 2px rgba(0, 0, 0, 0.5);
}

/* Outer wobble ring — hand-drawn feel */
.dot-tracker span::after {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
  clip-path: polygon(
    0% 10%, 10% 0%, 20% 5%, 30% 0%, 40% 8%,
    50% 0%, 60% 5%, 70% 0%, 80% 10%, 90% 0%,
    100% 10%, 95% 20%, 100% 30%, 95% 40%, 100% 50%,
    95% 60%, 100% 70%, 90% 80%, 100% 90%, 90% 100%,
    80% 95%, 70% 100%, 60% 95%, 50% 100%, 40% 92%,
    30% 100%, 20% 95%, 10% 100%, 0% 90%, 5% 80%,
    0% 70%, 5% 60%, 0% 50%, 5% 40%, 0% 30%, 5% 20%
  );
}

.dot-tracker span:hover {
  background: color-mix(in oklch, var(--accent), transparent 80%);
  border-color: var(--accent-hover);
}

.dot-tracker span.filled {
  background: radial-gradient(
    circle,
    var(--accent-hover) 0%,
    var(--accent) 60%,
    var(--accent-active) 100%
  );
  box-shadow:
    inset 0 -1px 3px rgba(0, 0, 0, 0.4),
    0 0 8px color-mix(in oklch, var(--accent), transparent 60%);
}

.dot-tracker span.aggravated {
  background: radial-gradient(
    circle,
    var(--crimson-700) 0%,
    var(--crimson-800) 100%
  );
  border-color: var(--danger);
  box-shadow:
    inset 0 0 4px color-mix(in srgb, var(--crimson-600) 40%, transparent),
    0 1px 2px rgba(0, 0, 0, 0.6);
}

.dot-tracker span.superficial {
  background: color-mix(in srgb, var(--crimson-700) 40%, transparent);
  border-color: var(--crimson-600);
}

/* =========================================================================
   §Button — deco outline button (.btn-hex)
   Static Art Deco frame: two horizontal rules (border-top/border-bottom)
   closing into a mitred point at each end. Transparent at rest in every
   splat — no fill, no --accent, anywhere in the rest state. On hover the
   button and its end caps take a flat --accent tint (--btn-hover-bg),
   which is the only place --accent appears. No clip-path anywhere.
   On .btn-hex-deco specifically, a cursor-tracked metallic rim-shine also
   travels along the rules and caps — see "RIM SHINE" below.

   TWO-LEVEL STRUCTURE
     .btn-hex        — base. Works on a bare <button> with no children:
                       the two rules plus both end caps, drawn as ::before/
                       ::after on the button itself. No diamonds — a bare
                       element only has two pseudo-element slots, and the
                       caps already spend both. This is what
                       button[type="submit"] gets on raw markup, so every
                       form renders a styled submit button without opting
                       into the macro.
     .btn-hex-deco   — additive modifier. Requires the macro's children:
                       the caps become real <span> elements
                       (.btn-hex-cap-left/-right), which frees up ::before/
                       ::after on each cap to draw its diamond. Layered on
                       top of the base, not baked into a tier, so later
                       work can add effects to the deco caps without
                       touching the base geometry.

   GEOMETRY — one square per end, carrying only border-bottom and
   border-left. Those two sides meet at the square's corner; rotating the
   whole square turns that corner into a mitred point:
     left cap:  rotate(45deg)   → point aims left,  forms "<"
     right cap: rotate(225deg)  → point aims right, forms ">"
   (225deg, not -135deg — same angle, chosen because it reads unambiguously
   as "45deg plus a half turn" next to the left cap's rotate(45deg) instead
   of forcing the reader to reconcile a negative angle against a positive
   one.) Each cap is square with side --btn-cap, sized so its rotated
   point-to-point DIAGONAL — not the raw side length — is ~1.45× --btn-h
   (the rule gap); see --btn-cap's own comment for why the ÷√2 is there.
   It's positioned with translate(-50%, -50%) so its centre sits exactly
   on the button's own edge — half the square outside the button's box,
   half overlapping it. That overlap is what lines the cap's borders up
   with the two rules with no gap and no stroke-weight mismatch.

   HOVER FILL — .btn-hex and both caps take the same flat --btn-hover-bg.
   The button's own fill covers the rectangular body; each cap, being a
   full square (not just its two visible border edges), fills as a diamond
   — half inside the button (merging invisibly with the body fill), half
   outside (the triangular point). Body + two half-diamonds together read
   as one filled hexagon with no seam and no clip-path.

   DIAMONDS (.btn-hex-deco only) — each real cap's own ::before (bordered
   square, the outline) and ::after (smaller solid square, the fill) are
   positioned at the cap's own mitre corner (left:0; bottom:0, the same
   corner the border-bottom/border-left meet at) rather than centred on
   the cap, so the diamond straddles the point instead of the square's
   centroid. Both pseudo-elements inherit the cap's rotation automatically
   — a transform on an element carries its ::before/::after with it — so
   two squares positioned on an unrotated corner render as diamonds once
   the cap rotates. Static in this branch: outline plus solid centre,
   always visible, no proximity flare.

   RIM SHINE (.btn-hex-deco only) — a metallic catch-light, not a coloured
   glow: a tight near-white highlight (--btn-shine) that slides along the
   rules and brightens the caps as the cursor nears, modelled on
   htr-glow.js's pattern (one delegated, rAF-throttled mousemove listener;
   --shine-x/--shine-intensity custom properties) but as its own module,
   btn-shine.js — htr-glow.js is untouched. Two techniques, chosen after
   the more obvious one broke:
     Rules: border-image-source with a linear-gradient stepping from
     --btn-rule to --btn-shine and back at --shine-x, sliced 1 onto the
     existing border-top/border-bottom. The obvious approach — a
     radial-gradient masked to a border-box/content-box ring, the same
     technique htr-sheet.css's own glow already uses — turned out to only
     paint anything when the gradient's centre sits at exactly 50% of its
     own box; any other position rendered nothing, with no error. That
     technique is fine for htr-sheet.css's case (glow position is
     incidental there) but wrong for a highlight that must track the
     cursor, so this uses border-image instead, which has no such failure
     mode and needs no mask at all. Confirmed by rendering both
     side-by-side and sampling pixels at multiple cursor positions before
     committing to this approach — don't revert to the masked-gradient
     version without re-verifying off-centre positions actually paint.
     Caps: no gradient needed — border-color and the diamond's border/
     fill both color-mix toward --btn-shine by --shine-intensity, the same
     idiom htr-sheet.css uses for its own proximity brightening. This is
     intensity-only (no position), since the caps are rotated and mapping
     --shine-x into each cap's own rotated coordinate space isn't worth
     the complexity for two small corners.

   TOKENS — --btn-rule (brass off the Gold ramp for shell/HTR/VTM, silver
   for WTA — swapped in tokens.css §03), --btn-hover-bg (a flat color-mix
   of --accent, so the splat identity only shows on hover), and
   --btn-shine (near-white metallic highlight, brass/silver-swapped
   alongside --btn-rule).

   TIERS (unchanged in shape from tier to tier — only the rule colour and
   whether the deco modifier is present differ)
     Action       .btn-hex + .btn-hex-deco — full frame, diamonds, hover fill.
     Secondary    .btn-hex + .btn-hex-secondary — muted rule colour, base
                  frame only, no diamonds.
     Destructive  .btn-hex + .btn-hex-destructive — off --danger, hover
                  fill tinted with --danger instead of --accent.
     Ghost        .btn-hex-ghost — text only, no frame.
     Icon         .btn-hex-icon — square, no diamonds.
   Plus .btn-hex-sm / .btn-hex-full, orthogonal to tier.

   Usage — see frontend/templates/macros/button.html for the macro that
   assembles the tier classes and (for tier='action') the deco cap markup.
   ========================================================================= */

/* ---- Engine + Action palette (also button[type="submit"]'s default look,
   so raw markup outside the macro still renders on-system) ---- */
.btn-hex,
button[type="submit"] {
  --btn-h: 40px;                              /* content height between the two rules */
  /* End-cap square side, 0.725×--btn-h — the ratio the reference this
     component is ported from actually uses (cap: 1.45rem square at a
     2rem button height, i.e. cap = 0.725 × height; "1.45×" in prose
     referred to half the height, not the whole of it). Confirmed by
     rendering the reference verbatim and measuring: at that ratio the
     cap's rotated point-to-point diagonal lands almost exactly on the
     button's own content height, so the mitre point meets the rules
     instead of overshooting or falling short of them. */
  --btn-cap: calc(var(--btn-h) * 0.725);

  /* position + z-index give this element its own stacking context, so the
     cap pseudo-elements (and, under .btn-hex-deco, the real cap children)
     paint in a predictable order relative to the label regardless of what
     stacking contexts exist further up the page. */
  position: relative;
  z-index: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--btn-h);
  /* A square's rotated bounding box is its side × √2; half of that (0.71,
     not the raw side or a smaller fraction) is how far the cap actually
     pokes past the button's edge, so that's the minimum margin that
     clears it without an adjacent element touching the mitre tip. */
  margin: 0 calc(var(--btn-cap) * 0.71);
  padding: 0 calc(var(--btn-cap) / 2 + var(--space-md));
  /* Explicit reset, not all: unset — that strips the focus outline along
     with everything else. Clear the UA button chrome by hand, then add
     back only the two rules. */
  background: transparent;
  border: none;
  border-top: 1px solid var(--btn-rule);
  border-bottom: 1px solid var(--btn-rule);
  color: var(--btn-rule);
  font-family: 'Special Elite', 'Courier New', monospace;
  font-size: 0.77rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--motion-base) var(--ease-standard),
              transform var(--motion-fast) var(--ease-standard),
              opacity var(--motion-base) var(--ease-standard);
}

/* End caps — see "GEOMETRY" above. */
.btn-hex::before,
.btn-hex::after,
button[type="submit"]::before,
button[type="submit"]::after {
  content: '';
  position: absolute;
  top: 50%;
  width: var(--btn-cap);
  height: var(--btn-cap);
  border: none;
  border-bottom: 1px solid var(--btn-rule);
  border-left: 1px solid var(--btn-rule);
  pointer-events: none;
  transition: background var(--motion-base) var(--ease-standard);
}
.btn-hex::before,
button[type="submit"]::before {
  left: 0;
  transform: translate(-50%, -50%) rotate(45deg);
}
.btn-hex::after,
button[type="submit"]::after {
  right: 0;
  transform: translate(50%, -50%) rotate(225deg);
}

/* Hover fill — see "HOVER FILL" above. */
.btn-hex:not(:disabled):hover,
button[type="submit"]:not(:disabled):hover {
  background: var(--btn-hover-bg);
}
.btn-hex:not(:disabled):hover::before,
.btn-hex:not(:disabled):hover::after,
button[type="submit"]:not(:disabled):hover::before,
button[type="submit"]:not(:disabled):hover::after {
  background: var(--btn-hover-bg);
}

.btn-hex:not(:disabled):active,
button[type="submit"]:not(:disabled):active {
  transform: scale(0.96);
}

.btn-hex:disabled,
button[type="submit"]:disabled {
  opacity: var(--disabled-opacity);
  cursor: not-allowed;
}

.btn-hex:focus-visible,
button[type="submit"]:focus-visible,
.btn-hex-ghost:focus-visible,
.btn-hex-icon:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* ---- Deco modifier — requires the macro's children (real cap elements) ---- */

/* The button's own pseudo caps are suppressed; the real .btn-hex-cap-*
   elements take over so they have pseudo-element slots free for diamonds. */
.btn-hex-deco::before,
.btn-hex-deco::after {
  content: none;
}

/* Rim shine — see "RIM SHINE" above. --shine-x/--shine-intensity are
   written by btn-shine.js; 0 intensity at rest means the border-image
   below is indistinguishable from a flat --btn-rule border until then. */
.btn-hex-deco {
  --shine-x: 50%;
  --shine-intensity: 0;
  border-image-slice: 1;
  border-image-source: linear-gradient(90deg,
    var(--btn-rule) 0%,
    var(--btn-rule) calc(var(--shine-x) - 18%),
    color-mix(in srgb, var(--btn-shine) calc(var(--shine-intensity) * 100%), var(--btn-rule)) var(--shine-x),
    var(--btn-rule) calc(var(--shine-x) + 18%),
    var(--btn-rule) 100%);
}

.btn-hex-cap {
  position: absolute;
  top: 50%;
  width: var(--btn-cap);
  height: var(--btn-cap);
  z-index: 0;
  border: none;
  border-bottom: 1px solid color-mix(in srgb, var(--btn-shine) calc(var(--shine-intensity) * 100%), var(--btn-rule));
  border-left: 1px solid color-mix(in srgb, var(--btn-shine) calc(var(--shine-intensity) * 100%), var(--btn-rule));
  pointer-events: none;
  transition: background var(--motion-base) var(--ease-standard);
}
.btn-hex-cap-left {
  left: 0;
  transform: translate(-50%, -50%) rotate(45deg);
}
.btn-hex-cap-right {
  right: 0;
  transform: translate(50%, -50%) rotate(225deg);
}

/* Deco tier skips the flat --btn-hover-bg fill entirely (overriding the
   base rule above) — it already has proximity feedback via the rim shine
   and cap brightening, and a flat wash across the whole body competed
   with the shine instead of reading as a second signal. Compound class
   selector, not source order, is what wins here: deco buttons are real
   `<button type="submit">` elements too, so the base rule's
   `button[type="submit"]:not(:disabled):hover` (0,0,3,1) outranks a bare
   `.btn-hex-deco:not(:disabled):hover` (0,0,3,0) regardless of which
   comes later in the file — `.btn-hex.btn-hex-deco` bumps the class count
   to clear that bar unambiguously. */
.btn-hex.btn-hex-deco:not(:disabled):hover {
  background: transparent;
}

/* Diamonds — see "DIAMONDS" above. Positioned at the cap's own mitre
   corner (left:0; bottom:0 — the border-bottom/border-left corner) so
   they inherit the cap's rotation as diamonds straddling the point.
   Sizes (0.517 / 0.345 of --btn-cap) are the reference's own ratios —
   0.75rem and 0.5rem outline/solid squares at a 1.45rem cap. Border/fill
   color-mix toward --btn-shine with proximity, same as the cap itself. */
.btn-hex-cap::before,
.btn-hex-cap::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  border: 1px solid color-mix(in srgb, var(--btn-shine) calc(var(--shine-intensity) * 100%), var(--btn-rule));
  transition: border-color var(--motion-base) var(--ease-standard),
              background var(--motion-base) var(--ease-standard);
}
.btn-hex-cap::before {
  width: calc(var(--btn-cap) * 0.517);
  height: calc(var(--btn-cap) * 0.517);
  transform: translate(-50%, 50%);
}
.btn-hex-cap::after {
  width: calc(var(--btn-cap) * 0.345);
  height: calc(var(--btn-cap) * 0.345);
  background: color-mix(in srgb, var(--btn-shine) calc(var(--shine-intensity) * 100%), var(--btn-rule));
  transform: translate(-50%, 50%);
}

.btn-hex-label {
  position: relative;
  z-index: 1;
}

/* ---- Tiers ---- */

/* Secondary — muted rule colour, base frame only, no diamonds */
.btn-hex-secondary {
  --btn-rule: var(--parchment-400);
}

/* Destructive — off --danger; hover tints with --danger, not --accent */
.btn-hex-destructive {
  --btn-rule: var(--danger);
}
.btn-hex-destructive:not(:disabled):hover {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
}

/* Icon — square, no diamonds */
.btn-hex-icon {
  --btn-h: 40px;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--btn-h);
  height: var(--btn-h);
  padding: 0;
  background: transparent;
  border: 1px solid var(--btn-rule);
  border-radius: var(--radius-sm);
  color: var(--btn-rule);
  text-decoration: none;
  cursor: pointer;
  transition: background var(--motion-base) var(--ease-standard),
              transform var(--motion-fast) var(--ease-standard),
              opacity var(--motion-base) var(--ease-standard);
}
.btn-hex-icon:not(:disabled):hover { background: var(--btn-hover-bg); }
.btn-hex-icon:not(:disabled):active { transform: scale(0.94); }
.btn-hex-icon:disabled { opacity: var(--disabled-opacity); cursor: not-allowed; }

/* Ghost — text only, no frame; does not use the button engine at all */
.btn-hex-ghost {
  --btn-h: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--btn-h);
  padding: 0 var(--space-md);
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-family: 'Special Elite', 'Courier New', monospace;
  font-size: 0.77rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: color var(--motion-base) var(--ease-standard),
              opacity var(--motion-base) var(--ease-standard);
}
.btn-hex-ghost:not(:disabled):hover { color: var(--accent); }
.btn-hex-ghost:disabled { opacity: var(--disabled-opacity); cursor: not-allowed; }

/* ---- Size / width modifiers — orthogonal to tier, so declared last to win
   the cascade over each tier's own --btn-h default ---- */
.btn-hex-sm {
  --btn-h: 30px;
  font-size: 0.68rem;
}

.btn-hex-full {
  width: calc(100% - var(--btn-cap) * 1.1);
  margin-left: auto;
  margin-right: auto;
}

/* -------------------------------------------------------------------------
   INPUT FIELDS — ink-stained parchment
   ------------------------------------------------------------------------- */
/* :where() holds every selector below at zero specificity on purpose —
   this is the shared cross-sheet default, so any splat sheet's own
   class-level input styling (e.g. wta-sheet.css's .header-input) should
   always be able to override it without needing to out-specificity an
   attribute selector. Before this, input[type="text"] etc. (specificity
   0,1,1) silently beat plain class selectors (0,1,0) regardless of
   stylesheet load order, which caused several WTA sheet inputs to
   silently render this shared parchment background instead of their
   own declared one — see wta-sheet.css's .header-input/.list-entry-input/
   .wta-advantage-name-input history. Purely a specificity change: same
   selectors, same declarations, same elements matched. */
:where(
  input[type="text"],
  input[type="email"],
  input[type="password"],
  input[type="number"],
  select,
  textarea
) {
  background: linear-gradient(
    to bottom,
    var(--parchment-800) 0%,
    var(--parchment-700) 100%
  );
  border: 1px solid var(--border);
  border-bottom-width: 2px;
  color: var(--text);
  font-family: 'Crimson Text', serif;
  font-size: 1rem;
  box-shadow:
    inset 0 2px 4px rgba(0, 0, 0, 0.4),
    0 1px 0 rgba(191, 176, 154, 0.05);
  transition: border-color var(--motion-base) var(--ease-standard),
              box-shadow   var(--motion-base) var(--ease-standard);
}

:where(
  input[type="text"]:focus,
  input[type="email"]:focus,
  input[type="password"]:focus,
  input[type="number"]:focus,
  select:focus,
  textarea:focus
) {
  border-color: var(--accent);
  background: var(--parchment-700);
  outline: none;
  box-shadow: var(--focus-ring);
}

:where(
  input[type="text"]:disabled,
  input[type="email"]:disabled,
  input[type="password"]:disabled,
  input[type="number"]:disabled,
  select:disabled,
  textarea:disabled
) {
  opacity: var(--disabled-opacity);
  cursor: not-allowed;
}

/* Native <option> popup — best-effort theming only. Chromium/WebKit render
   the open listbox as an OS-native widget and ignore font-family plus the
   :checked highlight color, so this cannot fully match the app chrome
   there; Firefox honors it completely. */
select option {
  background: var(--parchment-800);
  color: var(--text);
  font-family: 'Crimson Text', serif;
}

select option:checked,
select option:hover {
  background: var(--accent);
  color: var(--parchment-900);
}

/* Browser autofill override — see the matching .hi-input rule below for why
   this is necessary (UA autofill background can't be beaten with `background`). */
input[type="text"]:-webkit-autofill,
input[type="email"]:-webkit-autofill,
input[type="password"]:-webkit-autofill,
input[type="number"]:-webkit-autofill {
  -webkit-text-fill-color: var(--text);
  -webkit-box-shadow: 0 0 0 1000px var(--parchment-700) inset;
  box-shadow: 0 0 0 1000px var(--parchment-700) inset;
  caret-color: var(--accent);
  transition: background-color 5000s ease-in-out 0s;
}

/* Placeholder as faded ink */
::placeholder {
  color: var(--border);
  font-style: italic;
  opacity: 0.8;
}

/* -------------------------------------------------------------------------
   INPUT — underline sweep with floating label (.hi-wrap)
   No border/fill at rest — a single dim bottom rule. On focus, an accent
   art-deco double-line sweeps in from the left and the italic typewriter
   label rises into place. "Filled" state uses the placeholder-shown trick,
   so every .hi-input needs placeholder=" " (a single space) even when a
   floating label is present.

   Markup:
     <div class="hi-wrap">
       <input class="hi-input" placeholder=" " id="x">
       <label class="hi-label" for="x">Label</label>
       <span class="hi-sweep"></span>
       <span class="hi-hint">optional hint</span>
     </div>
   ------------------------------------------------------------------------- */
.hi-wrap {
  position: relative;
  margin-bottom: var(--space-md);
}

.hi-input {
  display: block;
  width: 100%;
  padding: 22px 0 8px;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--parchment-700);
  color: var(--text);
  font-family: 'Crimson Text', Georgia, serif;
  font-size: 0.9rem;
  caret-color: var(--accent);
  transition: background var(--motion-base) var(--ease-standard);
}

.hi-input:hover {
  background: color-mix(in oklab, var(--parchment-800) 30%, transparent);
}

.hi-input:focus {
  outline: none;
}

.hi-input:disabled {
  opacity: var(--disabled-opacity);
  cursor: not-allowed;
}

.hi-input.hi-mono {
  font-family: 'Special Elite', 'Courier New', monospace;
}

/* Browser autofill (saved credentials) forces its own yellow background via
   UA styles with very high specificity — normal background/color rules can't
   touch it. This is the standard override: paint an inset box-shadow the
   size of the field in the surface color, and force the text color, instead
   of fighting the background directly. The absurd transition delay stops
   Chrome/Safari from flashing yellow before the override applies. */
.hi-input:-webkit-autofill,
.hi-input:-webkit-autofill:hover,
.hi-input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--text);
  -webkit-box-shadow: 0 0 0 1000px var(--parchment-900) inset;
  box-shadow: 0 0 0 1000px var(--parchment-900) inset;
  caret-color: var(--accent);
  transition: background-color 5000s ease-in-out 0s;
}

.hi-label {
  position: absolute;
  left: 0;
  top: 22px;
  font-family: 'Special Elite', 'Courier New', monospace;
  font-style: italic;
  font-size: 0.88rem;
  color: var(--text-muted);
  pointer-events: none;
  transition: top var(--motion-slow) var(--ease-standard),
              font-size var(--motion-slow) var(--ease-standard),
              color var(--motion-slow) var(--ease-standard);
}

.hi-wrap:focus-within .hi-label,
.hi-input:not(:placeholder-shown) ~ .hi-label {
  top: 2px;
  font-size: 0.68rem;
  color: var(--accent);
}

/* Accent sweep — art deco double-line stub grows to full width on focus */
.hi-sweep {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 6%;
  height: 3px;
  background: linear-gradient(
    to bottom,
    var(--accent) 0px, var(--accent) 1px,
    transparent 1px, transparent 2px,
    color-mix(in srgb, var(--accent) 50%, transparent) 2px,
    color-mix(in srgb, var(--accent) 50%, transparent) 3px
  );
  transition: width 0.4s cubic-bezier(0.16, 1, 0.3, 1);
  pointer-events: none;
}

.hi-wrap:focus-within .hi-sweep {
  width: 100%;
}

.hi-hint {
  display: block;
  margin-top: 4px;
  font-family: 'Crimson Text', Georgia, serif;
  font-style: italic;
  font-size: 0.75rem;
  color: var(--text-muted);
}

/* -------------------------------------------------------------------------
   BADGE — typewriter-stamped tag (.hbadge)
   Octagon shape via clip-path (3px chamfers, never border-radius), flanking
   diamond glyphs, uppercase wide-tracked typewriter label. This is the
   canonical recipe — the app's other tag-like elements (.specialty-tag in
   htr/vtm-sheet.css + sheet-layout.css, .tag in character_public.html,
   .coterie-npc-badge/.mf-type-badge in coterie-sheet.css) apply the same
   clip-path/typography onto their existing class names in place, since
   pointing dozens of call sites at a new class name isn't worth the churn.
   ------------------------------------------------------------------------- */
.hbadge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 10px;
  border-radius: 0;
  clip-path: polygon(3px 0%, calc(100% - 3px) 0%, 100% 3px, 100% calc(100% - 3px), calc(100% - 3px) 100%, 3px 100%, 0% calc(100% - 3px), 0% 3px);
  background: transparent;
  border: 1px solid var(--accent);
  color: var(--accent);
  font-family: 'Special Elite', 'Courier New', monospace;
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  white-space: nowrap;
}

.hbadge::before,
.hbadge::after {
  content: '\25C6';   /* ◆ */
  font-size: 0.48em;
  opacity: 0.55;
}

.hbadge-solid {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
}

.hbadge-tier {
  border-color: var(--gold-500);
  color: var(--gold-500);
  background: color-mix(in srgb, var(--gold-500) 15%, transparent);
}

/* -------------------------------------------------------------------------
   DIALOG — journal-card modal on an ink scrim (.hdialog)
   Canonical recipe. Several hand-rolled modals across the sheet templates
   (character_sheet.html, vtm_character_sheet.html, wta-sheet.css
   .share-modal*, character_new.html wizard overlays) predate this; migrate
   their scrim/panel rules to reference this shape where practical rather
   than restructuring their Alpine x-show markup.

   Markup: <div class="hdialog-scrim"><div class="hdialog-panel"
   role="dialog" aria-modal="true"> ... </div></div>
   ------------------------------------------------------------------------- */
.hdialog-scrim {
  position: fixed;
  inset: 0;
  z-index: 8000; /* modal backdrop/content tier — see z-index scale doc in sheet-layout.css */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-lg);
  background: color-mix(in srgb, var(--scrim) 78%, transparent);
  backdrop-filter: blur(2px);
}

.hdialog-panel {
  position: relative;
  overflow: hidden;
  max-width: 420px;
  width: 100%;
  padding: var(--space-xl);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background-color: var(--parchment-800);
  background-image:
    var(--texture-splat),
    var(--texture-parchment),
    linear-gradient(to bottom, var(--parchment-800), var(--parchment-700));
  box-shadow: var(--shadow-e3), 0 0 0 1px rgba(26, 22, 18, 0.8);
}

.hdialog-panel > * {
  position: relative;
  z-index: 1;
}

.hdialog-title {
  font-family: 'IM Fell English', Georgia, serif;
  font-size: 1.235rem;
  color: var(--heading);
  margin: 0 0 var(--space-md);
}

/* Destructive confirmation titles (archive/delete) read as danger, not heading gold */
.hdialog-title-danger {
  color: var(--danger);
}

.hdialog-panel-wide {
  max-width: 480px;
}

/* Destructive-confirmation panel — blood-tinted ground instead of parchment */
.hdialog-panel-danger {
  background-color: var(--crimson-900);
  background-image: var(--texture-splat), var(--texture-parchment);
  border-color: var(--crimson-800);
}

.hdialog-actions {
  display: flex;
  gap: var(--space-md);
  justify-content: flex-end;
  margin-top: var(--space-xl);
}

/* -------------------------------------------------------------------------
   DICE RESULTS — occult symbols / hand-annotated results
   ------------------------------------------------------------------------- */
.die-result {
  width: 32px;
  height: 32px;
  border-radius: 4px;
  font-family: 'Special Elite', monospace;
  font-weight: bold;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(0, 0, 0, 0.6);
  position: relative;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    0 2px 4px rgba(0, 0, 0, 0.5);
}

/* -------------------------------------------------------------------------
   FLASH MESSAGES — aged parchment notifications
   ------------------------------------------------------------------------- */
/* Success flash */
.bg-green-900 {
  background-color: var(--success) !important;
  border-color: #2d4a2d !important;
}

/* Error flash — blood stain */
.bg-blood-700 {
  background: radial-gradient(
    circle at 30% 30%,
    var(--crimson-700),
    var(--crimson-800)
  );
}

/* -------------------------------------------------------------------------
   SCROLLBAR — worn leather feel
   ------------------------------------------------------------------------- */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: var(--radius-sm); }
::-webkit-scrollbar-thumb:hover { background: var(--parchment-500); }

/* -------------------------------------------------------------------------
   MISC UTILITY OVERRIDES
   ------------------------------------------------------------------------- */

/* Section dividers — ink rule */
.border-t {
  border-top-color: var(--parchment-700);
}

/* Monospace text (invite codes, dice) */
.font-mono {
  font-family: 'Special Elite', 'Courier New', monospace;
  letter-spacing: 0.05em;
}

/* Outcome badges — restyle with journal palette */
.bg-green-800 {
  background-color: var(--success-surface) !important;
}

/* -------------------------------------------------------------------------
   LOGIN PAGE — parchment scroll feel
   ------------------------------------------------------------------------- */
.login-container {
  background: linear-gradient(
    to bottom,
    var(--parchment-800),
    var(--parchment-700)
  );
  border: 1px solid var(--border);
  box-shadow:
    0 4px 24px rgba(0, 0, 0, 0.8),
    inset 0 1px 0 rgba(191, 176, 154, 0.05);
}

/* Discord button keeps its brand color but gets slightly aged */
a[href*="discord"] {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.15),
    0 2px 6px rgba(0, 0, 0, 0.5);
}

/* -------------------------------------------------------------------------
   DARK PACK NOTICE — partials/dark_pack_notice.html, required sitewide.
   Badge + a tight unofficial-content disclaimer carry the visual weight;
   the verbatim Paradox copyright/trademark line is kept intact but
   demoted to fine print underneath.
   ------------------------------------------------------------------------- */
.dp-notice {
  border-top: 1px solid var(--parchment-700);
  background: var(--parchment-900);
  padding: var(--space-2xl) var(--space-md);
  text-align: center;
}
.dp-notice-badge {
  display: block;
  width: 36px;
  height: 36px;
  margin: 0 auto var(--space-sm);
}
.dp-notice-disclaimer {
  font-family: 'Crimson Text', Georgia, serif;
  font-style: italic;
  font-size: 0.95rem;
  color: var(--text);
  margin: 0 0 var(--space-sm);
}
.dp-notice-fine {
  max-width: 640px;
  margin: 0 auto;
  font-size: 0.7rem;
  line-height: 1.5;
  color: var(--text-muted);
}
.dp-notice-fine a { color: var(--htr-500); text-decoration: none; }
.dp-notice-fine a:hover { text-decoration: underline; }
