Skip to content
@import "tailwindcss";
@import "tailwind-animations";
@plugin "daisyui" {
  themes: false;
}

/* ==========================================================================
   THEME — Aetherkeep by candlelight
   Dark keep interior: warm soot-stained stone and oak, amethyst arcana,
   antique gold leaf. Low chroma warm neutrals, heavy borders, carved radii.
   ========================================================================== */
@plugin "daisyui/theme" {
  name: "aetherkeep";
  default: true;
  prefersdark: true;
  color-scheme: dark;

  --color-base-100: oklch(24% 0.018 62);
  --color-base-200: oklch(19% 0.016 55);
  --color-base-300: oklch(14% 0.014 48);
  --color-base-content: oklch(90% 0.028 84);
  --color-primary: oklch(72% 0.13 305);
  --color-primary-content: oklch(17% 0.03 300);
  --color-secondary: oklch(70% 0.095 158);
  --color-secondary-content: oklch(16% 0.03 158);
  --color-accent: oklch(74% 0.14 80);
  --color-accent-content: oklch(18% 0.05 62);
  --color-neutral: oklch(26% 0.026 52);
  --color-neutral-content: oklch(91% 0.028 86);
  --color-info: oklch(70% 0.1 242);
  --color-success: oklch(68% 0.12 145);
  --color-info-content: oklch(16% 0.05 245);
  --color-success-content: oklch(16% 0.05 145);
  --color-warning: oklch(78% 0.13 78);
  --color-warning-content: oklch(19% 0.05 62);
  --color-error: oklch(64% 0.16 26);
  --color-error-content: oklch(96% 0.015 30);

  --radius-selector: 0.5rem;
  --radius-field: 0.3rem;
  --radius-box: 0.6rem;
  --size-selector: 0.25rem;
  --size-field: 0.25rem;
  --border: 2px;
  --depth: 1;
  --noise: 1;
}

@theme {
  --font-gothic: "Grenze Gotisch", "Uncial Antiqua", serif;
  --font-display: "Uncial Antiqua", "Cormorant SC", serif;
  --font-heading: "Cormorant SC", serif;
}

/* ==========================================================================
   ORNAMENT TOKENS
   Silhouette clip paths and inline-SVG motifs shared by the ornament layer.
   Data-URIs use a fixed antique gold (%23c9a35a); intensity is tuned per use
   with opacity so the motifs sit quietly on dark surfaces.
   ========================================================================== */
:root {
  /* Heraldic silhouettes */
  --shield-clip: polygon(
    4% 0,
    96% 0,
    96% 42%,
    92% 58%,
    82% 74%,
    66% 88%,
    50% 100%,
    34% 88%,
    18% 74%,
    8% 58%,
    4% 42%
  );
  --banner-clip: polygon(0 0, 100% 0, 100% 100%, 50% 74%, 0 100%);
  --pennant-clip: polygon(0 0, 100% 0, 82% 50%, 100% 100%, 0 100%);

  /* Gilding and forge palette */
  --gold-line: color-mix(in oklab, var(--color-accent) 58%, transparent);
  --gold-bright: color-mix(in oklab, var(--color-accent) 88%, white);
  --iron-dark: color-mix(in oklab, var(--color-neutral) 42%, black);
  --iron-light: color-mix(in oklab, var(--color-neutral) 88%, white);
  --wax-red: oklch(42% 0.15 24);

  /* Staggered ashlar courses: mortar joints on a 160x88 tile. */
  --tex-ashlar: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='88'%3E%3Cg fill='none' stroke='%23000' stroke-opacity='0.5' stroke-width='2'%3E%3Cpath d='M0 44h160M0 88h160'/%3E%3Cpath d='M80 0v44M0 44v44M120 44v44'/%3E%3C/g%3E%3Cg fill='none' stroke='%23fff' stroke-opacity='0.06'%3E%3Cpath d='M0 45.5h160M0 1.5h160'/%3E%3C/g%3E%3C/svg%3E");

  /* Aged fibre noise for the .grain overlay. */
  --tex-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23g)'/%3E%3C/svg%3E");

  /* Illuminated margin vine with interlace knots, 48x120 seamless tile. */
  --orn-vine: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='120'%3E%3Cg fill='none' stroke='%23c9a35a' stroke-width='1.4' stroke-linecap='round'%3E%3Cpath d='M24 0c-13 15 13 30 0 45s13 30 0 45 13 22 0 30'/%3E%3Cpath d='M24 14c7-5 14-2 13 5-6 5-12 1-13-5z'/%3E%3Cpath d='M24 62c-7-5-14-2-13 5 6 5 12 1 13-5z'/%3E%3Cpath d='M24 100c7-4 13-1 12 5-5 4-11 1-12-5z'/%3E%3C/g%3E%3Cg fill='%23c9a35a' fill-opacity='0.8'%3E%3Ccircle cx='24' cy='45' r='2.6'/%3E%3Ccircle cx='24' cy='90' r='2.2'/%3E%3C/g%3E%3C/svg%3E");

  /* Vine terminal that caps a hairline rule. */
  --orn-terminal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='14'%3E%3Cg fill='none' stroke='%23c9a35a' stroke-width='1.3' stroke-linecap='round'%3E%3Cpath d='M0 7h11'/%3E%3Cpath d='M11 7c3 0 4-4 8-4 4 0 5 5 1 6-3 .8-5-1.6-2-3'/%3E%3C/g%3E%3C/svg%3E");

  /* Corner brackets, one path mirrored into each of the four corners. */
  --orn-corner-tl: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cg fill='none' stroke='%23c9a35a' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M1 20V7c0-3.3 2.7-6 6-6h13'/%3E%3Cpath d='M7 1c0 3.3-2.7 6-6 6'/%3E%3C/g%3E%3Ccircle cx='4.6' cy='4.6' r='1.5' fill='%23c9a35a'/%3E%3C/svg%3E");
  --orn-corner-tr: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cg transform='translate(24,0) scale(-1,1)'%3E%3Cg fill='none' stroke='%23c9a35a' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M1 20V7c0-3.3 2.7-6 6-6h13'/%3E%3Cpath d='M7 1c0 3.3-2.7 6-6 6'/%3E%3C/g%3E%3Ccircle cx='4.6' cy='4.6' r='1.5' fill='%23c9a35a'/%3E%3C/g%3E%3C/svg%3E");
  --orn-corner-bl: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cg transform='translate(0,24) scale(1,-1)'%3E%3Cg fill='none' stroke='%23c9a35a' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M1 20V7c0-3.3 2.7-6 6-6h13'/%3E%3Cpath d='M7 1c0 3.3-2.7 6-6 6'/%3E%3C/g%3E%3Ccircle cx='4.6' cy='4.6' r='1.5' fill='%23c9a35a'/%3E%3C/g%3E%3C/svg%3E");
  --orn-corner-br: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cg transform='translate(24,24) scale(-1,-1)'%3E%3Cg fill='none' stroke='%23c9a35a' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M1 20V7c0-3.3 2.7-6 6-6h13'/%3E%3Cpath d='M7 1c0 3.3-2.7 6-6 6'/%3E%3C/g%3E%3Ccircle cx='4.6' cy='4.6' r='1.5' fill='%23c9a35a'/%3E%3C/g%3E%3C/svg%3E");
}

body {
  min-height: 100vh;
  font-family: "Spectral", serif;
  background-color: var(--color-base-300);
  background-image:
    radial-gradient(
      circle at 50% -8%,
      color-mix(in oklab, var(--color-primary) 15%, transparent) 0,
      transparent 32rem
    ),
    radial-gradient(
    circle at 8% 92%,
    color-mix(in oklab, var(--color-accent) 10%, transparent) 0,
    transparent 26rem
  ),
    repeating-linear-gradient(
    135deg,
    color-mix(in oklab, var(--color-accent) 3%, transparent) 0 1px,
    transparent 1px 3rem
  );
  background-attachment: fixed;
}

.font-display,
.font-heading,
.font-gothic {
  text-wrap: balance;
}

/* ==========================================================================
   ORNAMENT · TEXTURES
   Pure CSS / inline-SVG surfaces. No image assets.
   ========================================================================== */

/* Dark ashlar masonry with mortar joints and a top-down candlelight falloff. */
.stone-wall {
  position: relative;
  background-color: color-mix(
    in oklab,
    var(--color-neutral) 34%,
    var(--color-base-300)
  );
  background-image:
    linear-gradient(
      180deg,
      color-mix(in oklab, var(--color-accent) 11%, transparent) 0,
      transparent 34%,
      rgb(0 0 0 / 34%) 100%
    ),
    repeating-linear-gradient(
    92deg,
    rgb(255 255 255 / 3%) 0 0.6rem,
    transparent 0.6rem 1.7rem
  ),
    var(--tex-ashlar);
  background-size: auto, auto, 10rem 5.5rem;
  box-shadow: inset 0 0 3rem rgb(0 0 0 / 45%);
}

/* Dark-tanned vellum for reading surfaces: warm tone, fibre grain, worn edges. */
.vellum {
  position: relative;
  background-color: color-mix(
    in oklab,
    var(--color-accent) 8%,
    var(--color-base-100)
  );
  background-image:
    radial-gradient(
      ellipse at 22% 14%,
      rgb(255 238 196 / 7%),
      transparent 55%
    ),
    radial-gradient(ellipse at 80% 86%, rgb(255 220 160 / 5%), transparent 52%),
    repeating-linear-gradient(
    102deg,
    rgb(255 240 204 / 4%) 0 1px,
    transparent 1px 4px
  ),
    repeating-linear-gradient(
    8deg,
    rgb(0 0 0 / 5%) 0 1px,
    transparent 1px 7px
  ),
    radial-gradient(
    ellipse 132% 124% at 50% 48%,
    transparent 50%,
    rgb(0 0 0 / 36%) 100%
  );
  box-shadow: inset 0 0 2.5rem rgb(0 0 0 / 32%);
}

/* Woven diagonal weave for large empty backdrops. */
.tapestry {
  background-color: var(--color-base-300);
  background-image:
    radial-gradient(
      ellipse at 50% 0,
      color-mix(in oklab, var(--color-primary) 15%, transparent),
      transparent 62%
    ),
    repeating-linear-gradient(
    45deg,
    rgb(255 255 255 / 3%) 0 2px,
    transparent 2px 5px
  ),
    repeating-linear-gradient(
    -45deg,
    rgb(0 0 0 / 14%) 0 2px,
    transparent 2px 5px
  );
}

/* Aged fibre noise overlay. Kept at or below 6% so text stays crisp. */
.grain {
  position: relative;
}

.grain::after {
  content: "";
  position: absolute;
  z-index: 0;
  inset: 0;
  background-image: var(--tex-noise);
  background-size: 180px 180px;
  mix-blend-mode: overlay;
  opacity: 0.05;
  pointer-events: none;
}

/* ==========================================================================
   ORNAMENT · GOTHIC ARCHITECTURE
   Note: .arch-panel owns ::before (keystone) and .corner-fleurons owns
   ::before/::after — apply .corner-fleurons to the inner body element.
   ========================================================================== */

/* Panel crowned by a pointed/ogee arch with a carved keystone at the apex. */
.arch-panel {
  position: relative;
  border: var(--border) solid var(--gold-line);
  border-radius: 46% 46% var(--radius-box) var(--radius-box) / 22% 22% 0 0;
  padding-top: 1.75rem;
  box-shadow:
    0 1rem 2.25rem rgb(0 0 0 / 45%),
    inset 0 0 0 1px rgb(255 255 255 / 6%),
    inset 0 1.5rem 2rem rgb(0 0 0 / 22%);
}

.arch-panel::before {
  content: "";
  position: absolute;
  z-index: 2;
  top: -0.6rem;
  left: 50%;
  width: 1.35rem;
  height: 1.35rem;
  border: 1px solid var(--gold-line);
  background: linear-gradient(
    145deg,
    var(--gold-bright),
    color-mix(in oklab, var(--color-accent) 40%, var(--color-neutral))
  );
  box-shadow: 0 0 0.8rem
    color-mix(in oklab, var(--color-accent) 45%, transparent);
  pointer-events: none;
  transform: translateX(-50%) rotate(45deg);
}

/* Flat-topped carved frame. Used where an arch curve would clip a content
   grid (deck builder columns) but the panel still needs gold-rimmed framing. */
.frame-panel {
  position: relative;
  border: var(--border) solid var(--gold-line);
  border-radius: var(--radius-box);
  box-shadow:
    0 1rem 2.25rem rgb(0 0 0 / 45%),
    inset 0 0 0 1px rgb(255 255 255 / 6%),
    inset 0 0 2rem rgb(0 0 0 / 25%);
}

/* Selected heraldic plaque: gold-lit rim and a warm bloom. */
.plaque-lit {
  border-color: color-mix(in oklab, var(--color-accent) 85%, transparent);
  box-shadow:
    0 1rem 2.25rem rgb(0 0 0 / 45%),
    inset 0 0 0 1px color-mix(in oklab, var(--color-accent) 48%, transparent),
    inset 0 1.5rem 2rem rgb(0 0 0 / 18%),
    0 0 2rem color-mix(in oklab, var(--color-accent) 32%, transparent);
}

/* Arched inner window for card art and portraits. */
.arch-frame {
  position: relative;
  overflow: hidden;
  border-radius: 46% 46% 0.25rem 0.25rem / 30% 30% 0 0;
  box-shadow:
    inset 0 0 0 1px var(--gold-line),
    inset 0 0 1.5rem rgb(0 0 0 / 48%);
}

/* Vertical carved-column divider. */
.pillar-rule {
  width: 0.7rem;
  min-height: 1.5rem;
  flex: none;
  align-self: stretch;
  border-radius: 0.35rem;
  background:
    linear-gradient(
      90deg,
      transparent 0 8%,
      rgb(0 0 0 / 45%) 14%,
      color-mix(
        in oklab,
        var(--color-accent) 20%,
        var(--color-neutral)
      ) 34% 66%,
      rgb(0 0 0 / 45%) 86%,
      transparent 92%
    ),
    repeating-linear-gradient(
    180deg,
    rgb(255 255 255 / 6%) 0 1px,
    transparent 1px 6px
  );
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 35%);
}

/* ==========================================================================
   ORNAMENT · HERALDRY
   ========================================================================== */

/* Shield silhouette: flat top, curved point. The gold rim is drawn with
   stacked zero-blur drop-shadows so it hugs the clipped silhouette. */
.shield,
.mystic-class-icon {
  --shield-glow: var(--color-primary);
  --shield-lift: 42%;
  box-sizing: border-box;
  border: 0;
  border-radius: 0;
  clip-path: var(--shield-clip);
  filter: drop-shadow(0 1px 0 var(--gold-line)) drop-shadow(
    0 -1px 0 var(--gold-line)
  ) drop-shadow(1px 0 0 var(--gold-line)) drop-shadow(-1px 0 0 var(--gold-line))
    drop-shadow(
    0 0.35rem 0.9rem color-mix(
      in oklab,
      var(--shield-glow) var(--shield-lift),
      transparent
    )
  );
}

.mystic-class-icon {
  padding: 8% 16% 32%;
  object-fit: contain;
  object-position: center;
  box-shadow:
    inset 0 1rem 0 -0.75rem rgb(255 255 255 / 18%),
    inset 0 -1.4rem 1.2rem -0.9rem rgb(0 0 0 / 50%);
  transition: filter 220ms ease, transform 220ms ease;
}

/* Square mount giving the shield art a correct percentage basis: percentage
   padding resolves against the CONTAINING BLOCK's width, not the icon's own
   box, so the art must be absolutely positioned inside a shield-sized mount. */
.shield-mount {
  position: relative;
  display: block;
  flex: none;
  aspect-ratio: 1;
}

.shield-mount > .mystic-class-icon {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.mystic-class-maiden {
  --shield-glow: var(--color-info);
  background: linear-gradient(
    145deg,
    color-mix(in oklab, var(--color-info) 72%, var(--color-neutral)),
    color-mix(in oklab, var(--color-info) 35%, var(--color-neutral))
  );
}

.mystic-class-witch {
  --shield-glow: var(--color-primary);
  background: linear-gradient(
    145deg,
    color-mix(in oklab, var(--color-primary) 82%, var(--color-neutral)),
    color-mix(in oklab, var(--color-primary) 48%, var(--color-neutral))
  );
}

.mystic-class-assassin {
  --shield-glow: var(--color-error);
  background: linear-gradient(
    145deg,
    color-mix(in oklab, var(--color-error) 68%, var(--color-neutral)),
    color-mix(in oklab, var(--color-error) 28%, var(--color-neutral))
  );
}

button:hover .mystic-class-icon,
button:focus-visible .mystic-class-icon,
.combat-hero:hover .mystic-class-icon {
  --shield-lift: 78%;
  transform: translateY(-0.12rem) scale(1.04);
}

/* Hanging cloth banner: swallowtail hem, two rivet rings on the hoist bar. */
.banner,
.city-building span {
  position: relative;
  color: var(--color-neutral-content);
  background:
    linear-gradient(
      180deg,
      rgb(255 255 255 / 10%),
      transparent 32%,
      rgb(0 0 0 / 32%)
    ),
    repeating-linear-gradient(
    90deg,
    rgb(0 0 0 / 8%) 0 2px,
    transparent 2px 6px
  ),
    linear-gradient(
    180deg,
    color-mix(in oklab, var(--color-neutral) 94%, black),
    color-mix(in oklab, var(--color-neutral) 66%, black)
  );
  font-family: var(--font-heading);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-align: center;
  text-transform: uppercase;
  clip-path: var(--banner-clip);
  filter: drop-shadow(0 0.3rem 0.45rem rgb(0 0 0 / 45%));
}

.banner {
  display: block;
  padding: 0.6rem 0.6rem 1.1rem;
  font-size: 0.8rem;
  line-height: 1.1;
}

.banner::before,
.city-building span::before {
  content: "";
  position: absolute;
  top: 0.2rem;
  right: 0.45rem;
  left: 0.45rem;
  height: 0.32rem;
  background-image:
    radial-gradient(
      circle at 0.16rem 50%,
      var(--gold-bright) 0 0.13rem,
      transparent 0.14rem
    ),
    radial-gradient(
    circle at calc(100% - 0.16rem) 50%,
    var(--gold-bright) 0 0.13rem,
    transparent 0.14rem
  );
  opacity: 0.85;
  pointer-events: none;
}

/* Heraldic glyph medallion for carved map tablets. */
.node-medallion {
  display: grid;
  width: 4.5rem;
  height: 5rem;
  place-items: center;
  color: var(--gold-bright);
  background: linear-gradient(
    160deg,
    color-mix(in oklab, var(--color-primary) 72%, black),
    color-mix(in oklab, var(--color-primary) 32%, black)
  );
  font-size: 2.25rem;
  line-height: 1;
  text-shadow: 0 0 0.8rem
    color-mix(in oklab, var(--color-accent) 60%, transparent);
}

/* Small triangular pennant for badges and labels. */
.pennant {
  position: relative;
  padding-inline: 0.7rem 1.6rem;
  border: 0;
  border-radius: 0;
  clip-path: var(--pennant-clip);
  box-shadow: inset 0.18rem 0 0
    color-mix(in oklab, var(--color-accent) 70%, transparent);
}

/* ==========================================================================
   ORNAMENT · MANUSCRIPT ILLUMINATION
   ========================================================================== */

/* Illuminated initial: a gilded gothic capital, lit from within.

   Deliberately paints no background box. An inline box's background is sized
   from the font's own ascent/descent, not from line-height or padding, and
   Grenze Gotisch reports a content area ~1.47x its font-size — so any filled
   ground on ::first-letter renders as a slab that rides up over the eyebrow
   text above the heading and cannot be trimmed back down. The gilding is
   carried by colour and a layered aura instead, which stays inside the line
   box at every heading size. */
.illuminated::first-letter {
  color: var(--gold-bright);
  font-family: var(--font-gothic);
  font-size: 1.14em;
  letter-spacing: 0.02em;
  text-shadow:
    0 0 0.15rem color-mix(in oklab, var(--color-accent) 70%, transparent),
    0 0 0.7rem color-mix(in oklab, var(--color-accent) 45%, transparent),
    0 0 1.4rem color-mix(in oklab, var(--color-primary) 40%, transparent);
}

/* Hairline rule with vine terminals; .fleuron-rule gilds the centred glyph. */
.mystic-rule,
.fleuron-rule {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.mystic-rule::before,
.mystic-rule::after,
.fleuron-rule::before,
.fleuron-rule::after {
  content: "";
  height: 0.9rem;
  flex: 1;
  background-image:
    linear-gradient(
      90deg,
      transparent,
      color-mix(in oklab, var(--color-accent) 65%, transparent) 78%
    ),
    var(--orn-terminal);
  background-repeat: no-repeat, no-repeat;
  background-position: left center, right center;
  background-size:
    calc(100% - 1.75rem) 1px,
    1.75rem 0.9rem;
}

.mystic-rule::after,
.fleuron-rule::after {
  transform: scaleX(-1);
}

.fleuron-rule > * {
  flex: none;
  color: var(--color-accent);
  text-shadow: 0 0 0.65rem
    color-mix(in oklab, var(--color-accent) 60%, transparent);
}

/* Illuminated vine-and-knot margins for full screens. */
.margin-vines::before,
.margin-vines::after,
.room-screen::before,
.room-screen::after {
  content: "";
  position: fixed;
  z-index: 0;
  top: 4rem;
  bottom: 0;
  width: clamp(1.5rem, 6vw, 3.5rem);
  background-image: var(--orn-vine);
  background-repeat: repeat-y;
  background-position: center top;
  background-size: 100% auto;
  opacity: 0.42;
  pointer-events: none;
}

.margin-vines::before,
.room-screen::before {
  left: 0;
}

.margin-vines::after,
.room-screen::after {
  right: 0;
}

/* Gold corner brackets. ::before carries the top pair, ::after the bottom. */
.corner-fleurons {
  --fleuron-size: 1.5rem;
  --fleuron-inset: 0.4rem;
  position: relative;
}

.corner-fleurons::before,
.corner-fleurons::after {
  content: "";
  position: absolute;
  z-index: 1;
  right: var(--fleuron-inset);
  left: var(--fleuron-inset);
  height: var(--fleuron-size);
  background-repeat: no-repeat, no-repeat;
  background-size: var(--fleuron-size) var(--fleuron-size);
  opacity: 0.7;
  pointer-events: none;
}

.corner-fleurons::before {
  top: var(--fleuron-inset);
  background-image:
    var(--orn-corner-tl),
    var(--orn-corner-tr);
  background-position: left top, right top;
}

.corner-fleurons::after {
  bottom: var(--fleuron-inset);
  background-image:
    var(--orn-corner-bl),
    var(--orn-corner-br);
  background-position: left bottom, right bottom;
}

/* ==========================================================================
   ORNAMENT · CRAFT DETAILS
   ========================================================================== */

/* Crimson wax seal with an embossed sigil ring and an irregular poured edge. */
.wax-seal {
  --seal-size: 4.5rem;
  position: relative;
  display: grid;
  width: var(--seal-size);
  height: var(--seal-size);
  place-items: center;
  gap: 0.2rem;
  border: 0;
  border-radius: 46% 54% 52% 48% / 50% 46% 54% 50%;
  color: color-mix(in oklab, var(--color-accent) 62%, white);
  background:
    radial-gradient(circle at 34% 26%, rgb(255 255 255 / 24%), transparent 44%),
    radial-gradient(circle at 64% 76%, rgb(0 0 0 / 36%), transparent 54%),
    conic-gradient(
    from 20deg,
    var(--wax-red),
    color-mix(in oklab, var(--wax-red) 70%, black) 22%,
    var(--wax-red) 46%,
    color-mix(in oklab, var(--wax-red) 78%, black) 70%,
    var(--wax-red)
  );
  box-shadow:
    0 0.4rem 0.9rem rgb(0 0 0 / 50%),
    inset 0 -0.3rem 0.6rem rgb(0 0 0 / 32%),
    inset 0 0.25rem 0.5rem rgb(255 255 255 / 10%);
  text-shadow: 0 1px 0 rgb(0 0 0 / 45%);
}

.wax-seal-sm {
  --seal-size: 2.25rem;
  font-size: 0.9rem;
}

/* Anchors a seal onto the corner of a control. .wax-seal sets position:
   relative unlayered, which would beat a Tailwind `absolute` utility. */
.wax-seal-affix {
  position: absolute;
  z-index: 2;
  right: calc(var(--seal-size) / -2);
  bottom: calc(var(--seal-size) / -2.6);
  pointer-events: none;
  transform: rotate(-8deg);
}

.wax-seal::before {
  content: "";
  position: absolute;
  inset: 15%;
  border: 1px solid currentColor;
  border-radius: 50%;
  opacity: 0.45;
  pointer-events: none;
}

/* Gilded coin used for currency badges. */
.anima-coin {
  border: 0;
  border-radius: 9999px;
  color: var(--color-accent-content);
  background:
    radial-gradient(circle at 34% 28%, rgb(255 255 255 / 42%), transparent 48%),
    linear-gradient(
    150deg,
    var(--gold-bright),
    color-mix(in oklab, var(--color-accent) 62%, var(--color-neutral))
  );
  box-shadow:
    0 0.2rem 0.5rem rgb(0 0 0 / 45%),
    inset 0 0 0 1px color-mix(in oklab, var(--color-accent) 40%, black);
}

/* Forged iron strap: hammered sheen, darkened lower lip, deep seat shadow. */
.iron-band {
  position: relative;
  background-image:
    linear-gradient(
      180deg,
      rgb(255 255 255 / 8%),
      transparent 30%,
      rgb(0 0 0 / 26%)
    ),
    repeating-linear-gradient(
    90deg,
    rgb(255 255 255 / 3%) 0 2px,
    transparent 2px 7px
  ),
    linear-gradient(
    180deg,
    color-mix(in oklab, var(--color-neutral) 82%, black),
    var(--iron-dark)
  );
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 10%),
    inset 0 -2px 0 rgb(0 0 0 / 38%),
    0 0.35rem 0.9rem rgb(0 0 0 / 38%);
}

/* Rivet rows along the top and bottom edges of a forged element. */
.rivets {
  position: relative;
}

.rivets::before,
.rivets::after {
  content: "";
  position: absolute;
  z-index: 1;
  right: 0.6rem;
  left: 0.6rem;
  height: 0.34rem;
  background-image: radial-gradient(
    circle at 50% 50%,
    var(--iron-light) 0 0.06rem,
    color-mix(in oklab, var(--color-neutral) 30%, black) 0.07rem 0.16rem,
    transparent 0.17rem
  );
  background-repeat: repeat-x;
  background-position: left center;
  background-size: 2.75rem 0.34rem;
  opacity: 0.75;
  pointer-events: none;
}

.rivets::before {
  top: 0.28rem;
}

.rivets::after {
  bottom: 0.28rem;
}

/* Warm pool of candlelight. Flicker is disabled under reduced motion. */
.candle-glow {
  --candle-tint: color-mix(
    in oklab,
    var(--color-accent) 65%,
    var(--color-warning)
  );
  position: relative;
}

.candle-glow::before {
  content: "";
  position: absolute;
  z-index: 0;
  inset: -60% -70%;
  border-radius: 50%;
  background: radial-gradient(
    closest-side,
    color-mix(in oklab, var(--candle-tint) 26%, transparent),
    transparent 78%
  );
  pointer-events: none;
  animation: candle-flicker 6.5s ease-in-out infinite;
}

@keyframes candle-flicker {
  0%,
  100% {
    opacity: 0.72;
    transform: scale(1);
  }
  28% {
    opacity: 0.95;
    transform: scale(1.035);
  }
  54% {
    opacity: 0.66;
    transform: scale(0.985);
  }
  76% {
    opacity: 0.88;
    transform: scale(1.02);
  }
}

/* ==========================================================================
   COMPONENTS · daisyUI surfaces restyled as stone, oak and iron
   ========================================================================== */

.navbar {
  position: relative;
  background-color: color-mix(
    in oklab,
    var(--color-neutral) 40%,
    var(--color-base-300)
  );
  background-image:
    linear-gradient(
      180deg,
      color-mix(in oklab, var(--color-accent) 8%, transparent),
      transparent 45%,
      rgb(0 0 0 / 32%)
    ),
    repeating-linear-gradient(
    90deg,
    rgb(255 255 255 / 3%) 0 0.5rem,
    transparent 0.5rem 1.6rem
  ),
    var(--tex-ashlar);
  background-size: auto, auto, 10rem 5.5rem;
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 8%),
    inset 0 -3px 0 var(--iron-dark),
    0 0.5rem 1.6rem rgb(0 0 0 / 45%);
}

.card,
.modal-box,
.stats {
  background-image: linear-gradient(
    145deg,
    rgb(255 255 255 / 4%),
    color-mix(in oklab, var(--color-accent) 5%, transparent)
  );
  box-shadow:
    0 1rem 2.5rem rgb(0 0 0 / 42%),
    inset 0 0 0 1px rgb(255 255 255 / 7%);
}

.card {
  border-radius: var(--radius-box);
}

.card:hover {
  box-shadow:
    0 1.25rem 2.75rem rgb(0 0 0 / 48%),
    0 0 1.5rem color-mix(in oklab, var(--color-accent) 16%, transparent);
}

/* Buttons read as an oak plank bound in iron straps. The vertical 1px grain
   that used to sit here read as a candy stripe on saturated fills, so the
   wood is now expressed with soft horizontal shading plus end straps only. */
.btn {
  position: relative;
  overflow: hidden;
  border-bottom-width: 3px;
  font-family: var(--font-heading);
  font-weight: 700;
  letter-spacing: 0.08em;
  background-image:
    linear-gradient(
      180deg,
      rgb(255 255 255 / 7%),
      transparent 38%,
      rgb(0 0 0 / 26%)
    ),
    linear-gradient(
    90deg,
    rgb(0 0 0 / 26%) 0 0.3rem,
    transparent 0.3rem calc(100% - 0.3rem),
    rgb(0 0 0 / 26%) calc(100% - 0.3rem)
  );
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 10%),
    inset 0 -0.16rem 0 rgb(0 0 0 / 24%);
  transform: translateY(0);
  transition:
    transform 90ms ease,
    border-bottom-width 90ms ease,
    box-shadow 180ms ease,
    filter 180ms ease;
}

/* Solid daisyUI variants become enamelled jewel plates in a gold bezel.
   Scoped away from outline/ghost/soft/link, which set their own --btn-bg. */
:is(
  .btn-primary,
  .btn-secondary,
  .btn-accent,
  .btn-info,
  .btn-success,
  .btn-warning,
  .btn-error,
  .btn-neutral
):not(:is(.btn-outline, .btn-ghost, .btn-soft, .btn-link, .btn-dash)) {
  --btn-bg: color-mix(in oklab, var(--btn-color) 40%, oklch(17% 0.02 52));
  --btn-border: color-mix(in oklab, var(--color-accent) 55%, black);
  --btn-fg: color-mix(
    in oklab,
    var(--color-base-content) 88%,
    var(--btn-color)
  );
  box-shadow:
    inset 0 0 0 1px color-mix(in oklab, var(--color-accent) 30%, transparent),
    inset 0 1px 0 rgb(255 255 255 / 12%),
    inset 0 -0.18rem 0.5rem rgb(0 0 0 / 40%),
    0 0.35rem 0.9rem rgb(0 0 0 / 40%);
}

:is(
  .btn-primary,
  .btn-secondary,
  .btn-accent,
  .btn-info,
  .btn-success,
  .btn-warning,
  .btn-error,
  .btn-neutral
):not(:is(.btn-outline, .btn-ghost, .btn-soft, .btn-link, .btn-dash)):disabled {
  box-shadow: none;
}

/* Warm candlelight glint replaces the old white sheen sweep. */
.btn::after {
  content: "";
  position: absolute;
  inset: -120% 45%;
  width: 22%;
  background: linear-gradient(
    90deg,
    transparent,
    color-mix(in oklab, var(--color-accent) 34%, transparent),
    transparent
  );
  pointer-events: none;
  transform: rotate(24deg) translateX(-320%);
  transition: transform 480ms ease;
}

.btn:hover:not(:disabled)::after,
.btn:focus-visible:not(:disabled)::after {
  transform: rotate(24deg) translateX(320%);
}

.btn:hover:not(:disabled),
.btn:focus-visible:not(:disabled) {
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 14%),
    0 0.45rem 0.9rem rgb(0 0 0 / 42%),
    0 0 1rem color-mix(in oklab, var(--color-accent) 22%, transparent);
  filter: saturate(1.06) brightness(1.04);
}

.btn:active:not(:disabled) {
  border-bottom-width: 1px;
  box-shadow: inset 0 0.2rem 0.45rem rgb(0 0 0 / 30%);
  transform: translateY(2px) scale(0.985);
}

.input,
.textarea,
.select {
  background-color: color-mix(in oklab, var(--color-base-300) 72%, transparent);
  background-image: linear-gradient(
    180deg,
    rgb(0 0 0 / 22%),
    transparent 38%
  );
  box-shadow: inset 0 2px 0.4rem rgb(0 0 0 / 32%);
}

.fieldset-legend {
  font-family: var(--font-heading);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.modal-backdrop {
  background: color-mix(in oklab, var(--color-base-300) 55%, transparent);
  backdrop-filter: blur(5px);
}

/* ==========================================================================
   SCREENS · transitions
   ========================================================================== */

/* Every screen is rendered inside this stage so a departing screen can dim and
   sink away before the next one rises, instead of cutting like a glitch. */
.screen-stage {
  transform-origin: 50% 42%;
}

.screen-stage.is-leaving {
  pointer-events: none;
}

/* The battle spoils arrive as an illuminated leaf laid over the battlefield. */
.reward-scroll {
  border: var(--border) solid var(--gold-line);
  box-shadow:
    0 1.25rem 3rem rgb(0 0 0 / 55%),
    0 0 2.5rem color-mix(in oklab, var(--color-accent) 22%, transparent),
    inset 0 0 0 1px rgb(255 255 255 / 8%);
}

/* The fight stays visible behind the spoils, so the veil is kept thin. */
.reward-modal.modal-open {
  background-color: color-mix(in oklab, var(--color-base-300) 45%, transparent);
}

.reward-modal .modal-backdrop {
  background: transparent;
  backdrop-filter: blur(2px);
}

/* ==========================================================================
   SCREENS · room interiors
   ========================================================================== */

.room-screen {
  position: relative;
  animation: room-aura-shift 10s ease-in-out infinite alternate;
  background:
    linear-gradient(
      90deg,
      color-mix(in oklab, var(--room-color) 10%, transparent),
      transparent 20% 80%,
      color-mix(in oklab, var(--room-color) 10%, transparent)
    ),
    radial-gradient(
    ellipse at 50% 0,
    color-mix(in oklab, var(--room-light) 26%, transparent),
    transparent 58%
  ),
    var(--tex-ashlar),
    var(--color-base-200);
  background-size: 100% 100%, 130% 130%, 10rem 5.5rem, auto;
}

.room-screen > div {
  position: relative;
  z-index: 1;
}

.room-keep {
  --room-color: var(--color-primary);
  --room-light: var(--color-accent);
}

.room-archive {
  --room-color: var(--color-info);
  --room-light: var(--color-primary);
}

.room-atelier {
  --room-color: var(--color-accent);
  --room-light: var(--color-warning);
}

.room-sanctum {
  --room-color: var(--color-secondary);
  --room-light: var(--color-info);
}

/* ==========================================================================
   SCREENS · arcana pack
   ========================================================================== */

/* Dark tooled-leather pack bound in antique gold foil. */
.arcana-pack {
  position: relative;
  display: flex;
  width: min(14rem, 72vw);
  min-height: 20rem;
  overflow: hidden;
  border: var(--border) solid
    color-mix(in oklab, var(--color-accent) 62%, black);
  border-radius: 0.4rem;
  padding: 0;
  color: color-mix(
    in oklab,
    var(--color-base-content) 90%,
    var(--color-accent)
  );
  background:
    radial-gradient(
      ellipse at 50% 12%,
      color-mix(in oklab, var(--color-accent) 12%, transparent),
      transparent 58%
    ),
    repeating-linear-gradient(
    118deg,
    rgb(255 255 255 / 2%) 0 1px,
    transparent 1px 6px
  ),
    repeating-linear-gradient(
    62deg,
    rgb(0 0 0 / 10%) 0 1px,
    transparent 1px 7px
  ),
    linear-gradient(
    150deg,
    color-mix(in oklab, var(--color-neutral) 62%, oklch(16% 0.03 48)),
    oklch(14% 0.025 44) 52%,
    color-mix(in oklab, var(--color-neutral) 48%, oklch(13% 0.025 42))
  );
  box-shadow:
    0 1.25rem 2rem rgb(0 0 0 / 55%),
    0 0 1.5rem color-mix(in oklab, var(--color-accent) 20%, transparent),
    inset 0 0 0 1px color-mix(in oklab, var(--color-accent) 22%, transparent),
    inset 0 0 2rem rgb(0 0 0 / 45%);
  cursor: pointer;
  transform: rotate(-1deg);
  transition: transform 180ms ease, box-shadow 180ms ease;
}

.arcana-pack::before,
.arcana-pack::after {
  content: "";
  position: absolute;
  z-index: 2;
  width: 2.25rem;
  height: 2.25rem;
  border: 1px solid color-mix(in oklab, var(--color-accent) 62%, transparent);
  pointer-events: none;
  transform: rotate(45deg);
}

.arcana-pack::before {
  top: 3.4rem;
  left: -1.35rem;
}

.arcana-pack::after {
  right: -1.35rem;
  bottom: 3.4rem;
}

.arcana-pack:hover:not(:disabled),
.arcana-pack:focus-visible {
  outline: none;
  box-shadow:
    0 1.7rem 2.4rem rgb(0 0 0 / 50%),
    0 0 2.2rem color-mix(in oklab, var(--color-accent) 50%, transparent),
    inset 0 0 1.5rem rgb(255 255 255 / 18%);
  transform: translateY(-0.45rem) rotate(1deg) scale(1.025);
}

.arcana-pack:active:not(:disabled) {
  transform: translateY(-0.1rem) rotate(0deg) scale(0.98);
}

.arcana-pack:disabled {
  cursor: not-allowed;
  filter: saturate(0.55);
  opacity: 0.58;
}

/* Stitched leather seal strips that bind the pack top and bottom. */
/* Stitched leather binding strips that bind the pack top and bottom. */
.arcana-pack-seal {
  position: relative;
  z-index: 3;
  display: block;
  height: 1.05rem;
  flex: none;
  border-bottom: 1px solid
    color-mix(in oklab, var(--color-accent) 55%, transparent);
  background: repeating-linear-gradient(
    90deg,
    color-mix(in oklab, var(--color-accent) 50%, black) 0 0.32rem,
    oklch(15% 0.02 46) 0.32rem 0.58rem
  );
  box-shadow: 0 0.2rem 0.45rem rgb(0 0 0 / 45%);
}

.arcana-pack-seal:last-child {
  border-top: 1px solid
    color-mix(in oklab, var(--color-accent) 65%, transparent);
  border-bottom: 0;
  box-shadow: 0 -0.2rem 0.45rem rgb(0 0 0 / 32%);
}

.arcana-pack-face {
  position: relative;
  z-index: 1;
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  margin: 0.5rem;
  border: 3px double color-mix(in oklab, var(--color-accent) 76%, transparent);
  border-radius: 46% 46% 0.5rem 0.5rem / 24% 24% 0 0;
  padding: 1.1rem 0.65rem 0.8rem;
  text-align: center;
  background: radial-gradient(
    circle at 50% 42%,
    color-mix(in oklab, var(--color-accent) 20%, transparent),
    transparent 45%
  );
}

.arcana-pack-maker {
  font-size: 0.6rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  opacity: 0.72;
}

/* Kept as the sizing hook; the wax material comes from .wax-seal. */
.arcana-pack-sigil {
  --seal-size: 7rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
}

.arcana-pack-sigil strong {
  font-size: 3rem;
  line-height: 1;
  text-shadow: 0 0 0.75rem
    color-mix(in oklab, var(--color-accent) 70%, transparent);
}

.arcana-pack-sigil span {
  font-size: 1rem;
}

.arcana-pack h4 {
  font-size: 1.65rem;
  line-height: 1;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.arcana-pack h4 + p {
  margin-top: 0.3rem;
  font-size: 0.7rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}

.arcana-pack-rarity {
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.72;
}

.arcana-pack-price {
  min-width: 7.5rem;
  border-color: color-mix(
    in oklab,
    var(--color-accent-content) 24%,
    transparent
  );
  font-family: var(--font-heading);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.arcana-pack small {
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  opacity: 0.66;
}

/* ==========================================================================
   SCREENS · progression board (enamelled heraldic escutcheons)
   ========================================================================== */

.passive-board {
  max-width: 64rem;
  margin-inline: auto;
}

.passive-level {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1.25rem;
  padding: 0.85rem 1.1rem;
  border: var(--border) solid var(--gold-line);
  border-radius: var(--radius-box);
  background:
    linear-gradient(
      180deg,
      rgb(255 255 255 / 5%),
      transparent 40%,
      rgb(0 0 0 / 20%)
    ),
    color-mix(in oklab, var(--color-base-100) 88%, transparent);
  box-shadow: inset 0 0 1.5rem rgb(0 0 0 / 35%);
}

/* Level marker rendered as a gilt-rimmed shield. */
.passive-level-number {
  position: relative;
  display: grid;
  width: 3.1rem;
  height: 3.4rem;
  flex: none;
  place-items: center;
  border-radius: 0;
  color: var(--gold-bright);
  background: linear-gradient(
    160deg,
    var(--gold-bright),
    color-mix(in oklab, var(--color-accent) 45%, var(--color-neutral))
  );
  font-family: var(--font-display);
  font-size: 1.2rem;
  clip-path: var(--shield-clip);
  animation: shield-heartbeat 2.8s ease-in-out infinite;
}

.passive-level-number::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 2px;
  background: linear-gradient(
    160deg,
    color-mix(in oklab, var(--color-primary) 82%, black),
    color-mix(in oklab, var(--color-primary) 42%, black)
  );
  clip-path: var(--shield-clip);
}

.passive-level div {
  flex: 1;
}

.passive-level p {
  font-family: var(--font-heading);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.passive-level small {
  color: color-mix(in oklab, var(--color-base-content) 60%, transparent);
}

.passive-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 0.9rem;
}

/* Dark enamelled escutcheon: deep jewel-tone field under a gold rim, with an
   enamel sheen and a gold inner hairline. Light-on-dark text. */
.passive-tile {
  --passive-color: var(--color-primary);
  --passive-content: var(--color-primary-content);
  position: relative;
  display: flex;
  min-height: 12rem;
  grid-column: span 2;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.65rem;
  overflow: hidden;
  padding: 1rem;
  border: var(--border) solid
    color-mix(in oklab, var(--color-accent) 62%, black);
  border-radius: 34% 34% var(--radius-box) var(--radius-box) / 12% 12% 0 0;
  color: color-mix(
    in oklab,
    var(--color-base-content) 92%,
    var(--passive-color)
  );
  background:
    radial-gradient(
      ellipse at 78% 6%,
      color-mix(in oklab, var(--passive-color) 30%, transparent),
      transparent 46%
    ),
    linear-gradient(
    180deg,
    color-mix(
      in oklab,
      var(--passive-color) var(--passive-mix, 34%),
      oklch(19% 0.02 52)
    ),
    color-mix(
      in oklab,
      var(--passive-color) calc(var(--passive-mix, 34%) / 2),
      oklch(13% 0.015 48)
    )
  );
  text-align: left;
  box-shadow:
    0 0.75rem 1.5rem rgb(0 0 0 / 46%),
    inset 0 0 0 1px color-mix(in oklab, var(--color-accent) 26%, transparent),
    inset 0 1px 0 rgb(255 255 255 / 10%),
    inset 0 -1.2rem 1.8rem rgb(0 0 0 / 34%);
  transition: transform 180ms ease, filter 180ms ease;
}

.passive-tile:nth-child(n+4) {
  grid-column: span 3;
}

.passive-tile:not(:disabled) {
  cursor: pointer;
}

.passive-tile:not(:disabled):hover,
.passive-tile:not(:disabled):focus-visible {
  outline: none;
  filter: saturate(1.12) brightness(1.04);
  transform: translateY(-0.25rem);
}

.passive-tile:not(:disabled):active {
  box-shadow: inset 0 0.5rem 1rem rgb(0 0 0 / 28%);
  transform: translateY(0.12rem) scale(0.98);
}

.passive-tile:disabled {
  opacity: 0.62;
}

.passive-vigor {
  --passive-mix: 30%;
  --passive-color: var(--color-secondary);
  --passive-content: var(--color-secondary-content);
}

.passive-energy {
  --passive-color: var(--color-info);
  --passive-content: var(--color-info-content);
}

.passive-strength {
  --passive-mix: 24%;
  --passive-color: var(--color-warning);
  --passive-content: var(--color-warning-content);
}

.passive-critical {
  --passive-color: var(--color-error);
  --passive-content: var(--color-error-content);
}

.passive-poison {
  --passive-color: var(--color-success);
  --passive-content: var(--color-success-content);
}

/* Enamelled roundel boss on each escutcheon. */
.passive-icon {
  display: grid;
  width: 3rem;
  height: 3rem;
  place-items: center;
  border: 1px solid color-mix(in oklab, var(--color-accent) 60%, transparent);
  border-radius: 50%;
  color: color-mix(in oklab, var(--passive-color) 65%, white);
  background:
    radial-gradient(circle at 32% 26%, rgb(255 255 255 / 16%), transparent 52%),
    radial-gradient(
    circle at 50% 60%,
    color-mix(in oklab, var(--passive-color) 40%, transparent),
    transparent 70%
  ),
    rgb(0 0 0 / 30%);
  box-shadow:
    inset 0 -0.2rem 0.4rem rgb(0 0 0 / 34%),
    0 0.2rem 0.4rem rgb(0 0 0 / 34%);
  font-size: 1.4rem;
  will-change: transform;
}

.passive-copy strong {
  display: block;
  font-family: var(--font-heading);
  font-size: 1.05rem;
  letter-spacing: 0.08em;
}

.passive-copy small {
  display: block;
  margin-top: 0.15rem;
  color: currentColor;
  font-size: 0.72rem;
  opacity: 0.78;
}

.passive-rank,
.passive-pips {
  display: flex;
  gap: 0.25rem;
  margin-top: auto;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
}

.passive-pips i {
  width: 0.5rem;
  height: 0.5rem;
  border: 1px solid currentColor;
  border-radius: 50%;
  opacity: 0.55;
}

.passive-pips i.is-filled {
  background: currentColor;
  box-shadow: 0 0 0.35rem currentColor;
  opacity: 1;
  animation: pip-glint 2.4s ease-in-out infinite;
}

.passive-tile em {
  position: absolute;
  right: 0.9rem;
  bottom: 0.75rem;
  font-size: 0.65rem;
  font-style: normal;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

@media (max-width: 767px) {
  .passive-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .passive-tile,
  .passive-tile:nth-child(n+4) {
    grid-column: span 1;
  }

  .passive-level {
    flex-wrap: wrap;
  }
}

@media (max-width: 440px) {
  .passive-grid {
    grid-template-columns: 1fr;
  }
}

/* ==========================================================================
   SCREENS · the city at night
   ========================================================================== */

.cityscape {
  position: relative;
  display: grid;
  grid-template-columns: 1.15fr 0.8fr 1.25fr 1fr;
  align-items: end;
  gap: clamp(0.25rem, 1.5vw, 1rem);
  overflow: hidden;
  padding: 4rem clamp(0.5rem, 3vw, 2rem) 2.5rem;
  border-radius: 2rem 2rem 0.5rem 0.5rem;
  box-shadow: inset 0 0 0 1px var(--gold-line), inset 0 0 4rem rgb(0 0 0 / 50%);
}

/* Night sky with a low moon behind the keep. */
.cityscape::after {
  content: "";
  position: absolute;
  z-index: 0;
  inset: 0 0 auto;
  height: 66%;
  background:
    radial-gradient(
      circle at 78% 24%,
      color-mix(in oklab, var(--color-accent) 60%, white) 0 2.1rem,
      color-mix(in oklab, var(--color-accent) 26%, transparent) 2.2rem 3rem,
      transparent 8rem
    ),
    radial-gradient(
    ellipse at 50% 100%,
    color-mix(in oklab, var(--color-primary) 22%, transparent),
    transparent 68%
  ),
    linear-gradient(
    180deg,
    color-mix(in oklab, var(--color-primary) 14%, var(--color-base-300)),
    transparent
  );
  pointer-events: none;
}

/* Masonry footing the buildings stand on. */
.cityscape::before {
  content: "";
  position: absolute;
  z-index: 0;
  inset: auto 0 0;
  height: 2.75rem;
  border-top: 0.35rem double var(--gold-line);
  background-color: color-mix(
    in oklab,
    var(--color-neutral) 40%,
    var(--color-base-300)
  );
  background-image:
    linear-gradient(180deg, rgb(255 255 255 / 6%), rgb(0 0 0 / 32%)),
    var(--tex-ashlar);
  background-size: auto, 8rem 4.4rem;
  pointer-events: none;
}

.city-title {
  position: absolute;
  z-index: 2;
  top: 1.1rem;
  left: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  color: color-mix(in oklab, var(--color-accent) 80%, transparent);
  transform: translateX(-50%);
  white-space: nowrap;
}

.city-title > strong {
  animation: magical-heartbeat 4s ease-in-out infinite;
}

.city-title strong {
  color: color-mix(in oklab, var(--color-accent) 92%, white);
  font-family: var(--font-gothic);
  font-size: clamp(1.4rem, 3.4vw, 2.35rem);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-shadow:
    0 0.15rem 0.4rem rgb(0 0 0 / 70%),
    0 0 1.2rem color-mix(in oklab, var(--color-accent) 45%, transparent);
}

/* Gold fleuron rule beneath the city name. */
.city-title-rule {
  width: clamp(9rem, 26vw, 18rem);
  height: 0.7rem;
  background-image:
    linear-gradient(
      90deg,
      transparent,
      color-mix(in oklab, var(--color-accent) 70%, transparent) 22% 78%,
      transparent
    ),
    var(--orn-terminal);
  background-repeat: no-repeat, no-repeat;
  background-position: center center, center center;
  background-size: 100% 1px, 1.75rem 0.7rem;
}

.city-building {
  position: relative;
  z-index: 1;
  display: flex;
  min-width: 0;
  width: 100%;
  height: var(--building-height);
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  transition: transform 200ms ease, filter 200ms ease;
}

.city-building img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: bottom;
  padding: 0.2rem;
  filter: brightness(0.78) saturate(60%) sepia(30%) opacity(0.82) drop-shadow(
    0 0.75rem 0.55rem rgb(0 0 0 / 55%)
  );
  transition: transform 200ms ease, filter 200ms ease;
}

.city-building span {
  z-index: 1;
  width: calc(100% - 0.5rem);
  margin-bottom: 0.25rem;
  padding: 0.5rem 0.25rem 0.85rem;
  font-size: clamp(0.55rem, 1.4vw, 0.8rem);
  line-height: 1;
}

.city-building i {
  position: absolute;
  z-index: 2;
  top: 0.5rem;
  right: 0.5rem;
  font-style: normal;
}

.city-building:hover,
.city-building:focus-visible {
  outline: none;
  filter: drop-shadow(0 0 1rem var(--building-glow));
  transform: translateY(-0.6rem) rotate(-0.4deg);
}

.city-building:hover img,
.city-building:focus-visible img {
  transform: scale(1.065) translateY(-0.2rem);
}

.city-building:active {
  filter: drop-shadow(0 0 0.45rem var(--building-glow));
  transform: translateY(0.1rem) scale(0.975);
}

.city-building:active img {
  transform: scale(0.98) translateY(0.25rem);
}

.city-keep {
  --building-height: 78%;
  --building-glow: color-mix(in oklab, var(--color-primary) 30%, transparent);
}

.city-arcana {
  --building-height: 92%;
  --building-glow: color-mix(in oklab, var(--color-info) 34%, transparent);
}

.city-sanctum {
  --building-height: 84%;
  --building-glow: color-mix(in oklab, var(--color-secondary) 32%, transparent);
}

.city-gate {
  --building-height: 68%;
  --building-glow: color-mix(in oklab, var(--color-accent) 32%, transparent);
}

@media (max-width: 639px) {
  .navbar-start,
  .navbar-end {
    width: 24%;
  }

  .navbar-center {
    width: 52%;
  }

  .cityscape {
    padding-inline: 0.25rem;
    padding-bottom: 2rem;
  }
}

@media (max-height: 650px) {
  .city-title {
    top: 0.5rem;
  }

  .cityscape {
    padding-top: 2.5rem;
  }
}

/* ==========================================================================
   SCREENS · combat furnishings
   ========================================================================== */

/* Flagstone arena floor. The combatants stand ON this band, so it fills the
   lower half of the battlefield and fades up into the dark rather than
   floating as a detached stripe. */
.battlefield {
  position: relative;
  overflow: hidden;
}

.battlefield::before {
  content: "";
  position: absolute;
  z-index: 0;
  inset: 38% -3rem 0;
  border-top: 1px solid
    color-mix(in oklab, var(--color-accent) 26%, transparent);
  background-color: color-mix(in oklab, var(--color-neutral) 42%, black);
  background-image:
    linear-gradient(
      180deg,
      color-mix(in oklab, var(--color-accent) 12%, transparent),
      transparent 32%,
      rgb(0 0 0 / 45%)
    ),
    var(--tex-ashlar);
  background-size: auto, 11rem 6rem;
  mask-image: linear-gradient(180deg, transparent, black 30%, black);
  pointer-events: none;
}

.battlefield > div {
  position: relative;
  z-index: 1;
}

/* Wall sconce: iron cresset with a warm flame and a pool of candlelight. */
.sconce {
  --candle-tint: color-mix(
    in oklab,
    var(--color-accent) 68%,
    var(--color-warning)
  );
  position: absolute;
  z-index: 0;
  width: 0.85rem;
  height: 1.5rem;
  border-radius: 45% 45% 30% 30%;
  background: linear-gradient(
    180deg,
    color-mix(in oklab, var(--candle-tint) 92%, white) 0 38%,
    color-mix(in oklab, var(--color-neutral) 60%, black) 44%
  );
  box-shadow:
    0 0 0.9rem color-mix(in oklab, var(--candle-tint) 75%, transparent),
    0 0 2.4rem color-mix(in oklab, var(--candle-tint) 45%, transparent);
  pointer-events: none;
}

.sconce::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: -420% -900% -260%;
  background: radial-gradient(
    closest-side,
    color-mix(in oklab, var(--candle-tint) 22%, transparent),
    transparent 76%
  );
  pointer-events: none;
  animation: candle-flicker 6.5s ease-in-out infinite;
}

/* Oak-and-iron rail that carries the hand of cards. */
.hand-rail {
  position: relative;
  border-top: 0.28rem solid var(--iron-dark);
  background-color: color-mix(
    in oklab,
    var(--color-neutral) 44%,
    var(--color-base-300)
  );
  background-image:
    linear-gradient(
      180deg,
      rgb(255 255 255 / 6%),
      transparent 22%,
      rgb(0 0 0 / 30%)
    ),
    repeating-linear-gradient(
    90deg,
    rgb(0 0 0 / 9%) 0 1px,
    transparent 1px 9px
  ),
    repeating-linear-gradient(
    180deg,
    rgb(255 255 255 / 3%) 0 1px,
    transparent 1px 1.4rem
  );
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 8%),
    0 -0.6rem 1.5rem rgb(0 0 0 / 45%);
}

/* Chronicle strip that logs the fight. */
.chronicle {
  position: relative;
  border-block: 1px solid var(--gold-line);
  background:
    linear-gradient(
      90deg,
      color-mix(in oklab, var(--color-accent) 7%, transparent),
      transparent 18% 82%,
      color-mix(in oklab, var(--color-accent) 7%, transparent)
    ),
    color-mix(in oklab, var(--color-base-300) 45%, transparent);
}

/* The latest two events sit in a fixed band; a longer line is clipped instead
   of shoving the battlefield upward. */
.chronicle-latest {
  height: 2.25rem;
}

/* Status pips wink in and out mid-fight, so their row reserves two lines and
   the battlefield never reflows around a new shield or poison badge. */
.status-badges {
  min-height: 2.75rem;
}

/* Iron-riveted enemy plate. */
.enemy-plate {
  position: relative;
  background-color: color-mix(in oklab, var(--color-base-100) 88%, transparent);
  background-image: linear-gradient(
    180deg,
    rgb(255 255 255 / 5%),
    transparent 38%,
    rgb(0 0 0 / 26%)
  );
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 8%),
    0 0.5rem 1.1rem rgb(0 0 0 / 40%);
}

.enemy-plate::before {
  content: "";
  position: absolute;
  z-index: 1;
  right: 0.3rem;
  left: 0.3rem;
  top: 0.22rem;
  height: 0.3rem;
  background-image: radial-gradient(
    circle at 50% 50%,
    var(--iron-light) 0 0.055rem,
    color-mix(in oklab, var(--color-neutral) 28%, black) 0.065rem 0.14rem,
    transparent 0.15rem
  );
  background-repeat: repeat-x;
  background-size: 1.6rem 0.3rem;
  opacity: 0.7;
  pointer-events: none;
}

/* Currently targeted foe: gold-lit rivet frame. */
.enemy-plate-target {
  border-color: color-mix(in oklab, var(--color-accent) 80%, transparent);
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 10%),
    0 0.5rem 1.1rem rgb(0 0 0 / 44%),
    0 0 1.4rem color-mix(in oklab, var(--color-accent) 38%, transparent);
}

/* Iron-band frame around a vitality gauge. */
.gauge-frame {
  display: flex;
  align-items: center;
  padding: 0.28rem;
  border: var(--border) solid var(--iron-dark);
  border-radius: 0.35rem;
}

.gauge-frame .progress {
  display: block;
}

/* ==========================================================================
   ANIMATION
   ========================================================================== */

@keyframes room-aura-shift {
  from {
    background-position: 0 0, 50% 0, 0 0, 0 0;
  }
  to {
    background-position: 0 0, 50% 15%, 0 0, 0 0;
  }
}

@keyframes magical-heartbeat {
  0%,
  88%,
  100% {
    filter: brightness(1);
    transform: scale(1);
  }
  92% {
    filter: brightness(1.25);
    transform: scale(1.06);
  }
  96% {
    transform: scale(1.02);
  }
}

/* Scale-only pulse: keeps the shield rim filter intact. */
@keyframes shield-heartbeat {
  0%,
  88%,
  100% {
    transform: scale(1);
  }
  92% {
    transform: scale(1.06);
  }
  96% {
    transform: scale(1.02);
  }
}

@keyframes pip-glint {
  0%,
  70%,
  100% {
    filter: brightness(1);
    transform: scale(1);
  }
  80% {
    filter: brightness(1.7);
    transform: scale(1.3);
  }
}

@keyframes combat-rune-spin {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .room-screen,
  .room-screen::before,
  .room-screen::after,
  .margin-vines::before,
  .margin-vines::after,
  .passive-level-number,
  .passive-icon,
  .passive-pips i.is-filled,
  .candle-glow::before,
  .sconce::before,
  .city-title > strong {
    animation: none;
  }

  .city-building,
  .city-building img,
  .game-card,
  .game-card::before,
  .mystic-class-icon,
  .card-fan-slot {
    transition: none;
  }

  .combat-effect span {
    animation: none;
  }

  .btn,
  .btn::after,
  .passive-tile {
    transition: none;
  }
}

/* ==========================================================================
   CARDS · illuminated manuscript leaves
   ========================================================================== */

/* The fan's slots are transformed (rotate + lift), which does not grow the
   flow box, so the fan reserves extra height to enclose the rotated cards and
   keep the pile counts clear of them. */
.card-fan {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  min-height: 12.75rem;
  padding-bottom: 1.25rem;
}

.card-fan-flip {
  margin: 0 -0.75rem;
}

.card-fan-slot {
  transform: rotate(var(--rot, 0deg)) translateY(var(--lift, 0));
  transform-origin: bottom center;
  transition: transform 200ms ease, filter 200ms ease;
}

.card-fan-slot:hover {
  z-index: 10;
  filter: drop-shadow(
    0 0 10px color-mix(in oklab, var(--color-accent) 45%, transparent)
  );
  transform: rotate(0deg) translateY(-0.75rem);
}

.combat-card-slot {
  perspective: 40rem;
}

.game-card {
  --game-card-color: var(--color-primary);
  position: relative;
  overflow: hidden;
  border-width: 2px;
  border-color: color-mix(in oklab, var(--color-accent) 42%, transparent);
  border-radius: 0.4rem;
  box-shadow:
    inset 0 0 0 1px color-mix(in oklab, var(--color-accent) 55%, transparent),
    inset 0 0 0 3px rgb(0 0 0 / 34%),
    inset 0 0 0 4px color-mix(in oklab, var(--color-accent) 32%, transparent),
    0 0.75rem 1.4rem rgb(0 0 0 / 38%),
    0 0 1rem color-mix(in oklab, var(--game-card-color) 18%, transparent);
  transform-style: preserve-3d;
  transition:
    border-color 200ms ease,
    box-shadow 200ms ease,
    transform 200ms ease;
}

/* Warm candlelight glide across the leaf. */
.game-card::before {
  content: "";
  position: absolute;
  z-index: 5;
  inset: -80% -120%;
  background: linear-gradient(
    110deg,
    transparent 42%,
    color-mix(in oklab, var(--color-accent) 32%, transparent) 49%,
    transparent 56%
  );
  pointer-events: none;
  transform: translateX(-35%) rotate(4deg);
  transition: transform 600ms ease;
}

.game-card:hover::before,
.game-card:focus-visible::before {
  transform: translateX(35%) rotate(4deg);
}

.game-card:hover,
.game-card:focus-visible {
  border-color: var(--game-card-color);
  outline: none;
  box-shadow:
    inset 0 0 0 1px color-mix(in oklab, var(--color-accent) 75%, transparent),
    inset 0 0 0 3px rgb(0 0 0 / 34%),
    inset 0 0 0 4px color-mix(in oklab, var(--color-accent) 48%, transparent),
    0 1.15rem 1.8rem rgb(0 0 0 / 44%),
    0 0 1.5rem color-mix(in oklab, var(--game-card-color) 48%, transparent);
  transform: translateY(-0.75rem) rotateX(5deg) scale(1.045);
}

.game-card:active {
  box-shadow: inset 0 0.45rem 0.8rem rgb(0 0 0 / 30%);
  transform: translateY(-0.2rem) rotateX(-3deg) scale(0.97);
}

.game-card-physical {
  --game-card-color: var(--color-error);
}

.game-card-defense {
  --game-card-color: var(--color-info);
}

.game-card-poison {
  --game-card-color: var(--color-success);
}

.game-card-arcane {
  --game-card-color: var(--color-primary);
}

.card-fan-card {
  width: 8rem;
  min-height: 11rem;
  box-shadow: 0 0.5rem 1.25rem rgb(0 0 0 / 40%);
}

/* Card art sits in an arched window; .arch-frame supplies the silhouette. */
.card-art-frame {
  --card-art-color: var(--color-primary);
  --card-art-glow: color-mix(
    in oklab,
    var(--color-neutral-content) 55%,
    transparent
  );
  border-bottom: 1px solid var(--card-art-color);
  background:
    radial-gradient(
      circle at center,
      color-mix(in oklab, var(--card-art-color) 42%, transparent),
      transparent 68%
    ),
    linear-gradient(
    145deg,
    var(--color-neutral),
    color-mix(in oklab, var(--card-art-color) 62%, var(--color-neutral))
  );
}

.card-art-physical {
  --card-art-color: var(--color-error);
}

.card-art-defense {
  --card-art-color: var(--color-info);
}

.card-art-poison {
  --card-art-color: var(--color-success);
}

.card-art-arcane {
  --card-art-color: var(--color-primary);
}

/* Gold-leaf shimmer marks a rare leaf. */
.card-art-rare {
  --card-art-glow: color-mix(in oklab, var(--color-accent) 90%, transparent);
  background-image:
    repeating-linear-gradient(
      115deg,
      color-mix(in oklab, var(--color-accent) 20%, transparent) 0 2px,
      transparent 2px 9px
    ),
    radial-gradient(
    circle at center,
    color-mix(in oklab, var(--card-art-color) 42%, transparent),
    transparent 68%
  ),
    linear-gradient(
    145deg,
    color-mix(in oklab, var(--color-accent) 26%, var(--color-neutral)),
    color-mix(in oklab, var(--card-art-color) 62%, var(--color-neutral))
  );
  box-shadow:
    inset 0 0 0 1px color-mix(in oklab, var(--color-accent) 60%, transparent),
    inset 0 0 1.4rem color-mix(in oklab, var(--color-accent) 22%, transparent);
}

.card-art-frame img {
  filter: drop-shadow(0 0 0.2rem var(--card-art-glow)) drop-shadow(
    0 0 0.65rem var(--card-art-glow)
  );
}

.game-card .corner-fleurons {
  --fleuron-size: 0.65rem;
  --fleuron-inset: 0.18rem;
}

/* ==========================================================================
   COMBAT EFFECTS
   ========================================================================== */

.combat-effects {
  position: fixed;
  z-index: 60;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.combat-effect {
  --effect-color: var(--color-primary);
  position: absolute;
  width: 4rem;
  height: 4rem;
  will-change: transform, opacity;
}

.combat-effect span {
  position: absolute;
  inset: -0.5rem;
  border: 2px solid var(--effect-color);
  border-radius: 50%;
  box-shadow:
    0 0 1rem var(--effect-color),
    inset 0 0 0.75rem var(--effect-color);
  animation: combat-rune-spin 700ms linear infinite;
}

.combat-effect span::before,
.combat-effect span::after {
  content: "✦";
  position: absolute;
  color: var(--effect-color);
  font-size: 0.7rem;
  text-shadow: 0 0 0.5rem currentColor;
}

.combat-effect span::before {
  top: -0.4rem;
  left: 50%;
}

.combat-effect span::after {
  right: -0.3rem;
  bottom: 0.2rem;
}

.combat-effect img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: 0.55rem;
  filter: drop-shadow(0 0 0.35rem var(--effect-color)) drop-shadow(
    0 0 0.75rem var(--effect-color)
  );
}

.combat-effect-physical {
  --effect-color: var(--color-error);
}

.combat-effect-defense {
  --effect-color: var(--color-info);
}

.combat-effect-poison {
  --effect-color: var(--color-success);
}

.combat-effect-arcane {
  --effect-color: var(--color-primary);
}

.combat-effect-aoe span {
  border-style: double;
  border-width: 5px;
}

.enemy-art {
  padding: 0.35rem;
  border-radius: 45% 45% 50% 50%;
  background: color-mix(in oklab, var(--color-error) 72%, transparent);
  filter: drop-shadow(
    0 0 0.55rem color-mix(in oklab, var(--color-error) 55%, transparent)
  );
}

@media (max-width: 640px) {
  .card-fan {
    min-height: 8rem;
    overflow-x: auto;
    padding-bottom: 0.5rem;
    justify-content: flex-start;
  }
  .card-fan::after {
    content: "";
    flex: 0 0 0.25rem;
  }
  .card-fan-flip {
    margin: 0 -0.15rem;
  }
  .card-fan-slot {
    transform: rotate(calc(var(--rot, 0deg) * 0.5))
      translateY(calc(var(--lift, 0px) * 0.5));
  }
  .card-fan-card {
    width: 4.25rem;
    min-height: 7.25rem;
  }
  .card-art-frame img {
    padding: 0.4rem;
  }
  .card-body {
    padding: 0.5rem;
    gap: 0.25rem;
  }
  .card-body p {
    font-size: 0.55rem;
    line-height: 1.1;
  }
  .card-body .font-heading {
    font-size: 0.55rem;
  }
  .game-card .corner-fleurons {
    --fleuron-size: 0.5rem;
  }
}