/*
 * L2 Arcanis visual identity, ported from the l2arcanis-website scaffold.
 * Dark fantasy: near-black ground, parchment type, one accent family
 * (arcane violet). Every color on the site comes from these tokens.
 * Do not introduce loose hex values in templates.
 */

@font-face {
  font-family: "Cinzel";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("../fonts/cinzel-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2013-2014, U+2018-201A,
    U+201C-201E, U+2026, U+2212;
}
@font-face {
  font-family: "Cinzel";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("../fonts/cinzel-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF;
}
/* W10 1.2: metric-compatible fallback so a Cinzel swap moves nothing.
   The overrides are computed from the real font tables (fontTools):
   size-adjust is the mean Cinzel/Georgia advance-width ratio over the
   site's actual headings; ascent/descent divide Cinzel's hhea metrics
   (0.976 / 0.372 em) by that adjust so the line box is identical in
   both faces. Recompute if either font file ever changes. */
@font-face {
  font-family: "Cinzel Fallback";
  src: local("Georgia");
  size-adjust: 122.33%;
  ascent-override: 79.79%;
  descent-override: 30.41%;
  line-gap-override: 0%;
}

:root {
  /* Ground: near-black with a cold violet cast */
  --ink: #08060e;
  --ink-raised: #0f0b1a;
  --ink-panel: #161022;
  /* W51 (owner spec Addendum A item 3, 2026-08-26): the TWO shared
     zebra row shades - every table.arcanis data row alternates
     a/b/a/b site-wide through the one rule in the table skin, never
     per-page. Shade a sits on the panel ground (--ink-raised's
     value); shade b is the subtle raised step (--ink-panel's value):
     both opaque, one soft luma step apart, so striping stays quiet
     and text contrast is untouched. */
  --row-shade-a: #0f0b1a;
  --row-shade-b: #161022;
  /* Edges and rules */
  --edge: #2a2140;
  --edge-soft: #1d1730;
  /* W26 items 3+5 (owner spec): the DEFINED control edge - a clearly
     brighter violet border for header action buttons (Register, LOG
     OUT) so they read as intentional controls against the near-black
     header; --edge stays the quiet rule/panel edge everywhere else.
     W29 section 1 (owner spec): promoted to the SITE-WIDE standard
     (tier 3) button edge - every .btn now carries the border
     intensity the owner already judged clearly visible on the
     header controls, so buttons everywhere read as obviously
     interactive against their panels. */
  --edge-bright: #4b3c76;
  /* W29 section 2 (owner spec): the ELEVATED (tier 2) button edge -
     Register, Create Account, and Server Features sit one clear
     step ABOVE the standard --edge-bright outline and one clear
     step BELOW the filled --arcane primary. The value lies on the
     edge-bright -> arcane line so the three tiers read as one
     ordered family: #4b3c76 (~67 luma) -> #6b57a8 (~97) -> #9b6dff
     (~129), an even ~30-level step per tier. */
  --edge-elevated: #6b57a8;
  /* W26 item 2 amended by W27 item 2 (owner verdict on the live W26):
     the 88% ground was imperceptible in his browser - measured on the
     rendered pixels the panel interior sat within ~5 luma levels of
     the opaque ground. 70% is CLEARLY translucent over bright page
     content while the stronger 12px blur (up from 9) smears whatever
     bleeds through into a soft wash, so the parchment entries keep
     their contrast (screenshot-verified over the hero, the busiest
     ground). Browsers without backdrop-filter still get a readable
     70% ink ground. */
  /* W43 section 6 + owner decision A (owner spec
     SITE_HERO_EVENTS_RATES_W43_2026-08-25, superseding the W29
     section-3 "exactly unchanged" rule and the W41/W42 dropdown
     exclusions by explicit owner instruction): the dropdown glass
     transparency reduces by 30% - a' = a + 0.30 x (1 - a) =
     0.7 + 0.09 = 0.79. ONE token, so GAME and COMMUNITY move
     together; the hue, the 12px blur, and every W27-W29
     centering/width/hover rule stay untouched. */
  --ink-raised-glass: rgba(15, 11, 26, 0.9);
  /* Type: aged parchment */
  --parchment: #ece3d0;
  --parchment-dim: #a99e89;
  --parchment-faint: #6f6757;
  /* W69 section 18: the small-secondary-text tier for the media
     surfaces (one step above --parchment-dim; the dark theme and the
     rest of the site keep --parchment-dim). */
  --parchment-soft: #bdb29c;
  /* Accent: arcane violet. W8 rule: violet is the LINK color; a link is
     the only violet text in body copy. */
  --arcane: #9b6dff;
  --arcane-bright: #c9aeff;
  --arcane-deep: #3d2766;
  /* Emphasis: antique gold (W8 1.2). Reserved for headings and emphasis
     values - never for body-copy links, so color alone separates the
     two. W14 2a (owner directive): the two ACCOUNT header controls
     (Sign in, the signed-in username) are the one deliberate exception
     - gold is the account accent that separates them from every other
     header control. */
  --gold: #d8b97c;
  --gold-bright: #edd9a8;
  /* W27 item 3 (owner verdict on the live W26): the deep antique-gold
     control ground - the accent pair's hover FILL. The W26 solid --gold
     fill measured a ~129-level mean pixel flash (glaring); this deep
     ground mirrors --arcane-deep's relation to --arcane, so the accent
     hover speaks the SAME soft fill language as the plain pair: deep
     tinted ground + brightened edge + brightened text. */
  --gold-deep: #4a3a1f;
  /* Status signals */
  --signal-online: #6fd2a1;
  --signal-offline: #e08585;
  /* W77 sections 1-3 + 5 (docs/specs/W77-SITE-UPDATES.md; SUPERSEDES the
     W59 sections 9 + 46 / W61 section 17 / W63 1.3 / W74 section 7
     "server date and time = the Online green" rule EVERYWHERE on the
     website): the ONE server date & time color is a bright sky blue -
     --time-server - clearly apart from the green Online / Alive status
     signal (which keeps --signal-online), 10.2:1 on the ink ground
     (the W73 / W75 AA standard). The ONE local date & time color is the
     standard parchment ink - --time-local. Every countdown / time-
     remaining value reads in the LOCAL color (section 2), except inside
     a table that carries both a date & time column and a countdown
     column, where BOTH columns follow the selected time mode - the
     [data-time-basis] wrapper, server-rendered and switched in place by
     time-basis.js (section 5). The Local Time / Server Time toggle
     options fill with their own mode color (sections 1 + 3). */
  --time-server: #62c8ff;
  --time-local: var(--parchment);
  /* Form validation (owner visual batch): softer red for labels and
     messages, stronger red for input borders so invalid fields stay
     easy to spot. Used consistently by every error state. */
  --error-soft: #d49a96;
  --error-strong: #cc5f57;

  /* W13 item 1: the ONE vertical gap between sibling content blocks
     (.stack gap and the sibling-block margins).
     W17 item 5 (owner ruling, SUPERSEDES the W14 0.4rem value): the
     GAME page's measured 12.8px block rhythm is the site-wide
     standard. 12.8px was what the Game page actually rendered - the
     0.4rem flex gap and the 0.4rem sibling margin SUMMED inside a
     .stack - so the token becomes 0.8rem and the double-application
     is retired (inside a .stack only the flex gap applies). The Home
     hero card is the pinned exception (it is not a .panel and never
     rides this token). */
  --block-gap: 0.8rem;

  --font-display: "Cinzel", "Cinzel Fallback", Georgia, "Times New Roman", serif;
  --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, "Cascadia Mono", Consolas, monospace;
  /* W63 section 1: the ONE server date/time family - the family the
     header's Online label renders in. Every server date/time value
     site-wide (chip, dashboard card, schedule entries, boss tables,
     vote timers, live event) declares this token explicitly; size may
     vary per surface, the family never does. */
  --font-clock: var(--font-sans);
}

* { box-sizing: border-box; }

/* W10 1.2: scrollbar-gutter reserves the scrollbar column always, so
   navigating between short and long pages never shifts layout width. */
html { color-scheme: dark; scroll-behavior: smooth; scrollbar-gutter: stable; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background-color: var(--ink);
  background-image: radial-gradient(
    60rem 40rem at 50% -10rem,
    rgba(61, 39, 102, 0.45),
    transparent 70%
  );
  background-repeat: no-repeat;
  background-attachment: fixed;
  color: var(--parchment);
  font-family: var(--font-sans);
  line-height: 1.6;
}

h1, h2, h3 { font-family: var(--font-display); letter-spacing: 0.02em; }

/* W9 2.2: decorative page-title icon - ~1em, brand gold, centered
   beside the h1 text (aria-hidden; never part of the heading text). */
.h1-icon {
  width: 1em;
  height: 1em;
  margin-right: 0.4rem;
  vertical-align: -0.1em;
  color: var(--gold);
  flex: none;
}

/* W9 2.1: the footer wordmark carries the brand face (Cinzel 600, the
   f63 owner ruling) like the header logotype. */
.footer-wordmark { font-family: var(--font-display); font-weight: 600; letter-spacing: 0.04em; }
::selection { background-color: var(--arcane); color: var(--ink); }
:focus-visible {
  outline: 2px solid var(--arcane-bright);
  outline-offset: 2px;
  border-radius: 2px;
}

a { color: var(--arcane-bright); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* W8 1.1: one shared content width (~1200px); prose stays readable via
   the 70ch caps below, the extra room goes to tables and card grids. */
.container { max-width: 75rem; margin: 0 auto; padding: 0 1.25rem; }

/* ---------- Header / navigation ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(8, 6, 14, 0.92);
  backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--edge-soft);
}
/* W8 2.1 + W9 3.1 + W10 1.2: ONE 72px header row - logo, centered
   primary nav, right cluster. The height is FIXED at desktop widths so
   no header element can ever change the row's height; the sub-1100px
   burger layout wraps and restores auto height in the media query.
   W13 item 0 (the approved compaction): the row's content must fit the
   1200px container at every desktop width - the W12 bar overflowed it
   by ~121px (measured scrollWidth 1321). Every horizontal metric below
   (this gap, the nav gap, chip padding, header .btn padding, the
   cluster width) is part of that measured fit; the layout-stability
   harness pins the no-overflow result at 1366px. */
/* W14 item 2b (owner), amended W15 item 2a: the header reads as ONE
   centered group - the whole run (logo, nav, chip, account cluster;
   Install left the header in W15) packs with the shared gap and
   centers as a unit via justify-content, replacing the W9 auto-margin
   nav centering. The fixed chip/cluster slots keep the group's width
   constant across auth states and pages, so the centered position is
   deterministic; the W13 header-fit pin still holds.
   W24 items 3-5 (owner spec): the ONE group gap grows 0.5rem -> 1.5rem
   so logo -> HOME and DOWNLOADS -> chip visually match the chip ->
   Sign in reference gap (~28px measured signed out). The gap is the
   flex gap between the group's three segments - never a per-element
   margin - so both sides move together and centering is preserved by
   construction. Paired with the W24 desktop carets this widens the
   group ~62px; the burger breakpoint moves 1024 -> 1100 so every
   remaining desktop width still fits its container (measured: group
   ~1048px vs the 1061px container at the 1101px floor). The burger
   media query resets the gap to 0.5rem - at wrap widths the large gap
   would pad the wrapped rows apart vertically. */
.header-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.5rem;
  height: 4.5rem;
  padding: 0.35rem 0;
}
/* Brand wordmark (owner ruling 2026-08-12): Cinzel at weight 600, the
   face and weight the owner selected for the wordmark. Cinzel's lining
   figures sit on the cap line, so the "2" of "L2" renders at cap height
   by design - never substitute a face or feature that drops it. */
.logotype {
  font-family: var(--font-display);
  font-size: 1.45rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.logotype .l2 { color: var(--arcane); }
.logotype .name { color: var(--parchment); }

/* W9 3.3, retuned by W13 item 0: six primary items at 13px uppercase
   (the gap carries the spacing; items keep no horizontal padding so the
   active underline hugs the label). The W9 ~30px gap was the single
   largest contributor to the header's container overflow; the
   compaction pass trims it to ~16px. */
/* W14 item 2b: margin 0 (not auto) - the bar's justify-content centers
   the whole group; auto margins would re-center the nav independently.
   W14 item 2d: the 0.85rem flex gap is the ONE spacing between every
   adjacent primary item (the HOME-to-NEWS reference gap); with the
   desktop carets hidden (item 2c) no dropdown host is wider than its
   label, so the gaps are uniform by construction. */
.nav-primary { display: flex; align-items: center; gap: 0.85rem; margin: 0; }
.nav-primary > a, .nav-primary .nav-link {
  padding: 0.45rem 0;
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--parchment-dim);
  border-radius: 3px;
  white-space: nowrap;
}
.nav-primary > a:hover { color: var(--arcane-bright); text-decoration: none; }
.nav-primary > a.active, .nav-primary .nav-link.active {
  color: var(--arcane-bright);
  box-shadow: inset 0 -2px 0 var(--arcane);
}

/* W8 2.3: grouped dropdown hosted by a primary item (Information).
   W11 item 4: the host is a label LINK + caret button pair - the item
   lays them out on ONE unwrappable row (min-content flex shrinking
   would otherwise drop the caret under the label); the burger layout
   re-enables wrapping so its inline menu can fall below the pair. */
.nav-item {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex-wrap: nowrap;
  /* W26 item 1 (owner spec): the ONE caret-slot token. The host item
     is label + caret, so the label's midpoint is only computable in
     CSS if the caret column has a KNOWN width: the caret button rides
     this fixed width (glyph centered inside it) and the dropdown's
     left offset subtracts it - both ends of the centering contract
     move together through this token, never independently. 1rem
     carries the ~15.5px glyph+padding content with the W12 hit zone
     intact; the burger layout restores width: auto (centering is
     meaningless in its static inline menu). */
  --caret-w: 1rem;
}
.nav-link {
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}
.nav-link:hover { color: var(--arcane-bright); text-decoration: none; }
.nav-link .caret { font-size: 0.7em; color: var(--parchment-faint); }
/* W11 item 4: the dropdown host is a LINK; this separate caret button
   toggles the menu (nav.js) so the label click always navigates while
   subpages stay reachable on touch and keyboard.
   W12 item 6 (root cause of the unreliable GAME/COMMUNITY clicks): the
   old margin-left -0.15rem overlapped the caret button OVER the label
   link's tail, so a click near the end of the word toggled the menu
   instead of navigating, and the caret's sub-16px hit zone made the
   opposite miss just as easy. The hit areas are now contiguous and
   non-overlapping: no negative margin ever, the label keeps its full
   clickable text, and the caret carries a wider padded hit zone of its
   own. The layout-stability harness clicks both and pins this. */
.nav-caret {
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
  font-size: 0.8125rem;
  padding: 0.45rem 0.2rem;
  color: var(--parchment-faint);
  border-radius: 3px;
  /* W24 item 1 (owner spec, SUPERSEDES the W14 item 2c removal): GAME
     and COMMUNITY carry a small, subtle downward indicator beside the
     label at EVERY width, so the dropdown hosts are visibly marked.
     Desktop keeps the full W11/W12 contract: the label click always
     navigates, hovering the label OR the caret opens the menu (the
     .nav-item:hover rule covers both children), and the caret's hit
     zone stays contiguous and non-overlapping with the label (the W12
     root-cause fix - never any negative offset). The horizontal hit
     padding is compacted 0.3rem -> 0.2rem as part of the W24 measured
     header fit; the burger layout keeps the same button as the touch
     toggle for the inline submenu. */
  display: inline-flex;
  align-items: center;
  /* W26 item 1: the fixed caret slot (see --caret-w on .nav-item).
     border-box, so the 0.2rem hit padding lives INSIDE the 1rem width
     and the desktop hit zone grows slightly (~15.5 -> 16px); the
     glyph centers in the slot. Still contiguous and non-overlapping
     with the label - the W12 contract is untouched. */
  width: var(--caret-w);
  justify-content: center;
}
.nav-caret .caret { font-size: 0.7em; }
.nav-caret:hover { color: var(--arcane-bright); }
/* W9 1.2: closed by default via visibility (not display), so leaving
   the host forgives a ~250ms detour before the menu closes (the delays
   on the closed-state transition ARE the close delay - do not remove
   them). Opening is immediate. */
/* W24 item 1 (owner spec): the internal layout is shared by BOTH menus
   so GAME and COMMUNITY stay consistent by construction - one padding,
   one row treatment, one min-width (below) that renders the two menus
   at the same width. Entries stay LEFT-aligned; balance comes from the
   internal padding, never from centering the text. */
.nav-menu {
  display: grid;
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.15s ease 0.25s, visibility 0s linear 0.4s;
  position: absolute;
  top: calc(100% + 0.45rem);
  /* W26 item 1 (owner spec): the panel centers on the LABEL midpoint,
     not the item midpoint. The host item is label + caret, so the old
     50% anchor centered the panel on the pair and it read shifted to
     the caret side; subtracting the fixed --caret-w slot puts the
     anchor exactly at the label's middle - (100% - caret) / 2 IS
     labelWidth / 2 by construction. The translateX(-50%) then centers
     the panel body on that anchor. The burger layout is position:
     static and untouched by this offset. */
  left: calc((100% - var(--caret-w)) / 2);
  transform: translateX(-50%);
  grid-auto-flow: column;
  /* W28 item 8 (owner spec, supersedes the W27 max-content-group
     mechanism the owner read as options "shifted toward the right"):
     TRUE row centering. justify-content: stretch grows the auto
     track to the panel's content width, the group fills it, and each
     option row spans the full dropdown width with its text centered
     WITHIN the row (text-align on .nav-group a below) - never a left
     padding/margin/translate simulation. Box center = row center =
     label glyph midpoint via the W26 caret-slot anchor above, so the
     rendered ink centers by construction. W38 section 10 (owner
     spec, SUPERSEDES the W24 equal-width contract): each menu sizes
     to its own longest label - this min-width is the shared FLOOR,
     and the nowrap rows grow the panel past it when a label needs
     the room (per-menu width, the W38 rule). W39 owner amendment:
     the shortened Community labels (Community Channels / Support)
     fit inside the floor again, so BOTH menus rest compactly on the
     shared 12.5rem floor - no retained empty width. Both menus still
     share every spacing token (this padding, the row padding, the
     gap). The burger override keeps its start/auto geometry with
     left-aligned rows. */
  justify-content: stretch;
  min-width: 12.5rem;
  gap: 1.6rem;
  /* W26 item 2 (owner spec): slightly transparent glass - the
     --ink-raised ground at 88% plus a mild backdrop blur (the same
     treatment family as the sticky header's 92% + blur), so the open
     menu feels lighter while bright page content behind it stays
     smeared and the parchment entries keep their contrast. Browsers
     without backdrop-filter still get the 88% ground - readable on
     its own by design. */
  background: var(--ink-raised-glass);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  /* W69 sections 16-17 (owner spec, supersedes the W43 0.79 glass):
     the ground steps to 90% - darker and more opaque so page content
     behind the menu no longer competes with the labels; the blur and
     the hue stand; the border steps one tier to --edge-bright. */
  border: 1px solid var(--edge-bright);
  border-radius: 6px;
  padding: 0.9rem 1rem 1rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
  z-index: 60;
}
/* W98 section 17 (docs/specs/W98-SITE-WIDE-CONSISTENCY-AUDIT.md): with
   scripting, .open (nav.js) is the ONE open state - the :focus-within
   rule serves only the no-JS baseline (html:not(.js)). Under JS the
   caret button keeps focus after a click, so focus-within kept the menu
   visible however many times the caret toggled .open (the menu could
   not be closed with the caret in Chrome); nav.js now owns keyboard
   focus (Tab out closes, Escape closes and refocuses the caret). */
.nav-item.open > .nav-menu, html:not(.js) .nav-item:focus-within > .nav-menu {
  visibility: visible;
  opacity: 1;
  transition: opacity 0.12s ease;
}
@media (hover: hover) {
  .nav-item:hover > .nav-menu {
    visibility: visible;
    opacity: 1;
    transition: opacity 0.12s ease;
  }
  /* W98 section 17: after a scroll, a menu choice or a bfcache restore
     the header is "hushed" (nav.js) - the hover rule stays off until the
     pointer moves again, so a menu the wheel happened to leave under the
     pointer never rides the sticky header down the page and a restored
     page never shows a menu it was left with. */
  .site-header.nav-hush .nav-item:not(.open):hover > .nav-menu {
    visibility: hidden;
    opacity: 0;
    transition: none;
  }
}
/* W24 item 1 as amended by W38 section 10: the .nav-menu min-width is
   the shared floor and each menu's nowrap rows size the panel to its
   own longest label (per-menu width - the W24 same-width reading is
   superseded). Rows carry their own horizontal padding (total left
   inset = menu 1rem + row 0.75rem = 28px) so labels never hug the
   dropdown edge, and the padded row is what the hover background
   fills. */
/* W28 item 8: the group's width stays auto so the menu's stretched
   track fills the panel and every option row spans the full dropdown
   width (the W27 max-content hug is retired with the mechanism it
   served); the shared width FLOOR lives on .nav-menu's min-width.
   The hover background now fills the whole row - the clickable row
   IS the dropdown width. */
.nav-group { display: flex; flex-direction: column; gap: 0.1rem; }
.nav-group a {
  padding: 0.32rem 0.75rem;
  font-size: 0.85rem;
  text-transform: none;
  letter-spacing: normal;
  /* W69 section 17: resting rows read as live options, never as
     disabled - full parchment ink over the darker glass. */
  color: var(--parchment);
  white-space: nowrap;
  border-radius: 3px;
  /* W28 item 8: the text centers WITHIN the full-width row - the
     padding stays symmetric, no left inset or translate ever. The
     burger override restores left alignment for its inline menu. */
  text-align: center;
}
/* W29 section 3 (owner spec): the dropdown row hover fills the DEEP
   arcane ground - the site's established soft-fill hover language
   (the W27 account-cluster precedent) - instead of the old
   --ink-panel fill, which sat within a few luma levels of the glass
   ground and was too subtle to spot. The brightened text rides
   along. The row fill is OPAQUE by design and does not touch the
   menu's glass: the 70% + 12px-blur transparency is unchanged. */
.nav-group a:hover { color: var(--arcane-bright); background: var(--arcane-deep); text-decoration: none; }
.nav-group a.active { color: var(--arcane-bright); }

.nav-actions { display: flex; gap: 0.5rem; align-items: center; white-space: nowrap; }
/* W12 item 1: every header action button shares ONE explicit control
   height (the .btn family's natural 2.5rem), centered content - so the
   minor button's smaller type can never change the row's geometry.
   W13 item 0: header buttons carry a compacted horizontal padding (the
   global .btn keeps its 1.1rem for page-body buttons). */
.nav-actions .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 2.5rem;
  padding: 0 0.6rem;
}
/* W12 item 1: the account cluster is a FIXED-width slot in BOTH auth
   states (signed out: Sign in + Register; signed in: username + Log
   out), so the centered nav sits at identical x whether or not the
   player is signed in - the W11 "same overall dimensions" rule made
   literal (and the W11 open thread closed). */
/* W13 item 0: the cluster width follows its measured content (the W12
   value did the same): signed-in 116+8+62 = 186px, signed-out
   two 0.6rem-padded buttons = 172px; 12rem = 192px carries both with
   slack. */
.account-cluster {
  display: inline-flex;
  gap: 0.5rem;
  align-items: center;
  justify-content: flex-end;
  width: 12rem;
}

/* W8 2.1 + W10 5.1, amended by W61 section 16 (supersedes the W59/W60
   abbreviation treatment): the chip is the ONE combined single-line
   header unit - dot + state + the Server clock component (Server + the
   live 12-hour AM/PM clock + the dynamic UTC offset), every text run
   in the chip's state color. Every text run keeps the single shared
   baseline; only the dot self-centers. */
.status-chip {
  display: inline-flex;
  align-items: baseline;
  /* W13 item 0: centered content in a FIXED width (the cluster doctrine
     extended to the chip). Once the header fits its container, the
     nav's auto margins gain real slack - a chip that grew with the
     clock digits or the Online/Offline/Pending label would nudge the
     centered nav. The width carries the widest state + clock; W58
     section 13 widened it for the legible Server label + offset. */
  justify-content: center;
  width: 16.5rem;
  gap: 0.4rem;
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 0.42rem 0.45rem;
  font-size: 0.82rem;
  color: var(--parchment-dim);
  white-space: nowrap;
}
.status-chip .status-dot { align-self: center; margin-right: 0; }
.status-chip[data-status-state="online"] { color: var(--signal-online); border-color: var(--signal-online); }
/* W77 section 1: the header chip's server clock run ("Server 9:59:27 PM
   UTC−4") is a server date & time value - it takes --time-server in
   every chip state; the dot and the state word keep the status color. */
.status-chip .clock-pair-chip { color: var(--time-server); }
.status-chip[data-status-state="offline"] { color: var(--signal-offline); border-color: var(--signal-offline); }
/* W61 section 16: the chip reads [● Online] · [Server 9:59:27 PM
   UTC−4] - the separation AROUND the dot separator is deliberately
   wider than the compact spacing inside the Server clock component,
   so the status and the clock group visually. */
.status-chip .chip-sep { color: var(--parchment-faint); margin: 0 0.25rem; }

/* W8 2.3: hamburger for small widths (nav.js toggles .nav-open). */
.nav-burger {
  display: none;
  background: var(--ink-panel);
  /* W29c (owner ruling 2026-08-24): the burger is a button-shaped
     micro-control - it joins the tier-3 --edge-bright border (its
     text was already the tier-3 --parchment ink). Color only. */
  border: 1px solid var(--edge-bright);
  border-radius: 3px;
  color: var(--parchment);
  padding: 0.35rem 0.65rem;
  font-size: 1rem;
  cursor: pointer;
}
.nav-burger:hover { border-color: var(--arcane); color: var(--arcane-bright); }
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Disabled navigation and buttons: requirements section 5 (visible but
   clearly dull/faded so players immediately read "not enabled yet"). */
.nav-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  user-select: none;
}
/* W9 5.1: .soon is the ONE disabled/annotation label, styled globally
   so it can never render at its parent's size (the "START A
   PURCHASEAVAILABLE AT LAUNCH" defect). It is always its own small
   block line; never place it inside a control's text.
   W33 section 8 + carried ruling E: the annotation was the faint half
   of the "still somewhat faint" verdict (0.6rem --parchment-faint,
   ~3.3:1). ONE global slight step - 0.65rem on --parchment-dim - so
   every AVAILABLE AT LAUNCH label (Community channels, footer socials,
   panels) reads comfortably while staying plainly an annotation.
   W43 carried item F (owner spec SITE_HERO_EVENTS_RATES_W43_2026-08-25,
   generalizing W42 section 5 site-wide): the W42 Support-scoped
   --parchment step (7.3:1 -> 15.2:1) becomes the ONE global tier -
   Community channels, footer socials, panel annotations, every
   instance - at the unchanged 0.65rem size and tracking. The W42
   .hub-grid-balanced scope is retired with the step it carried. */
.soon {
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--parchment);
  display: block;
  line-height: 1.4;
  font-family: var(--font-sans);
}
/* W59 sections 15 + 27: the ONE compact per-row availability chip -
   "Available at Launch" beside the feature it describes, replacing
   the oversized Planned callouts and the repeated section sentences.
   Always an annotation, never a control. */
.chip-launch {
  display: inline-block;
  border: 1px solid var(--edge);
  border-radius: 999px;
  padding: 0.12rem 0.5rem;
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  /* W69 section 19: the badge reads easily while staying secondary -
     parchment ink and a one-tier brighter edge, size and spacing kept. */
  color: var(--parchment);
  white-space: nowrap;
  vertical-align: middle;
}

/* W9 5.1: a disabled control pairs with its label as SEPARATE elements. */
.disabled-control {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
  vertical-align: top;
  margin-right: 0.5rem;
}

/* ---------- Buttons ---------- */

/* W29 (owner spec 2026-08-24): the THREE-TIER button system, one
   shared component family - never per-button fixes.
     Tier 1  .btn-primary   the page's major CTA (Install L2 Arcanis
                            class): filled --arcane-deep ground behind
                            an --arcane edge - unchanged, still the
                            most prominent by fill AND border.
     Tier 2  .btn-elevated  Register / Create Account / Server
                            Features: the --edge-elevated outline,
                            one clear step above standard.
     Tier 3  .btn           every other button site-wide: the
                            --edge-bright outline (section 1 - the
                            faint --edge outline is retired from
                            buttons). W29b (owner verdict on the
                            live W29): the 600 label weight is
                            ROLLED BACK - it pushed tier-3 text too
                            close to the elevated tier - so the
                            label keeps its previous restrained
                            styling (inherited normal weight, the
                            unchanged parchment ink and tracking);
                            the 600 weight moves down onto tiers 1
                            and 2, whose deployed look is unchanged.
   All grounds stay in the dark palette - definition comes from the
   ordered edges (and the upper tiers' retained label weight), never
   from brightness. */
.btn {
  display: inline-block;
  padding: 0.55rem 1.1rem;
  border: 1px solid var(--edge-bright);
  border-radius: 3px;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--parchment);
  background: var(--ink-panel);
}
.btn:hover { border-color: var(--arcane); color: var(--arcane-bright); text-decoration: none; }
/* W28 items 9-10 as amended by W29 section 2: the ELEVATED (tier 2)
   outline. Create Account and Server Features (Home hero) carry the
   same --edge-elevated border as the header Register button - the
   unchanged 1px weight - so the three named controls sit visibly
   above the standard tier while both hero buttons stay secondary
   actions (panel-ink ground, no fill) beside the filled Install
   primary. The standard .btn:hover arcane edge wins on hover at
   higher specificity.
   W29b: the 600 label weight moves DOWN from the .btn base onto the
   two upper tiers so their deployed W29 appearance is unchanged by
   the tier-3 text rollback - text weight now separates the elevated
   and primary tiers from the restrained standard tier instead of
   being shared by all three. */
.btn-elevated { border-color: var(--edge-elevated); font-weight: 600; }
.btn-primary {
  background: var(--arcane-deep);
  border-color: var(--arcane);
  color: var(--arcane-bright);
  font-weight: 600;
}
.btn-primary:hover { background: var(--arcane); color: var(--ink); }
.btn[aria-disabled="true"], .btn.btn-disabled, .btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}
/* Fail-closed CAPTCHA notice injected by form-gate.js when the widget
   is missing, unloaded, or errored on a form that requires it. */
.captcha-hint {
  color: var(--error-soft);
  font-size: 0.85rem;
  margin: 0.5rem 0;
}

/* ---------- Form rows, attempt errors, password checklist ---------- */
.form-row { margin: 0 0 1rem; }
/* Validation messages are BLOCKS directly below their field, never
   beside it: small gap to the input above, clear gap to the next row. */
.field-msg {
  display: block;
  color: var(--error-soft);
  /* W56 section 37: helper/secondary text steps up modestly
     (0.8 -> 0.85rem, roomier line) - better readability, same
     compact hierarchy. */
  font-size: 0.85rem;
  line-height: 1.45;
  margin: 0.3rem 0 0;
}
/* Green success state for field labels, same live style as the
   password checklist. Labels do NOT turn red on errors (owner change):
   the in-field message plus the red border carry the error state. */
label.ok { color: var(--signal-online); }
.field-error input, .field-error select, .field-error textarea {
  border-color: var(--error-strong);
}
/* Shared help text under a field. W33 section 33: instructional text
   steps up from --parchment-faint (~3.3:1) to --parchment-dim so field
   guidance is comfortably readable against the dark card - deliberately
   NOT bright white; the hierarchy below labels and inputs stands. */
.help-msg {
  display: block;
  color: var(--parchment-dim);
  /* W56 section 37: the modest readability step (0.8 -> 0.85rem). */
  font-size: 0.85rem;
  line-height: 1.45;
  margin-top: 0.3rem;
}
/* W56 section 37: the ONE base rule for record/table metadata lines
   (.le-meta) - previously unstyled outside .login-entry. */
.le-meta { color: var(--parchment-dim); font-size: 0.85rem; line-height: 1.45; }
/* The terms error renders below the complete checkbox-and-text row. */
.accept-msg { margin: -0.6rem 0 1rem; }
.gate-attempt-wrap { display: inline-block; }

/* In-field validation messages: a floating red message INSIDE the
   input box. Left placeholder position while the field is empty,
   right-aligned beside the text otherwise (rendered only when it fits
   - form-gate.js measures - so it can never obscure typed text), and
   hidden entirely while the field has focus. */
.form-row { position: relative; }
.infield-msg {
  position: absolute;
  left: 0.75rem;
  right: 0.75rem;
  pointer-events: none;
  color: var(--error-soft);
  font-size: 0.78rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: left;
}
.infield-msg.infield-right { text-align: right; }
/* Server-rendered fallback (JS off): the message rides as a red
   placeholder, which the browser only shows while the input is empty;
   focus hides it so it never interferes with typing. */
.input-invalid::placeholder { color: var(--error-soft); opacity: 1; }
input:focus::placeholder { color: transparent; }

/* ---------- Account form cards (register, login, recovery) ---------- */
/* ONE shared layout so the account pages cannot drift apart: one card
   width, equal internal padding, and a centered 24rem field column.
   Fields stay left-aligned inside the card.
   W50 ruling D11 (owner spec W50_CATEGORY_STANDARDS_2026-08-25): the
   .account-section wrapper is RETIRED - the five account form pages
   (login, register, recover, recover-confirm, resend-verification)
   render through base_section.html now (the GATE category: the Frame
   lane, the W43 hero skeleton with the 3-line reservation, the 25.6px
   hero-to-content gap; header centering comes from the standing W28
   public page-header rule). The form keeps its purpose-built narrow
   column: the centered 30rem .account-card below, unchanged. */
/* Headroom above the first field and below the last button; equal
   left/right internal padding. Width trimmed (owner batch) so little
   space sits unused either side of the 24rem field column. */
.account-card { max-width: 30rem; margin: 0 auto; padding: 1.8rem 2rem 2rem; }
/* The fields keep their NORMAL width (the global 24rem cap); balance
   comes from centering the whole content column inside the card, so
   the empty space left and right of the field boxes is equal.
   Everything in the form - inputs, checklists, messages, the
   acceptance row, the CAPTCHA, and the buttons - lives in this column
   and shares the fields' left/right boundaries. */
.account-card form { max-width: 24rem; margin: 0 auto; }
.account-card input:not([type="checkbox"]), .account-card select {
  width: 100%;
  box-sizing: border-box;
}
.account-card .cf-turnstile { width: 100%; }
.account-card .gate-attempt-wrap { display: block; width: 100%; }
/* One vertical button column per card: exactly field width, identical
   padding, height, border, radius, and centered text. */
.account-card .btn {
  display: block;
  width: 100%;
  box-sizing: border-box;
  text-align: center;
  padding: 0.65rem 1.1rem;
  line-height: 1.2;
  border-width: 1px;
  border-radius: 3px;
}
.account-card .gate-attempt-wrap + .btn,
.account-card .btn + .btn { margin-top: 0.6rem; }

/* Live requirement checklist: readable-but-inactive while incomplete
   (hollow circle), green text with a checkmark while satisfied; the
   class toggle reverts it the moment the rule stops holding. */
.pw-checklist {
  list-style: none;
  margin: 0.4rem 0 0;
  padding: 0;
  font-size: 0.8rem;
  color: var(--parchment-dim);
}
.pw-checklist li { margin: 0.15rem 0; }
.pw-checklist li::before {
  content: "\25CB";
  display: inline-block;
  width: 1.1em;
  margin-right: 0.35em;
  color: var(--parchment-dim);
}
.pw-checklist li.ok { color: var(--signal-online); }
.pw-checklist li.ok::before { content: "\2713"; color: var(--signal-online); }

/* Terms acceptance: checkbox and linked statement read as one element. */
.accept-terms { display: flex; gap: 0.55rem; align-items: flex-start; }
.accept-terms input[type="checkbox"] { margin-top: 0.3rem; }

/* ---------- Panels, callouts, page furniture ---------- */

.panel {
  background: var(--ink-raised);
  border: 1px solid var(--edge-soft);
  border-radius: 6px;
  padding: 1.4rem 1.6rem;
}
/* W13 item 1: ONE shared vertical rhythm between content blocks - the
   sibling-panel margin and the .stack gap ride the same token so the
   card pages (Game, Community, Downloads, the board pages, the panel)
   read compact and identical in rhythm. Tune the token, never a page.
   W15 item 1 (owner: the Community Media / Hall of Records gap IS the
   standard): callouts join the sibling rule, and .block-gap-top covers
   a block whose previous sibling is neither a panel nor a callout
   (grids, pagination rows) - no template carries an inline block gap. */
.panel + .panel,
.panel + .callout,
.callout + .callout,
.callout + .panel { margin-top: var(--block-gap); }
/* W17 item 5: a .stack is itself a block - it separates from sibling
   blocks on the same token (the castle-siege callout -> stack pair
   rendered flush before this rule). */
.panel + .stack,
.callout + .stack,
.stack + .panel,
.stack + .callout,
.stack + .stack { margin-top: var(--block-gap); }
.block-gap-top { margin-top: var(--block-gap); }
/* W51 (owner spec Addendum A item 4, 2026-08-26): the Home Community
   Media / Hall of Records pair shares ONE card height - equal 1fr
   grid rows, the seam on the same --block-gap token as the grid gap
   (one mechanism per seam, W17 item 5: the sibling margin yields to
   the gap inside this grid exactly as it does inside a .stack). */
.home-equal-cards {
  display: grid;
  grid-auto-rows: auto;
  row-gap: var(--block-gap);
}
/* W68 section 3 (docs/specs/W68-COMMUNITY-MEDIA-GALLERY.md) supersedes
   the W51 equal-rows rule (Addendum A item 4): Community Media is now
   the richer preview (carousel + stream cards) and must not stretch
   Hall of Records to its height - each card keeps its own height; the
   grid and its one-token seam stand. */
.home-equal-cards > .panel + .panel { margin-top: 0; }
/* W17 item 5: ONE mechanism per seam. Inside a .stack the flex gap
   carries the whole separation - the sibling margins above are zeroed
   there so gap + margin can never SUM again (the pre-W17 bug that
   made stack pages render 2 x the token while section-wrapped pages
   rendered 1 x). */
.stack > .panel + .panel,
.stack > .panel + .callout,
.stack > .callout + .callout,
.stack > .callout + .panel,
.stack > .panel + .stack,
.stack > .callout + .stack,
.stack > .stack + .panel,
.stack > .stack + .callout,
.stack > .stack + .stack,
.stack > .block-gap-top { margin-top: 0; }
/* W17 item 5: the remaining measured off-token seams join the token -
   a callout after a table (the panel activity help block rendered
   flush), and the User Panel in-page section boundaries (the h2s
   that separate sections inside .panel-content rode the browser's
   default heading margin). The heading's own bottom margin is
   heading-to-own-content and stays untouched. */
.table-wrap + .callout { margin-top: var(--block-gap); }
.panel-content > h2 { margin-top: var(--block-gap); }
/* W8 1.2: gold headings - violet stays exclusive to links. */
.panel h2 { color: var(--gold); margin-top: 0; font-size: 1.3rem; }
.panel h3 { color: var(--gold); font-size: 1.05rem; }

.callout {
  border: 1px solid var(--edge);
  border-left: 3px solid var(--arcane);
  background: var(--ink-panel);
  border-radius: 4px;
  padding: 0.9rem 1.2rem;
  color: var(--parchment-dim);
  font-size: 0.92rem;
}
.callout .label {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.7rem;
  color: var(--arcane-bright);
  margin-bottom: 0.3rem;
}
.callout-warning { border-left-color: var(--signal-offline); }
.callout-warning .label { color: var(--signal-offline); }

.page-header { margin: 2.2rem 0 1.6rem; }
/* W10 phase 2: sibling-group reserved heights - the intro slot
   reserves the TALLEST intro (in 1.6em text lines) so a one-line vs
   three-line intro never moves the tab row or the first content block
   between sibling pages.
   W43 section 3 + owner decision E (owner spec
   SITE_HERO_EVENTS_RATES_W43_2026-08-25): the ONE shared internal-page
   hero standard. Every internal page (HOME excluded) renders the
   identical hero geometry - top offset (2.2rem above), eyebrow-title
   gap, title size, description width, and hero-to-content gap - so
   the per-group reservation tokens are RETIRED and the reservation
   becomes the ONE site-wide 3-line value (the measured site maximum,
   raised from boards 2 / legal 1 / recruit 1 / rates 2.5; the W10
   stability property still holds - 3 covers every group's tallest
   intro). The W40 section-3 margin reduction and the W41 section-12
   2.5-line rates reservation are SUPERSEDED by this standard (the
   spec's section-3 normalization names Server Rates explicitly);
   /rates/ rejoins the shared hero geometry, and the Legal index gains
   its short subtitle so no page jumps title-to-content. */
.section-layout .page-header .intro { min-height: calc(1.6em * var(--sl-intro-lines, 3)); }
.page-header .eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.18em;
  font-size: 0.72rem;
  color: var(--arcane);
}
.page-header h1 { margin: 0.35rem 0 0.55rem; font-size: 2rem; }
/* W9 4.4: text-wrap pretty keeps a short fragment like "(Alt+B)." from
   wrapping alone onto the last line (no-op in engines without it). */
.page-header .intro { color: var(--parchment-dim); max-width: 70ch; margin: 0; text-wrap: pretty; }

.stack { display: flex; flex-direction: column; gap: var(--block-gap); }
main { min-height: 60vh; padding-bottom: 3.5rem; }

/* ---------- Hero (home) ---------- */

.hero { text-align: center; padding: 4rem 0 2.5rem; }
/* W8 4.1: castle art behind the hero. The image arrives as the
   --hero-art custom property (ONE swappable static path in home.html);
   the dark overlay + vignette keep text contrast over any art. */
.hero-art {
  background-image:
    radial-gradient(90% 130% at 50% 0%, rgba(8, 6, 14, 0.55) 40%, rgba(8, 6, 14, 0.97) 100%),
    linear-gradient(rgba(8, 6, 14, 0.55), rgba(8, 6, 14, 0.78)),
    var(--hero-art);
  background-size: cover, cover, cover;
  background-position: center, center, center 30%;
  border: 1px solid var(--edge-soft);
  border-radius: 8px;
  margin-top: 1.2rem;
  padding: 4.5rem 1.5rem 3rem;
}
/* The hero title is a brand-wordmark surface: Cinzel 600 like the header
   logotype (owner ruling 2026-08-12), "L2" accented via the same class. */
.hero h1 { font-size: 2.6rem; font-weight: 600; margin: 0 0 0.6rem; }
.hero h1 .l2 { color: var(--arcane); }
.hero .tagline { color: var(--parchment-dim); max-width: 40rem; margin: 0 auto 1.6rem; }
.hero .cta-row { display: flex; gap: 0.8rem; justify-content: center; flex-wrap: wrap; }
.hero .facts {
  display: flex;
  gap: 2rem;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 2.2rem;
}
.hero .fact .k {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.68rem;
  color: var(--parchment-faint);
}
.hero .fact .v { font-family: var(--font-display); color: var(--gold); font-size: 1.05rem; }
.hero .fact .v a { color: var(--arcane-bright); }

/* ---------- News ---------- */

.news-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr)); gap: 1.25rem; }
.news-card {
  background: var(--ink-raised);
  border: 1px solid var(--edge-soft);
  border-radius: 6px;
  padding: 1.2rem 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.news-card:hover { border-color: var(--arcane-deep); }
/* W24 item 7 (owner spec): a clickable CARD never underlines every
   nested text run. The anchor kills the global a:hover underline (the
   decoration would otherwise propagate to date, badges, excerpt), the
   card keeps its border-color feedback, and ONLY the card title takes
   a subtle underline - text-decoration on the h3, so line height and
   layout never move. This is the ONE shared card-link rule: every
   clickable card on the site is an a.news-card (news, wiki, legal,
   media via .media-card), so the fix lands at component level. */
a.news-card:hover { text-decoration: none; }
a.news-card:hover h3 {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.news-card .date {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--parchment-faint);
}
.news-card h3 { margin: 0; font-size: 1.1rem; }
.news-card .excerpt { color: var(--parchment-dim); font-size: 0.92rem; margin: 0; }
/* W12 item 2: a badge chip CENTERS its text both ways (inline-flex);
   the old inline-block stretched to the news filter row's input height
   and left the category label sitting high in the chip. */
.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  vertical-align: middle;
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  border: 1px solid var(--edge);
  border-radius: 3px;
  padding: 0.15rem 0.45rem;
  color: var(--parchment-dim);
}
.badge-pinned { border-color: var(--arcane); color: var(--arcane-bright); }
/* W29c (owner ruling 2026-08-24): a LINKED badge chip (the news
   category chips, the media platform chips) rests on the tier-3
   standard-button colors - the --edge-bright border and --parchment
   ink - so the chip controls are clearly readable. Interactive chips
   only: plain span badges keep the quiet dim treatment above, and
   the :not() guard leaves the active/pinned chip's arcane treatment
   untouched. This rule must sit BEFORE a.badge:hover (equal 0-2-1
   specificity) so the hover feedback still wins. COLOR ONLY - no
   geometry, radius, padding, or text metric changes. */
a.badge:not(.badge-pinned) {
  border-color: var(--edge-bright);
  color: var(--parchment);
}
/* W24 item 7: a badge that happens to be a link (the news category
   chips) is a CONTROL - border + color feedback like the .btn family,
   never a text underline. */
a.badge:hover { text-decoration: none; border-color: var(--arcane); color: var(--arcane-bright); }
/* W12 item 2: the news search/category row is a named component (the
   inline styles left the template); center alignment keeps the small
   chips from stretching to the search input's height. */
.news-filter {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
  align-items: center;
  /* W17 item 5: the filter -> results seam rides the ONE token. */
  margin-bottom: var(--block-gap);
}
.news-filter input[type="search"] {
  background: var(--ink-panel);
  border: 1px solid var(--edge);
  border-radius: 3px;
  color: var(--parchment);
  padding: 0.5rem 0.8rem;
}

/* W17 item 5: the results -> pagination seam rides the ONE token. */
.pagination { display: flex; gap: 0.6rem; align-items: center; margin-top: var(--block-gap); }
.pagination .current { color: var(--parchment-dim); font-size: 0.85rem; }

/* W59 sections 40-41: the ONE compact table pagination component.
   Purple carries the interactive states (the site's selection color,
   section 46); the active page reads as selected, the end buttons
   disable visibly, and the whole strip stays one quiet line under its
   table. */
.table-pagination { gap: 0.35rem; margin-top: 0.8rem; flex-wrap: wrap; }
.table-pagination .pg-btn,
.table-pagination .pg-num {
  display: inline-block;
  border: 1px solid var(--edge);
  border-radius: 3px;
  padding: 0.22rem 0.55rem;
  font-size: 0.8rem;
  color: var(--parchment-dim);
  text-decoration: none;
}
.table-pagination a.pg-btn:hover,
.table-pagination a.pg-num:hover { border-color: var(--arcane-bright); color: var(--arcane-bright); }
.table-pagination .pg-current {
  border-color: var(--arcane-bright);
  color: var(--arcane-bright);
  font-weight: 600;
}
.table-pagination .pg-disabled { opacity: 0.45; }
.table-pagination .pg-ellipsis { color: var(--parchment-faint); padding: 0 0.1rem; }

/* ---------- Tables (rates, downloads, rankings later) ---------- */

/* W74 section 6: NO table scrolls horizontally - the wrap no longer
   clips or scrolls; tables fit their container by construction (the
   stacked layout below each table's breakpoint, wrapping cells above
   it - see the W74 block at the end of this file). */
.table-wrap { overflow: visible; }
/* W14 item 3 (owner ruling, supersedes the W13 "ALL headers center"
   rule FOR TEXT COLUMNS ONLY): the ONE global table alignment system,
   applied to every table on every page through this skin, as per-type
   column classes - never per-table rules. Rules of record:
   - TEXT columns (names, classes, clans, titles, owners, packages,
     activities, results, statuses, dates, times, IPs, IDs, and every
     placeholder in them): the header LEFT-aligns and every value
     LEFT-aligns, sharing ONE left edge (the shared --cell-pad-x
     padding) - the owner's reference style, "the C in Character
     vertically over the first letter of every name". Never a centered
     text header above left-aligned data. Dates/times and statuses are
     text-typed on purpose: value and header share the same left edge.
   - NUMERIC columns (th.col-num + td.col-num): the W13 corrected
     treatment PRESERVED EXACTLY - header and cells CENTER (center is
     the only alignment that puts the value beneath the header at ANY
     column width), tabular figures so digit runs stay uniform.
   - "-" placeholders inherit their column's alignment.
   Density is tuned via the ONE padding token (clans-table below). */
table.arcanis {
  --cell-pad-x: 0.9rem;
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}
/* W8 1.6: the ONE table skin, public and panel alike - serif small-caps
   gold headers, neutral (non-link-colored) emphasis values, centered
   numeric columns (.col-num, W13), no bare-dash placeholders. */
table.arcanis th {
  font-family: var(--font-display);
  font-variant-caps: small-caps;
  text-align: left;
  color: var(--gold);
  border-bottom: 1px solid var(--edge);
  padding: 0.6rem var(--cell-pad-x);
  letter-spacing: 0.04em;
  white-space: nowrap;
}
/* W14 item 3: the numeric header keeps the W13 centered treatment -
   th.col-num is part of the type system now, not a page-local rule. */
table.arcanis th.col-num { text-align: center; }
table.arcanis td {
  padding: 0.55rem var(--cell-pad-x);
  border-bottom: 1px solid var(--edge-soft);
  color: var(--parchment);
}
table.arcanis td.col-num {
  text-align: center;
  font-variant-numeric: tabular-nums;
}
/* W15 items 4-6 amended by W16 (the even-distribution round): the ONE
   column-width system - width HINTS per column TYPE, in this shared
   skin only; no table carries raw widths.
   FIXED-grid tables type EVERY <col> (the W15 untyped slack-sink
   column is retired): each type's base is its expected content
   ceiling (widest header or value) plus a uniform breathing share,
   and the browser distributes the table's remaining width over the
   fully-specified columns PROPORTIONALLY to these bases - so every
   column shares the air in proportion to its content class and no
   column pools it. The sum of a grid's bases is its natural floor
   (below it the .table-wrap scrolls).
   AUTO-layout tables: a column is either SHRUNK (th.col-num /
   th.col-fit, the 1% hint - short bounded content: numerics,
   statuses, dates, names, vocabulary strings; values never wrap) or
   FLEXIBLE (genuinely long text: reasons, tasks, descriptions, clan
   lists), and slack concentrates in the flexible columns
   deliberately. A table whose columns are ALL shrunk spreads the
   surplus EQUALLY per column (the store-ladder behavior) - the ideal
   even distribution for short-content tables. Label/value tables
   (.table-kv) shrink the label column so values sit beside their
   labels instead of across a void. */
table.arcanis col.c-no { width: 6.5rem; }      /* positions (incl. a sortable "No." + arrow); W17: raised
                                                  6.5 <- 5.5 so the No. -> Character pair stops being every
                                                  board's crowd point (measured ~47px vs ~200px dead spans) */
table.arcanis col.c-count { width: 6.5rem; }   /* small counts: W, L, Grade */
table.arcanis col.c-score { width: 8rem; }     /* comma-grouped figures, Points, Matches; W17: 8 <- 8.5 -
                                                  the score columns fed the boards' widest dead spans */
table.arcanis col.c-figure { width: 9rem; }    /* long figures: Time Played, Weekly Lease; W17: 9 <- 9.5,
                                                  keeping the online board's sum within a few px of the
                                                  family's now-equal pvp/pk sums */
table.arcanis col.c-status { width: 7.5rem; }  /* status words */
table.arcanis col.c-date { width: 12.5rem; }   /* full zoned datetimes */
table.arcanis col.c-date-long { width: 17rem; }/* verbose datetimes ("1 August 2026 at 10:00 PM (EDT)") */
table.arcanis col.c-name { width: 11rem; }     /* the identity column (character/boss names); W17: 11 <-
                                                  12.5 - the measured Character slack ran ~150px over
                                                  realistic names on every board and ladder */
table.arcanis col.c-text { width: 10rem; }     /* standard text: Class, Clan, Residence, Owner;
                                                  W17: 10 <- 10.5 (still above the 16-char content ceiling).
                                                  W18 item 1 (owner ruling): with Title gone from Top PvP all
                                                  three character boards carry the SAME five columns and share
                                                  one identical colgroup, so the c-text-wide type (which
                                                  existed only to balance the pk/online two-column text region
                                                  against pvp's three) is RETIRED - the family's sums are
                                                  equal by identity and the leading columns cannot drift */
table.arcanis col.c-window { width: 16.5rem; }   /* W58: +0.75rem - the offset suffix (UTC−4) is wider than the retired (EDT) *//* the boss Respawn column (boss tables are its only user).
                                                  W19: 15.75 <- 17 - the balance retune keeps the W18 floor
                                                  property (a worst-case September exact time, 219.4px
                                                  measured, still fits AT THE FLOOR: 252 - 28.8 = 223.2px
                                                  room) while the freed slack moves the composition toward
                                                  even gaps and comparable edge insets (the W19 balance
                                                  walk); W96 retired the estimated window's multi-line
                                                  block - the column holds the ONE window span */
table.arcanis col.c-icons { width: 8rem; }     /* the ward-icon column */
/* W16: the DENSE variant of the same map for the column-rich Top
   Clans grid (which already rides the dense --cell-pad-x token) -
   ~0.85 scale so its nine columns fit the container without
   scrolling. Defined here, centrally, next to the base map. */
table.arcanis.clans-table col.c-no { width: 4.5rem; }
table.arcanis.clans-table col.c-count { width: 5.5rem; }
table.arcanis.clans-table col.c-score { width: 7.5rem; }
table.arcanis.clans-table col.c-text { width: 9rem; }
table.arcanis.clans-table col.c-icons { width: 8rem; }
/* W19 (owner complaint, 2026-08-17): the BALANCE variants of the
   same map, defined centrally next to the base map like the dense
   one. The named tables passed the gap law but READ left-clustered:
   these per-table maps re-place the columns so the first and last
   content blocks anchor with comparable edge insets and the content
   gaps run approximately even (centers evenly distributed once
   adjusted for content width). Solved by render at 1366 and pinned
   by the harness balance walk; no other table's geometry moves.
   BOSS (both tables via the shared F10 colgroup; the variant sum
   plus c-window is EXACTLY the unchanged 50rem/800px floor; the
   Boss column keeps ~209px of room for real long raid names).
   W77 section 9 (docs/specs/W77-SITE-UPDATES.md): the public pair
   gains the Countdown column (c-count) and the typed sum STAYS the
   50rem / 800px floor - No. 5rem (two digits + the sort glyph), Lv.
   4.75rem, Status 6.25rem ("Defeated" + room), Countdown 7.5rem
   ("10d 02:26:09" with the same edge inset as No. - the W19 balance
   walk). W77b ruling 1: every instant is the ONE time cell now ("21
   Jul 2026, 2:12 PM UTC−4", ~175px), so Respawn gives 1.5rem back to
   Boss: Respawn 14.25rem (the "and <cell>" window line, ~203px, stays
   whole from ~905px up - the pair stacks below 900px), Boss 12.25rem
   (the balance walk's content-gap rhythm within its 60px cap). */
/* W96 (docs/specs/W96-RESPAWN-WINDOW-COLUMNS.md): the same 50rem floor
   re-placed for the two renamed headers. "Respawn Window Starts In" in
   the header font measures 219px on one line and its first phrase
   "Respawn Window" 141px, so the countdown column takes 10.75rem (172px:
   the phrase plus the cell padding) and the header wraps at its ONE
   phrase boundary - two lines, never three (measured: at 7.5rem it broke
   "Respawn / Window Starts / In", a 93px header), so the countdown column
   is 11.25rem (180px at the floor: the phrase; ~249px at 1366, where the
   whole header fits one line and the W18 trailing-slack cap holds against
   a "15d 22h 13m 08s" value). The Respawn column holds the W96 cell
   instead of the W77b time cell - a duration or a "remaining" countdown,
   short most of the time - so it is 10rem (160px; ~221px at 1366, where
   the widest value, a raid's "1d 1h 59m 59s remaining" at ~180px, still
   sits on one line; nearer the floor the cell may wrap before
   "remaining"). Boss keeps 12.75rem (204px; ~282px at 1366 against the
   longest raid name's ~226px). The W19 balance walk re-solved at 1366
   over this budget (gaps ~48-80px, spread under the 60px cap). */
/* W97 (docs/specs/W97-BOSS-TABLES-AND-SORT-INDICATORS.md, spec item 1):
   the last header is "Starts In" (79.7px measured) where W96's "Respawn
   Window Starts In" needed 219px, so the public countdown column no
   longer needs 11.25rem. It becomes 9rem - the smallest width that
   never wraps the widest countdown ANYWHERE, including at the 800px
   floor where the table stops scaling: the countdown text measures
   115.1px and the cell's padding pair 28.8px, so 144px is the exact
   bound. The 2.25rem it gives up go to No. (5 -> 8rem) and back to
   Respawn Window (10 -> 9.25rem, whose widest value "18h 31m 08s
   remaining" needs 113.8px against a 119.2px box at the floor), which
   re-centers the W19 balance unit: the harness measured the composition
   36.1px from the left edge and 119.5px from the right after the
   rename (limit 24px, because the short header stopped filling the
   column), and this budget puts them at 69.3 and 69.7 on the same
   fixture. Boss keeps 12.75rem - the longest raid name plus the W97
   chevron needs ~246px against its 253.8px content box at 1366. */
table.arcanis.boss-table col.c-no { width: 8rem; }
table.arcanis.boss-table col.c-name { width: 12.75rem; }
table.arcanis.boss-table col.c-score { width: 4.75rem; }
table.arcanis.boss-table col.c-status { width: 6.25rem; }
table.arcanis.boss-table col.c-window { width: 9.25rem; }
table.arcanis.boss-table col.c-count { width: 9rem; }
/* The public pair's window cell may break before "remaining" near the
   800px floor (the panel's 13.5rem column never needs to). */
table.arcanis.boss-table:not(.boss-sched) .bt-window { white-space: normal; }
/* W64 sections 3 + 6 (Decision 1): the PANEL Server Schedule boss
   tables (.boss-sched, both tabs) carry their own budget - the public
   /boss-status/ pair above is untouched. Every typed column is sized
   to its widest value (No. two digits, Level two digits, Status
   "Alive"/"Dead", the schedule column's date/time chunks, a
   "10d 02:26:09" countdown) and Name is the ONE untyped column (the
   shared 11rem c-name hint lifted) - it takes every remaining pixel.
   The cell padding token tightens to 0.6rem (a ~847px panel column).
   W92 (item 7, ruled at W91 Q7): the 9rem Drops column is GONE from
   both tabs - the drop list rides the expanded row - and its width
   went back to the columns that were wrapping, measured before and
   after in docs/w92-screens/w92-rows-*.json: the schedule column
   widens to 13.5rem on BOTH tabs (an exact raid instant "2 Sep 2026,
   5:14 PM UTC−4" needs ~186px of content and was breaking into two
   lines inside 12.5rem; the Grand window's boundaries already fit) and
   Name takes the rest. The typed sum is 35.25rem (564px) since W92b
   widened the countdown column (below), so Name keeps ~276px at the
   840px floor and ~283px in the panel column at 1366 and above (the
   column never exceeds ~847px) - enough for the datapack's longest raid
   name ("Anakim's Nemesis Zakaron", ~226px) to stay on one line, which
   it did not before W92. */
table.arcanis.boss-sched { --cell-pad-x: 0.6rem; }
table.arcanis.boss-sched col.c-name { width: auto; }
table.arcanis.boss-sched col.c-no { width: 3.5rem; }
table.arcanis.boss-sched col.c-lv { width: 4.5rem; }
table.arcanis.boss-sched col.c-status { width: 5rem; }
/* W65 section 11: the schedule column carries a Server line and a
   Local line per instant ("Server 29 September 2026," over "10:15 PM
   UTC−4"). W92: 13.5rem on both tabs (W74 section 9: one cell per
   boundary on the Grand table) so the date and time chunks share one
   line wherever the table is not stacked. */
table.arcanis.boss-sched col.c-spawn { width: 13.5rem; }
/* W92b: the countdown column was 7.75rem (W65, "COUNTDOWN + the sort
   indicator"), whose 104.8px content box is narrower than a two-digit-day
   countdown - "15d 22h 13m 08s" measures 115.1px - so the value overflowed
   its cell by ~10px. Harmless while Drops sat to its right and absorbed it
   in padding; the moment W92 made "Respawns in" the LAST column, that
   overflow reached the table edge and the layout harness measured the
   table 1px wider than its wrap. 8.75rem (140px, 120.8px of content)
   holds the widest realistic countdown with ~6px of slack; Name gives up
   the 1rem (~283px on the Bosses tab, ~179px on the Grand tab). */
/* W96: the header is "Respawn Window Starts In" now (the spec's exact
   string); its first phrase needs 141px, so the column is 11rem (176px,
   147px of content) and the header wraps at its one phrase boundary
   onto two lines. The values need less (~115px), and Name absorbs the
   2.25rem (~247px on both tabs at 1366 - the longest raid name,
   "Anakim's Nemesis Zakaron", needs ~226px on one line). */
/* W97 (docs/specs/W97-BOSS-TABLES-AND-SORT-INDICATORS.md, spec items 1
   and 3): the header is "Starts In" now (~60px), so the 11rem W96 held
   for "Respawn Window" no longer has a reason; the column returns to
   9rem (144px, 124.8px of content - the widest countdown, "15d 22h 13m
   08s", needs ~115px) and Name takes the 2rem back. It needs it: the
   chevron now sits BESIDE the name (item 3) and costs the name ~20px,
   which made the datapack's two longest raid names ("Anakim's Nemesis
   Zakaron", "Shilen's Messenger Cabrio", ~226px) wrap onto two lines at
   1366 AND 1920 inside the 247px W96 left them (measured by
   scripts/w97-layout-probe.py: 60.6px rows against the Grand tab's
   40.1px). With ~279px every name sits on one line again and an alive
   or defeated row is the same height on both tabs (item 4). */
table.arcanis.boss-sched col.c-count { width: 9rem; }
/* W92b (owner ruling W92 Q1, Grand tab only): each estimated-window
   boundary sat on ONE line and the Grand table's schedule column widened
   to 20rem for it. W96 (docs/specs/W96-RESPAWN-WINDOW-COLUMNS.md)
   RETIRES that format: the column shows the window's duration ("40
   minutes") and its "39m 59s remaining" countdown on both tabs, which
   the shared 13.5rem holds with room (measured by
   scripts/w96-boss-rows-probe.py), so the Grand-only width is gone and
   the two tabs share one column budget again. */
/* W55 Decision 6e: the W18/W19 store-grid and store-tickets variant
   rules are SWEPT - no table has used them since the W54 card
   consolidation (the owner ratified the sweep of the retired
   variants). */
table.arcanis:not(.table-fixed):not(.boss-table) th.col-num,
table.arcanis:not(.table-fixed):not(.boss-table) th.col-fit { width: 1%; }
table.arcanis td.col-num,
table.arcanis td.col-fit { white-space: nowrap; }
table.arcanis.table-kv td:first-child { width: 1%; white-space: nowrap; }
/* W74 section 6: in a narrow container the label column wraps too. */
@media (max-width: 959px) { table.arcanis.table-kv td:first-child, table.arcanis.table-kv th { white-space: normal; } }
/* W13 item 3: the shared empty-state row every conditional table
   renders when it has zero records (includes/table-empty-row.html) -
   the table structure and headers always show; the honest message
   centers across the grid, like the W11 per-class empty rows. */
table.arcanis .table-empty-row td,
table.arcanis .class-empty-row td { text-align: center; }
/* W51 (owner spec Addendum A items 2-3, 2026-08-26): global zebra
   striping - EVERY data row carries one of the two shared row-shade
   tokens (declared in :root), alternating row by row, on every
   table.arcanis site-wide. The retired even-only --ink-raised rule
   left odd rows transparent, which is what made the fortresses sticky
   lead column (opaque by necessity) read darker than its row: with
   both shades explicit and opaque, every cell in a row - the sticky
   first cell included - shows the SAME background. */
table.arcanis tbody tr:nth-child(odd) td { background: var(--row-shade-a); }
table.arcanis tbody tr:nth-child(even) td { background: var(--row-shade-b); }
table.arcanis td.value { color: var(--parchment); font-weight: 600; white-space: nowrap; }
/* W56 sections 13 + 42: key/value VALUES wrap instead of forcing a
   horizontal scrollbar - Account Details must read without scrolling
   at desktop/laptop widths (the label column keeps its nowrap). Long
   unbroken tokens (IP addresses) break via overflow-wrap. */
table.arcanis.table-kv td.value { white-space: normal; overflow-wrap: anywhere; }
/* W56 section 16: rows built to become clickable - the clear hover
   state on marked panel record tables (the Characters table). */
table.arcanis.table-hover tbody tr:hover td { background: var(--arcane-deep); }
/* W56 sections 18 + 42: the per-row annotation inside a record cell
   (the seeded-login note) drops onto its own line instead of widening
   the nowrap column. */
table.arcanis td .le-meta { display: block; white-space: normal; }
/* W11 item 9: a label inside a table cell (the store's package radio
   labels) renders as table DATA - same face, size, and color as the
   surrounding cells, never the smaller dim form-label treatment. */
table.arcanis label { color: inherit; font-size: inherit; }
/* Wide tables (W8 1.6): the wrapper scrolls horizontally and the leading
   column stays pinned so rows keep their identity mid-scroll. */
.sticky-lead th:first-child, .sticky-lead td:first-child {
  position: sticky;
  left: 0;
  z-index: 1;
}
/* W51 items 2-3: the sticky header cell needs its own opaque ground
   (thead rows carry no row shade); it sits on the panel ground the
   header row shows, so the header reads uniform too. The retired
   --ink base and even-row override are gone - data cells take their
   opacity from the zebra row shades above. */
.sticky-lead th:first-child { background: var(--ink-raised); }

/* ---------- Rendered markdown ---------- */

.prose-arcanis { color: var(--parchment); line-height: 1.75; max-width: 70ch; }
.prose-arcanis > * + * { margin-top: 1.15em; }
.prose-arcanis h2, .prose-arcanis h3 { color: var(--arcane-bright); margin-top: 2em; font-size: 1.35rem; }
.prose-arcanis h3 { font-size: 1.15rem; }
.prose-arcanis a { color: var(--arcane-bright); text-decoration: underline; text-underline-offset: 3px; }
.prose-arcanis strong { color: var(--arcane-bright); }
.prose-arcanis ul, .prose-arcanis ol { padding-left: 1.5rem; }
.prose-arcanis li + li { margin-top: 0.4em; }
/* W36 sections 6/22 + directive 2 (owner spec 2026-08-24): ONE
   command-chip treatment everywhere a command renders. The chip now
   pins its own ink (--parchment) so a dim ancestor (the .fact-list dt
   label voice carried --parchment-faint onto the Commands-block chips)
   can never wash the command out, and the border steps up one tier
   (--edge-soft -> --edge) for clearer chip/background separation.
   Deliberately NOT the --edge-bright control border - chips must not
   read as buttons (section 22). */
.prose-arcanis code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  color: var(--parchment);
  background: var(--ink-panel);
  border: 1px solid var(--edge);
  border-radius: 3px;
  padding: 0.1em 0.35em;
}
.prose-arcanis blockquote {
  border-left: 2px solid var(--arcane);
  padding-left: 1rem;
  color: var(--parchment-dim);
  margin-left: 0;
}

/* ---------- Footer ---------- */

.site-footer {
  border-top: 1px solid var(--edge-soft);
  background: var(--ink-raised);
  margin-top: 3rem;
  padding: 2.2rem 0 2.6rem;
  font-size: 0.88rem;
}
.footer-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 1.6rem;
}
.footer-cols h4 {
  font-family: var(--font-display);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--parchment-dim);
  margin: 0 0 0.6rem;
}
.footer-cols ul { list-style: none; margin: 0; padding: 0; }
.footer-cols li + li { margin-top: 0.35rem; }
.footer-cols a { color: var(--parchment-dim); }
.footer-cols a:hover { color: var(--arcane-bright); }
.footer-bottom {
  margin-top: 1.8rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--edge-soft);
  color: var(--parchment-faint);
  font-size: 0.8rem;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

/* ---------- Status strip ---------- */

.status-dot {
  display: inline-block;
  width: 0.55rem;
  height: 0.55rem;
  /* W60 section 41: the dot is a TRUE fixed-size circle by
     construction. Inside the fixed-width chip flex row the dot was a
     shrinkable flex item - when the W59 12-hour clock content grew,
     flexbox squeezed the dot's width into a vertical oval. flex:none
     removes it from shrink negotiation entirely and aspect-ratio pins
     the shape even if one dimension is ever overridden. */
  flex: none;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  margin-right: 0.4rem;
  vertical-align: middle;
}
.status-dot.online { background: var(--signal-online); }
.status-dot.offline { background: var(--signal-offline); }
.status-dot.unknown { background: var(--parchment-faint); }

/* ---------- Responsive ---------- */

/* W8 2.3/2.4 + W9 1.2/3.3: below the breakpoint the burger appears,
   the primary nav collapses into a vertical panel, and the dropdown
   renders inline (toggle via nav.js only; no hover, no close delay -
   the menu flows in the panel via display, not visibility). */
/* W24 items 3-5: the breakpoint moves 1024 -> 1100. The wider group
   gap plus the desktop dropdown carets add ~62px to the header group
   (measured ~1048px signed out); 1101px is the narrowest viewport
   whose container still carries it, so narrower widths take the
   burger layout instead of compressing or overflowing the bar. */
@media (max-width: 1100px) {
  /* The 1.5rem desktop group gap resets to 0.5rem here: with the bar
     wrapping, gap applies on both axes and the large value would pad
     the wrapped rows apart vertically. */
  .header-bar { flex-wrap: wrap; height: auto; min-height: 4.5rem; gap: 0.5rem; }
  .nav-burger { display: inline-block; order: 2; margin-left: auto; }
  /* The caret keeps its W12 16px+ TOUCH hit zone here - the compacted
     0.2rem desktop padding is a pointer-precision allowance only.
     W26: width auto restores the content-driven touch zone (the fixed
     --caret-w slot exists for the desktop centering math only). */
  .nav-caret { padding: 0.45rem 0.3rem; width: auto; }
  .nav-actions { order: 3; width: 100%; flex-wrap: wrap; }
  .nav-primary { display: none; order: 4; width: 100%; flex-direction: column; align-items: stretch; gap: 0.1rem; margin: 0; padding-bottom: 0.5rem; }
  .site-header.nav-open .nav-primary { display: flex; }
  .nav-item { position: static; }
  .nav-menu {
    display: none;
    visibility: visible;
    opacity: 1;
    transition: none;
    position: static;
    transform: none;
    grid-auto-flow: row;
    gap: 0.9rem;
    border: none;
    border-left: 2px solid var(--edge);
    border-radius: 0;
    box-shadow: none;
    background: none;
    /* W26: the glass treatment is desktop-only - the inline burger
       menu sits ON the panel, so no ground and no blur here. */
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    padding: 0.4rem 0 0.4rem 1rem;
    margin-left: 0.6rem;
    /* W28 item 8 (carrying the W27 rule forward): the desktop row
       centering (the stretched track + the 12.5rem equal-width
       floor) is desktop-only - the inline burger menu stays a
       left-aligned block. */
    justify-content: start;
    min-width: 0;
  }
  /* W27 item 1: restore the pre-W27 burger row geometry - rows keep
     the 10.5rem floor so the touch targets never shrink to bare
     content width. */
  .nav-menu .nav-group { width: auto; min-width: 10.5rem; }
  /* W28 item 8: burger rows read as a nav list, not a centered
     dropdown - left-aligned text, unchanged from every round before. */
  .nav-menu .nav-group a { text-align: left; }
  @media (hover: hover) { .nav-item:hover > .nav-menu { display: none; } }
  .nav-item.open > .nav-menu { display: grid; }
  /* W11 item 4: in the burger panel the pair may wrap so the inline
     menu drops BELOW the label + caret (full-width flex row). */
  .nav-item { flex-wrap: wrap; }
  .nav-item > .nav-menu { flex-basis: 100%; }
}

@media (max-width: 860px) {
  .hero h1 { font-size: 2rem; }
  .panel { padding: 1.1rem 1.2rem; }
}

/* ---------- User panel (three-area layout, spec section 12) ---------- */

.panel-shell { margin-top: 1.5rem; }
.panel-serverbar {
  display: flex;
  gap: 1.5rem;
  flex-wrap: wrap;
  align-items: center;
  background: var(--ink-raised);
  border: 1px solid var(--edge-soft);
  border-radius: 6px;
  padding: 0.6rem 1.1rem;
  font-size: 0.82rem;
  color: var(--parchment-dim);
  margin-bottom: 1.25rem;
}
.panel-columns { display: grid; grid-template-columns: 15rem 1fr; gap: 1.25rem; }
.panel-sidebar {
  background: var(--ink-raised);
  border: 1px solid var(--edge-soft);
  border-radius: 6px;
  padding: 0.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  align-self: start;
}
/* W8 7.1: small section headers grouping the sidebar. */
.panel-sidebar .side-group-title {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  /* W56 section 34: one step up (0.64 -> 0.7rem) plus breathing room
     in the line - comfortably readable on laptop displays while
     staying visually subordinate to the items. */
  font-size: 0.7rem;
  line-height: 1.5;
  color: var(--gold);
  padding: 0.7rem 0.8rem 0.15rem;
}
/* W56 section 2: the sidebar's platform marks (the Discord entries)
   ride inline before their labels. */
.panel-sidebar .side-icon { width: 1rem; height: 1rem; margin-right: 0.45rem; vertical-align: -0.18em; }
.panel-sidebar .side-group-title:first-child { padding-top: 0.3rem; }
.panel-sidebar a, .panel-sidebar .dull-span {
  display: block;
  padding: 0.55rem 0.8rem;
  border-radius: 4px;
  color: var(--parchment-dim);
  font-size: 0.88rem;
}
.panel-sidebar a:hover { color: var(--arcane-bright); background: var(--ink-panel); text-decoration: none; }
.panel-sidebar a.active {
  color: var(--arcane-bright);
  background: var(--ink-panel);
  box-shadow: inset 2px 0 0 var(--arcane);
}
/* W52 section 3 (owner spec W52-PANEL-UIUX 2026-08-26): unavailable
   sidebar entries are readable, clearly inert spans - the 0.45 blanket
   opacity is raised so the item NAME stays comfortably legible while
   the .soon caption plus the missing hover/pointer affordance carry
   the unavailable signal. One treatment for every future entry. */
.panel-sidebar .dull, .panel-sidebar .dull-span { opacity: 0.72; }
.panel-sidebar .soon {
  display: block;
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--parchment-faint);
}
/* W59 section 27 (supersedes the W57 section 48 note): the sidebar
   sentence is REMOVED entirely - the dulled entries plus the missing
   hover/pointer affordance carry the unavailable signal on their own,
   and per-feature availability reads from each page's compact chip.
   The .side-soon-note rule left with the element. */
/* W52 section 3: the duplicate sidebar Logout is gone (the header LOG
   OUT is the one signout control); the divider slot now carries the
   Back to Website link that replaced the serverbar's "Exit panel"
   (section 4: navigation does not belong in the status bar). */
.sidebar-exit { margin-top: 0.8rem; border-top: 1px solid var(--edge-soft); padding-top: 0.6rem; }
.panel-content { min-width: 0; }

/* Section 11: password show/hide toggle (injected by panel-ux.js into
   password rows; the row is already position:relative). */
.pw-toggle {
  position: absolute;
  right: 0.4rem;
  background: none;
  border: none;
  color: var(--parchment-dim);
  font: inherit;
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  padding: 0 0.3rem;
}
.pw-toggle:hover { color: var(--arcane-bright); }

/* Section 11: the live requirement checklist gains an explicit ERROR
   state - a rule that stays unsatisfied after the field was touched
   (or a submit was attempted) marks red with a cross; .ok stays the
   satisfied state; the plain row stays "not yet satisfied". */
.pw-checklist li.err { color: var(--error-soft); }
.pw-checklist li.err::before { content: "\2717"; color: var(--error-soft); }

/* Sections 19+21: compact multi-column choice grid - the ONE control
   for long checkbox vocabularies (media content tags, recruitment
   roles). Real checkboxes (keyboard + no-JS safe), wrapped onto a
   responsive 3/2/1-column grid. */
.choice-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.2rem 1.2rem; }
.choice-grid label { display: flex; gap: 0.45rem; align-items: baseline; font-size: 0.88rem; color: var(--parchment-dim); }
.choice-grid input[type="checkbox"] { flex: none; }
@media (max-width: 640px) { .choice-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 420px) { .choice-grid { grid-template-columns: 1fr; } }

/* Section 19: grouped form sections (Content / Creator /
   Classification) and the responsive two-column field grid. */
.form-section { margin: 0 0 1.5rem; }
.form-section > h3 { margin: 0 0 0.7rem; color: var(--gold); font-size: 1.05rem; }
.form-grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 1.4rem; }
@media (max-width: 720px) { .form-grid-2 { grid-template-columns: 1fr; } }

/* Section 21: the unified Level Range row - both bounds side by side
   under one label, stacking only at narrow widths. */
.level-range { display: flex; align-items: center; gap: 0.6rem; }
.level-range input[type="number"] { max-width: 6.5rem; }
.level-range .lr-sep { color: var(--parchment-dim); }

/* Section 21: live character counter under the pitch textarea. */
.char-counter { display: block; text-align: right; font-size: 0.75rem; color: var(--parchment-faint); margin-top: 0.2rem; }

/* Section 10: the collapsible technical note - a native details
   element wearing the standard callout skin, so the explainer stays
   accessible without dominating the page. */
details.tech-note summary { cursor: pointer; }
details.tech-note summary .label { display: inline; }
details.tech-note[open] summary { margin-bottom: 0.4rem; }

/* Section 17: the selected social-task row (panel-ux.js claim flow) -
   paint-only arcane inset, the zebra shades stay untouched. */
.layout-panel table.arcanis tr.row-selected td { box-shadow: inset 0 0 0 1px var(--arcane); }

/* Forms */
input[type="text"], input[type="password"], input[type="email"], input[type="search"],
input[type="url"], input[type="number"], select, textarea {
  width: 100%;
  max-width: 24rem;
  background: var(--ink-panel);
  border: 1px solid var(--edge);
  border-radius: 3px;
  color: var(--parchment);
  padding: 0.5rem 0.7rem;
  font: inherit;
}
input:focus, select:focus, textarea:focus { border-color: var(--arcane); outline: none; }
label { color: var(--parchment-dim); font-size: 0.88rem; }
button.btn { cursor: pointer; font: inherit; }

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

/* ---------- W52 shared panel system (owner spec 2026-08-26) ---------- */

/* Section 2 (sticky sidebar on desktop) was superseded by the W67
   section 23 FIXED rule and then by the W68 sidebar fold-in: the
   sidebar scrolls naturally in document flow. The one desktop rule
   lives in the W67/W68 block below; nothing sticky or fixed remains. */

/* Section 4: the panel serverbar is the ONE server-status surface on
   panel pages - the header's combined status chip would repeat the
   same Online state a few pixels above it, so it hides here.
   visibility (not display): the chip's fixed-width slot is the
   header-centering contract (the logo/nav/actions unit is centered as
   ONE block on every page), so the slot stays and only the paint
   goes. */
.layout-panel .status-chip { visibility: hidden; }

/* Section 1: the form-width TIERS. Pages never pick arbitrary widths -
   a panel card is capped by one of these classes (or spans the full
   content column) and always starts at the standard column position.
   W56 section 35 (supersedes the W55 1.1 compact assignments): THREE
   tiers - standard ~680px carries every small utility form (password,
   email, Character PIN, unstuck); wide ~896px: long forms (media,
   recruitment, tickets); full: tables and dashboards (no class). The
   576px compact tier is retired - its pages read randomly narrow
   beside their neighbors. */
.form-standard { max-width: 42.5rem; }
.form-wide { max-width: 56rem; }
/* Inside a tiered card (or an explicit form grid) the controls span
   the row - the global 24rem cap serves the uncapped contexts. */
:is(.form-standard, .form-wide) :is(input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]), select, textarea),
.form-grid-2 :is(input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]), select, textarea) {
  max-width: 100%;
}

/* Section 5: the button-state system, completing the W29 three-tier
   family: a pressed state every tier shares, and the DESTRUCTIVE /
   security-sensitive tier (.btn-danger: Reset PIN, Unbind Device).
   Focus-visible is the standing global outline; disabled is the
   standing .btn:disabled rule (opacity + pointer-events). */
.btn:active:not(:disabled):not([aria-disabled="true"]) { transform: translateY(1px); }
.btn-danger { border-color: var(--signal-offline); color: var(--signal-offline); }
.btn-danger:hover { border-color: var(--signal-offline); color: #f2b3b3; background: rgba(224, 133, 133, 0.08); }

/* Section 6: input interaction states - hover steps the border up one
   edge tier; disabled is unmistakably inert. Focus keeps the standing
   arcane border; error keeps .field-error; success keeps label.ok. */
:is(input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]), select, textarea):hover:not(:focus):not(:disabled) {
  border-color: var(--edge-bright);
}
input:disabled, select:disabled, textarea:disabled { opacity: 0.55; cursor: not-allowed; }
select option:disabled { color: var(--parchment-faint); }

/* Section 7: the page-title icon container - the h1 becomes a flex
   row so every icon (1em square, gold) centers on the title text and
   different icon shapes can never shift the title's start. */
.panel-section-header h1 { display: flex; align-items: center; gap: 0.4rem; }
.panel-section-header .h1-icon { margin-right: 0; }

/* Game-data pages (W2-GAMEDATA): ranking boards, siege pages, boss board */
/* W8 1.5: the ONE Tab component (Rankings pill style) - uppercase pills,
   filled active state. Used by every board/section switcher and the
   panel's User Panel tabs.
   W50 ruling D10 (owner spec W50_CATEGORY_STANDARDS_2026-08-25): the
   tab-row-to-content seam joins the ONE 12.8px block token, so every
   LEDGER page's above-content gap decomposes as 25.6px (hero gap) +
   the actual tab-row height + 12.8px at EVERY width - the 1.4rem
   value is retired. The User Panel keeps its own margins via the
   .user-panel-head override (layout-panel untouched). */
.tab-row { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: var(--block-gap); }
/* W29c (owner ruling 2026-08-24): resting pills adopt the tier-3
   standard-button COLORS - the --edge-bright border and the
   --parchment ink - so the chip controls read clearly against their
   grounds. COLOR ONLY: geometry, radius, padding, text size/weight,
   and the filled .tab.active treatment are all byte-unchanged. */
.tab {
  display: inline-block;
  padding: 0.45rem 0.95rem;
  border: 1px solid var(--edge-bright);
  border-radius: 999px;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--parchment);
  background: var(--ink-panel);
}
.tab:hover { border-color: var(--arcane); color: var(--arcane-bright); text-decoration: none; }
.tab.active {
  background: var(--arcane-deep);
  border-color: var(--arcane);
  color: var(--arcane-bright);
}
/* W52 section 10: on PANEL pages only, the active pill adds one extra
   paint signal (an inner underline) so the selected tab is instantly
   obvious - paint only, no geometry change; the public treatment
   above is byte-unchanged. */
.layout-panel .tab.active { box-shadow: inset 0 -2px 0 var(--arcane); }
/* W64 section 9 (Decision 3): the Server Schedule strip is a
   DELIBERATE two-row balance - four pills per row (Automated Events /
   Olympiad / Tournament / Fortress Sieges over Castle Sieges /
   Territory War / Bosses / Grand Bosses) - wherever the eight cannot
   comfortably share one row: from 768px up it lays out as a
   four-column grid of content-sized tracks (the 0.5rem pill gap and
   every pill's own geometry unchanged); below 768 the standard
   wrapping row stands. The panel column never fits the eight pills
   on one row at the supported widths, so the grid is the desktop
   layout throughout (single-row would need a ~1250px column). */
@media (min-width: 768px) {
  .tab-row-balanced { display: grid; grid-template-columns: repeat(4, max-content); }
  .tab-row-balanced .tab { text-align: center; }
}
/* W8 1.5: AnchorChip - same-page jump links. Deliberately lighter than a
   Tab (no fill, no uppercase) so it never reads as a state toggle.
   W37 section 34 (owner spec 2026-08-24): the leading "#" marker read as
   a hashtag, not navigation - the items now separate with a quiet
   middle dot instead (the dot rides the item's trailing edge, so a
   wrapped row at 390 ends on the separator and the next row starts on a
   name). The W29c audited exclusion stands: dim ink, no border. */
.anchor-chips { display: flex; flex-wrap: wrap; gap: 0.3rem 0.8rem; }
.anchor-chip { font-size: 0.85rem; color: var(--parchment-dim); }
.anchor-chip:not(:last-child)::after {
  content: "\00B7";
  color: var(--parchment-faint);
  margin-left: 0.8rem;
}
/* W24 item 7: chips are controls - color feedback, no underline. */
.anchor-chip:hover { color: var(--arcane-bright); text-decoration: none; }
/* W8 1.4: the standard EmptyState - eyebrow tag, one sentence, optional
   CTA. Replaces every placeholder dash row and ad-hoc empty note. */
.empty-state {
  border: 1px dashed var(--edge);
  border-radius: 6px;
  background: var(--ink-raised);
  padding: 1.5rem 1.7rem;
}
.empty-state .eyebrow {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 0.7rem;
  color: var(--gold);
}
.empty-state p { margin: 0.4rem 0 0; color: var(--parchment-dim); }
.empty-state .btn { margin-top: 0.9rem; }
/* W56 section 28: the optional icon and secondary note complete the
   standard empty-state anatomy (icon / title / explanation / CTA /
   note). */
.empty-state .es-icon {
  width: 1.5rem;
  height: 1.5rem;
  color: var(--parchment-faint);
  margin-bottom: 0.5rem;
}
.empty-state .es-note { font-size: 0.8rem; color: var(--parchment-faint); }
/* W8 6.5: plain non-interactive tag (no hover affordance, never a link). */
.tag {
  display: inline-block;
  font-size: 0.65rem;
  /* W9 5.3: a tag inside a heading must not inherit the display face. */
  font-family: var(--font-sans);
  font-variant-caps: normal;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  border: 1px solid var(--edge);
  border-radius: 3px;
  padding: 0.15rem 0.45rem;
  color: var(--parchment-dim);
  vertical-align: middle;
}
/* W8 5.6: the SHA-256 cell - the hash wraps instead of forcing the
   table wide; the copy button sits beside it. */
td.hash-cell { white-space: normal; }
.hash-value {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  word-break: break-all;
}
.copy-btn {
  background: var(--ink-panel);
  /* W29 section 1: the auxiliary micro-control rides the standard
     button edge too - a button-shaped control never keeps the faint
     panel rule as its outline. */
  border: 1px solid var(--edge-bright);
  border-radius: 3px;
  /* W29c (owner ruling 2026-08-24): the resting text joins the
     tier-3 ink (color only; size/weight/padding unchanged). */
  color: var(--parchment);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0.2rem 0.55rem;
  margin-left: 0.5rem;
  cursor: pointer;
  vertical-align: middle;
}
.copy-btn:hover { border-color: var(--arcane); color: var(--arcane-bright); }

/* W8 5.2: balanced chip grid (vote sites and similar rosters). */
.chip-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: 0.6rem;
}
.chip-grid .btn, .chip-grid .tag { text-align: center; }
/* W12 item 5: the Olympiad/Tournament race + class dropdown filters
   (replaces the long class anchor-link jump list). Same labeled-control
   treatment as the raid toolbar so the two ladder pages and the boss
   board read as one control family. */
.class-filter { display: flex; gap: 0.8rem 1.1rem; flex-wrap: wrap; align-items: flex-end; margin-bottom: 1.2rem; }
.class-filter label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--parchment-faint);
}
.class-filter select {
  width: auto;
  background: var(--ink-panel);
  color: var(--parchment);
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 0 0.55rem;
  font-size: 0.88rem;
  height: 2.2rem;
  min-width: 11rem;
  max-width: 14rem;
}
/* The filters narrow the VIEW: every class still renders server-side
   (the W11 all-classes rule); class-filter.js toggles this class. */
.class-block.filtered-out { display: none; }
.status-alive { color: var(--signal-online); font-weight: 600; }
.status-dead { color: var(--signal-offline); }
/* W9 5.2: EVERY empty value renders dimmed - muted gray at ~85% size. */
.dull-span { color: var(--parchment-faint); font-size: 0.85em; }
.disabled-panel { opacity: 0.6; }
.dull-heading { color: var(--parchment-dim); }
.cycle-heading { margin-top: 1.4rem; color: var(--arcane-bright); }
.empty-note { color: var(--parchment-dim); }
.media-card h3 { margin: 0.2rem 0 0.4rem; }
.media-card .meta { color: var(--parchment-dim); font-size: 0.85rem; }

/* F2: external-link indication, media thumbnails. The floating panel
   Discord toast was removed by W8 7.7. */
.ext { font-size: 0.8em; opacity: 0.75; }
.media-thumb { max-width: 100%; border-radius: 4px; border: 1px solid var(--night-edge, #2a2440); display: block; margin-bottom: 0.6rem; }

/* Recruitment boards (F3) */
/* W50 ruling D9: the filter row joins the ONE 12.8px block token (with
   the D10 tab seam above it, the recruitment hero-to-content run is
   25.6 + tabs + 12.8 + filter row + 12.8 - the 151.9px dead span is
   retired). */
.recruit-filter { display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: center; margin-bottom: var(--block-gap); }
.recruit-filter label { color: var(--parchment-dim); font-size: 0.85rem; }
.recruit-filter input, .recruit-filter select {
  background: var(--night-soft, #17132a); color: var(--parchment);
  border: 1px solid var(--night-edge, #2a2440); border-radius: 4px;
  padding: 0.35rem 0.55rem; font-size: 0.9rem; max-width: 9rem;
}
.recruit-card h3 { margin: 0 0 0.3rem; }
.recruit-card .meta { color: var(--parchment-dim); font-size: 0.9rem; }

/* Live server status (F4 item 5) now renders inside the W8 combined
   header status chip - see .status-chip in the header section. */

/* Grand-opening countdown (F4 item 7) */
.countdown { margin: 1.4rem 0; }
.countdown-grid {
  display: flex; gap: 0.8rem; flex-wrap: wrap; justify-content: flex-start;
}
.countdown-cell {
  display: flex; flex-direction: column; align-items: center;
  min-width: 4.4rem; padding: 0.7rem 0.6rem;
  background: var(--night-raise, #1b1730);
  border: 1px solid var(--arcane-deep); border-radius: 4px;
}
.countdown-value {
  font-family: var(--font-display, inherit);
  font-size: 1.7rem; color: var(--time-local); line-height: 1.1; /* W77b ruling 2: a time value reads in the local color */
  font-variant-numeric: tabular-nums;
}
.countdown-label {
  font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--parchment-dim); margin-top: 0.25rem;
}
.countdown-open {
  font-family: var(--font-display, inherit);
  font-size: 1.4rem; color: var(--signal-online); margin: 0.4rem 0;
}
.countdown-target { color: var(--parchment-dim); font-size: 0.9rem; margin-top: 0.6rem; }
@media (max-width: 480px) {
  .countdown-grid { gap: 0.5rem; }
  .countdown-cell { min-width: 3.4rem; padding: 0.5rem 0.4rem; }
  .countdown-value { font-size: 1.3rem; }
}

/* Per-class jump lists (Olympiad, Tournament) and the events page TOC
   render as AnchorChip rows (W8 1.5) - see .anchor-chips above. */

/* Fixed-grid tables (F6 item 2, refined F7): stable column widths on
   every row; long values truncate with an ellipsis instead of shifting
   columns; a floor width keeps the grid legible on narrow screens (the
   surrounding .table-wrap scrolls horizontally instead). Column
   alignment comes from the global W12 table system, never manual
   spacing. */
.table-fixed { table-layout: fixed; width: 100%; min-width: 640px; }
.table-fixed th, .table-fixed td {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* W12 item 3: .col-center is retired - header alignment is the global
   center rule, cell alignment is the system default (text left,
   numbers right + inset); no page recenters its own columns. */
/* F10 item 5: contested clan halls - wider grid so Next Siege fits the
   full datetime on one line; that column never ellipsizes. */
.contested-table { min-width: 820px; }
/* W12 item 3: the class-ladder/board grids keep a floor width so no
   numeric value or header can ellipsize at the minimum (scroll-floor)
   width (.table-wrap scrolls below it). */
.rank-table { min-width: 760px; }
/* W11 items 7+8: the Top Clans grid - a wider floor, tighter cell
   padding (W12: via the shared density tokens, not local padding
   rules), and WRAPPABLE headers (a two-word header like "Territory
   Wards" breaks onto two lines instead of ellipsizing) so its eleven
   columns stay readable instead of being squeezed; .table-wrap scrolls
   below the container width; the ward cell shows its 32px icons and
   tooltips untrimmed. */
/* W16 item 3: Reputation and Alliance left the grid (owner ruling)
   and every remaining column is typed on the dense variant, so the
   floor is the dense type sum. W17 item 1: the Level column left
   every ranking board. W18 item 3 (owner ruling): the Leader column
   left too - seven columns remain and the floor drops 960px -> 51rem
   (816px, the new dense sum); the proportional scaling keeps them
   balanced above the floor; .table-wrap scrolls below it as
   before. */
.clans-table { min-width: 816px; --cell-pad-x: 0.55rem; }
/* table.arcanis th pins nowrap at 0-1-2 specificity; match it. */
table.clans-table th { white-space: normal; overflow: visible; text-overflow: clip; line-height: 1.25; vertical-align: bottom; }
.clans-table td.ward-cell { overflow: visible; text-overflow: clip; }
.col-siege { white-space: nowrap; overflow: visible; text-overflow: clip; }
/* W8 6.1 removed the padded placeholder rows (.empty-position) - empty
   boards render the shared EmptyState instead. */

/* Territory Ward icons (F6 item 1, refined F7: real client emblems at
   native 32px, hover AND keyboard-focus tooltip, screen-reader alt). */
.ward-icons { display: inline-flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; }
.ward-icon-wrap { position: relative; display: inline-block; line-height: 0; }
.ward-icon-wrap:focus { outline: 2px solid var(--arcane-bright); outline-offset: 1px; }
.ward-icon {
  width: 32px; height: 32px; display: inline-block;
  border: 1px solid var(--night-edge, #2a2440); border-radius: 4px;
}
.ward-tooltip {
  display: none; position: absolute; bottom: calc(100% + 6px); left: 50%;
  transform: translateX(-50%); white-space: nowrap; z-index: 20;
  background: var(--night-raise, #1b1730); color: var(--parchment);
  border: 1px solid var(--arcane); border-radius: 4px;
  padding: 0.25rem 0.55rem; font-size: 0.8rem; line-height: 1.3;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}
.ward-icon-wrap:hover .ward-tooltip,
.ward-icon-wrap:focus .ward-tooltip { display: block; }


/* Raid boss table: sortable headers + in-table filter row (F8 item 4).
   Filters live in a second thead row of the same table, so each control
   is aligned to its column by construction at every width. */
/* W18 item 4: the floor is the boss colgroup's base sum (50rem =
   800px) so the shared grand+raid grid (F10) scales proportionally
   above it and scrolls below it, like every other fixed grid. */
.boss-table { table-layout: fixed; min-width: 800px; }
/* W64 section 6: the panel schedule pair floors at its own typed sum
   plus ~228px of Name room. */
.boss-sched { min-width: 840px; }
/* W55 Decision 6e: the W19 .store-grid floor left with its variant
   rules (swept above). */
/* W74 section 6: a cell never clips - long boss names wrap (the W16-W19 ellipsis is retired). */
.boss-table td { overflow: visible; text-overflow: clip; white-space: normal; overflow-wrap: anywhere; }
/* W18 item 4's estimated respawn window (the three-line "Estimated:
   between" block) is RETIRED by W96 (docs/specs/
   W96-RESPAWN-WINDOW-COLUMNS.md): the Respawn Window column shows the
   window's duration and its countdowns, never an instant. */
/* W65 section 6 (Decision 4): the ONE sortable header (core/tablesort +
   the sort_th tag). The indicator is always present - a faint neutral
   glyph on inactive columns, the bright direction arrow on the active
   one - so the active column and direction are visible at all times
   and a header never changes width when the sort moves. */
.sort-link { color: inherit; display: inline-block; white-space: nowrap; }
.sort-link:hover { color: var(--arcane-bright); text-decoration: none; }
/* W97 (docs/specs/W97-BOSS-TABLES-AND-SORT-INDICATORS.md, spec item 6):
   ONE indicator rule for every sortable table site-wide. The inactive
   glyph (the neutral up-down arrow the sort_th tag renders) is
   parchment-faint at FULL opacity - the W75 item R marketplace rule
   (AA legibility, 6.1:1 inside the panel) generalized to the whole
   site, so no table fades its affordance below AA; the active column's
   single direction arrow is arcane-bright - measured ~3.6x the neutral
   glyph's relative luminance by scripts/w97-proofs.py proof 5. The
   marketplace-only (.mk-table) and recruitment-only (.rc-table, gold)
   overrides that used to sit beside these rules are RETIRED. */
.sort-ind { color: var(--parchment-faint); font-size: 0.7em; opacity: 1; }
.sort-link.is-active { color: var(--arcane-bright); }
.sort-link.is-active .sort-ind { color: var(--arcane-bright); opacity: 1; }
th.is-sorted { color: var(--arcane-bright); }
.is-loading [data-live-results], form.is-loading { opacity: 0.85; transition: opacity 0.15s; }
/* W8 6.3: the raid filters live in a labeled toolbar above the table. */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.8rem 1.1rem;
  align-items: flex-end;
  margin-bottom: 1rem;
}
.toolbar label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--parchment-faint);
}
/* W9 5.4: every toolbar control shares ONE height so the row aligns
   (inputs, selects, and the Filter/Reset buttons alike). */
.toolbar input, .toolbar select {
  background: var(--ink-panel); color: var(--parchment);
  border: 1px solid var(--edge); border-radius: 4px;
  padding: 0 0.55rem; font-size: 0.88rem;
  height: 2.2rem;
  max-width: 11rem; min-width: 0;
}
.toolbar input[type="search"] { max-width: 13rem; }
.toolbar-actions { display: flex; gap: 0.4rem; }
.toolbar-actions .btn {
  padding: 0 0.9rem;
  font-size: 0.78rem;
  height: 2.2rem;
  display: inline-flex;
  align-items: center;
}
.level-pair { display: flex; gap: 0.3rem; }
.level-pair input { max-width: 4.6rem; }
/* F10 item 6: remove number-input spinner arrows; typed digits only. */
.no-spin::-webkit-outer-spin-button,
.no-spin::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.no-spin { -moz-appearance: textfield; appearance: textfield; }

/* Castle entries with banner art (F9 item 2): banner beside the info
   on desktop, stacked above it on narrow screens; banners keep their
   native aspect (object-fit contain, never stretched). */
.castle-entry { display: flex; gap: 1.4rem; align-items: flex-start; }
.castle-media { flex: 0 0 320px; position: relative; }
/* W8 6.4: a subtle dark overlay + inner border so the renders sit on
   the theme instead of glowing against it. */
.castle-media::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 4px;
  background: linear-gradient(rgba(8, 6, 14, 0.12), rgba(8, 6, 14, 0.38));
  box-shadow: inset 0 0 0 1px rgba(42, 33, 64, 0.85);
  pointer-events: none;
}
.castle-image {
  width: 320px; aspect-ratio: 576 / 303; object-fit: cover; display: block;
  border: 1px solid var(--night-edge, #2a2440); border-radius: 4px;
  background: var(--night-soft, #17132a);
}
.castle-info { flex: 1 1 auto; min-width: 0; }
.castle-info h2 { margin-top: 0; }
/* W9 5.5: the card hugs its content - no trailing border or margin
   opens dead space under the wards row. */
.castle-info .table-wrap:last-child { margin-bottom: 0; }
.castle-info .table-wrap:last-child table.arcanis tbody tr:last-child td { border-bottom: none; padding-bottom: 0.2rem; }
@media (max-width: 640px) {
  .castle-entry { flex-direction: column; align-items: stretch; }
  .castle-media { flex: 0 0 auto; }
  .castle-image { width: 100%; }
  .castle-info { width: 100%; }
}

/* Shared ranking-table grid (F9 item 4) and class-block grid (item 5):
   text columns ellipsized, breathing room on the outer columns. The
   .col-num alignment rules live in the global table system above
   (W13: ALL headers center, numeric cells center beneath them). */
.rank-table th:first-child, .rank-table td:first-child { padding-left: 0.9rem; }

/* Expanded events page (F9 item 3).
   W33 section 25 (implementation directive 2): the cards are jump-link
   targets - scroll-margin-top clears the sticky header so a followed
   anchor lands with the card title visible, never underneath the bar
   (html already scrolls smoothly, with the reduced-motion opt-out). */
.event-section { margin-top: 2.2rem; scroll-margin-top: 5rem; }
/* W50 ruling D5 (owner spec W50_CATEGORY_STANDARDS_2026-08-25): the
   event-card titles unify to the CODEX panel-title standard - the
   bespoke 1.5rem size is retired and the cards ride the ONE
   .panel h2 rule (1.3rem, 0 above, 0.83em = 17.3px below, centered
   by the W28 boxed-title system). */
/* W42 section 15 (owner spec SITE_REFINEMENT_W42_2026-08-25): the
   one-line summaries step up the site ladder --parchment-dim (7.3:1)
   -> --parchment (15.2:1, the cream body ink - not white); italic and
   size keep them visually secondary to the gold title. */
/* W43 section 1A (owner spec SITE_HERO_EVENTS_RATES_W43_2026-08-25):
   the one-line summary centers directly beneath the centered event
   title (the W28 panel-title rule already centers the h2), so the
   card header reads as one balanced centered unit. */
.event-section .tagline { color: var(--parchment); margin-top: 0.2rem; font-style: italic; text-align: center; }
/* F10 item 3: details flow inside the one block - no sub-card borders. */
.event-detail { margin-top: 1.1rem; }
.event-detail-label {
  font-size: 1rem; color: var(--arcane-bright); margin: 0 0 0.2rem;
  text-transform: uppercase; letter-spacing: 0.05em;
}
.event-detail p { margin: 0.2rem 0; }
/* W9 4.1: the compact card's three-item fact strip.
   W43 section 1A + implementation directive 1 (owner spec
   SITE_HERO_EVENTS_RATES_W43_2026-08-25, superseding the W9
   left-flowing flex row): three EQUAL-WIDTH regions (CSS grid
   1fr x 3) - Arena / Scoring / Win - each label+value pair centered
   within its region, so the strip reads balanced instead of starting
   from the left edge, and the regions align identically across all
   eight cards by construction (equal tracks in equal-width cards).
   Below 640px the same DOM stacks to one column in the same order. */
.fact-strip {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.3rem 1rem;
  text-align: center;
  margin: 1rem 0 0;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--edge-soft);
  border-radius: 4px;
  background: var(--ink-raised);
  font-size: 0.88rem;
  color: var(--parchment);
}
@media (max-width: 640px) {
  .fact-strip { grid-template-columns: minmax(0, 1fr); }
}
.fact-strip .fact-label {
  /* W42 section 14 (owner spec SITE_REFINEMENT_W42_2026-08-25): one
     modest contrast step, --gold (10.3:1 on the card ground) ->
     --gold-bright (13.9:1); size, weight, and tracking untouched so
     the labels stay secondary to the event title. */
  color: var(--gold-bright);
  text-transform: uppercase;
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  margin-right: 0.3rem;
}
/* W9 4.2/4.3: the one "Full rules" expander per card (collapsed by
   default; events.js opens it when its card is anchor-targeted).
   W43 section 1B + owner decision D (owner spec
   SITE_HERO_EVENTS_RATES_W43_2026-08-25, superseding the W39
   sections 8 + 23 subtle-row treatment): the shared disclosure
   becomes a PROPER accordion control row - the ONE component behind
   all eight VIEW FULL RULES and the shared VIEW SHARED RULES. The
   whole summary row is the target (native details/summary keeps
   click-anywhere plus Enter/Space keyboard toggling): label on the
   left, the site's caret glyph on the right rotating 180deg when
   open, a visible border, the subtle --ink-raised fill against the
   panel ground, pointer cursor, hover and focus-visible states, and
   real vertical padding so the row reads as an interactive element.
   The native list-item marker retires with the label swap (the W39
   "frozen 0.78rem chevron" reading is superseded); the row's own box
   is IDENTICAL open and closed - only the content below appears - so
   toggling never jumps the layout.
   W44 sections 1-2 + owner decision A (owner spec
   SITE_ACCORDION_RATES_BANNERS_W44_2026-08-25, superseding the W43
   left-label/space-between arrangement): the label CENTERS in the row
   (symmetric horizontal padding so the true center is the geometric
   row center); the chevron stays at the trailing edge, absolutely
   positioned inside the row so it can never shift the centered text.
   One step up across the board so the row reads as a control at rest:
   label ink --parchment-dim -> --parchment, resting fill --ink-raised
   -> --ink-panel and border --edge-soft -> --edge (the standard .btn
   ground/one-below-its-edge), chevron --parchment-faint ->
   --parchment-dim at 1rem. Hover follows the .btn vocabulary (arcane
   edge + bright text, no fill change); focus-visible kept. */
.full-rules { margin-top: 1rem; }
.full-rules > summary {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  list-style: none;
  cursor: pointer;
  padding: 0.7rem 2.6rem;
  border: 1px solid var(--edge);
  border-radius: 4px;
  background: var(--ink-panel);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--parchment);
  user-select: none;
}
.full-rules > summary::marker { content: none; }
.full-rules > summary::-webkit-details-marker { display: none; }
.full-rules > summary:hover {
  color: var(--arcane-bright);
  border-color: var(--arcane);
}
.full-rules > summary:focus-visible {
  outline: 2px solid var(--arcane-bright);
  outline-offset: 2px;
}
/* The chevron rides the site's one caret glyph (the header dropdown
   vocabulary) at the row's trailing edge; open state rotates it in
   place. W44 decision A: absolutely positioned (a full-height flex
   box, so the 180deg rotation maps the box onto itself and the glyph
   stays vertically centered) - the centered label never shifts. */
.full-rules .fr-chevron {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0.95rem;
  display: flex;
  align-items: center;
  color: var(--parchment-dim);
  font-size: 1rem;
  line-height: 1;
  transition: transform 0.15s ease;
}
.full-rules[open] .fr-chevron { transform: rotate(180deg); }
/* The i18n label pair: exactly one of VIEW/HIDE renders per state. */
.full-rules .fr-open { display: none; }
.full-rules[open] .fr-closed { display: none; }
.full-rules[open] .fr-open { display: inline; }
.full-rules[open] > summary { margin-bottom: 0.6rem; }
@media (prefers-reduced-motion: reduce) {
  .full-rules .fr-chevron { transition: none; }
}

/* F13 spec 8 / W8 2.1 / W10 5.1, retouched by W11 item 1: compact live
   server time inside the status chip. The zone abbreviation SITS ON the
   time digits' baseline at the SAME font size and visual weight as the
   digits (the W10 reduced small-caps suffix is retired by owner
   instruction). */
.server-time {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  white-space: nowrap;
}
.server-time .st-zone {
  font-size: inherit;
  letter-spacing: 0.02em;
  color: inherit;
}
.server-time .st-clock {
  font-variant-numeric: tabular-nums;
  color: inherit;
}

/* F13 spec 3, reshaped by W11 items 2+3 (supersedes the W10 3.1
   dropdown): the account cluster is TWO .btn-family controls in BOTH
   auth states - Sign in + Register signed out, the username (opens the
   User Panel) + Log out signed in - so every header control shares one
   height, border, and type treatment and the row never shifts between
   states.
   W12 item 1 (owner instruction): emphasis SWAPPED - the username is
   the larger/wider control (the primary account link to the User
   Panel) at a fixed 9.5rem width with a centered ellipsized label; Log
   out is the compact minor button (smaller type, tighter padding) on
   the same shared control height, still clearly visible. */
.account-btn { white-space: nowrap; }
/* W26 items 3-6 (owner spec): the ONE header action-button system.
   Register and LOG OUT trade the faint --edge border for the DEFINED
   --edge-bright control edge (same 1px weight - the fixed cluster and
   the 2.5rem control height cannot move); the account-accent pair
   below keeps its gold edge, which was already defined. Hover is one
   consistent language across all four controls: the button FILLS (the
   .btn-primary precedent) - plain buttons fill --arcane-deep behind
   an --arcane edge, the gold accent pair fills solid gold with ink
   text (item 4: a change nobody can miss). Ordering contract: these
   rules sit BEFORE the .account-accent pair so gold wins the base
   border and the gold fill wins the hover at equal specificity. */
.nav-actions .account-btn { border-color: var(--edge-bright); }
.nav-actions .account-btn:hover {
  background: var(--arcane-deep);
  border-color: var(--arcane);
  color: var(--arcane-bright);
}
/* W29 section 2: header Register joins the ELEVATED tier. The
   selector matches .nav-actions .account-btn's 0-2-0 specificity and
   sits AFTER it so the elevated edge wins the base state, while the
   0-2-1 hover rule above still wins the hover fill - one hover
   language across the cluster, tiering on the resting edge only.
   Log out stays a standard (tier 3) control on the shared
   --edge-bright base border. */
.nav-actions .btn-elevated { border-color: var(--edge-elevated); }
/* inline-block (not the header flex treatment): text-overflow needs a
   block container; the shared 2.5rem control height is kept via the
   matched line-height instead. */
.nav-actions .account-name {
  display: inline-block;
  text-align: center;
  line-height: calc(2.5rem - 2px);
  text-transform: none;
  letter-spacing: 0.04em;
  font-weight: 600;
  color: var(--arcane-bright);
  width: 7.25rem;
  padding-left: 0.55rem;
  padding-right: 0.55rem;
  flex: none;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* .nav-actions scope so the minor size beats button.btn's font:inherit
   (0-1-1) - without it the Log out BUTTON silently renders at 1rem. */
.nav-actions .account-minor {
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  /* W13 item 0: compacted with the rest of the header buttons. */
  padding-left: 0.4rem;
  padding-right: 0.4rem;
}
.header-logout-form { margin: 0; display: inline-flex; }
/* W14 item 2a (owner): the ACCOUNT ACCENT - Sign in (signed out) and
   the username (signed in) carry the antique-gold accent no other
   header control uses, so the account entry point reads instantly:
   nav links are dim parchment/violet, the chip is green/red, Register
   and Log out stay plain panel buttons, Install stays the filled
   violet primary. Placed after .account-name so the gold text beats
   its violet at equal specificity. */
.nav-actions .account-accent {
  border-color: var(--gold);
  color: var(--gold);
}
/* W26 item 4 (owner spec): the accent hover FILLS solid gold with ink
   text - the strongest default->hover contrast step in the header, so
   Sign in (and the username, the same accent system) reads instantly
   interactive; the brightened gold edge rides along. Supersedes the
   W14 text-only gold-bright hover, which the owner found too easy to
   miss. */
.nav-actions .account-accent:hover {
  /* W27 item 3 (supersedes the W26 solid-gold fill the owner found
     glaring): the SAME soft fill language as the plain pair - a deep
     tinted ground behind a brightened edge and brightened text. Still
     an unmissable default->hover step (the W24 too-subtle state stays
     dead - the harness measures the rendered pixel delta into a
     noticeable-but-not-glaring band), never a full-luminance flash. */
  background: var(--gold-deep);
  border-color: var(--gold-bright);
  color: var(--gold-bright);
}
/* W15 item 2a: the W10 3.2 .install-yield rule is retired - the header
   Install button is gone entirely (the hero and Downloads carry the
   install flow), so the nav sits at identical x on every page by
   construction. */

/* W10 phase 2: the shared panel section header (panel/_section_header).
   One margin rule instead of per-page inline styles, so every panel
   child starts its content at the same offset. */
.panel-section-header h1 { margin: 0 0 1.1rem; font-size: 2rem; }
.panel-section-header .dull-title { opacity: 0.6; }

/* F13 specs 4-7: user panel tabs. */
.user-panel-head .up-desc { color: var(--parchment-dim); margin: 0.2rem 0 0.6rem; }
.user-panel-head .up-user { color: var(--parchment); }
.user-panel-head .up-user strong { color: var(--arcane-bright); }
/* W8 7.4: the User Panel tabs use the standard Tab component (.tab-row);
   the bespoke underline style is gone. */
.user-panel-head .tab-row { margin: 0.9rem 0 1.2rem; }
.login-entry {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--edge-soft);
}
.login-entry .le-icon { color: var(--arcane); flex: none; }
.login-entry .le-meta { color: var(--parchment-dim); font-size: 0.85rem; }
.linked-explain { margin-top: 1.2rem; }
.linked-explain h3 { margin-bottom: 0.3rem; }
.captcha-refresh {
  background: none;
  /* W29 section 1: the standard button edge (see .copy-btn).
     W29c: the resting text joins the tier-3 ink (color only). */
  border: 1px solid var(--edge-bright);
  color: var(--parchment);
  border-radius: 4px;
  padding: 0.3rem 0.7rem;
  font-size: 0.8rem;
  cursor: pointer;
}
.captcha-refresh:hover { border-color: var(--arcane); color: var(--arcane-bright); }

/* ---------- W28 alignment system (owner spec 2026-08-24) ---------- */
/* Component-level alignment rules - deliberately NOT one global
   text-align rule (spec section 1). Scope: body.layout-public (set by
   base.html); panel/base_panel.html sets layout-panel instead, so the
   User Panel keeps its dashboard layout untouched by construction
   (spec sections 1/7/11). Tables are NEVER centered by these rules
   (spec section 5): cells keep the W14 per-type system, and the
   .table-wrap guard below re-establishes it inside any centered card
   so a td can never inherit a center alignment. */

/* Spec section 2: the page composition centers - eyebrow, title (and
   its icon, an inline part of the h1 line), and the intro directly
   beneath it, on every public page. The intro keeps its 70ch
   readability cap and centers as a block. Section headings outside
   boxes (the ladder cycle headings) center too. */
.layout-public .page-header { text-align: center; }
.layout-public .page-header .intro { margin-left: auto; margin-right: auto; }
.layout-public .cycle-heading { text-align: center; }

/* Spec section 2: page-level tab/filter groups and page-level button
   groups center as groups. The boards/siege/recruitment tab rows, the
   news search+category row, pagination rows, the events anchor-chip
   jump row, and the media platform-chip row (a class-filter form at
   SECTION level only - the ladder race+class dropdown filters live
   inside a panel and are functional box content, which stays
   left-aligned per section 3, like the boss-table toolbar). */
.layout-public .tab-row { justify-content: center; }
.layout-public .news-filter { justify-content: center; }
.layout-public .pagination { justify-content: center; }
.layout-public .anchor-chips { justify-content: center; }
.layout-public .section-content > .class-filter { justify-content: center; }

/* Spec sections 2-3: the title of EVERY box centers, on every public
   page, regardless of how the rest of the box aligns - panel titles
   (Downloads, Community, Support, the boards' panels, the event
   cards), clickable-card titles (.news-card h3: news, wiki, legal,
   media surfaces), recruitment-card titles, the castle entries' name
   heading, and the callout's label line. Box BODY content keeps its
   default left alignment (the Home/News/Game exception below is the
   one place content centers too). */
.layout-public .panel > h2,
.layout-public .news-card h3,
.layout-public .recruit-card h3,
.layout-public .castle-info > h2,
.layout-public .callout > .label { text-align: center; }

/* Spec section 2: empty-state messages center. */
.layout-public .empty-state { text-align: center; }

/* Spec section 4: Home, News, and Game - normal card/box CONTENT
   centers too (titles already do above). Applied through the ONE
   .cards-centered group utility on those pages' card containers,
   never page-wide: panels center their inline flow (text, buttons),
   flex cards center their children and their text. The .table-wrap
   guard keeps any table inside a centered card on the section-5
   table rules - table content is never centered by this system. */
.layout-public .cards-centered .panel,
.layout-public .cards-centered .news-card { text-align: center; }
.layout-public .cards-centered .news-card { align-items: center; }
.layout-public .cards-centered .table-wrap { text-align: left; }

/* ---------- W31 addendum (owner spec 2026-08-24) ---------- */
/* One coordinated IA + UI update. Everything below builds on the
   systems already ruled: the W28 alignment system (the .cards-centered
   group utility carries centering, never a page-wide text-align), the
   W29/W29b/W29c three-tier button family (T1 .btn-primary, T2
   .btn-elevated, T3 .btn - no new tier is introduced and nothing
   outranks INSTALL L2 ARCANIS), and the W26-W29 dropdown geometry
   (untouched here apart from the chevron glyph). */

/* --- Section 5: the Online + clock widget stops looking like a button.
   The chip's whole purpose is to READ as information. The green/red
   rectangular border was the one thing making it look like another
   header control, so the border goes away in every state; the dot and
   the state COLOR still carry online/offline/pending, which is the
   informational signal the owner kept. Geometry is otherwise
   untouched: the fixed slot width (6.2rem since W55) and the W13 centered content stay
   exactly as they are, so the centered nav does not move. The former
   1px border is replaced by an equal transparent border, so the box
   size is identical to the pixel. */
.status-chip {
  border: 1px solid transparent;
  background: none;
}
.status-chip[data-status-state="online"],
.status-chip[data-status-state="offline"],
.status-chip[data-status-state="unknown"] {
  border-color: transparent;
}

/* --- Section 6, amended by W32 section 1: the dropdown chevron
   becomes recognizable. The ladder so far: 0.7em/--parchment-faint
   (pre-W31, barely visible) -> 0.92em/--parchment-dim (W31) -> the
   owner still found it too small and too quiet, so W32 steps up ONE
   more notch in BOTH size and contrast: 1.05em (~13.7px of the 13px
   nav type) on --parchment, the bright body ink - one step ABOVE the
   resting label ink, which is exactly what makes the indicator read
   at a glance without competing with the hover/active arcane. The
   caret SLOT keeps its 1rem --caret-w: that token is the
   dropdown-centering contract (W26 item 1), and changing it would
   move both menus off their label midpoints. Spacing and vertical
   alignment come from the existing inline-flex centering, so the
   glyph sits on the label's optical center at every width. ONE
   treatment, both hosts. */
.nav-caret .caret,
.nav-link .caret {
  font-size: 1.05em;
  color: var(--parchment);
  line-height: 1;
}
.nav-caret:hover .caret,
.nav-item:hover .nav-caret .caret { color: var(--arcane-bright); }

/* --- Section 4: CREATE ACCOUNT replaces REGISTER.
   The button keeps its W29 tier-2 .btn-elevated treatment unchanged -
   the label is longer, so only the fixed cluster slot grows. The W12
   contract (ONE cluster width in BOTH auth states, so the centered nav
   sits at identical x signed in or out) is preserved by widening the
   token, never by letting the cluster size follow its content.
   Measured: signed out = SIGN IN (~88px) + gap 8 + CREATE ACCOUNT
   (~154px) = 250px; signed in = username (116) + 8 + Log out (62) =
   186px. 16rem = 256px carries both with the same slack the W13 value
   had. */
.account-cluster { width: 16rem; }

/* --- Sections 17-20 + 24: the Game landing page card grid.
   A flex grid, not `repeat(auto-fill)`: auto-fill leaves the last row
   flush-left, and section 17 requires the second row to be visually
   CENTERED. With flex-wrap + justify-content: center, three cards fill
   the first row and the remaining two center themselves under it by
   construction, at any card count - no per-count rule, no placeholder
   elements. */
.nav-card-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1.25rem;
}
.nav-card {
  /* Three per row at the 1200px container; the basis is what decides
     the column count, and the max-width stops a lone trailing card
     from stretching. */
  flex: 1 1 20rem;
  max-width: 23rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Section 18: compact. The .panel padding is 1.4rem/1.6rem; these
     cards are navigation components, not page sections. */
  padding: 1.5rem 1.4rem 1.3rem;
  background: var(--ink-raised);
  border: 1px solid var(--edge-soft);
  border-radius: 6px;
  color: inherit;
}
/* Section 19: the WHOLE card is the click target (it is one <a>), and
   a clickable card never underlines its inner text - the W24 item 7
   rule, applied to the new component. */
.nav-card:hover { text-decoration: none; }
/* W50 ruling D7: the nav-card title joins the HALL card-title standard
   (1.3rem, 0 above, 0.83em = 17.3px below - the same values the
   panel-based cards take from .panel h2); the bespoke 1.15rem /
   0.6rem/0.4rem set is retired. */
.nav-card h2 {
  color: var(--gold);
  font-size: 1.3rem;
  margin: 0 0 0.83em;
}
.nav-card p {
  color: var(--parchment-dim);
  font-size: 0.92rem;
  margin: 0 0 1rem;
  /* The description holds the card height steady across the row so the
     CTAs line up; two lines is what the section-23 copy renders at. */
  flex: 1 1 auto;
}
/* Section 20: a small line icon in the shared sprite language, gold at
   restrained strength so it stays clearly secondary to the title. */
.nav-card-icon {
  width: 1.75rem;
  height: 1.75rem;
  color: var(--gold);
  opacity: 0.8;
  flex: none;
}
/* Section 24: ONE CTA treatment for every card - the CTA is a visual
   cue inside the clickable card, deliberately NOT a nested button (a
   nested control would fight the whole-card target and is invalid
   inside an anchor). It borrows the .btn type treatment so the five
   cards read as one family. */
.nav-card-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--arcane-bright);
}
.nav-card-cta .cta-arrow { transition: transform 0.15s ease; }
/* Section 19: restrained hover - a stronger border, a subtle ground
   lift, and a small CTA arrow move. No scale, no shadow bloom. */
.nav-card:hover,
.nav-card:focus-visible {
  border-color: var(--edge-bright);
  background: var(--ink-panel);
}
.nav-card:hover .cta-arrow { transform: translateX(0.25rem); }
@media (prefers-reduced-motion: reduce) {
  .nav-card-cta .cta-arrow { transition: none; }
  .nav-card:hover .cta-arrow { transform: none; }
}

/* Section 17: two columns on a smaller laptop/tablet, one on mobile.
   The basis does most of the work; these floors make the intent
   explicit at the two named widths. */
@media (max-width: 1080px) {
  .nav-card { flex: 1 1 18rem; max-width: 26rem; }
}
@media (max-width: 720px) {
  /* max-width 100% (not none): identical for a 100%-basis card, and
     the account-card contract forbids an uncapped max-width anywhere
     in this stylesheet. */
  .nav-card { flex: 1 1 100%; max-width: 100%; }
}

/* W32 section 5 retired the W31 related row (.related-row /
   .related-label): the Castles & Sieges page's own sub-navigation is
   the one home for Fortresses and Clan Halls. */

/* --- Sections 48-51 + 55: the Community hub 2x2 grid.
   Genuine two-column grid on desktop, one column when two would be
   cramped (section 49). The cards stretch to equal height per row, so
   the four areas read as one composition. */
.hub-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.25rem;
  align-items: stretch;
}
/* The hub cards are .panels, so the W13 sibling-margin rhythm rule
   (.panel + .panel) would add a margin-top to every card after the
   first - misaligning the FIRST grid row's tops by the token. Inside
   the grid, the grid gap is the ONE separation mechanism (the same
   single-mechanism doctrine as .stack, W17 item 5). */
.hub-grid > .panel + .panel { margin-top: 0; }
@media (max-width: 900px) {
  .hub-grid { grid-template-columns: minmax(0, 1fr); }
}
/* W37 section 39 (owner spec 2026-08-24): the Support page's balanced
   two-column grid. Flex like .news-grid-balanced so a partial last row
   CENTERS by construction; every card grows to the same 50%-minus-gap
   cap, so the lone Download and Installation card renders at exact
   sibling width (the W36 directive-1 equal-dimensions principle).
   Below the hub breakpoint the cards stack full-width, matching
   .hub-grid. */
.hub-grid-balanced {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1.25rem;
}
.hub-grid-balanced > .panel {
  flex: 1 1 24rem;
  max-width: calc(50% - 0.625rem);
  box-sizing: border-box;
}
.hub-grid-balanced > .panel + .panel { margin-top: 0; }
@media (max-width: 900px) {
  .hub-grid-balanced > .panel { flex-basis: 100%; max-width: 100%; }
}
/* Section 50: compact but comfortable - less internal vertical padding
   than the full-width .panel the page used to stack. */
.hub-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 1.4rem 1.4rem 1.2rem;
}
/* W38 section 3 (owner spec SITE_DROPDOWN_MIRROR_W38): the Connect
   card is an anchor target (/community/#connect from the COMMUNITY
   dropdown) - the same sticky-header clearance the W33 jump links use
   (.event-section), so the followed anchor lands with the card title
   visible, never underneath the bar. */
#connect { scroll-margin-top: 5rem; }
/* W50 ruling D6: the stray 0.5rem card-title top margin is retired -
   hub-card titles ride the HALL card-title standard (the .panel h2
   rule: 1.3rem, 0 above, 17.3px below, centered). */
.hub-card > p { color: var(--parchment-dim); margin: 0 0 0.9rem; }
/* Section 55: the same icon language as the Game cards. */
.hub-card-icon {
  width: 1.75rem;
  height: 1.75rem;
  color: var(--gold);
  opacity: 0.8;
  flex: none;
}
/* Section 51: primary actions CENTER inside the card instead of
   drifting to the left edge of a very wide box. */
.hub-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: 0.5rem;
  margin: 0 0 0.6rem;
}
/* Section 53: the quiet tier for policy links - a link, not a button,
   so it can never compete with the card's real actions.
   W32 section 9: "too small / easy to overlook" - the links step up
   somewhat: 0.85rem -> 0.92rem (larger type is itself the legibility
   gain at this size), a wider row gap, and a little more air above
   the row. The ink stays the standard --arcane link violet - already
   the site's one link color - and the tier stays plainly a text
   link, never a button, below every .btn on the card. */
.hub-secondary {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1.1rem;
  margin: auto 0 0;
  padding-top: 0.6rem;
}
/* W33 section 9: one more slight step - 0.92rem -> 0.95rem with a 500
   weight - so Submission Policy / Read Guidelines / View Server Rules
   read comfortably while staying plainly text links in the one link
   violet, below every .btn tier. */
.link-secondary {
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--arcane);
  text-decoration: none;
}
.link-secondary:hover { color: var(--arcane-bright); text-decoration: underline; }

/* Section 28: the official-channels area sits UNDER Discord as a
   smaller strip, never as a second large card. */
.channel-strip {
  width: 100%;
  margin-top: auto;
  padding-top: 0.8rem;
  border-top: 1px solid var(--edge-soft);
}
.channel-strip-label {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.68rem;
  color: var(--parchment-faint);
  margin-bottom: 0.5rem;
}
.channel-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem 1.1rem;
}
.channel-list li {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
}
.channel-link { font-size: 0.88rem; color: var(--arcane); }
.channel-link:hover { color: var(--arcane-bright); }
/* Section 29: an unavailable channel must NOT look clickable. No
   border, no button ground, no pointer - a dimmed label with the one
   standard .soon annotation beneath it (the W9 5.1 pattern), which is
   what separates it from the active links beside it.
   W32 section 8: the --parchment-faint ink measured ~3.3:1 against
   the panel ground - "extremely faint" in the owner's words. The
   label moves up to --parchment-dim (~7:1, comfortably readable)
   while everything that says "unavailable" stays: not a link, no
   border, no ground, no pointer, and the AVAILABLE AT LAUNCH
   annotation beneath. Unavailable is stated by FORM, not by
   illegibility.
   W33 section 8: the owner still found the labels somewhat faint, so
   the ink takes one more slight step to --parchment, the body ink -
   full readability, with "unavailable" carried entirely by form (no
   violet, no underline, not-allowed cursor, the .soon annotation).
   Carried ruling E: the footer's unavailable socials now share this
   exact class, one treatment for both surfaces. */
.channel-off {
  color: var(--parchment);
  cursor: not-allowed;
  user-select: none;
  text-decoration: none;
}
/* W52 section 26: the footer's unavailable channels carry a COMPACT
   inline badge instead of a full second line each - the repeated
   block-level "Available at launch" rows read as visual noise. Same
   string, same .soon component, inline presentation, footer only. */
.site-footer .footer-cols .soon { display: inline; margin-left: 0.45rem; }

/* --- Sections 33-45: the Media Gallery video hub. */
/* W50 ruling D8: the gallery heading adopts the HALL title-margin
   standard - 0.83em = 17.3px below (the 0.3rem value is retired);
   size and centering were already the standard. */
.gallery-heading {
  color: var(--gold);
  font-size: 1.3rem;
  margin: 0 0 0.83em;
  text-align: center;
}
.gallery-note {
  color: var(--parchment-dim);
  font-size: 0.9rem;
  text-align: center;
  margin: 0 0 1rem;
}

/* Section 45: the grid is responsive on its own terms - auto-fill with
   a sane minimum, so cards never become excessively narrow and the
   page never overflows horizontally. */
.video-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: 1.1rem;
}
@media (max-width: 560px) {
  .video-grid { grid-template-columns: minmax(0, 1fr); }
}
.video-card {
  display: flex;
  flex-direction: column;
  background: var(--ink-raised);
  border: 1px solid var(--edge-soft);
  border-radius: 6px;
  overflow: hidden;
}
/* Section 33 + 45: the highlights are a FIXED six-card block, so the
   row count is chosen rather than left to auto-fill. auto-fill packed
   four cards on the first row and stranded two on the left with a wide
   empty area beside them - exactly the "large unused areas" and
   "content leaning heavily to one side" section 45 rules out. Three
   columns divide six evenly into a clean 3x2 rectangle at desktop,
   two into 2x3 on a laptop/tablet, one on mobile. */
.video-grid-highlight { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1100px) {
  .video-grid-highlight { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 700px) {
  .video-grid-highlight { grid-template-columns: minmax(0, 1fr); }
}
.video-card-highlight { border-color: var(--edge-bright); }
.video-thumb-link { display: block; line-height: 0; }
.video-thumb {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  display: block;
  background: var(--ink-panel);
}
.video-thumb-empty {
  display: flex;
  align-items: center;
  justify-content: center;
}
.video-thumb-icon { width: 2.5rem; height: 2.5rem; color: var(--edge-bright); }
.video-body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  padding: 0.9rem 1rem 1rem;
  gap: 0.4rem;
}
.video-title { margin: 0; font-size: 1rem; color: var(--gold); }
.video-title a { color: inherit; }
.video-title a:hover { color: var(--gold-bright); text-decoration: none; }
.video-meta { margin: 0; color: var(--parchment-dim); font-size: 0.85rem; }
.video-tags {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}
.video-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: auto;
  padding-top: 0.6rem;
}
.detail-actions { margin-top: 1rem; }
.detail-description { margin-top: 1rem; }

/* Sections 34-38: the search / sort / filter controls. Section 45
   requires them to reorganize cleanly as the viewport narrows, so the
   row wraps and the search field is the element that gives up width. */
.gallery-controls { margin: 0 0 var(--block-gap); }
/* W33 section 13: slightly more breathing room between the control
   groups - a wider row gap, and more air above the tag row and the
   active-filter row (the 0.7rem margins below step to 1rem). */
.gallery-controls-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.8rem;
}
.gallery-field {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.gallery-field input[type="search"] {
  /* Grows into the available space, shrinks first when it runs out. */
  width: 22rem;
  max-width: 100%;
  min-width: 10rem;
}
/* W33 section 13: the SORT / TAGS / ACTIVE FILTERS labels step up from
   --parchment-faint to --parchment-dim so the control row's structure
   reads clearly at a glance. */
.gallery-field-label {
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.68rem;
  color: var(--parchment-dim);
}
.gallery-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  margin-top: 1rem;
}
/* A real checkbox styled as a chip: the filter state IS form state, so
   "clearly show active filters" survives with JavaScript off.
   W34 section 34 (owner spec 2026-08-24, SUPERSEDES the W29c tier-3
   resting colors FOR THESE CHIPS ONLY): the gallery tag chips read
   too faint - the resting border steps up one tier to --edge-elevated
   and the text to 0.8rem. Geometry and compactness stay; the tab
   pills and every other chip-style control keep the W29c tier-3
   colors untouched. */
.tag-chip {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  border: 1px solid var(--edge-elevated);
  border-radius: 999px;
  padding: 0.22rem 0.7rem;
  font-size: 0.8rem;
  color: var(--parchment);
  background: var(--ink-panel);
  user-select: none;
}
.tag-chip input { position: absolute; opacity: 0; width: 0; height: 0; }
.tag-chip:hover { border-color: var(--arcane); color: var(--arcane-bright); }
/* The active filter reads at a glance - the same filled treatment the
   pinned badge uses elsewhere. W34 section 34: the selected border
   brightens to --arcane-bright so the on-state separates clearly from
   hover (hover keeps the panel ground; selected keeps the deep fill -
   still below the filled primary buttons). */
.tag-chip-on {
  background: var(--arcane-deep);
  border-color: var(--arcane-bright);
  color: var(--arcane-bright);
}
/* Keyboard users must see the focused chip: the real input is visually
   hidden, so the focus ring rides the label. */
.tag-chip:focus-within { outline: 2px solid var(--arcane); outline-offset: 2px; }
.gallery-active {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  margin-top: 1rem;
}

/* Sections 41-42: the like control and its signed-out prompt. */
.like-form { display: inline-flex; margin: 0; }
.like-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  cursor: pointer;
  border: 1px solid var(--edge-bright);
  border-radius: 3px;
  background: var(--ink-panel);
  color: var(--parchment);
  font: inherit;
  font-size: 0.8rem;
  padding: 0.4rem 0.7rem;
}
.like-btn:hover { border-color: var(--arcane); color: var(--arcane-bright); }
.like-btn .like-glyph { color: var(--parchment-faint); line-height: 1; }
/* The liked state is unmistakable: filled ground, arcane edge, and a
   colored heart - three signals, not one (section 42). */
.like-btn-on {
  background: var(--arcane-deep);
  border-color: var(--arcane);
  color: var(--arcane-bright);
}
.like-btn-on .like-glyph { color: var(--arcane-bright); }
.like-btn[disabled] { opacity: 0.6; cursor: progress; }
.like-count { font-variant-numeric: tabular-nums; }
/* Section 41: the account-required prompt. A callout, so it inherits
   the site's established notice anatomy, marked with the arcane rule
   rather than an error red - it is a requirement, not a failure. */
.callout-signin { border-left-color: var(--gold); }
.callout-signin .label { color: var(--gold); }
.callout-signin .signin-text {
  color: var(--parchment);
  font-size: 0.95rem;
  margin: 0 0 0.3rem;
}
.callout-signin .signin-video {
  color: var(--parchment-dim);
  font-size: 0.88rem;
  margin: 0 0 0.7rem;
}
.callout-signin .hub-actions { justify-content: flex-start; margin-bottom: 0; }

/* Section 51 + the W28 system: inside the centered hub/card groups the
   text centers, but a table never does (the standing W28 section-5
   rule, restated for the new containers). */
.layout-public .cards-centered .nav-card,
.layout-public .cards-centered .hub-card { text-align: center; }
.layout-public .cards-centered .video-card { text-align: left; }

/* ==================================================================
   W32 follow-up (owner spec 2026-08-24,
   docs/specs/SITE_FOLLOWUP_W32_2026-08-24.md). Deltas on the live W31
   build only - the W31 layouts, the W28 alignment system, the
   W29-W29c button tiers, and the W26-W29 dropdown geometry all stand.
   The chevron step-up (section 1), the channel-label readability
   raise (section 8), and the secondary-link step-up (section 9) are
   edits to their existing rules above; the NEW components live here.
   ================================================================== */

/* --- Section 2: the Home Latest News group centers AS A GROUP.
   The base .news-grid is auto-fill, which packs a short row into the
   leftmost tracks and leaves the remaining width empty beside it.
   This modifier (Home's latest-news grid only) switches to auto-fit
   with a bounded track: auto-fit COLLAPSES the empty tracks, and
   justify-content centers the surviving ones, so any card count
   short of a full row sits centered under the News heading while a
   full row renders exactly as before. The 24rem cap keeps two cards
   near their three-column dimensions instead of stretching them
   across the freed width ("do not stretch the individual cards"). */
.news-grid-centered {
  grid-template-columns: repeat(auto-fit, minmax(18rem, 24rem));
  justify-content: center;
}

/* --- Section 8: the true UNAVAILABLE control tier (JOIN DISCORD).
   The W31 state was .btn-primary at 0.45 opacity - still recognizably
   the purple CTA, which is exactly what the owner vetoed. This tier
   is NOT a member of the three-button family: quiet --edge border,
   no fill, dim ink, no pointer and no hover feedback, so it reads as
   a labeled placeholder ("what will become available"), never as a
   clickable action. Pairs with the standard .soon annotation via the
   W9 5.1 .disabled-control anatomy. */
.btn-unavailable {
  display: inline-block;
  padding: 0.55rem 1.1rem;
  border: 1px dashed var(--edge-bright);
  border-radius: 3px;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--parchment-dim);
  background: none;
  cursor: not-allowed;
  user-select: none;
}

/* --- Sections 11-14 + 18: the gallery structure is PERMANENT. The
   two sections and their control row render on an empty database too;
   controls that genuinely cannot act on zero published videos are
   disabled but PRESENT (real disabled attributes carry the semantics;
   this paints the one quiet visual state for all of them). The
   in-section empty notes ride the .gallery-empty treatment - body
   copy inside the finished layout, not a boxed implementation
   notice. */
.gallery-controls-off input,
.gallery-controls-off select,
.gallery-controls-off .btn,
.tag-chip-off {
  opacity: 0.5;
  cursor: not-allowed;
}
.tag-chip-off:hover { border-color: var(--edge-bright); color: var(--parchment); }
.gallery-empty {
  color: var(--parchment-dim);
  text-align: center;
  margin: 0.6rem 0 0;
}
/* The one page-header submission pathway (section 18): a centered
   action row directly under the intro, present in both the empty and
   the populated state. */
.gallery-header-actions {
  display: flex;
  justify-content: center;
  margin: 0 0 var(--block-gap);
}

/* ==================================================================
   W33 polish (owner spec 2026-08-24,
   docs/specs/SITE_POLISH_W33_2026-08-24.md + owner decisions A-G).
   Deltas on the live W32 build only - every structure the spec
   preserves (header IA, Game 3+2 grid, Community 2x2, the gallery
   two-section shell, Download hierarchy, Events card anatomy) stands.
   The readability steps (sections 8, 9, 13, 33 and ruling E) are
   edits to their existing rules above; the NEW components live here.
   ================================================================== */

/* --- Section 11: the two gallery areas read as two independent major
   sections - the second opens on a ruled divider with clearly more
   air than the in-section rhythm. */
/* W50 ruling D8: the bespoke 2.6rem seam margin joins the ONE 12.8px
   block token; the ruled divider (border + its internal padding)
   stays - the two-major-sections structure is a standing W33 ruling. */
.gallery-section + .gallery-section {
  margin-top: var(--block-gap);
  padding-top: 1.8rem;
  border-top: 1px solid var(--edge);
}

/* --- Section 20 + decision A: the Server Features grid centers a
   partial last row the way the Game page does (the W31 flex principle:
   justify-content centers the remainder by construction, at any card
   count).
   W51 (owner spec Addendum A item 1, 2026-08-26): the lone last-row
   card matches its siblings' proportions - the retired flat 24rem cap
   let it grow WIDER than the row cards while rendering shorter. The
   cap is now the per-row share, derived from the same 20rem basis the
   flex wrap uses (three across from a 62.5rem row, two across from
   41.25rem; the breakpoints add the .container padding and the
   classic scrollbar the lane sits inside), so the centered remainder
   card can never grow past the cards above it. */
.news-grid-balanced {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1.25rem;
}
.news-grid-balanced > .news-card,
.news-grid-balanced > .callout {
  flex: 1 1 20rem;
  box-sizing: border-box;
}
@media (min-width: 716px) {
  .news-grid-balanced > .news-card,
  .news-grid-balanced > .callout { max-width: calc((100% - 1.25rem) / 2); }
}
@media (min-width: 1056px) {
  .news-grid-balanced > .news-card,
  .news-grid-balanced > .callout { max-width: calc((100% - 2.5rem) / 3); }
}
/* W51 Addendum A item 1 (height half): the features cards share ONE
   excerpt reservation (three 0.92rem lines - the tallest curated
   excerpt at the three-column width), so the lone Server Rates card
   stands as tall as the flex-stretched rows above it instead of
   rendering as the page's one short card. */
.sl-features .news-card .excerpt { min-height: 4.8em; }

/* --- Section 26: expanded Full Rules stop reading as a wall of text.
   A comfortable measure, more air between the subsections and between
   each heading and its body, and tuned list rhythm for the reflowed
   Restrictions bullets (gamedata migration 0005). Scoped to the
   expander so the compact card anatomy above it is untouched.
   W39 section 22 + implementation directive 2 (owner spec
   SITE_EVENTS_POLICIES_W39_2026-08-25): the measure reconciles onto
   the ONE W34 detail token (68ch -> var(--measure-detail)) and the
   reading column centers inside the card while the prose inside it
   stays left-aligned - the same centered-container / left-text
   distinction the detail pages already follow.
   W41 sections 34-35 (owner spec SITE_BALANCE_PASS_W41_2026-08-25):
   the centered reading column on the ONE detail token stays; the
   internal vertical rhythm tightens modestly - one consistent scale
   for every event card (subsection gap 1.6 -> 1.15rem, label gap
   0.45 -> 0.35rem, paragraph gap 0.55 -> 0.45rem), never cramped.
   W42 section 12 (owner spec SITE_REFINEMENT_W42_2026-08-25): the
   centered reading container is RESTORED - the W41 comment above was
   appended after this comment's close, and the stray text made the
   selector prelude invalid, so browsers DROPPED this whole rule and
   the expanded rules rendered full-card-width (the defect section 12
   describes). The ruled measure stays the ONE detail token: 70ch
   lands inside the section's 65-75 characters-per-line target where
   the alternative ~75%-of-card reading at 1920 would not (it measures
   far past 75 characters); symmetrical gutters by the auto margins,
   prose left-aligned. */
.full-rules .event-detail {
  max-width: var(--measure-detail);
  margin-left: auto;
  margin-right: auto;
  margin-top: 1.15rem;
}
.full-rules .event-detail-label { margin-bottom: 0.35rem; }
.full-rules .event-detail p { margin: 0.45rem 0; }
.full-rules .event-detail ul {
  margin: 0.55rem 0;
  padding-left: 1.35rem;
}
.full-rules .event-detail li { margin: 0.3rem 0; }
.full-rules .event-detail li::marker { color: var(--parchment-faint); }

/* ---------- W34 (owner spec 2026-08-24): the Server Features ---------- */
/* ---------- detail-page design system ---------- */

/* Sections 9-10's feature-detail container and the 70ch/84ch measure
   pair are RETIRED by W50 rulings D1/D2 (owner spec
   W50_CATEGORY_STANDARDS_2026-08-25): the features detail pages join
   the SCROLL category on the ONE 760px .doc-column component (the
   W39 long-form standard the legal documents and news pages ride),
   so the per-surface measure pair leaves with the container. The
   --measure-detail token itself STAYS: it is the standing W39/W41
   events reading measure inside the accordion cards. */
:root { --measure-detail: 70ch; }

/* Section 8: the SERVER FEATURES eyebrow is a subtle breadcrumb - it
   keeps the eyebrow's exact resting look (color, tracking, size all
   inherited) and only reveals itself as a link on hover/focus. */
.page-header .eyebrow a { color: inherit; text-decoration: none; }
.page-header .eyebrow a:hover,
.page-header .eyebrow a:focus-visible {
  color: var(--arcane-bright);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Section 12: the BACK TO SERVER FEATURES navigation at the bottom of
   every detail page, centered under the centered column. */
.feature-back { margin-top: 2.6rem; text-align: center; }

/* Section 11 + 14/16/19/20: the restrained content block for grouped
   facts (At a Glance, quick facts, command groups) - the site's dark
   box/border language, one step quieter than a .panel. */
.fact-block {
  border: 1px solid var(--edge);
  background: var(--ink-raised);
  border-radius: 6px;
  padding: 1.1rem 1.4rem 1.2rem;
}
/* The block title reads as a small label (the callout .label voice),
   never competing with the page's real heading hierarchy. Specificity
   note: .prose-arcanis h3 styles every markdown h3, so the overrides
   ride the two-class selector. */
.prose-arcanis .fact-title {
  font-family: var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.78rem;
  color: var(--arcane-bright);
  margin: 0 0 0.8rem;
  text-align: center;
}
/* W36 section 7 (owner spec 2026-08-24): the label/value seam tightens
   one step (1.3rem -> 0.9rem column gap) so short labels do not float
   far from their values; the compact two-column shape is unchanged. */
.fact-list {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.5rem 0.9rem;
  margin: 0;
}
.fact-list dt {
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.72rem;
  color: var(--parchment-faint);
  padding-top: 0.2em;
}
/* Section 20/24: a command term keeps its code-chip presentation - the
   dt label treatment must not uppercase or track the command itself. */
.fact-list dt.cmd {
  text-transform: none;
  letter-spacing: normal;
  font-size: 0.95rem;
  padding-top: 0;
}
.fact-list dd { margin: 0; color: var(--parchment); font-size: 0.95rem; }
@media (max-width: 560px) {
  .fact-list { grid-template-columns: 1fr; gap: 0.15rem; }
  .fact-list dd { margin-bottom: 0.55rem; }
}

/* Sections 16 + 25: the compact structured grid - the Server Features
   card language (same ground, edge, radius) at a smaller size, for
   non-navigating summary cards. Flex like .news-grid-balanced so a
   partial last row centers by construction.
   W36 sections 4/11 + directive 1 (owner spec 2026-08-24): every card
   in a mini-grid renders at ONE fixed width - flex-grow is off, so a
   lone card on a partial (centered) row can never take a different
   width than its siblings; shrink still lets the single-column mobile
   case fit narrow viewports. Fixed at the shared component level, so
   Tournament, Grand Olympiad, and Community Board all inherit it. */
.mini-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.8rem;
}
.mini-grid .mini-card {
  flex: 0 1 16.5rem;
  max-width: 16.5rem;
  box-sizing: border-box;
  background: var(--ink-raised);
  border: 1px solid var(--edge-soft);
  border-radius: 6px;
  padding: 0.9rem 1rem 1rem;
}
.prose-arcanis .mini-card h4 {
  font-family: var(--font-display);
  font-size: 0.98rem;
  letter-spacing: 0.02em;
  color: var(--parchment);
  margin: 0 0 0.4rem;
  text-align: center;
}
.prose-arcanis .mini-card p {
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--parchment-dim);
}

/* Section 18: the related-content row - visually secondary, clearly
   discoverable. */
.related-links {
  border-top: 1px solid var(--edge-soft);
  padding-top: 1rem;
}
.related-links .related-label {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.68rem;
  color: var(--parchment-faint);
  margin-bottom: 0.3rem;
}

/* Sections 21-23: compact value tables inside a reading column ride
   the ONE table skin; the wrap keeps the W28 never-center table rule
   and the narrow-screen scroll. W50 rulings D1/D2 generalize the
   scope from the retired feature-detail container to the shared
   .doc-column (currencies-and-shops carries the only such tables). */
.doc-column .table-wrap table.arcanis { font-size: 0.92rem; }

/* Section 29: the Premium Rates group reads as ITS OWN clearly labeled
   section - the callout's accent edge on the panel says "different
   tier" at a glance without inventing a new color.
   W36 section 42: the accent edge is 2px thicker than the .panel
   border, which would shift this panel's interior - and its table's
   shared value-column position - 2px right of the sibling panels; the
   left padding gives those 2px back so all four Setting/value columns
   align to the pixel. */
.rate-group-premium {
  border-left: 3px solid var(--arcane);
  padding-left: calc(1.6rem - 2px);
}

/* ---------- W36 (owner spec 2026-08-24): the Server Features ---------- */
/* ---------- completion and polish pass ---------- */

/* Sections 13/19's 0.75em detail-page bullet air is RETIRED by W50
   rulings D1/D2 with the feature-detail container it was scoped to:
   the features detail pages ride the SCROLL standard now, so their
   lists keep the ONE prose list rhythm (0.4em) like every other
   document page - no per-surface override survives the category
   adoption (recorded in the W50 report). */

/* Section 26: the restrained divider between the major content areas
   of a long detail page (Currencies and Shops carries it in its body
   markdown) - a quiet rule, not a boxed section. */
.prose-arcanis hr {
  border: 0;
  border-top: 1px solid var(--edge-soft);
  margin: 2.4rem 0 0.4rem;
}

/* Section 29: the subdued qualifying note that stays visually attached
   to the table it qualifies (the 1-day Copper Ticket note under the
   Gold Coin Activation table). */
.prose-arcanis .table-note {
  color: var(--parchment-dim);
  font-size: 0.88rem;
  margin-top: 0.6rem;
}

/* Section 42 + directive 5: the four Server Rates tables share ONE
   Setting-column width token, so the value column begins at the same
   horizontal position in every section. Narrow viewports keep the W16
   shrink-to-content behavior (a fixed column would force a horizontal
   scroll the content does not need).
   W40 section 1 made the token PROPORTIONAL (19rem -> 48%) so the
   Setting column filled the card evenly.
   W41 sections 1-13 (owner spec SITE_BALANCE_PASS_W41_2026-08-25)
   RETIRE the token with the table layout itself: the rate data now
   renders through the ONE .rate-grid entry component (the W41 block
   at the end of this file) - a responsive two-column grid of
   setting/value entries, so the card's width is used by the entry
   DISTRIBUTION rather than by stretching a two-column table. */

/* Section 33: the gallery empty states step up from bare lines of text
   to deliberate quiet containers - the same dark box/border language,
   restrained padding, clearly distinct from ordinary page copy. */
.gallery-empty {
  border: 1px solid var(--edge);
  border-radius: 6px;
  background: var(--ink-raised);
  padding: 1.3rem 1.4rem;
  margin: 1.1rem auto 0;
  max-width: 44rem;
}
.gallery-empty p { margin: 0; }

/* ---------- W39 (owner spec SITE_EVENTS_POLICIES_W39_2026-08-25): ---------- */
/* ---------- events balance + the long-form document standard ---------- */

/* Section 10 + implementation directive 2: the expanded HOW EVERY
   EVENT WORKS content balances across two columns at wide desktop
   widths only (>= 1366), inside the centered card. Each column bounds
   its own line length (the track replaces the single-column measure),
   and below the breakpoint the blocks fall back to the one centered
   detail column - section 25's one-column collapse. */
@media (min-width: 1366px) {
  .shared-rules-columns {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 3rem;
    align-items: start;
  }
  .shared-rules-columns .event-detail {
    /* 100%, never "none" - the standing account-card pin; the column
       track bounds the line length. */
    max-width: 100%;
    margin-left: 0;
    margin-right: 0;
  }
}

/* Sections 31-33 + 59: the ONE long-form document standard. The
   reading column is a shared token (760px - inside the ruled 700-800px
   band) declared beside the W34 detail pair; the column centers AS A
   WHOLE under the centered page heading while the prose inside stays
   left-aligned (the same container-centered / text-left distinction as
   feature-detail). The max-width is a ceiling: at narrow widths the
   column yields fluidly to the container's own side padding. */
:root { --measure-doc: 47.5rem; }
.doc-column {
  max-width: var(--measure-doc);
  margin-left: auto;
  margin-right: auto;
}
.doc-column .prose-arcanis { max-width: 100%; }
/* Section 34: one slightly tighter heading rhythm for long documents -
   sections stay clearly separated without stretching already long
   pages (2em -> 1.7em on the section headings, everything else keeps
   the standard prose rhythm). */
.doc-column .prose-arcanis h2,
.doc-column .prose-arcanis h3 { margin-top: 1.7em; }
/* W50 ruling D4 (generalized): a document whose body OPENS with a
   section heading trims that leading heading's top margin at content
   start, so the hero-to-content gap stays the Frame's 25.6px on every
   SCROLL page (currencies-and-shops is the one current case; the rule
   is the shared component's, never a page's). */
.doc-column .prose-arcanis > h2:first-child,
.doc-column .prose-arcanis > h3:first-child { margin-top: 0; }

/* The W39 doc-advisory component and the W39/W41 status-pill flag
   rule are RETIRED by W44 sections 4-5 + the clarification + owner
   decision C (owner spec SITE_ACCORDION_RATES_BANNERS_W44_2026-08-25):
   no public surface may carry draft / legal-review / unfinished-site
   messaging. The needs_legal_review field stays internal-only
   (model + admin). */

/* Section 50: clickable cards keep the restrained hover treatment and
   gain an equally visible keyboard focus ring (the site's standard
   focus language plus the hover border, so keyboard reach is never
   more ambiguous than mouse reach). */
a.news-card:focus-visible {
  outline: 2px solid var(--arcane-bright);
  outline-offset: 2px;
  border-color: var(--arcane-deep);
}

/* Sections 53-54 + implementation directive 6: the lower-emphasis
   alternate-index text link ("All Legal & Policy Pages") sits centered
   immediately above the page's ONE primary return button - a clear
   hierarchy, never two equal buttons. */
.doc-alt-index { margin-top: 2.2rem; text-align: center; }
.doc-alt-index + .feature-back { margin-top: 0.8rem; }

/* Sections 28 + 58's full-span Download and Installation card is
   REMOVED by W43 section 4 + owner decision B (owner spec
   SITE_HERO_EVENTS_RATES_W43_2026-08-25): its destinations all live
   on /download/, so the card leaves Support entirely - a clean 2x2
   grid of one card type - and the Account Help card carries the
   secondary "Installation help" text link to /download/
   #troubleshooting instead. The .hub-span-full and .hub-span-inner
   rules retire with the card. */

/* Section 29: the Support Policies card's lower-emphasis third action
   - the same understated .link-secondary text tier the Community
   cards use, kept left-aligned with this card's button actions
   (.hub-secondary centers, which belongs to the centered hub cards). */
.card-secondary { margin: 0.6rem 0 0; }

/* ---------- W40 (owner spec SITE_RATES_REFINEMENT_W40_2026-08-25): ---------- */
/* ---------- the Server Rates final refinement ---------- */

/* Section 3's page-scoped margin reduction (0.35rem) and the W41
   section-12 2.5-line reservation are RETIRED by W43 section 3 +
   owner decision E (owner spec SITE_HERO_EVENTS_RATES_W43_2026-08-25,
   supersession): /rates/ rides the ONE internal-page hero standard -
   identical hero-to-content gap on every internal page - so the
   page-scoped gap rules leave and /rates/ rejoins the shared
   sibling-group hero contract. */

/* Sections 4-7: the in-card explanation note. Enchant Behavior and
   the Premium explanation left their standalone callout blocks and
   sit at the bottom of their own cards - a quiet divider (the
   .related-links rule voice), the established label line, and the
   body on the ONE W34 detail measure so the text never stretches the
   full card width (section 7): the container centers in the card,
   the prose inside stays left-aligned - the same distinction the
   detail pages and the W39 event rules follow. No left accent edge of
   its own: the card carries the accent (section 6 - one treatment per
   card; Premium's --arcane panel edge stays the single Premium
   accent). */
.rates-note {
  border-top: 1px solid var(--edge-soft);
  margin-top: 1.4rem;
  padding-top: 1.1rem;
}
.rates-note-body {
  /* W42 section 3 + implementation directive 1 (owner spec
     SITE_REFINEMENT_W42_2026-08-25): the explanatory measure steps
     off the W34 detail token onto ~55% of the parent card width at
     desktop (the ruled 50-60% band; the container still centers,
     the prose stays left) and yields to the full card width below
     the 900px grid gate. */
  max-width: 55%;
  margin-left: auto;
  margin-right: auto;
  color: var(--parchment-dim);
  font-size: 0.92rem;
  text-align: left;
}
@media (max-width: 900px) {
  .rates-note-body { max-width: 100%; }
}
.rates-note .label {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.7rem;
  color: var(--arcane-bright);
  margin-bottom: 0.3rem;
  /* The box-title-centers doctrine (W28 sections 2-3) carried over
     from the retired callout labels. */
  text-align: center;
}

/* Section 8: the two-row Buff & Character Limits card sheds part of
   its vertical padding (1.4rem -> 0.95rem, -14.4px overall) - a
   modest reduction, same tokens as the siblings otherwise; the
   horizontal padding is untouched so the shared column geometry
   holds. */
.panel-compact { padding-top: 0.95rem; padding-bottom: 0.95rem; }

/* ================================================================== */
/* ---------- W41 (owner spec SITE_BALANCE_PASS_W41_2026-08-25): ---------- */
/* ---------- the site balance pass ---------- */

/* Sections 1-13: the Server Rates setting/value entry grid - the ONE
   shared component behind all four rate sections (the include
   templates/includes/rate-grid.html). Desktop renders a row-major
   two-column grid so the data actively uses both halves of the card;
   below 900px it collapses to one column in the same DOM (= reading)
   order. Section 8: labels left-aligned in the body ink, values
   right-aligned one weight step up - the exact label/value contrast
   pair the retired table carried (td parchment / td.value 600), so
   the emphasis system is unchanged. Section 9: structured-data
   styling by restrained means - the quiet row separator is the only
   chrome; no per-entry cards, grounds, or accents. Section 10:
   compact 0.5rem entry padding keeps the shorter page short. Shared
   grid rows keep the two columns at equal height by construction
   (each row is as tall as its taller entry), so the sides can never
   end unevenly. */
.rate-grid {
  margin: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}
/* W45 (owner spec SITE_RATES_COLUMNS_W45_2026-08-25, superseding
   W44 decision B's remaining-width gutter and right-aligned labels):
   each block is ONE four-column grid with FIXED shared tracks - A
   (widest left label, max-content), B (widest left value,
   max-content), the fixed 4.5rem gutter track, C (widest right
   label, max-content), D (widest right value, max-content). Every
   cell is LEFT-aligned on its column's left edge, so every row's
   label starts on the same vertical line and every value starts on
   the same vertical line - nothing shifts with text length (Spoil
   opens exactly where Experience does). The pair gap (A-B and C-D)
   is the ONE small 1.25rem column-gap; the visible B-C gutter reads
   gap + 4.5rem track + gap = 7rem (112px) >= 4rem and >= 3x the
   pair gap, so the two pairs read as tight Setting-Value units
   clearly separated across the middle. Because the gutter track is
   FIXED (not 1fr), the grid's total width is tracks + gaps and
   justify-content: center centers the whole four-column block
   inside the card - the left pair no longer hugs the left edge nor
   the right pair the right edge. Entries keep their own boxes via
   subgrid (the row separators and 0.5rem padding stay on the
   entry), odd entries occupy tracks 1-2, even entries tracks 4-5 -
   DOM order IS the reading order, and the sub-900 one-column
   collapse keeps A/B then C/D row-major at the same left
   alignment. The 600 value emphasis carries over unchanged. */
.rate-entry {
  display: grid;
  grid-template-columns: auto max-content;
  justify-content: start;
  align-items: baseline;
  column-gap: 1.25rem;
  padding: 0.5rem 0.1rem;
  border-bottom: 1px solid var(--edge-soft);
}
.rate-entry:last-child { border-bottom: 0; }
.rate-entry dt { margin: 0; color: var(--parchment); }
.rate-entry dd {
  margin: 0;
  color: var(--parchment);
  font-weight: 600;
  text-align: left;
}
@media (min-width: 900px) {
  .rate-grid {
    grid-template-columns: max-content max-content 4.5rem max-content max-content;
    column-gap: 1.25rem;
    justify-content: center;
  }
  .rate-entry { grid-template-columns: subgrid; }
  .rate-entry:nth-child(odd) { grid-column: 1 / 3; }
  .rate-entry:nth-child(even) { grid-column: 4 / 6; }
  /* W45: labels open on their column's LEFT edge like the values -
     the four alignment guides are the track edges themselves, so
     short labels never shift inward. */
  .rate-entry dt { text-align: left; }
  /* Even entry counts throughout (6/10/2/4): the last grid row is
     always the last two entries, which close the grid without a
     trailing rule - matching the single-column :last-child rule. */
  .rate-grid .rate-entry:nth-last-child(-n+2) { border-bottom: 0; }
}

/* Section 12's 2.5-line rates reservation is RETIRED by W43 section 3
   + owner decision E (the shared internal-page hero standard - see
   the W10/W43 reservation block above): every internal page reserves
   the same 3 lines now, so /rates/ carries no page-local override. */

/* Section 16: the Legal index card anchors its update metadata to the
   card bottom - the title stays at the top, the purpose line is the
   flexible middle, and rows of the .news-grid (whose grid tracks
   already stretch same-row cards to equal height) present their
   metadata on one shared baseline however differently the
   descriptions wrap. Scoped to the legal index; the news/wiki/media
   cards keep their natural flow. (Section 15 is already standing:
   legal cards are whole-card anchors with the W24 hover and the W39
   focus-visible ring.) */
.sl-legal .news-card .date { margin-top: auto; }

/* Sections 19-21: consistent Support-card interiors. The five cards
   already share the .panel tokens (padding, centered heading, left
   copy); what varied was the control row - natural inline .btn height
   differences between tiers and no wrap gap. One scoped rule set
   gives every action in the grid the same control height (the W12
   2.5rem header-button standard), centered label, and a consistent
   gap when controls wrap at narrow widths. The Policies card's ALL
   LEGAL PAGES stays on the W39 secondary text-link tier (section 21)
   - deliberately not a button, so it is untouched here. */
.hub-grid-balanced .btn,
.hub-grid-balanced .btn-unavailable {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.5rem;
  padding-top: 0.2rem;
  padding-bottom: 0.2rem;
  box-sizing: border-box;
  margin: 0 0.45rem 0.45rem 0;
}
/* The W9 5.1 disabled-control anatomy spaces its own annotation; the
   wrap-gap margin stays off it so the pair keeps its ruled 0.3rem. */
.hub-grid-balanced .disabled-control .btn-unavailable { margin: 0; }

/* Section 37: the BACK TO GAME return connects to the page content -
   the events page's own modifier trims the shared component's
   2.6rem offset to 1.5rem (-17.6px) WITHOUT touching the component
   itself, so every other child page keeps the ruled W34 geometry. */
.sl-events .feature-back { margin-top: 1.5rem; }

/* ================================================================== */
/* ---------- W42 (owner spec SITE_REFINEMENT_W42_2026-08-25): ---------- */
/* ---------- the site refinement pass ---------- */
/* (The W42 deltas living inside earlier rules: the rate-grid gutter
   3rem -> 4.5rem (section 2), the .rates-note-body 55% measure with
   its sub-900 full-width yield (section 3), the restored
   .full-rules .event-detail reading container with the W41 comment
   defect repaired (section 12), the .fact-strip .fact-label
   gold -> gold-bright step (section 14), and the .event-section
   .tagline dim -> parchment step (section 15).) */

/* Section 4's .hub-span-inner container retired with the Download
   card itself - W43 section 4 + owner decision B (see the W39 note
   above). */

/* Section 5's Support-scoped AVAILABLE AT LAUNCH step is generalized
   by W43 carried item F: the --parchment tier lives on the global
   .soon rule now (ONE shared token - see the W9/W33/W43 block), so
   the page scope is retired. */

/* Section 7: the Legal index update metadata steps up one ladder
   token, --parchment-faint (3.5:1) -> --parchment-dim (7.3:1), at
   its unchanged size and weight - readable, still clearly
   subordinate to the title and the description. Scoped beside the
   W41 bottom-anchor rule; news/wiki/media card dates keep the
   established faint tier. */
.sl-legal .news-card .date { color: var(--parchment-dim); }

/* Section 17 (superseding the W37 section-9 hub rule for Server
   Features only): BACK TO GAME joins /features/ on the same
   connected spacing pattern as /events/ - the page-scoped modifier
   trims the shared component's 2.6rem offset to 1.5rem without
   touching the component or any other page. */
.sl-features .feature-back { margin-top: 1.5rem; }

/* ================================================================== */
/* ---------- W43 (owner spec SITE_HERO_EVENTS_RATES_W43_ ---------- */
/* ---------- 2026-08-25): hero / events / rates refinement ---------- */
/* (The W43 deltas living inside earlier rules: the fact-strip
   1fr x 3 centered regions + centered tagline (section 1A), the
   accordion control row with the label swap and rotating chevron
   (section 1B), the rate-entry pair grid with the fixed 1.5rem
   label-to-value gap (section 5), the dropdown glass 0.7 -> 0.79
   (section 6, decision A), the site-wide 3-line hero reservation
   with the retired sl-rates overrides (section 3, decision E), the
   global .soon --parchment tier (carried item F), and the retired
   hub-span pair (section 4, decision B).) */

/* Section 2's verified-note rule pair is RETIRED by the W45 owner follow-up
   (owner spec SITE_RATES_COLUMNS_W45_2026-08-25): the Server-Verified
   Information element leaves /features/ entirely - label, sentence,
   and rule pair - with no replacement; validation/QA messaging never
   renders publicly. Its quiet voice lives on as the W44 .info-note
   below. */
/* W44 section 5 + owner decision C (owner spec
   SITE_ACCORDION_RATES_BANNERS_W44_2026-08-25): .info-note is the
   quiet information line the public pages' former informational
   .callout boxes de-banner onto - the retired W43 verified-note voice
   generalized (small label + text, no fill, no accent bar, no
   border), so no public page carries a bordered advisory box for
   plain information. Status lines, empty states, and pointer notes
   ride it; genuine transactional errors keep .callout-warning. The
   former .callout sibling-gap seams carry over so the surrounding
   rhythm is unchanged. */
.info-note {
  margin: 0;
  color: var(--parchment-dim);
  font-size: 0.92rem;
  text-align: center;
}
.info-note .label {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.7rem;
  color: var(--arcane);
  margin-bottom: 0.3rem;
}
.info-note + .stack,
.info-note + .panel,
.panel + .info-note,
.stack + .info-note { margin-top: var(--block-gap); }

/* Section 4 + owner decision B: the Download page's Troubleshooting
   card is the stable target of the Support "Installation help" link
   - the anchor clears the sticky header exactly like the event
   cards' jump targets (the W33 section-25 pattern). */
#troubleshooting { scroll-margin-top: 5rem; }

/* ================================================================== */
/* ---------- W46 (owner spec W46_RATES_CROSS_BLOCK_ALIGNMENT_ ---------- */
/* ---------- 2026-08-25): the rates cross-block column system ---------- */

/* One shared column system across ALL four rate blocks. The W45 rules
   above stay as the no-script fallback (per-block max-content tracks,
   aligned within each block); js/rates-columns.js measures the natural
   width of every label and value across the four blocks, takes the
   per-column maxima, and publishes them as the custom properties
   --rates-col-a/-b/-c/-d (the shared A/B/gutter/C/D tracks) and
   --rates-col-label (the shared sub-900 pair guide) on the section,
   then arms .rates-aligned / .rates-aligned-wide. Because every card
   interior is pixel-identical (the W36 premium-edge compensation) and
   every block consumes the SAME fixed tracks under the SAME centering,
   every block's A/B/C/D column edges land on identical x-positions
   page-wide - the spec's vertical guides. */

/* The off-screen nowrap measuring pass: spans reproduce the dt (body
   ink) and dd (600 weight) faces so the measured naturals are the
   rendered naturals. */
.rates-measurer {
  position: absolute;
  left: -9999px;
  top: 0;
  visibility: hidden;
  white-space: nowrap;
  pointer-events: none;
}
.rates-measurer span { display: inline-block; }
.rates-measurer .m-value { font-weight: 600; }

@media (min-width: 900px) {
  /* The shared four-column set: identical px tracks on every block,
     the W45 4.5rem gutter and 1.25rem pair gap (column-gap and
     justify-content: center carry over from the W45 rule above). Each
     track is the ceil of the widest natural in its column page-wide,
     so no cell ever wraps and no track takes remaining width. */
  .rates-aligned-wide .rate-grid {
    grid-template-columns: var(--rates-col-a) var(--rates-col-b) 4.5rem var(--rates-col-c) var(--rates-col-d);
  }
  /* Fit fallback: at viewports where the shared set is wider than the
     card (roughly 900-1100px), the blocks render the SAME one-column
     shared pair guides as sub-900 instead of an overflowing grid -
     the W45 per-block four-column set could already exceed the card
     in part of this range; the shared set may not. DOM order stays
     the reading order (A/B then C/D row-major). */
  .rates-aligned:not(.rates-aligned-wide) .rate-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .rates-aligned:not(.rates-aligned-wide) .rate-entry {
    grid-template-columns: var(--rates-col-label) minmax(0, 1fr);
    grid-column: auto;
  }
  .rates-aligned:not(.rates-aligned-wide) .rate-grid
    .rate-entry:nth-last-child(-n+2) {
    border-bottom: 1px solid var(--edge-soft);
  }
  .rates-aligned:not(.rates-aligned-wide) .rate-grid
    .rate-entry:last-child {
    border-bottom: 0;
  }
}

@media (max-width: 899.98px) {
  /* Owner decision B (STRICT SHARED COLUMNS ON MOBILE): below 900px
     every pair keeps the W44-shipped values-beside-labels row, but on
     ONE shared guide pair for every block - the fixed shared label
     track (natural where it fits, wrap-capped by the same formula for
     every block otherwise) and the 1.25rem token, so the label edge
     and the value edge each sit on one vertical line page-wide. */
  .rates-aligned .rate-entry {
    grid-template-columns: var(--rates-col-label) minmax(0, 1fr);
  }
}

@media (max-width: 860px) {
  /* The W36 premium-edge compensation, extended to the narrow panel
     padding: below 860px .panel drops to 1.2rem side padding, so the
     accent panel's give-back must drop with it or its interior sits
     6.4px right of (and narrower than) the siblings - exactly the
     cross-block drift this round removes. */
  .rate-group-premium { padding-left: calc(1.2rem - 2px); }
}

/* ================================================================== */
/* ---------- W49 (owner spec W49_NEWS_BUTTONS_SPACING_ ---------- */
/* ---------- AUDIT_2026-08-25): centered section headings ---------- */
/* Decision E: section-level headings within page content center on
   every public page EXCEPT the home page - ONE rule in the shared
   standard, never per-page overrides. The W28 system already centers
   every boxed title (.panel > h2, card titles, .castle-info > h2,
   .gallery-heading, .cycle-heading); what remained left-aligned were
   the flowing-document section headings - the markdown h2/h3 titles
   inside .prose-arcanis (news articles, legal and support documents,
   features detail pages: WHAT YOU CAN DO TODAY, THE SHORT VERSION,
   and their equivalents). The .layout-public scope excludes the
   panel surfaces; Home renders no .prose-arcanis, so the home-page
   exception holds by construction. Body text, lists, and paragraph
   content keep their prose left alignment - only the headings
   center. */
.layout-public .prose-arcanis h2,
.layout-public .prose-arcanis h3 { text-align: center; }

/* ================================================================== */
/* ---------- W50 (owner spec W50_CATEGORY_STANDARDS_ ---------- */
/* ---------- 2026-08-25): the page-category standards ---------- */
/* The six-category taxonomy (SCROLL / CODEX / HALL / LEDGER / GATE /
   THRONE under the FRAME) is recorded in docs/STANDARDS-PAGE-
   CATEGORIES.md - the reference for all future rounds. The W50
   deltas live INSIDE the rules they standardize (deleted, never
   shadowed, per implementation directive 2): the .tab-row and
   .recruit-filter seams on the --block-gap token (D9/D10), the
   .event-section title on the panel standard (D5), the hub-card and
   nav-card titles on the HALL card-title standard (D6/D7), the
   gallery heading margin and section seam (D8), the retired
   feature-detail measure pair with the features pages on the
   .doc-column SCROLL component (D1-D3), the .doc-column leading-
   heading trim (D4), and the retired .account-section wrapper with
   the GATE pages on the base_section skeleton (D11). No LEDGER table
   interior rule was touched (pinned by the W50 harness walk against
   scripts/w50-ledger-interiors.json). */

/* ==================================================================
   W54 USER PANEL CONSOLIDATION (owner spec W54-USER-PANEL-CONSOLIDATE,
   2026-08-26). The design-system layer: status states, the branded
   combobox, the icon password toggle, the panel contrast step, and
   the compact panel footer.
   ================================================================== */

/* Section 10: ONE scoped contrast step for the panel's secondary
   text tiers - sidebar links, labels, helper copy, table secondary
   info, empty-state text and annotations all read through these two
   tokens. W60 section 38: the values live in the ONE panel token
   block further down (the W54/W59 duplicate pair collapsed - two
   competing .layout-panel redefinitions were exactly the page-level
   layering the shared-layout rule forbids). */

/* Section 13: a disabled button stays OBVIOUSLY a button - the border
   and ground survive, only the ink dims and the interaction leaves.
   (The 0.45 blanket opacity made disabled CTAs read as broken.) */
.btn[aria-disabled="true"], .btn.btn-disabled, .btn:disabled {
  opacity: 1;
  color: var(--parchment-faint);
  border-color: var(--edge);
  background: var(--ink-raised);
  cursor: not-allowed;
  pointer-events: none;
}
.btn-primary[aria-disabled="true"], .btn-primary.btn-disabled, .btn-primary:disabled {
  background: var(--ink-panel);
  border-color: var(--edge-bright);
  color: var(--parchment-dim);
}
/* Section 13: the explain-why line under a disabled action. */
.btn-reason {
  display: block;
  font-size: 0.78rem;
  color: var(--parchment-dim);
  margin-top: 0.35rem;
}

/* Sections 39-40: the status-state component (includes/status-state
   .html) - icon + eyebrow + one sentence + optional action, solid
   card ground (the nested dashed boxes retire; the dashed skin stays
   on the bare .empty-state table replacement). */
.status-state {
  display: flex;
  gap: 0.9rem;
  align-items: flex-start;
  border: 1px solid var(--edge);
  border-left: 3px solid var(--edge-bright);
  border-radius: 6px;
  background: var(--ink-raised);
  padding: 1.1rem 1.3rem;
}
.status-state .ss-icon {
  width: 1.5rem;
  height: 1.5rem;
  flex: none;
  margin-top: 0.1rem;
  color: var(--parchment-dim);
}
.status-state .ss-eyebrow {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.72rem;
  color: var(--gold);
}
.status-state .ss-text { margin: 0.35rem 0 0; color: var(--parchment-dim); }
.status-state .ss-action { margin-top: 0.8rem; }
.ss-success { border-left-color: var(--signal-online); }
.ss-success .ss-eyebrow, .ss-success .ss-icon { color: var(--signal-online); }
.ss-warning { border-left-color: var(--signal-offline); }
.ss-warning .ss-eyebrow, .ss-warning .ss-icon { color: var(--signal-offline); }
.ss-soon { border-left-color: var(--gold); }
.ss-soon .ss-icon { color: var(--gold); }
.layout-public .status-state { text-align: left; }

/* W56 section 29: the ONE Coming Soon / Unavailable component
   (includes/coming-soon.html) - visually DISTINCT from the empty
   state: a state pill leads (Coming Soon / Available at Launch /
   Temporarily Unavailable / Requires Connection), then the feature
   name and one sentence. Empty states stay for live features with no
   records (section 28). */
.soon-state {
  display: flex;
  gap: 0.9rem;
  align-items: flex-start;
  border: 1px solid var(--edge);
  border-left: 3px solid var(--gold);
  border-radius: 6px;
  background: var(--ink-raised);
  padding: 1.1rem 1.3rem;
}
.soon-state .cs-icon {
  width: 1.5rem;
  height: 1.5rem;
  flex: none;
  margin-top: 0.1rem;
  color: var(--gold);
}
.soon-state .cs-pill {
  display: inline-block;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 0.66rem;
  color: var(--gold);
  border: 1px solid var(--edge-bright);
  border-radius: 999px;
  padding: 0.18rem 0.6rem;
}
.soon-state .cs-eyebrow {
  display: block;
  margin-top: 0.45rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.72rem;
  color: var(--parchment);
}
.soon-state .cs-text { margin: 0.35rem 0 0; color: var(--parchment-dim); }
.soon-state .cs-action { margin-top: 0.8rem; }
.cs-unavailable { border-left-color: var(--signal-offline); }
.cs-unavailable .cs-icon, .cs-unavailable .cs-pill { color: var(--signal-offline); }
.layout-public .soon-state { text-align: left; }

/* Sections 8-9 + 53: the branded combobox (combobox.js). The native
   select stays in the DOM as the submitted control; .cbx-native hides
   it from view and the tab order while the button + listbox carry the
   interaction. Dark-purple menu, purple active row, gold/cream inks -
   never the OS bright-blue selection. */
.cbx { position: relative; }
.cbx .cbx-native {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.cbx-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  width: 100%;
  max-width: 24rem;
  background: var(--ink-panel);
  border: 1px solid var(--edge);
  border-radius: 3px;
  color: var(--parchment);
  padding: 0.5rem 0.7rem;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
:is(.form-standard, .form-wide) .cbx-btn,
.form-grid-2 .cbx-btn { max-width: 100%; }
.cbx-btn:hover { border-color: var(--edge-bright); }
.cbx-btn:focus { border-color: var(--arcane); outline: none; }
.cbx-btn[aria-expanded="true"] { border-color: var(--arcane); }
.cbx-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cbx-value.cbx-placeholder { color: var(--parchment-faint); }
.cbx-caret { color: var(--parchment-faint); flex: none; font-size: 0.8em; }
.cbx-pop {
  position: absolute;
  z-index: 60;
  top: calc(100% + 0.25rem);
  left: 0;
  right: 0;
  max-width: 24rem;
  background: var(--ink-raised);
  border: 1px solid var(--edge-bright);
  border-radius: 6px;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.5);
  padding: 0.35rem;
}
:is(.form-standard, .form-wide) .cbx-pop,
.form-grid-2 .cbx-pop { max-width: 100%; }
.cbx-search {
  width: 100%;
  max-width: 100%;
  margin-bottom: 0.35rem;
  background: var(--ink-panel);
  border: 1px solid var(--edge);
  border-radius: 3px;
  color: var(--parchment);
  padding: 0.45rem 0.6rem;
  font: inherit;
}
.cbx-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 16rem;
  overflow-y: auto;
}
.cbx-option {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.45rem 0.6rem;
  border-radius: 4px;
  color: var(--parchment);
  font-size: 0.9rem;
  cursor: pointer;
}
.cbx-option .cbx-icon { width: 1.05rem; height: 1.05rem; flex: none; color: var(--parchment-dim); }
/* W58 section 16: the CLOSED field's platform icon renders at the same
   normal size as the menu rows' - "[YouTube icon] YouTube", a colored
   mark COMPLEMENTING the visible platform name, never an oversized
   icon replacing it (the unsized closed-field svg was the bug). */
.cbx-value .cbx-icon {
  width: 1.05rem;
  height: 1.05rem;
  vertical-align: -0.2em;
  margin-right: 0.3rem;
}
/* W89 item 2: a class emblem inside a dropdown - the ONE emblem box,
   the same border and ground .cls-emblem gives it everywhere else, in
   the menu rows and in the closed field alike. A native <option> cannot
   hold an image, so this is how a selector joins the emblem standard. */
.cbx-option .cbx-emblem,
.cbx-value .cbx-emblem {
  width: 20px;
  height: 20px;
  flex: none;
  border: 1px solid var(--edge);
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.35);
  object-fit: contain;
}
.cbx-value .cbx-emblem { vertical-align: -0.35em; margin-right: 0.4rem; }
.cbx-option.cbx-active { background: var(--ink-panel); color: var(--arcane-bright); }
.cbx-option[aria-selected="true"] { color: var(--gold-bright); }
.cbx-option[aria-selected="true"] .cbx-label::after {
  content: " \2713";
  color: var(--gold);
}
.cbx-option[aria-disabled="true"] { color: var(--parchment-faint); cursor: not-allowed; }
.cbx-detect { margin-top: 0.4rem; display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap; }
.cbx-detect .btn { font-size: 0.7rem; padding: 0.35rem 0.7rem; }
.cbx-detect [data-tz-detect-note] { font-size: 0.78rem; color: var(--parchment-dim); }

/* Section 12.1: the icon password toggle (password-toggle.js) - the
   eye/eye-off pair replaces the textual SHOW control everywhere. The
   button anchors onto the input box (inline style top/height from the
   script); the icons swap via the .pw-visible class. */
.pw-toggle-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
}
.pw-toggle-icon svg { width: 1.1rem; height: 1.1rem; }
.pw-toggle-icon .pwt-eye-off { display: none; }
.pw-toggle-icon.pw-visible .pwt-eye { display: none; }
.pw-toggle-icon.pw-visible .pwt-eye-off { display: block; }
.pw-toggle-icon:focus-visible { outline: 2px solid var(--arcane); outline-offset: 1px; border-radius: 3px; }
input.has-pw-toggle { padding-right: 2.6rem; }

/* Section 3 (AD3): the shared Online + Time widget - the header
   status chip - renders on panel pages again; the W52 visibility hide
   is retired (the serverbar no longer repeats the state, so there is
   no duplication to suppress). */
.layout-panel .status-chip { visibility: visible; }

/* W55 section 2 retired the W54 section 83 compact panel footer (the
   shared site footer serves every page now) and W55 section 3 retired
   the W54 section 6 static time pair (.time-pair) - the live clock
   pair (.clock-pair, end of file) replaced it. */

/* Section 42: inline social platform icon beside a link or heading. */
.social-icon { width: 1.05rem; height: 1.05rem; vertical-align: -0.2em; }

/* Section 55: the media tag CHIPS - the checkbox matrix restyled as
   selectable chips; real checkboxes stay underneath (keyboard + no-JS
   safe), the label paints the chip and the :checked state fills it. */
.chip-grid { display: flex; flex-wrap: wrap; gap: 0.45rem; }
.chip-grid label {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  border: 1px solid var(--edge-bright);
  border-radius: 999px;
  padding: 0.32rem 0.8rem;
  font-size: 0.8rem;
  color: var(--parchment);
  background: var(--ink-panel);
  cursor: pointer;
}
.chip-grid label:hover { border-color: var(--arcane); color: var(--arcane-bright); }
.chip-grid input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  pointer-events: none;
}
.chip-grid label:has(input:checked) {
  background: var(--arcane-deep);
  border-color: var(--arcane);
  color: var(--arcane-bright);
}
.chip-grid label:has(input:focus-visible) { outline: 2px solid var(--arcane); outline-offset: 1px; }

/* Sections 60-70: the grouped class selector - role-group headings
   over chip rows; the same chip skin as .chip-grid. */
.class-groups { display: grid; gap: 0.9rem; }
.class-group > .cg-title {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 0.7rem;
  color: var(--gold);
  margin-bottom: 0.35rem;
}
.class-group .cg-note { font-size: 0.75rem; color: var(--parchment-faint); text-transform: none; letter-spacing: 0; }
.class-search { margin-bottom: 0.7rem; }
.class-search input { max-width: 18rem; }

/* Section 45: Gold Coin package CARDS - responsive grid, one card per
   ladder tier: coins, price, per-coin value line, savings, CTA.
   W56 section 8 (Decision 2.2, supersedes the W55 46.2 four-column
   arrangement): TWO columns at every width above mobile - the eight
   packages read as 2 x 4 with real horizontal room in each card;
   identical widths by construction (equal 1fr tracks). */
.package-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.9rem;
}
@media (max-width: 560px) { .package-grid { grid-template-columns: 1fr; } }
.package-card {
  border: 1px solid var(--edge);
  border-radius: 6px;
  background: var(--ink-raised);
  padding: 1rem 1.1rem 1.1rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.25rem;
}
/* W56 section 8: a clear hover state and an unmistakable selected
   state (double arcane edge + tinted ground). */
.package-card:hover { border-color: var(--arcane); }
.package-card.pkg-selected {
  border-color: var(--arcane);
  box-shadow: inset 0 0 0 1px var(--arcane);
  background: var(--arcane-deep);
}
.package-card .pkg-icon { width: 1.6rem; height: 1.6rem; color: var(--gold); }
.package-card .pkg-coins { font-family: var(--font-display); font-weight: 600; font-size: 1.15rem; color: var(--gold-bright); }
.package-card .pkg-price { font-size: 1rem; color: var(--parchment); }
.package-card .pkg-per { font-size: 0.75rem; color: var(--parchment-dim); }
.package-card .pkg-save { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--signal-online); }
.package-card .pkg-save.pkg-base { color: var(--parchment-faint); }
.package-card label { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; cursor: pointer; color: inherit; font-size: inherit; }
.package-card input[type="radio"] { position: absolute; opacity: 0; width: 1px; height: 1px; }
.package-card .pkg-unavailable { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--parchment-faint); }

/* Section 74: public recruitment banner - wide ratio, never
   distorted; restrained fallback strip when no banner exists. */
.recruit-banner {
  width: 100%;
  aspect-ratio: 3 / 1;
  object-fit: cover;
  border-radius: 6px 6px 0 0;
  display: block;
}
.recruit-banner-fallback {
  width: 100%;
  aspect-ratio: 6 / 1;
  border-radius: 6px 6px 0 0;
  background: linear-gradient(135deg, var(--ink-panel), var(--arcane-deep));
  opacity: 0.55;
}
.banner-preview { max-width: 24rem; }
.banner-preview img { width: 100%; aspect-ratio: 3 / 1; object-fit: cover; border-radius: 6px; border: 1px solid var(--edge); }

/* Section 75: the structured playtime day picker rides the chip skin;
   the time pair sits side by side until narrow widths. */
.play-days { display: flex; flex-wrap: wrap; gap: 0.45rem; }
.play-time-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.play-time-row input[type="time"] {
  background: var(--ink-panel);
  border: 1px solid var(--edge);
  border-radius: 3px;
  color: var(--parchment);
  padding: 0.5rem 0.7rem;
  font: inherit;
  max-width: 9rem;
}
.play-time-row .pt-sep { color: var(--parchment-dim); }

/* W54 section 18: the Dashboard two-column overview - Account Summary
   beside Account Details, one column below 900px; Characters follows
   full-width. */
/* W58 section 11: Account Summary and Account Details read as a
   coordinated, intentionally symmetrical pair - equal column widths
   (1fr 1fr), stretched to EQUAL heights, headers at the same position,
   the same table anatomy, and any help line pinned to the bottom edge
   so the boxes close on the same line. */
.dash-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; align-items: stretch;
  /* W60 section 22: the standard block seam between the overview pair
     and the Account Activity card below - the same --block-gap token
     every major section seam rides, never an unusually large gap. */
  margin-bottom: var(--block-gap); }
.dash-grid > .panel { display: flex; flex-direction: column; }
/* The sibling-panel block-gap margin was the "slightly offset" bug:
   inside the grid the gap token already spaces the pair, so the
   second card's margin must not push it 12.8px lower (the
   home-equal-cards precedent). */
.dash-grid > .panel + .panel { margin-top: 0; }
.dash-grid > .panel > .help-msg { margin-top: auto; }
/* W55: a grid item's implicit min-width is its CONTENT width - the
   long nowrap Account Details rows (Time Since Last Game Login) blew
   the column past a 390px viewport; min-width 0 lets the inner
   .table-wrap scroll instead (the W52 .panel-content precedent). */
.dash-grid > * { min-width: 0; }
@media (max-width: 900px) { .dash-grid { grid-template-columns: 1fr; } }

/* W54 section 75: the quick day-set buttons above the day chips. */
.play-quick { display: flex; gap: 0.45rem; flex-wrap: wrap; margin-bottom: 0.5rem; }
.play-quick .btn { font-size: 0.7rem; padding: 0.32rem 0.7rem; }
/* W54 section 74: a recruit card whose banner leads tucks the image
   against the card's own top edge. */
.recruit-card .recruit-banner { margin: -1.4rem -1.6rem 0.9rem; width: calc(100% + 3.2rem); max-width: calc(100% + 3.2rem); }


/* =====================================================================
   W55 USER PANEL SPECIFICATION 2 (docs/specs/W55-USER-PANEL-SPEC-2.md)
   ===================================================================== */

/* Section 2: header / flex-grow main / footer column - the shared site
   footer sits at the viewport bottom on short pages and follows the
   content on long ones; no dead region ever opens beneath it. */
body { display: flex; flex-direction: column; min-height: 100vh; }
main.container { flex: 1 0 auto; width: 100%; }

/* Section 3, reshaped by W56 section 1 (Decision 1): the stacked
   stacked header pair treatment is RETIRED - the header clock lives
   inside the combined status chip as the single-line chip variant.
   Width-stable slots stand (the W55 zero-layout-shift wall): the
   ticking digits and the zone abbreviation hold FIXED widths so
   nothing after them can ever move. Every run inherits the chip's
   state color (Decision 1: SERVER, the time, and the abbreviation all
   carry the same green as Online). */
/* W61 section 16 (supersedes the W59/W60 abbreviation chip): the
   chip's server clock is ONE coherent inline component - "Server
   9:59:27 PM UTC−4" - label, live 12-hour clock with AM/PM, and the
   dynamic UTC offset in effect now, all on the SAME baseline with
   natural, COMPACT spacing (the Server-to-time gap is deliberately
   tighter than the separation after the Online status). Fixed slots
   stand (the W55 zero-layout-shift wall): the 12-hour hour flips
   between one and two digits across the day, so the time renders
   inside a fixed slot sized tightly for "12:59:59 PM", right-aligned
   so the one-digit-hour slack never opens a hole before the offset. */
.clock-pair-chip { display: inline-flex; }
.clock-pair-chip .cp-cell { display: inline-flex; align-items: baseline; }
.clock-pair-chip .cp-line { display: inline-flex; align-items: baseline; gap: 0.3rem; }
.clock-pair-chip .cp-label {
  flex: none;
  font-size: 0.85rem;
  font-weight: 600;
  color: inherit;
}
.clock-pair-chip .cp-time {
  display: inline-block;
  width: 4.5rem; /* W64 section 14: 5.1 -> 4.5rem, see the W64 block */
  color: inherit;
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-align: right;
}
.clock-pair-chip .cp-offset {
  display: inline-block;
  width: 2.75rem;
  font-size: 0.78rem;
  color: inherit;
  overflow: hidden;
}

/* W57 sections 1-2 as amended by W61 sections 17-19 (supersedes the
   W60 abbreviation + parenthesized-offset card): the card cells carry
   place, an independent DATE per zone, and the time row - the clock
   and its bare dynamic UTC offset as ONE cohesive baseline-aligned
   run: "9:59:27 PM UTC−4", never a detached annotation. The offset
   hugs the time with the component's one 0.45rem gap; both values are
   server-rendered and re-rendered identically each tick, so nothing
   shifts at load. */
.clock-pair-card { display: flex; flex-wrap: wrap; gap: 1.4rem 3rem; }
.clock-pair-card .cp-cell { min-width: 15rem; }
.clock-pair-card .cp-label {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.68rem;
  color: var(--gold);
}
.clock-pair-card .cp-place { display: block; margin-top: 0.25rem; color: var(--parchment-dim); font-size: 0.9rem; }
.clock-pair-card .cp-date { display: block; margin-top: 0.3rem; color: var(--parchment); font-size: 1rem; }
.clock-pair-card .cp-line {
  display: inline-flex;
  align-items: baseline;
  gap: 0.45rem;
  margin-top: 0.1rem;
  color: var(--parchment);
  font-size: 1.15rem;
}
.clock-pair-card .cp-time {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.clock-pair-card .cp-offset { color: var(--parchment-dim); font-size: 1rem; white-space: nowrap; }
/* W61 section 17: the ENTIRE server block - label, place, date, time,
   offset - renders in the established header server green (the ONE
   --signal-online token; no new green), while the local block keeps
   its normal/default colors. Site-wide rule: server date/time
   information = server green, local = default.
   W77 section 1 SUPERSEDES the color: the server block renders in the
   ONE --time-server sky blue; the local block in --time-local. */
.clock-pair-card [data-clock="server"] .cp-label,
.clock-pair-card [data-clock="server"] .cp-place,
.clock-pair-card [data-clock="server"] .cp-date,
.clock-pair-card [data-clock="server"] .cp-line,
.clock-pair-card [data-clock="server"] .cp-offset { color: var(--time-server); }
.clock-pair-card [data-clock="local"] .cp-date,
.clock-pair-card [data-clock="local"] .cp-line { color: var(--time-local); }

/* W57 section 4: the timezone selector block keeps DETECT MY TIMEZONE
   and SAVE TIMEZONE but tightens the vertical rhythm - the card was
   using more height than the content needs. W59 section 6: the block
   is a FULL-WIDTH subsection under both clock columns - a ruled
   divider spanning the card and the YOUR TIMEZONE eyebrow in the
   card-label voice, so the controls belong to the whole card, never
   just the server column. */
.tz-form.block-gap-top { margin-top: 1rem; }
.tz-form .form-row { margin-bottom: 0.7rem; }
.tz-form .help-msg { margin-top: 0.25rem; }
.tz-subsection {
  border-top: 1px solid var(--edge);
  padding-top: 1rem;
}
.tz-subsection .form-row > label {
  display: inline-block;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.68rem;
  color: var(--gold);
  margin-bottom: 0.35rem;
}

/* Section 53 as restyled by W74 section 3 (docs/specs/W74-CONSOLIDATED-
   UPDATES.md): the persistent floating Join Our Discord CTA - fixed to
   the bottom-right corner, stationary while scrolling, the Discord
   logo and the label on a blue-to-purple gradient (Discord's blurple
   into the site's arcane violet), rounded corners, a subtle violet
   glow that reads on the dark ground. Hover lifts the glow one step;
   prefers-reduced-motion drops every transition and keeps the resting
   glow. The label stays at every width (a tighter pill below 480px).
   Sits above the footer's bottom padding and never over a primary
   form control (the pages' actions sit left or centered). */
.discord-fab {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  z-index: 60;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  background: linear-gradient(135deg, #4f6cf5 0%, #6a5cf0 55%, #8a4dff 100%);
  color: #ffffff;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 14px;
  padding: 0.7rem 1.15rem;
  font-family: var(--font-display);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  box-shadow: 0 0 0 1px rgba(122, 92, 255, 0.25), 0 6px 18px rgba(0, 0, 0, 0.5), 0 0 22px rgba(122, 92, 255, 0.35);
  transition: transform 0.15s ease, box-shadow 0.2s ease;
}
.discord-fab:hover, .discord-fab:focus-visible {
  color: #ffffff;
  text-decoration: none;
  transform: translateY(-2px);
  box-shadow: 0 0 0 1px rgba(122, 92, 255, 0.4), 0 8px 22px rgba(0, 0, 0, 0.55), 0 0 30px rgba(122, 92, 255, 0.55);
}
.discord-fab:focus-visible { outline: 2px solid #ffffff; outline-offset: 2px; }
.discord-fab .social-icon { width: 1.25rem; height: 1.25rem; vertical-align: 0; flex: 0 0 auto; }
@media (max-width: 479px) {
  .discord-fab { right: 0.8rem; bottom: 0.8rem; padding: 0.55rem 0.85rem; font-size: 0.72rem; gap: 0.4rem; }
}
@media (prefers-reduced-motion: reduce) {
  .discord-fab, .discord-fab:hover, .discord-fab:focus-visible { transition: none; transform: none; }
}

/* W59 section 61 as amended by W60 section 37: the ONE panel-scoped
   contrast block (the earlier W54 duplicate collapsed into it, W60
   section 38). The dim/faint parchment steps lift one more modest
   shade so secondary and disabled text reads comfortably at normal
   viewing distance while staying clearly below body ink - the
   hierarchy survives (gold headings > primary #ece3d0 > secondary >
   helper > disabled), nothing becomes bright white, and disabled
   controls keep their dimmer tier so they never look enabled. */
.layout-panel {
  --parchment-dim: #c5bba6;
  --parchment-faint: #99907e;
}

/* W60 section 38: the ONE class for panel intro/helper paragraphs.
   Every panel page used to carry the same inline
   style="color: var(--parchment-dim);" - a page-by-page hack for a
   shared-layout concern. The class replaces every panel-surface
   occurrence; public pages keep their own treatments. */
.panel-intro { color: var(--parchment-dim); }


/* W55 section 39: the class controls read as SEARCH fields - the
   magnifier mark inside the input, search styling, a strong focus
   state; selected classes become removable chips, and the grouped
   browsing waits behind Browse Classes (a native details element). */
.class-search { position: relative; margin-bottom: 0.7rem; max-width: 22rem; }
.class-search .cs-icon {
  position: absolute;
  left: 0.6rem;
  top: 50%;
  transform: translateY(-50%);
  width: 1rem;
  height: 1rem;
  color: var(--parchment-faint);
  pointer-events: none;
}
.class-search input {
  max-width: 100%;
  width: 100%;
  padding-left: 2.1rem;
  border-radius: 999px;
}
.class-search input:focus {
  outline: none;
  border-color: var(--arcane);
  box-shadow: 0 0 0 2px var(--arcane-deep);
}
.class-selected { margin-bottom: 0.7rem; }
.class-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  border: 1px solid var(--arcane);
  border-radius: 999px;
  background: var(--arcane-deep);
  color: var(--parchment);
  font: inherit;
  font-size: 0.8rem;
  padding: 0.25rem 0.7rem;
  cursor: pointer;
}
.class-chip:hover { border-color: var(--arcane-bright); }
.class-chip .cc-x { color: var(--parchment-dim); font-size: 0.9em; }
.class-chip:hover .cc-x { color: var(--parchment); }
.class-browse > summary {
  cursor: pointer;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.72rem;
  color: var(--gold);
  margin-bottom: 0.5rem;
}
.class-browse[open] > summary { margin-bottom: 0.7rem; }

/* W55 section 41: a quick-day preset reads pressed exactly while the
   individual selection equals its set. */
.play-quick .btn.preset-on {
  border-color: var(--arcane);
  background: var(--arcane-deep);
  color: var(--parchment);
}

/* W55 section 42 as reshaped by W56 section 4 (Decision 6): the
   dial-based playtime picker. The three real selects stay the
   submitted controls and the no-JS baseline; with scripting they hide
   (.tpk-native) behind ONE closed field per endpoint that opens the
   three-column dial (hour 01-12 / minutes 00-15-30-45 / AM-PM). */
.play-time-row select.tp-part {
  background: var(--ink-panel);
  border: 1px solid var(--edge);
  border-radius: 3px;
  color: var(--parchment);
  padding: 0.45rem 0.4rem;
  font: inherit;
}
.play-time-row select.tp-hour,
.play-time-row select.tp-minute { min-width: 3.6rem; }
.play-time-row select.tp-period { min-width: 3.9rem; }
.tp-field { display: inline-flex; align-items: center; gap: 0.45rem; position: relative; }
.tp-field .tp-cap { color: var(--parchment-dim); font-size: 0.85rem; }
/* W58 section 18: the Playtime range reads as ONE control - a single
   bordered field carrying both dial endpoints joined by the en-dash;
   the inner buttons drop their own chrome so nothing inside looks like
   a second unrelated field. The dial behavior is untouched. */
.time-range {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  border: 1px solid var(--edge);
  border-radius: 3px;
  background: var(--ink-panel);
  padding: 0.15rem 0.6rem;
  flex-wrap: nowrap;
}
.time-range .tr-sep { color: var(--parchment-dim); }
/* W58 section 18: the recruitment listing forms ride a slightly
   tighter vertical rhythm - less stretch without feeling cramped. */
form[data-rc-form] .form-row { margin-bottom: 0.8rem; }
form[data-rc-form] .help-msg { margin-top: 0.2rem; }
.js .time-range .tpk-btn { border: none; background: transparent; padding: 0.35rem 0.15rem; }
.time-range select.tp-part { border: none; background: transparent; }
@media (max-width: 520px) {
  /* The joined control wraps as a whole on very narrow screens. */
  .time-range { flex-wrap: wrap; }
}
/* Zero-layout-shift swap: the closed field is server-rendered and the
   base.html synchronous .js flag decides the presentation from the
   FIRST frame - scripting on shows the button and visually-hides the
   selects before anything paints; scripting off never adds the class,
   so the native selects stay the whole interface. panel-ux only wires
   behavior. */
.tp-field .tpk-btn { display: none; }
.js .tp-field select.tp-part {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
}
.js .tp-field .tpk-btn { display: inline-flex; }
.tpk-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--ink-panel);
  border: 1px solid var(--edge);
  border-radius: 3px;
  color: var(--parchment);
  padding: 0.45rem 0.65rem;
  font: inherit;
  cursor: pointer;
}
.tpk-btn:focus-visible { outline: 2px solid var(--arcane-bright); outline-offset: 1px; }
/* FIXED width: the value swaps between the placeholder and "07:30 PM"
   without the button ever changing size (the zero-CLS wall). */
.tpk-btn .tpk-value { font-variant-numeric: tabular-nums; display: inline-block; width: 5.2rem; overflow: hidden; text-align: left; }
.tpk-btn .tpk-placeholder { color: var(--parchment-faint); }
.tpk-btn .tpk-caret { color: var(--parchment); font-size: 1.05em; }
.tpk-pop {
  position: absolute;
  top: calc(100% + 0.3rem);
  left: 0;
  z-index: 40;
  background: var(--ink-panel);
  border: 1px solid var(--edge-bright);
  border-radius: 6px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5);
  padding: 0.5rem;
}
.tpk-cols { display: flex; gap: 0.35rem; }
.tpk-col {
  max-height: 10.5rem;
  overflow-y: auto;
  min-width: 3.4rem;
  border: 1px solid var(--edge);
  border-radius: 4px;
  scrollbar-width: thin;
}
.tpk-col:focus-visible { outline: 2px solid var(--arcane-bright); outline-offset: 1px; }
.tpk-option {
  padding: 0.35rem 0.7rem;
  text-align: center;
  color: var(--parchment-dim);
  cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.tpk-option:hover { background: var(--arcane-deep); color: var(--parchment); }
.tpk-option[aria-selected="true"] { background: var(--arcane-deep); color: var(--gold-bright); }
.tpk-clear {
  margin-top: 0.45rem;
  width: 100%;
  background: none;
  border: 1px solid var(--edge);
  border-radius: 4px;
  color: var(--parchment-dim);
  font: inherit;
  font-size: 0.8rem;
  padding: 0.3rem 0.6rem;
  cursor: pointer;
}
.tpk-clear:hover { color: var(--parchment); border-color: var(--edge-bright); }

/* W55 section 44: the contact input with the connected-Discord
   shortcut beside it. W56 section 5: the row spans the form column so
   Contact no longer reads disproportionately small beside the other
   controls. */
.contact-row { display: flex; align-items: center; gap: 0.5rem; max-width: 100%; }
.contact-row input { flex: 1 1 auto; min-width: 0; width: 100%; }
.contact-row .contact-discord { padding: 0.45rem 0.6rem; }
.contact-row .contact-discord .social-icon { color: #5865f2; vertical-align: -0.15em; }

/* W55 section 45: the themed banner uploader - click opens the picker
   through the label, drag/drop highlights the zone. */
.upload-zone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  border: 1px dashed var(--edge-bright);
  border-radius: 6px;
  background: var(--ink-raised);
  padding: 1.4rem 1.6rem;
  max-width: 30rem;
  text-align: center;
  cursor: pointer;
  color: var(--parchment-dim);
  font-size: inherit;
}
.upload-zone:hover, .upload-zone.uz-over { border-color: var(--arcane); color: var(--parchment); }
.upload-zone .uz-icon { width: 1.6rem; height: 1.6rem; color: var(--gold); }
.upload-zone .uz-title { color: var(--parchment); }
.upload-zone .uz-hint { font-size: 0.75rem; color: var(--parchment-faint); }


/* W55 sections 10-21: the Dashboard ACCOUNT ACTIVITY card. One quiet
   section per rule; the section-11 escalation tiers tint the section
   edge progressively (color plus the explicit warning line - never
   color alone); the progress bar is the one graphic element. */
/* W60 section 23: while the policy has not launched the card is a
   COMPACT informational component - the status-state inside it drops
   the roomier default padding so the card never reads as an oversized
   empty panel. The active-policy sections keep their W55 geometry. */
.activity-card .status-state { padding: 0.7rem 0.9rem; }
.activity-card .ac-subhead {
  margin: 1.4rem 0 0.8rem;
  font-size: 1.05rem;
}
.ac-section {
  border-left: 3px solid var(--edge-bright);
  padding: 0.9rem 1.1rem;
  background: var(--ink-raised);
  border-radius: 6px;
}
.ac-section + .ac-section { margin-top: 1rem; }
.ac-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem 2.4rem;
  margin-bottom: 0.8rem;
}
.ac-cell { min-width: 10rem; }
.ac-label {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.66rem;
  color: var(--gold);
}
.ac-value { display: block; margin-top: 0.2rem; color: var(--parchment); }
.ac-status-met { color: var(--signal-online); }
.ac-status-warn { color: var(--error-soft); }
.ac-bar {
  height: 0.5rem;
  border-radius: 999px;
  background: var(--ink-panel);
  border: 1px solid var(--edge);
  overflow: hidden;
}
.ac-bar-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--arcane-deep), var(--arcane-bright));
}
.ac-warning-line { color: var(--parchment); margin: 0.7rem 0 0.2rem; }
/* Escalation tiers (section 11): 14d subtle, 7d warning, 72h
   prominent, 24h critical. */
.ac-info { border-left-color: var(--edge-bright); }
.ac-subtle { border-left-color: var(--gold); }
.ac-warning { border-left-color: #b8860b; }
.ac-prominent { border-left-color: #c05621; }
.ac-critical { border-left-color: #c53030; background: rgba(197, 48, 48, 0.07); }
.ac-critical .ac-warning-line, .ac-prominent .ac-warning-line { color: var(--error-soft); }
.ac-reset-notice ul.ac-reset-list { margin: 0.4rem 0 0.4rem 1.2rem; }


/* W55 sections 46.2-46.3: the storefront checkout. The largest pack
   carries the subtle BEST VALUE mark; payment methods are real radio
   pills; the checkout summary is the ONE commitment point - character,
   package, method, and the highly visible total with the single BUY
   action - sticky near the viewport bottom on desktop so the total
   stays in sight while browsing packages. */
.package-card .pkg-best {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--gold-bright);
  border: 1px solid var(--gold);
  border-radius: 999px;
  padding: 0.1rem 0.55rem;
  margin-top: 0.2rem;
}
.method-list { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.method-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  border: 1px solid var(--edge-bright);
  border-radius: 999px;
  padding: 0.4rem 0.9rem;
  cursor: pointer;
  color: var(--parchment);
  font-size: 0.85rem;
}
.method-pill:has(input:checked) {
  border-color: var(--arcane);
  background: var(--arcane-deep);
}
.method-pill input[type=radio] { accent-color: var(--arcane-bright); }
/* W56 section 10 (Decision 5): the not-yet-connected method pill -
   readable, clearly inert, honestly labeled. */
.method-pill.method-soon {
  cursor: default;
  border-style: dashed;
  border-color: var(--edge);
  color: var(--parchment-dim);
}
.method-pill .mp-detail { font-size: 0.72rem; color: var(--parchment-faint); }
/* W57 section 23 (Decision 4, supersedes the W55/W56 bottom-sticky
   overlay): the Checkout Summary must NEVER cover a package card. On
   the large desktop the store form splits into two columns - packages
   and selectors left, the summary a TOP-sticky sibling on the right;
   below that width the summary simply stacks after the selectors in
   normal flow (no overlay, nothing covered at any width). */
.store-summary {
  margin-top: 1.1rem;
  border: 1px solid var(--edge-bright);
  border-radius: 6px;
  background: var(--ink-raised);
  padding: 1rem 1.2rem 1.1rem;
  max-width: 30rem;
}
@media (min-width: 1400px) {
  .store-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 17rem;
    gap: 0 1.4rem;
    align-items: start;
  }
  .store-layout .store-main { min-width: 0; }
  /* The 17rem grid column is narrower than the stacked 30rem cap, so
     no max-width override is needed (the no-max-width:none rule). */
  .store-layout .store-summary {
    position: sticky;
    top: 1rem;
    margin-top: 0;
  }
}
.store-summary .ss-head {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.7rem;
  color: var(--gold);
  margin-bottom: 0.6rem;
}
.store-summary .ss-row {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.18rem 0;
}
.store-summary .ss-label {
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.68rem;
  color: var(--parchment-dim);
  align-self: center;
}
.store-summary .ss-value { color: var(--parchment); text-align: right; }
.store-summary .ss-total { border-top: 1px solid var(--edge); margin-top: 0.35rem; padding-top: 0.5rem; }
.store-summary .ss-total .ss-value { font-size: 1.15rem; font-weight: 600; color: var(--gold-bright); }
.store-summary .ss-policy { font-size: 0.78rem; color: var(--parchment-dim); margin: 0.6rem 0; }
.store-summary .ss-buy { width: 100%; justify-content: center; display: inline-flex; }


/* W55 section 51: support tickets - the ticket header pair, the
   conversation thread (player right-accented, staff gold-accented),
   and the attachment link. */
.ticket-head { display: flex; align-items: baseline; gap: 0.9rem; flex-wrap: wrap; }
.ticket-head .th-number {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.15rem;
  color: var(--gold-bright);
}
.ticket-head .th-category {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--parchment-dim);
  border: 1px solid var(--edge);
  border-radius: 999px;
  padding: 0.15rem 0.6rem;
}
.th-subject { color: var(--parchment); font-size: 1.05rem; margin: 0.5rem 0 0.2rem; }
.ticket-thread { margin: 1.1rem 0; display: flex; flex-direction: column; gap: 0.7rem; }
.ticket-msg {
  border: 1px solid var(--edge);
  border-left-width: 3px;
  border-radius: 6px;
  background: var(--ink-raised);
  padding: 0.7rem 1rem;
  max-width: 44rem;
}
.ticket-msg.tm-player { border-left-color: var(--arcane); }
.ticket-msg.tm-staff { border-left-color: var(--gold); }
.ticket-msg .tm-meta {
  display: block;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--parchment-dim);
  margin-bottom: 0.3rem;
}
.ticket-msg .tm-body { margin: 0; color: var(--parchment); }
.ticket-msg .tm-attachment { display: inline-block; margin-top: 0.4rem; font-size: 0.82rem; }


/* W55 sections 27 + 54: the Connected Accounts provider rows - the
   recognizable brand color on each mark, the L2 Arcanis frame
   otherwise (colors aid recognition without dominating the page). */
.provider-list { display: flex; flex-direction: column; gap: 0.6rem; max-width: 42.5rem; }
.provider-row {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  border: 1px solid var(--edge);
  border-radius: 6px;
  background: var(--ink-raised);
  padding: 0.75rem 1rem;
}
.provider-row .pr-icon { width: 1.45rem; height: 1.45rem; flex: none; }
.provider-row .pr-name { color: var(--parchment); min-width: 6.5rem; }
.provider-row .pr-status { flex: 1 1 auto; font-size: 0.85rem; color: var(--parchment-dim); }
.provider-row .pr-connected { color: var(--signal-online); }
/* W59 section 20: the one-line provider purpose under the status. */
.provider-row .pr-purpose { display: block; margin-top: 0.15rem; font-size: 0.76rem; color: var(--parchment-faint); }
.provider-row .pr-actions { flex: none; }
/* W59 section 45: at phone widths the row wraps - the status line
   (whose Available at Launch chip is a nowrap unit) takes its own
   full-width line under icon+name, so the row can never force the
   page sideways. */
@media (max-width: 640px) {
  .provider-row { flex-wrap: wrap; gap: 0.4rem 0.9rem; }
  .provider-row .pr-status { flex-basis: 100%; }
}
/* W56 section 2: platform marks carry their OFFICIAL brand treatment
   site-wide - only the icon takes the brand color; surrounding text,
   borders, and cards stay on the Arcanis system. Multicolor marks keep
   their meaningful multicolor appearance: Instagram strokes the sprite
   gradient (ig-grad, defined in the icon sprite; the solid color is
   the fallback for engines without url() paint), and TikTok layers its
   official cyan/red offsets under the light glyph. */
.brand-discord { color: #5865f2; }
.brand-youtube { color: #ff0000; }
.brand-tiktok {
  color: #eef4f8;
  filter: drop-shadow(0.7px 0.7px 0 #fe2c55) drop-shadow(-0.7px -0.7px 0 #25f4ee);
}
.brand-instagram { color: #e1306c; stroke: url(#ig-grad) #e1306c; }
.brand-facebook { color: #1877f2; }
.brand-phone { color: var(--gold-bright); }
.brand-twitch { color: #9146ff; }


/* W55 section 33: the Social Rewards platform cards. Full-color
   platform marks (the brand-* tokens), a state pill per card, the
   selected card carries the same arcane accent as a selected package.
*/
.social-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.9rem; margin-bottom: 1rem; }
@media (max-width: 700px) { .social-grid { grid-template-columns: 1fr; } }
.social-card {
  border: 1px solid var(--edge);
  border-radius: 6px;
  background: var(--ink-raised);
  padding: 0.9rem 1.05rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.social-card.row-selected { border-color: var(--arcane); box-shadow: inset 0 0 0 1px var(--arcane); }
.social-card .sc-head { display: flex; align-items: center; gap: 0.5rem; }
.social-card .sc-icon { width: 1.25rem; height: 1.25rem; flex: none; }
.social-card .sc-platform {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--parchment-dim);
}
.social-card .sc-state {
  margin-left: auto;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  border: 1px solid var(--edge-bright);
  border-radius: 999px;
  padding: 0.12rem 0.55rem;
  color: var(--parchment-dim);
  white-space: nowrap;
}
.social-card.sc-verified .sc-state, .social-card.sc-claimed .sc-state { color: var(--signal-online); border-color: var(--signal-online); }
.social-card.sc-action_required .sc-state { color: var(--error-soft); border-color: var(--error-soft); }
.social-card.sc-verifying .sc-state { color: var(--gold-bright); border-color: var(--gold); }
.social-card .sc-title { color: var(--parchment); font-weight: 600; }
.social-card .sc-req { margin: 0; font-size: 0.85rem; color: var(--parchment-dim); }
.social-card .sc-note { margin: 0; font-size: 0.78rem; color: var(--parchment-faint); }
.social-card .sc-meta { display: flex; justify-content: space-between; gap: 0.6rem; font-size: 0.8rem; color: var(--parchment-dim); margin-top: 0.2rem; }
.social-card .sc-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.45rem; }


/* W55 section 31: the vote provider cards - a clearly separated
   2-column grid (1 column below 700px), consistent dimensions so no
   provider dominates, per-card state and cooldown timer; the claim
   area sits apart below with the progress figure. */
.vote-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.9rem; }
@media (max-width: 700px) { .vote-grid { grid-template-columns: 1fr; } }
.vote-card {
  border: 1px solid var(--edge);
  border-radius: 6px;
  background: var(--ink-raised);
  padding: 0.85rem 1rem;
  min-height: 7.2rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
/* W56 section 6: the plain name span retired - the typographic
   .vc-banner carries the site identity (Decision 2.3). */
.vote-card .vc-state {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--parchment-dim);
}
.vote-card.vc-registered { border-color: var(--signal-online); }
.vote-card.vc-registered .vc-state { color: var(--signal-online); }
.vote-card.vc-awaiting .vc-state { color: var(--gold-bright); }
.vote-card .vc-note { font-size: 0.78rem; color: var(--parchment-faint); }
.vote-card .vc-timer { font-variant-numeric: tabular-nums; color: var(--parchment); }
.vote-card .vc-action { margin-top: auto; padding-top: 0.4rem; }
.vote-claim {
  margin-top: 1.1rem;
  border-top: 1px solid var(--edge-soft);
  padding-top: 1rem;
}
.vote-claim .vc-progress { font-size: 1.25rem; font-weight: 600; color: var(--gold-bright); }
.vote-reminders { margin-top: 1.1rem; display: flex; flex-direction: column; gap: 0.3rem; }
.vote-reminders .vr-option { display: inline-flex; gap: 0.45rem; align-items: baseline; color: var(--parchment-dim); font-size: 0.85rem; }

/* ===== W56: User Panel feedback pass ===== */

/* Decision 2.1: the standalone FAQ page - native details accordions on
   the shared panel skin. */
/* W59 section 35 as sharpened by W60 section 36: the FAQ keeps its
   design with tightened rhythm - less dead vertical space in collapsed
   category cards, the WHOLE question row clickable with a visible
   hover state (background, not just ink), keyboard-focus feedback,
   and a larger expand/collapse chevron. */
.faq-stack .panel { padding: 1rem 1.3rem; }
.faq-item {
  border-top: 1px solid var(--edge-soft);
}
.faq-item:first-of-type { border-top: none; }
.faq-item summary {
  cursor: pointer;
  display: list-item;
  padding: 0.5rem 0.4rem;
  color: var(--parchment);
  list-style-position: inside;
  border-radius: 3px;
}
.faq-item summary::marker { font-size: 1.25em; color: var(--parchment-dim); }
.faq-item summary:hover {
  color: var(--arcane-bright);
  background: var(--ink-panel);
}
.faq-item summary:focus-visible {
  outline: 2px solid var(--arcane-bright);
  outline-offset: 2px;
  background: var(--ink-panel);
}
.faq-item[open] summary { color: var(--gold); }

/* W60 ratification (Decision 8): the FAQ search. Hidden until the
   synchronous .js flag - the flag lands before first paint, so
   revealing it never shifts the page, and without scripting the
   accordions stand alone. */
.faq-search { display: none; }
.js .faq-search {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: var(--block-gap);
}
.faq-search input[type="search"] { max-width: 20rem; }
.faq-search-none { font-size: 0.85rem; color: var(--parchment-dim); }
.faq-item p { margin: 0.15rem 0 0.7rem 1.35rem; color: var(--parchment-dim); }

/* Sections 5 + 43 (Decision 7): the restored-draft banner. */
/* W59 section 31: the compact draft-restored notice - one quiet line
   above the form (its own block, so form grid/card dimensions never
   change), no Discard button. */
.draft-note { margin-bottom: 0.9rem; font-size: 0.85rem; }

/* Section 25: the live email-match confirmation note. */
.field-msg.match-msg { color: var(--signal-online); }

/* Section 6 (Decision 5): the full Vote Rewards interface. The
   typographic site banner (Decision 2.3) stands in until real provider
   artwork arrives; the placeholder Coming Soon card state joins the
   real states; the claim area gains the progress bar. */
.vote-card .vc-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 3.4rem;
  border: 1px solid var(--edge);
  border-radius: 4px;
  background: var(--ink-panel);
  padding: 0.45rem 0.7rem;
  margin-bottom: 0.4rem;
  text-align: center;
}
.vote-card .vc-banner-name {
  font-family: var(--font-display);
  font-size: 1.02rem;
  letter-spacing: 0.06em;
  color: var(--gold);
}
.vote-card.vc-soon { border-style: dashed; }
.vote-card.vc-soon .vc-banner-name { color: var(--parchment-dim); }
.vote-claim .ac-bar { margin: 0.6rem 0 0.9rem; }
.vote-claim .form-row select[disabled] { color: var(--parchment-faint); cursor: not-allowed; }

/* ===== W57: User Panel UI/UX update ===== */

/* Sections 5-7 (Decision 2) as reworked by W58 sections 1-4 and W59
   sections 3 + 8-11: the Server Schedule entry component family -
   SERVER TIME first (left) with GREEN values (the site's server/live
   color), Local Time second, the countdown cell, the status pill; one
   component for every tab, extended with owner/wards/registration rows
   on the siege tabs, the registration range row, and the Epic/Grand
   respawn window (Earliest/Latest). */
.sched-list { display: grid; gap: 0.9rem; }
.sched-entry {
  border: 1px solid var(--edge);
  border-radius: 6px;
  background: var(--ink-raised);
  padding: 0.85rem 1rem;
}
.se-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.8rem; flex-wrap: wrap; }
.se-name { font-family: var(--font-display); font-size: 1.05rem; letter-spacing: 0.05em; color: var(--gold); }
.se-status {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--parchment-dim);
  white-space: nowrap;
}
.se-status.ses-in-progress { color: var(--signal-online); }
.se-status.ses-alive { color: var(--signal-online); }
.se-cells {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11.5rem, 1fr));
  gap: 0.7rem 1.4rem;
  margin-top: 0.55rem;
}
.se-cell { min-width: 0; }
.se-cap {
  display: block;
  font-size: 0.64rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--parchment-faint);
}
.se-date { display: block; margin-top: 0.2rem; color: var(--parchment-dim); font-size: 0.85rem; }
.se-time { display: block; color: var(--parchment); font-size: 1.05rem; }
/* W59 sections 9 + 46 as reoffset by W61 sections 17-18: the dynamic
   UTC offset rides inline right after the time (never an
   abbreviation), and every SERVER-time value - date, time, offset,
   ranges, window lines - is green so the association is immediate;
   local values never are. */
.se-offset { color: var(--parchment-dim); font-size: 0.8rem; }
/* W77 section 1: the server values take the ONE --time-server sky blue
   (the W59 / W61 green is superseded); the local values and the
   countdown read in --time-local (section 2). */
.se-server .se-date,
.se-server .se-time,
.se-server .se-offset,
.se-server .se-range-val,
.se-server .se-wval { color: var(--time-server); }
.se-local .se-date,
.se-local .se-time,
.se-local .se-range-val,
.se-local .se-wval { color: var(--time-local); }
/* W59 sections 10-11: the range row (registration) and the Epic/Grand
   respawn window lines share the cell grid. */
.se-range-val { display: block; margin-top: 0.2rem; font-size: 0.92rem; color: var(--parchment); }
.se-wline { display: block; margin-top: 0.2rem; font-size: 0.8rem; color: var(--parchment-faint); }
.se-wval { font-size: 0.92rem; color: var(--parchment); }
.se-detect { display: block; margin-top: 0.15rem; font-size: 0.7rem; color: var(--parchment-faint); }
.se-timer {
  display: block;
  margin-top: 0.2rem;
  font-variant-numeric: tabular-nums;
  font-size: 1.05rem;
  color: var(--time-local); /* W77 section 2: countdowns = the local color */
}
.se-foot { margin-top: 0.55rem; }
.se-meta { font-size: 0.76rem; color: var(--parchment-faint); }
.se-note { margin: 0.25rem 0 0; font-size: 0.8rem; color: var(--parchment-dim); }
/* W58 sections 3-4: the siege extension rows - owner line under the
   head, fact metas, and the wards / attackers / defenders rows with
   the compact expandable clan lists (cards never grow excessively
   tall when many clans register). */
.se-owner { margin-top: 0.3rem; font-size: 0.85rem; color: var(--parchment-dim); }
.se-owner .value { color: var(--parchment); }
.se-facts { margin-top: 0.45rem; display: flex; flex-wrap: wrap; gap: 0.2rem 1.2rem; }
.se-siege-rows {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11.5rem, 1fr));
  gap: 0.55rem 1.4rem;
  margin-top: 0.55rem;
}
.se-siege-row { min-width: 0; }
.se-siege-val { display: block; margin-top: 0.2rem; font-size: 0.85rem; color: var(--parchment-dim); overflow-wrap: anywhere; }
.se-clans summary {
  cursor: pointer;
  color: var(--arcane-bright);
  font-size: 0.8rem;
}
.se-clans[open] summary { margin-bottom: 0.2rem; }

/* Section 10 (Decision 5) as recolumned by W59 section 28: the merged
   Login History table at SIX readable columns (MAC/LAN fold into the
   per-row Technical details expander), fitting the panel column at
   desktop AND laptop widths (the W56 section 42 no-sideways-scroll
   rule) by compact density, wrapping headers, and identifier cells
   that break anywhere. The timestamp renders deliberately as date
   over time+marker - two controlled lines, never a triple wrap. */
.table-history { --cell-pad-x: 0.5rem; font-size: 0.8rem; }
.table-history th { white-space: normal; }
.table-history td.lh-wrap {
  overflow-wrap: anywhere;
  word-break: break-all;
  font-variant-numeric: tabular-nums;
}
.lh-when .lh-date { display: block; white-space: nowrap; }
.lh-when .lh-time { display: block; white-space: nowrap; color: var(--parchment-dim); }
.lh-tech { margin-top: 0.15rem; }
.lh-tech summary { cursor: pointer; color: var(--arcane-bright); font-size: 0.72rem; }
.lh-tech-line { display: block; font-size: 0.75rem; color: var(--parchment-dim); overflow-wrap: anywhere; }
/* W60 section 27: the intentional unavailable state for rows without
   technical values - same slot, same size, visibly non-interactive. */
.lh-tech-none { display: block; font-size: 0.72rem; cursor: default; }
/* W60 section 29: the subdued provider-attribution line at the page
   foot - technical-area typography, never part of the main flow. */
.tech-attribution { margin-top: 0.5rem; font-size: 0.7rem; color: var(--parchment-faint); }
.tech-attribution a { color: var(--parchment-faint); }

/* Sections 11 + 48: pre-launch provider rows dim as a set; the ONE
   section-level message carries the wording, so no per-row caption. */
.provider-row.pr-soon { opacity: 0.75; }

/* W60 section 31: the Account Recovery overview's Configured state -
   the same positive green the status system uses. */
.rec-ok { color: var(--signal-online); }

/* W59 sections 16-19 as consolidated by W61 sections 1-14: the Alerts
   & Connections preference rows - the compact grouped list of the 14
   primary preferences, the tier badge, the live Website toggle, the
   locked ALWAYS ON marker for mandatory alerts, the inert
   advance-warning select beside launch-gated server-event rows, and
   the functional launch-gated masters. */
.alert-list { display: grid; gap: 0.45rem; margin-top: 0.6rem; }
/* W93: inside an open category the row's border and radius are
   SUPERSEDED by the W93 block (a subtle shared ground and the list gap
   organise the rows; the category head is the one boundary). */
.alert-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 0.5rem 0.75rem;
}
.alert-row.al-soon { opacity: 0.75; }
.al-main { min-width: 0; }
.al-name { color: var(--parchment); font-size: 0.92rem; margin-right: 0.45rem; }
/* W60 section 7: the tier labels step up one legibility notch -
   slightly larger and one contrast tier brighter - while staying
   clearly secondary. REQUIRED is the most distinct (gold): the player
   cannot disable those alerts. */
.al-tier {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--parchment-dim);
}
.al-tier-required { color: var(--gold); }
.al-desc { display: block; margin-top: 0.1rem; font-size: 0.78rem; color: var(--parchment-faint); }
.al-ctl { flex: none; display: inline-flex; align-items: center; gap: 0.5rem; }
.al-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.8rem;
  color: var(--parchment-dim);
  cursor: pointer;
}
.al-live,
.al-locked {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--signal-online);
}
.al-warn { font-size: 0.75rem; }
.al-warn[disabled] { color: var(--parchment-faint); cursor: not-allowed; }
/* W60 section 9: the lead-time control reads as one labeled unit -
   "Alert me: [15 minutes before]". */
.al-warnlab { display: inline-flex; align-items: center; gap: 0.35rem; }
.al-warnlead { font-size: 0.75rem; color: var(--parchment-dim); white-space: nowrap; }
.al-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; }
/* W60 section 8: while changes are pending the actions bar sticks to
   the viewport bottom on its own raised ground, so the pending state
   and the save action stay visible no matter which categories are
   open. The indicator is amber-adjacent gold - visible, not alarming. */
.al-actions.is-dirty {
  position: sticky;
  bottom: 0.75rem;
  z-index: 5;
  background: var(--ink-raised);
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 0.6rem 0.75rem;
}
.al-dirty {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--gold);
}
.al-required-note { margin-top: 0.6rem; font-size: 0.82rem; }
/* W60 section 6: collapsible alert categories. The whole header is
   the click target (native details/summary - works without JS); the
   chevron is the disclosure signal and rotates open; hover and
   keyboard focus give clear feedback. */
.alert-group { margin-top: 0.9rem; }
.alert-group-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  cursor: pointer;
  list-style: none;
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 0.5rem 0.75rem;
}
.alert-group-head::-webkit-details-marker { display: none; }
.alert-group-head h2 { margin: 0; font-size: 1.02rem; flex: 1 1 auto; }
.alert-group-head:hover { background: var(--ink-raised); border-color: var(--gold-dim, var(--gold)); }
.alert-group-head:focus-visible { outline: 2px solid var(--arcane-bright); outline-offset: 2px; }
.ag-count {
  font-size: 0.68rem;
  color: var(--parchment-faint);
  border: 1px solid var(--edge);
  border-radius: 999px;
  padding: 0.05rem 0.5rem;
}
.ag-chevron {
  flex: none;
  width: 0.55rem;
  height: 0.55rem;
  border-right: 2px solid var(--parchment-dim);
  border-bottom: 2px solid var(--parchment-dim);
  transform: rotate(45deg);
  transition: transform 0.15s ease;
  margin-top: -0.2rem;
}
.alert-group[open] > .alert-group-head .ag-chevron { transform: rotate(225deg); margin-top: 0.2rem; }
/* W61 section 7: the Boss & Grand Boss Respawn Alerts selection area.
   The master label carries the row-name voice; the Grand Boss line is
   the always-included statement in the positive green; the regular-
   boss list is a bounded scrollable checkbox grid that stays fully
   functional without scripting (the search / Select All / Clear tools
   are the scripting convenience, revealed by the synchronous .js flag
   with zero layout shift). */
.al-master { font-size: 0.92rem; color: var(--parchment); }
.boss-alert-area {
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 0.6rem 0.75rem;
  margin-top: 0.1rem;
}
.ba-grand {
  margin: 0;
  font-size: 0.82rem;
  color: var(--signal-online);
}
.ba-help { margin: 0.35rem 0 0; font-size: 0.75rem; color: var(--parchment-faint); }
.ba-tools { display: none; }
.js .ba-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.55rem;
}
.ba-tools input[type="search"] { max-width: 15rem; }
.ba-none { font-size: 0.78rem; color: var(--parchment-dim); }
.ba-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0.15rem 0.9rem;
  max-height: 16rem;
  overflow-y: auto;
  margin-top: 0.55rem;
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 0.45rem 0.6rem;
}
.ba-boss {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: 0.8rem;
  color: var(--parchment-dim);
  cursor: pointer;
  min-width: 0;
}
.ba-boss input { flex: none; align-self: center; }
.ba-boss-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ba-lv { flex: none; font-size: 0.7rem; color: var(--parchment-faint); }
@media (max-width: 640px) {
  .alert-row { flex-direction: column; align-items: flex-start; gap: 0.4rem; }
}

/* Section 15 (Decision 3): the vote cards compact - the two-column
   grid stays; banner and paddings tighten so eight cards need less
   scrolling. */
.vote-card { min-height: 0; padding: 0.7rem 0.85rem; gap: 0.25rem; }
.vote-card .vc-banner { min-height: 2.6rem; margin-bottom: 0.3rem; }
.vc-cooldown { display: block; }
.vc-cooldown .vc-next { display: block; }

/* Section 19: the eight-segment VOTES COMPLETED indicator - one
   segment per required site, filled left to right. */
.vp-segments { display: flex; gap: 0.35rem; margin: 0.6rem 0 0.9rem; }
.vp-seg {
  flex: 1 1 0;
  height: 0.55rem;
  border: 1px solid var(--edge);
  border-radius: 3px;
  background: var(--ink-panel);
}
.vp-seg.vp-on { background: var(--gold); border-color: var(--gold); }

/* Section 21: the separated VOTE REMINDERS subsection; unavailable
   options explain their prerequisite. */
.vote-reminders .vr-req { font-size: 0.72rem; color: var(--parchment-faint); }

/* Section 22 (Decision 4): the persistent SELECTED indicator - a
   check line every card reserves (visibility keeps geometry stable),
   lit only on the selected card so selection can never be confused
   with hover. */
.package-card .pkg-check {
  visibility: hidden;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--arcane-bright);
}
.package-card.pkg-selected .pkg-check { visibility: visible; }

/* Sections 27-28 (Decision 6): the search results strip beneath the
   class search field (toggle chips over the same checkbox state) and
   the nested race accordion inside Browse Classes. */
.class-results {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-bottom: 0.7rem;
}
.class-result {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--edge-bright);
  border-radius: 999px;
  background: var(--ink-panel);
  color: var(--parchment);
  padding: 0.25rem 0.7rem;
  font-size: 0.82rem;
  cursor: pointer;
}
.class-result:hover { border-color: var(--arcane); }
.class-result.cr-on {
  border-color: var(--arcane);
  background: var(--arcane-deep);
}
.class-results .cr-none { color: var(--parchment-faint); font-size: 0.82rem; }
.race-group { border: none; padding: 0; }
.race-group > summary.cg-title {
  cursor: pointer;
  list-style: none;
  margin-bottom: 0.35rem;
}
.race-group > summary.cg-title::before {
  content: "\25B8";
  display: inline-block;
  margin-right: 0.4rem;
  color: var(--parchment-dim);
  transition: transform 0.15s ease;
}
.race-group[open] > summary.cg-title::before { transform: rotate(90deg); }
.race-group > .chip-grid { margin-bottom: 0.5rem; }

/* Section 36: the unread staff-response indicator on Your Tickets. */
.ticket-unread {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  margin-left: 0.4rem;
  font-size: 0.72rem;
  color: var(--signal-online);
  white-space: nowrap;
}

/* Section 40: the Character PIN guidance list. */
.pin-guide { margin: 0.5rem 0 0; padding-left: 1.2rem; color: var(--parchment-dim); }
.pin-guide li { margin-bottom: 0.4rem; }

/* Section 42: the required-field marker (opt-in per form via
   mark_required) and the stronger selected-tag state. */
.req-star { color: var(--gold-bright); }
.req-legend { font-size: 0.78rem; color: var(--parchment-faint); margin: 0 0 0.8rem; }
.chip-grid label:has(input:checked) {
  font-weight: 600;
  box-shadow: inset 0 0 0 1px var(--arcane);
}
.chip-grid label:has(input:checked)::before {
  content: "\2713";
  font-weight: 400;
  color: var(--arcane-bright);
}

/* W62 Decision 1: the Live Event Data area on the Automated Events tab
   - the real telemetry feed (custom_event_live), replacing the W58/W60
   AVAILABLE AT LAUNCH state once the server publishes. Server-rendered
   first paint; event-live.js swaps the same fragment in place, so the
   shell owns the spacing and nothing shifts on a poll. Live values
   share the server green (the W59 section 9 association). */
.live-event {
  margin-top: 0.9rem;
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 0.6rem 0.75rem;
}
.lv-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.2rem 1rem; }
.lv-eyebrow {
  font-size: 0.64rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--parchment-faint);
}
.lv-updated { font-size: 0.7rem; color: var(--parchment-faint); }
.lv-summary { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2rem 1.2rem; margin-top: 0.35rem; }
.lv-name { color: var(--parchment); font-size: 1.02rem; }
.lv-phase {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--parchment-dim);
}
.lv-phase.lvp-fight,
.lv-phase.lvp-registration { color: var(--signal-online); }
.lv-count { font-size: 0.82rem; color: var(--parchment-dim); }
.lv-num { color: var(--signal-online); font-variant-numeric: tabular-nums; }
.lv-slot { margin: 0.3rem 0 0; font-size: 0.78rem; color: var(--parchment-faint); }
.lv-teams { display: flex; flex-wrap: wrap; gap: 0.45rem 1.4rem; margin-top: 0.5rem; }
.lv-team { display: flex; align-items: baseline; gap: 0.6rem; min-width: 0; }
.lv-team-name { color: var(--parchment); font-size: 0.88rem; }
.lv-team-fact { font-size: 0.78rem; color: var(--parchment-dim); }
.lv-table { font-size: 0.8rem; }
.live-event .table-wrap { margin-top: 0.5rem; }

/* ------------------------------------------------------------------ */
/* W63 section 1: header clock typography + spacing. Online, Server,   */
/* the clock, AM/PM and the UTC offset share --font-clock at the chip  */
/* size and weight; the time slot keeps its fixed width (zero CLS) but */
/* is LEFT-aligned so the gap between the final "r" of Server and the  */
/* first digit is the same word-space the clock puts between the last  */
/* digit and AM/PM - the display reads as one cohesive element.        */
.status-chip,
.status-chip [data-status-label],
.clock-pair .cp-label,
.clock-pair .cp-place,
.clock-pair .cp-date,
.clock-pair .cp-time,
.clock-pair .cp-offset { font-family: var(--font-clock); }
.clock-pair-chip .cp-line { gap: 0.25em; }
.clock-pair-chip .cp-label { font-size: 0.82rem; font-weight: 500; }
.clock-pair-chip .cp-time { text-align: left; }
.clock-pair-chip .cp-offset { font-size: 0.82rem; width: 3rem; }
/* W64 section 14 (Decision 5): spacing only - the fixed time slot
   (the W55 zero-CLS wall) is re-measured to the width of "12:59:59
   PM" at the chip size (70.9px, 4.5rem with a hairline of safety) so
   the offset sits one word-space after PM at two-digit hours - the
   same separation the clock puts between the last digit and PM - and
   the leftover slack at one-digit hours halves (the slot cannot
   shrink per hour without a layout shift). Nothing else changes. The
   width lives on the W59 base rule above. */
/* W65 section 1 (Decision 1): the W63/W64 per-part slots are RETIRED
   for the chip - measured on rendered pixels the PM-to-UTC ink gap was
   14px against a 5-7px digit-to-PM gap at every width, because the
   one-digit-hour slack of the left-aligned 4.5rem time slot sat in
   front of the offset. The time and the offset now render as ONE
   inline text run separated by a literal space in the same font and
   size (the gap IS the word space the clock puts before AM/PM) inside
   the fixed 7.5rem .cp-clock slot ("12:59:59 PM UTC−12" fits), slack
   after the offset: the chip and the slot keep fixed widths (the W55
   zero-CLS wall), and the harness proves the two ink gaps equal within
   1px at 1920/1366/768/390 (scripts/w65-chip-gap.py). */
.clock-pair-chip .cp-clock { display: inline-block; width: 7.5rem; white-space: pre; text-align: left; overflow: hidden; }
.clock-pair-chip .cp-time { display: inline; width: auto; overflow: visible; }
.clock-pair-chip .cp-offset { display: inline; width: auto; overflow: visible; }
/* W63 section 1.3: every other server date/time value site-wide. */
.se-date, .se-time, .se-offset, .se-timer, .se-wval, .se-range-val,
.vc-next, .vc-timer, .lv-num, .lv-clock,
.boss-table .bt-time, .boss-table .bt-timer,
.panel-serverbar { font-family: var(--font-clock); }

/* W63 section 7.7: the draft banner's visible Discard control. */
.draft-note { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; }
.draft-discard {
  margin-left: auto;
  background: none;
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 0.2rem 0.6rem;
  color: var(--parchment);
  font: inherit;
  font-size: 0.8rem;
  cursor: pointer;
}
.draft-discard:hover { border-color: var(--gold); color: var(--gold); }

/* W63 section 3: boss status colors - the NAME only (green alive, red
   dead); time values keep the server-green rule untouched. */
.boss-name-alive { color: var(--signal-online); }
.boss-name-dead { color: var(--signal-offline); }
.ba-tools-level { margin-top: 0.4rem; }
.ba-tools-level .ba-tool { display: inline-flex; align-items: baseline; gap: 0.35rem; font-size: 0.78rem; color: var(--parchment-dim); }
.ba-tools-level select { font-size: 0.8rem; }
/* W65 sections 3 + 14: the typed Level From/To fields (the W63 5-step
   selects retired) in the toolbar control vocabulary. */
.ba-tools-level input[type="number"] { width: 4.4rem; height: 2rem; font-size: 0.8rem; padding: 0 0.45rem; background: var(--ink-panel); color: var(--parchment); border: 1px solid var(--edge); border-radius: 4px; }

/* W63 section 4 as superseded by W64 sections 1-6 + 10 (Decision 1):
   the Bosses / Grand Bosses SCHEDULE-FIRST tables
   (.boss-sched - the panel pair; the public /boss-status/ .boss-table
   pair keeps its own W16-W19 rules). No. is the fixed
   first column; Name carries the status color with the location
   beneath; Status / Next Respawn (Respawn Window) / Countdown are the
   schedule columns (the Drops column and its View Drops toggle left
   in W92 - item 7 / W91 Q7 - the drop list rides the expanded row;
   the shared budget above carries the widths). Every cell
   top-aligns with the ONE td padding (section 5); the secondary
   sub-line sits one shared gap beneath its primary value. The schedule
   values keep the server date/time family (W77: --time-server sky blue;
   the countdown follows the table's time mode - section 5 below). */
.boss-sched th, .boss-sched td { vertical-align: top; }
.boss-sched td { padding-top: 0.55rem; padding-bottom: 0.55rem; line-height: 1.35; }
/* Section 6: the name WRAPS at its spaces when the column cannot hold
   it on one line (the complete name always renders); the fixed-layout
   ellipsis applies only to a single word wider than the column. */
.boss-sched .bt-name { display: block; font-family: var(--font-display); letter-spacing: 0.04em; white-space: normal; overflow-wrap: normal; }
.boss-sched .bt-sub { display: block; margin-top: 0.15rem; font-size: 0.7rem; color: var(--parchment-faint); font-family: var(--font-sans); letter-spacing: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.boss-sched .bt-level { font-variant-numeric: tabular-nums; }
.boss-sched .bt-time { color: var(--time-server); white-space: normal; font-variant-numeric: tabular-nums; }
/* Section 3: the schedule value breaks ONLY between its date and its
   time chunk (a designed phrase-boundary wrap), never mid-date and
   never against the Countdown header - the chunks themselves stay
   whole. */
.boss-sched .bt-chunk { white-space: nowrap; }
.boss-sched .bt-line { display: block; }
/* The W64 .bt-wlabel / .bt-estimated window-label rules left with the
   estimated window (W96). */
/* W65 section 11: the Server line keeps the green server family; the
   Local line reads in parchment with the same caps vocabulary as the
   schedule cards' "Server Time" / "Local Time". */
.boss-sched .bt-cap { color: var(--parchment-faint); font-family: var(--font-sans); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.08em; }
.boss-sched .bt-local { color: var(--time-local); }
/* W65 section 9: every drop name is a link that applies the item as
   the search filter. */
.bt-drop-link { color: inherit; text-decoration: underline dotted; text-underline-offset: 0.15em; }
.bt-drop-link:hover { color: var(--arcane-bright); }
.bt-drops-hint { font-size: 0.72rem; color: var(--parchment-faint); font-style: italic; margin-left: 0.4rem; }
.boss-sched .bt-timer { color: var(--time-local); white-space: nowrap; font-variant-numeric: tabular-nums; }
.boss-sched .bt-timer .bt-sub { color: var(--parchment-faint); }
.boss-sched .bt-status-alive { color: var(--signal-online); }
.boss-sched .bt-status-dead { color: var(--signal-offline); }
/* Section 2's View Drops toggle and its .bt-drop cell are GONE (W92,
   item 7 / W91 Q7): the row is the expansion control. */
/* Section 2 + 10: the details row spans the table (it can never
   narrow a schedule column) and stays visually attached to its boss
   row - no rule between the pair, the boss row's shade continued, an
   inset accent along the left edge. Hidden until toggled with
   scripting (the synchronous .js flag), always visible without. */
.js .boss-sched tr.bt-details:not(.is-open) { display: none; }
.boss-sched tr[data-boss-row] td { border-bottom: none; }
.boss-sched tr.bt-details td { padding-top: 0; padding-bottom: 0.7rem; }
/* W93 (the expandable interface rule): the accent left rule and its
   padding are SUPERSEDED by the W93 block at the end of this file; the
   3.5rem margin stays because it is what aligns the reveal with the
   boss name. Do not restore the rule. */
.boss-sched .bt-details-inner { margin-left: 3.5rem; padding: 0.35rem 0 0 0.9rem; border-left: 2px solid var(--arcane); }
.boss-sched .bt-facts { display: flex; flex-wrap: wrap; gap: 0.2rem 1.6rem; margin: 0 0 0.45rem; font-size: 0.78rem; }
.boss-sched .bt-facts div { display: flex; gap: 0.4rem; }
.boss-sched .bt-facts dt { color: var(--parchment-faint); }
.boss-sched .bt-facts dt::after { content: ":"; }
.boss-sched .bt-facts dd { margin: 0; color: var(--parchment); font-family: var(--font-clock); }
.boss-sched .bt-drops-title { margin: 0 0 0.15rem; font-size: 0.76rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--gold); }
.boss-sched .bt-drop-list { margin: 0; padding: 0; list-style: none; font-size: 0.78rem; color: var(--parchment-dim); columns: 2; column-gap: 2rem; }
.boss-sched .bt-drop-list li { display: flex; justify-content: space-between; gap: 0.8rem; padding: 0.12rem 0; border-top: 1px dotted var(--edge); break-inside: avoid; }
.boss-sched .bt-drop-list .bt-chance { flex: none; font-variant-numeric: tabular-nums; color: var(--parchment-faint); }
/* Section 10: the alternating shades cover the boss+details PAIR
   (every boss row is followed by its details row, so the pairs sit at
   4n+1/4n+2 and 4n+3/4n+4) - the pair reads as one entry. */
table.arcanis.boss-sched tbody tr:nth-child(4n+1) td,
table.arcanis.boss-sched tbody tr:nth-child(4n+2) td { background: var(--row-shade-a); }
table.arcanis.boss-sched tbody tr:nth-child(4n+3) td,
table.arcanis.boss-sched tbody tr:nth-child(4n+4) td { background: var(--row-shade-b); }
/* Section 7: the boss toolbar's selects size to their longest option -
   the selected option can never be clipped. */
.boss-toolbar select { width: auto; max-width: 16rem; }
@media (max-width: 767px) {
  .boss-sched .bt-drop-list { columns: 1; }
}

/* W63 section 5.1: the Daily Competition (and every schedule entry)
   cell grid follows ONE explicit track set above the compact tier -
   Server / Local / Countdown - so labels, dates, times and the
   Matches range row line up column for column across the card. */
@media (min-width: 700px) {
  .sched-entry .se-cells { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1.15fr) minmax(8.5rem, 0.7fr); }
}
.sched-entry .se-cells + .se-cells { margin-top: 0.6rem; padding-top: 0.5rem; border-top: 1px dotted var(--edge); }
/* W65 section 12 (Decision 5): the siege lower row takes the SAME
   explicit three tracks and column gap as the time row above it, so
   Territory Wards Held / Attackers / Defenders (and the content
   beneath each) line up under Server Time / Local Time / Starts In
   across every castle card. */
@media (min-width: 700px) {
  .sched-entry .se-siege-rows { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1.15fr) minmax(8.5rem, 0.7fr); column-gap: 1.4rem; }
}
/* W65 section 13: the first three clans inline, the rest behind the
   native Show More / Show Less disclosure. */
.se-clans-first { display: block; }
.se-more { margin-top: 0.15rem; }
.se-more summary { cursor: pointer; list-style: none; font-size: 0.76rem; color: var(--arcane-bright); text-transform: uppercase; letter-spacing: 0.06em; }
.se-more summary::-webkit-details-marker { display: none; }
.se-more:not([open]) .se-more-close, .se-more[open] .se-more-open { display: none; }
.se-clans-rest { display: block; margin-top: 0.15rem; }
.se-cell > .se-cap { min-height: 1.1em; }

/* W63 section 6.1: the Voting Rewards summary above the site columns. */
.vote-rewards {
  border: 1px solid var(--edge);
  border-radius: 6px;
  background: var(--ink-raised);
  padding: 0.8rem 1rem;
  margin-bottom: var(--block-gap);
}
.vote-rewards .vr-title { margin: 0; font-size: 0.95rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--gold); }
.vote-rewards .vr-lead { margin: 0.35rem 0 0; font-size: 0.85rem; color: var(--parchment-dim); }
.vote-rewards .vr-list { list-style: none; margin: 0.5rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.35rem 1.2rem; }
.vote-rewards .vr-item { font-size: 0.9rem; color: var(--parchment); }
.vote-rewards .vr-count { color: var(--gold); font-variant-numeric: tabular-nums; }
.vote-rewards .vr-note { margin: 0.5rem 0 0; font-size: 0.74rem; color: var(--parchment-faint); }

/* W63 sections 7-11: the Recruitment Center. */
.rc-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.8rem; flex-wrap: wrap; }
.rc-kind { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--gold); white-space: nowrap; }
.rc-event-chip { display: inline-block; border: 1px solid var(--edge); border-radius: 999px; padding: 0.05rem 0.55rem; font-size: 0.78rem; color: var(--parchment); }
.rc-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 1rem; margin-top: 0.6rem; }
.rc-matched { color: var(--signal-online); font-weight: 600; }
.rc-pending, .rc-note { color: var(--parchment-dim); font-size: 0.85rem; }
.rc-complete { color: var(--gold); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.08em; }
.badge-filled { opacity: 0.55; text-decoration: line-through; }
.fb-term { border-bottom: 1px dotted var(--gold); cursor: help; }
.fb-info { display: inline-block; margin-left: 0.3rem; color: var(--gold); cursor: help; font-size: 0.9em; }
.rc-switch { font-size: 0.85rem; color: var(--parchment-dim); margin: 0 0 0.8rem; }
.rc-switch a.active { color: var(--gold); }
.needs-rows { display: grid; gap: 0.4rem; margin-top: 0.4rem; }
.need-row { display: grid; grid-template-columns: minmax(0, 1fr) 6rem auto; gap: 0.5rem; align-items: center; }
.need-row select { max-width: 100%; }
.need-fb { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.85rem; white-space: nowrap; }
.rc-table { font-size: 0.85rem; }
.rc-table .rc-actions { margin-top: 0; gap: 0.3rem; }
.tab .draft-pill { margin-left: 0.4rem; font-size: 0.62rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--gold); border: 1px solid var(--gold); border-radius: 999px; padding: 0 0.4rem; vertical-align: middle; }
.recruit-filter select { max-width: 12rem; }
@media (max-width: 767px) {
  .need-row { grid-template-columns: minmax(0, 1fr) 5rem; }
  .need-row .need-fb { grid-column: 1 / -1; }
}

/* ---- W66 (docs/specs/W66-RECRUITMENT-CENTER-CONSOLIDATED.md): the
   Recruitment Center around LISTINGS and MY RECRUITMENT. Segmented
   controls (the secondary Browse | Applications / Received | Sent /
   Clan | Player | Party-CP selectors and the two-option form radios),
   the position rows with minus/plus quantities, the card's labeled
   time line and expanded view, the applications tables. ---- */
.seg-row { display: flex; flex-wrap: wrap; gap: 0; margin: 0 0 var(--block-gap); }
.layout-public .seg-row { justify-content: center; }
.seg-row .seg, .seg-radio .seg-opt {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.32rem 0.85rem; border: 1px solid var(--edge);
  font-size: 0.78rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--parchment-dim); background: transparent; text-decoration: none;
  cursor: pointer; line-height: 1.3; white-space: nowrap;
}
.seg-row .seg + .seg, .seg-radio .seg-opt + .seg-opt { border-left: 0; }
.seg-row .seg:first-child, .seg-radio .seg-opt:first-child { border-radius: 4px 0 0 4px; }
.seg-row .seg:last-child, .seg-radio .seg-opt:last-child { border-radius: 0 4px 4px 0; }
.seg-row .seg:hover, .seg-radio .seg-opt:hover { color: var(--parchment); border-color: var(--arcane); }
.seg-row .seg.active, .seg-radio .seg-opt:has(input:checked) {
  color: var(--ink); background: var(--gold); border-color: var(--gold); font-weight: 600;
}
.seg-row .seg .badge { font-size: 0.65rem; padding: 0 0.4rem; }
.seg-radio { display: inline-flex; vertical-align: middle; max-width: 100%; }
.seg-radio .seg-opt { flex: 0 1 auto; min-width: 0; white-space: normal; text-align: center; }
@media (max-width: 480px) { .seg-radio .seg-opt { padding: 0.32rem 0.55rem; letter-spacing: 0.03em; } }
.seg-radio input[type="radio"] { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.seg-radio .seg-opt:has(input:focus-visible) { outline: 2px solid var(--arcane); outline-offset: 1px; }
.seg-row .seg .draft-pill { margin-left: 0.35rem; font-size: 0.6rem; letter-spacing: 0.08em; color: var(--gold); border: 1px solid var(--gold); border-radius: 999px; padding: 0 0.35rem; }
.seg-row .seg.active .draft-pill { color: var(--ink); border-color: var(--ink); }
.rc-type-row { margin-top: 0.2rem; }
.tb-row { display: block; margin-top: 0.45rem; }
.recruit-filter .rc-basis { margin-left: auto; }
.recruit-filter .rc-basis .seg-opt { padding: 0.28rem 0.7rem; }

/* The card: the labeled time line, the expanded view, the states. */
.rc-time .rc-basis-tag { display: inline-block; margin-left: 0.4rem; font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--gold); border: 1px solid var(--edge); border-radius: 999px; padding: 0 0.45rem; vertical-align: middle; }
.rc-pitch { margin: 0.4rem 0 0.2rem; }
.rc-more { margin: 0 0 0.3rem; font-size: 0.9rem; }
.rc-more > summary { cursor: pointer; color: var(--arcane-bright); font-size: 0.8rem; letter-spacing: 0.06em; text-transform: uppercase; list-style: none; }
.rc-more > summary::-webkit-details-marker { display: none; }
.rc-more > summary::before { content: "\25B8"; display: inline-block; margin-right: 0.3rem; transition: transform 0.15s; }
.rc-more[open] > summary::before { transform: rotate(90deg); }
.rc-more[open] > summary { margin-bottom: 0.3rem; }
.rc-declined { color: var(--parchment-faint); font-size: 0.85rem; }
.rc-apply { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.rc-apply select { max-width: 22rem; }
.rc-apply input[type="text"] { max-width: 16rem; }

/* The forms: positions, quantities, the Still Needed rows. */
.pos-name { font-size: 0.9rem; }
.qty-ctl { display: inline-flex; align-items: stretch; border: 1px solid var(--edge); border-radius: 4px; overflow: hidden; }
.qty-ctl .qty-btn { border: 0; background: transparent; color: var(--parchment-dim); width: 1.9rem; cursor: pointer; font-size: 1rem; line-height: 1; }
.qty-ctl .qty-btn:hover { color: var(--gold); background: var(--ink-raised); }
:is(.form-standard, .form-wide) .qty-ctl input[type="number"] { width: 2.6rem; max-width: 2.6rem; text-align: center; border: 0; border-left: 1px solid var(--edge); border-right: 1px solid var(--edge); border-radius: 0; padding: 0.3rem 0.2rem; -moz-appearance: textfield; appearance: textfield; }
.qty-ctl input[type="number"]::-webkit-outer-spin-button, .qty-ctl input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.needs-rows { display: grid; gap: 0.4rem; margin: 0.4rem 0 0.6rem; }
.needs-rows .need-row { grid-template-columns: minmax(0, 1fr) auto auto auto; }
.need-row select { min-width: 0; width: 100%; }
.need-row .need-fb:has(input:disabled) { color: var(--parchment-faint); }
.need-remove { padding: 0.25rem 0.6rem; font-size: 0.9rem; line-height: 1; }
.rc-submit { display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap; margin-top: 1.2rem; }
.rc-submit .btn-submit { font-size: 0.85rem; padding: 0.6rem 1.4rem; }
.draft-status { font-size: 0.78rem; color: var(--parchment-faint); min-height: 1em; }
.btn-quiet { border-color: transparent; color: var(--parchment-dim); }
.btn-quiet:hover { color: var(--parchment); border-color: var(--edge); }
.inline-check { display: inline-flex; gap: 0.4rem; align-items: baseline; margin-top: 0.5rem; }
.tz-row[hidden] { display: none; }
form[data-rc-form] textarea[data-autosize] { overflow: hidden; resize: vertical; }
form[data-rc-form] .help-msg { color: var(--parchment-dim); }

/* The tables: My Active Listings and Applications. */
.rc-apps .rc-msg { max-width: 16rem; }
.rc-msg-more > summary { cursor: pointer; list-style: none; }
.rc-msg-more > summary::-webkit-details-marker { display: none; }
.rc-msg-more > summary::after { content: " \2026\00A0\25BE"; color: var(--arcane-bright); }
.rc-msg-more[open] > summary { display: none; }
.rc-state { font-size: 0.78rem; letter-spacing: 0.06em; text-transform: uppercase; }
.rc-state-live { color: var(--signal-online); }
.rc-state-filled { color: var(--gold); }
.rc-state-closed, .rc-state-unpublished, .rc-state-rejected { color: var(--parchment-faint); }
.rc-state-pending-review { color: var(--parchment-dim); }
.rc-mine-table .rc-actions { display: inline-flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; }
.rc-mine-table .rc-actions form { display: inline; }
table.arcanis .table-empty-row .empty-text { color: var(--parchment-dim); }
table.arcanis .table-empty-row .btn { margin-left: 0.6rem; font-size: 0.7rem; padding: 0.3rem 0.7rem; vertical-align: middle; }
.rc-mine { margin-bottom: var(--block-gap); }
@media (max-width: 767px) {
  .recruit-filter .rc-basis { margin-left: 0; }
  .needs-rows .need-row { grid-template-columns: minmax(0, 1fr) auto; }
  .needs-rows .need-row .need-fb { grid-column: 1; }
  .needs-rows .need-row .need-remove { grid-column: 2; justify-self: end; }
}

/* ---- W67 (docs/specs/W67-RECRUITMENT-USERPANEL-UPDATE.md): Recruitment
   Center refinements, User Panel sidebar stability, Alerts & Connections
   boss filtering. ---- */

/* The hidden attribute ALWAYS hides. Root cause of the roster defect
   behind W67 section 3: an element carrying hidden but an author
   display (grid / flex / inline-flex - the position rows, the Remove
   Selected Banner button) rendered anyway, because the UA's
   [hidden] { display: none } loses to any author display. One rule,
   site-wide, by construction. (The status chip keeps its own
   visibility-based hiding - it never uses the attribute.) */
[hidden] { display: none !important; }

/* Sections 12, 23, 31: ONE deterministic sidebar rule for the whole
   User Panel. Sticky positioning could never be stable here - the
   sidebar's grid track ends where the page content ends, so on every
   side page the track bottom pushed the sidebar up by a content-
   dependent amount at the same scroll offset (measured 72 / 88 / 15 /
   -141px across four Server Schedule tabs). On desktop widths the
   sidebar is therefore FIXED at one top offset (below the 72px site
   header) with one width (its grid track, which stays reserved), taller
   sidebars scrolling internally; content height, tab, filter, accordion
   and loading changes cannot move it. left: auto keeps its static
   horizontal position (the grid container's content edge), so the
   centered layout is untouched at every width. The 860px collapse keeps
   the normal flow. */
/* W68 sidebar fold-in (owner problem report, 2026-08-28; supersedes
   the W67 section 23 FIXED rule above and moots the W67b "footer
   overlap acceptable" ruling): a viewport-anchored sidebar read as
   detached from the page while the content scrolled past it. The
   sidebar now scrolls NATURALLY with the page - normal document flow,
   no fixed, no sticky, no floating: it is a plain grid item at the top
   of its track (align-self: start). The W67 stability requirements
   still hold BY CONSTRUCTION: its position is the grid's top edge on
   every User Panel page, so content height, tab switches, filters,
   accordions and loading states cannot move it, and nothing overlaps
   the footer. ONE site-wide rule; the 860px collapse is unchanged. */
/* W83 section 11 (docs/specs/W83-MY-RECRUITMENT-WORKFLOW.md, the
   owner's consolidated instructions - SUPERSEDES the W68 natural-flow
   rule above): the side menu is PERSISTENT on every user-panel side
   page - sticky beneath the MEASURED header offset (panel-ux.js
   initStickyMeasure; 72px is only the no-JS fallback), its own scroll
   region when taller than the viewport, and geometry immune to tab
   switches, filters, data loads and in-page navigation BY CONSTRUCTION
   (sticky inside its own grid track: content changes cannot move it,
   and it can never overlap the header, the footer or the content
   column). Below 861px the W83 collapsible control takes over (the
   block at the end of this sheet). */
@media (min-width: 861px) {
  .panel-sidebar {
    position: sticky;
    top: calc(var(--header-h, 72px) + 1rem);
    max-height: calc(100vh - var(--header-h, 72px) - 2rem);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}
/* Section 12: hover reads clearly different from the true active state
   - hover is a quiet lift (parchment ink, faint ground, no accent bar);
   only the current side page carries the accent color, ground and bar.
   Visited and focused links keep the resting look. */
.panel-sidebar a:visited { color: var(--parchment-dim); }
.panel-sidebar a:hover { color: var(--parchment); background: rgba(255, 255, 255, 0.04); }
.panel-sidebar a.active, .panel-sidebar a.active:hover, .panel-sidebar a.active:visited {
  color: var(--arcane-bright);
  background: var(--ink-panel);
  box-shadow: inset 2px 0 0 var(--arcane);
}
.panel-sidebar a:focus-visible { outline: 2px solid var(--arcane); outline-offset: -2px; }

/* Section 9: the listing forms read in four labeled sections. */
.form-section { padding-top: 0.35rem; margin-top: 1.1rem; border-top: 1px solid var(--edge-soft); }
.form-section:first-of-type { margin-top: 0.4rem; border-top: 0; padding-top: 0; }
.form-section-title { margin: 0 0 0.7rem; font-family: var(--font-display); font-size: 1rem; letter-spacing: 0.06em; color: var(--gold); }

/* Sections 3-5, 14-15: the ONE requirement component. */
.positions-field .pos-picker { margin-top: 0.4rem; }
.positions-field .needs-rows { margin: 0.6rem 0 0.3rem; }
.needs-rows .need-row {
  grid-template-columns: minmax(0, 1fr) auto auto auto;
  padding: 0.3rem 0.5rem; border: 1px solid var(--edge-soft); border-radius: 4px; background: rgba(255, 255, 255, 0.02);
}
.needs-rows .need-row.is-flash { box-shadow: 0 0 0 2px var(--gold); transition: box-shadow 0.6s; }
.needs-rows .pos-name { font-weight: 600; color: var(--parchment); }
.qty-ctl .qty-btn { width: 2.25rem; min-height: 2.15rem; font-size: 1.1rem; }
.qty-ctl input[type="number"] { min-height: 2.15rem; font-weight: 600; }
.need-fb { padding: 0.3rem 0.55rem; border: 1px solid var(--edge); border-radius: 4px; cursor: pointer; gap: 0.45rem; }
.need-fb:hover { border-color: var(--arcane); }
.need-fb input[type="checkbox"] { width: 1.15rem; height: 1.15rem; margin: 0; accent-color: var(--arcane); cursor: pointer; }
.need-fb:has(input:checked) { border-color: var(--gold); color: var(--gold); }
.need-remove { min-width: 2.15rem; min-height: 2.15rem; padding: 0; font-size: 1.1rem; }
button.fb-info { background: none; border: 0; padding: 0 0.15rem; color: var(--gold); cursor: pointer; font-size: 1em; line-height: 1; border-radius: 999px; }
button.fb-info:hover, button.fb-info[aria-expanded="true"] { color: var(--arcane-bright); }
button.fb-info:focus-visible { outline: 2px solid var(--arcane); outline-offset: 1px; }
/* W93: the note's border, accent edge and radius are SUPERSEDED by the
   W93 block (an indented note on a subtle ground). */
.fb-rule { margin: 0.35rem 0 0.2rem; padding: 0.5rem 0.7rem; border: 1px solid var(--edge); border-left: 3px solid var(--gold); border-radius: 4px; font-size: 0.82rem; color: var(--parchment-dim); }
.any-class { margin-top: 0.4rem; }
.any-class input[type="checkbox"] { width: 1.05rem; height: 1.05rem; accent-color: var(--arcane); }
.pos-empty { margin: 0.5rem 0 0; }
.pos-summary { margin: 0.35rem 0 0; font-size: 0.85rem; color: var(--parchment-dim); }
.pos-summary strong { color: var(--gold); }
.pos-summary.is-full { color: var(--gold); }
.positions-field .class-results { margin-top: 0.4rem; }
.positions-field .class-result[disabled] { opacity: 0.45; cursor: not-allowed; }
/* Section 8: the account's own classes as chips (no free claim). */
.class-own { margin-top: 0.3rem; }

/* Section 10: the draft marker is a separated pill after the type name. */
.seg-row .seg .draft-pill { margin-left: 0.5rem; padding: 0.05rem 0.45rem; text-transform: uppercase; }
.seg-row .seg .draft-pill::before { content: "\00B7"; margin-right: 0.4rem; color: inherit; }
.draft-note { max-width: 100%; box-sizing: border-box; }

/* Sections 16-17: the day shortcuts read as shortcuts; the read-only
   server zone line under Server Time. */
.play-quick { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
.play-quick-hint { font-size: 0.75rem; color: var(--parchment-faint); }
.play-quick .btn[aria-pressed="true"], .play-quick .btn.preset-on { border-color: var(--gold); color: var(--gold); }
.tz-readonly { margin: 0.2rem 0 0.15rem; font-size: 0.9rem; color: var(--parchment-dim); }
.tz-readonly strong { color: var(--parchment); }
.tz-lock { font-size: 0.8em; margin-right: 0.2rem; }

/* Section 29: the Grand Bosses in the same filterable list, locked. */
.ba-grand-row { opacity: 0.85; cursor: default; }
.ba-grand-row .ba-lock { flex: none; color: var(--gold); font-weight: 700; width: 1rem; text-align: center; }
.ba-tools [data-boss-reset] { font-size: 0.72rem; }

@media (max-width: 767px) {
  .needs-rows .need-row { grid-template-columns: minmax(0, 1fr) auto; }
  .needs-rows .need-row .pos-name { grid-column: 1 / -1; }
  .needs-rows .need-row .need-fb { grid-column: 1; justify-self: start; }
  .needs-rows .need-row .need-remove { grid-column: 2; justify-self: end; }
}

/* W67 owner ruling 1: the Applications section under MY RECRUITMENT -
   its own gold title above the Received | Sent control, separated
   from My Active Listings above and the type pills below. */
.rc-apps-section { margin: 0 0 var(--block-gap); padding-top: 0.2rem; border-top: 1px solid var(--edge-soft); }
.rc-apps-title { color: var(--gold); font-size: 1.15rem; margin: 0.6rem 0 0.5rem; }

/* ==================================================================
   W68 (docs/specs/W68-COMMUNITY-MEDIA-GALLERY.md): Community Media &
   Media Gallery - the shared video card, the structural placeholders,
   the stream cards, the highlights carousel, the 3-2-1 grids. One
   component system for the homepage preview and the gallery
   (section 16); the established Arcanis language throughout
   (section 14): dark surfaces, purple edges, gold headings, purple
   interactive accents, small platform marks only.
   ================================================================== */

/* --- Section 10: the standard video card. The thumbnail keeps 16:9
   (rule above); the play indicator floats over it; the title clamps
   at two lines; metadata stays visually secondary; the footer holds
   the tag/platform chips and the like control on one line. */
.video-thumb-link { position: relative; }
.video-thumb-empty { position: relative; }
.video-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.video-play-glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  background: rgba(12, 8, 24, 0.72);
  border: 1px solid var(--edge-bright);
  color: var(--parchment);
  font-size: 0.95rem;
  padding-left: 0.2rem;
  line-height: 1;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.video-thumb-link:hover .video-play-glyph { background: var(--arcane-deep); border-color: var(--arcane-bright); }
.video-title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.3;
  min-height: 2.6em;
}
.video-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: auto;
  padding-top: 0.5rem;
}
.video-foot .video-tags { flex: 1 1 auto; min-width: 0; }
.video-foot .like-form { flex: 0 0 auto; margin: 0; }
.badge-platform { gap: 0.3rem; }
.platform-icon { width: 0.85rem; height: 0.85rem; flex: 0 0 auto; }
.badge-link:hover { border-color: var(--arcane-bright); color: var(--parchment); text-decoration: none; }
.video-date { color: var(--parchment-faint); }

/* --- Sections 1.3, 6.2, 11: structural placeholders. Clearly empty
   (dashed edge, muted ink, the ruled wording), never fake data: the
   metadata area is a pair of empty bars. Same dimensions as a card. */
.video-card-placeholder,
.stream-card-placeholder {
  border-style: dashed;
  border-color: var(--edge);
  background: var(--ink-panel);
}
.video-card-placeholder .video-thumb-icon,
.stream-card-placeholder .stream-preview-icon { color: var(--edge); }
.video-card-placeholder .video-play-glyph { opacity: 0.55; }
.video-title-placeholder { color: var(--parchment-dim); }
.ph-bar {
  display: inline-block;
  height: 0.85rem;
  border-radius: 3px;
  background: var(--edge-soft);
}
.ph-bar-tag { width: 3.6rem; }
.ph-bar-like { width: 2.2rem; margin-left: auto; }
.video-foot-placeholder { display: flex; gap: 0.3rem; align-items: center; }
.gallery-empty-line {
  color: var(--parchment-dim);
  text-align: center;
  margin: 1rem 0 0;
}

/* --- Sections 9.2 + 12: the All Videos grid - three per row on large
   desktops, two at intermediate widths, one on mobile. */
.video-grid-all { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1100px) {
  .video-grid-all { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 700px) {
  .video-grid-all { grid-template-columns: minmax(0, 1fr); }
}
.video-grid-note { grid-column: 1 / -1; }

/* --- Sections 2.2, 7, 12: stream cards and the 3-2-1 stream grid.
   One anchor (the card opens the stream); the preview area is the
   Arcanis surface carrying the platform mark, the LIVE pill and the
   viewer count - never a platform look-alike. */
.stream-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.1rem;
}
@media (max-width: 1100px) {
  .stream-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 700px) {
  .stream-grid { grid-template-columns: minmax(0, 1fr); }
}
.stream-card {
  display: flex;
  flex-direction: column;
  background: var(--ink-raised);
  border: 1px solid var(--edge-soft);
  border-radius: 6px;
  overflow: hidden;
  color: inherit;
  text-decoration: none;
  transition: border-color 0.2s ease;
}
a.stream-card:hover { border-color: var(--arcane-deep); text-decoration: none; }
a.stream-card:focus-visible { outline: 2px solid var(--arcane); outline-offset: 2px; }
.stream-preview {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 16 / 9;
  background: var(--ink-panel);
  border-bottom: 1px solid var(--edge-soft);
}
.stream-preview-icon { width: 2.6rem; height: 2.6rem; color: var(--edge-bright); }
.live-pill {
  position: absolute;
  top: 0.6rem;
  left: 0.6rem;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  background: rgba(12, 8, 24, 0.82);
  border: 1px solid var(--error-strong);
  color: var(--parchment);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
}
.live-pill-off { border-color: var(--edge); color: var(--parchment-faint); }
.live-dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--error-strong);
  animation: live-pulse 1.8s ease-in-out infinite;
}
@keyframes live-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}
.stream-viewers {
  position: absolute;
  right: 0.6rem;
  bottom: 0.6rem;
  padding: 0.15rem 0.5rem;
  border-radius: 4px;
  background: rgba(12, 8, 24, 0.82);
  color: var(--parchment-dim);
  font-size: 0.72rem;
}
.stream-body {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 0.8rem 0.9rem 0.9rem;
  text-align: left;
}
.stream-title {
  color: var(--gold);
  font-size: 0.95rem;
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.6em;
}
.stream-title-placeholder { color: var(--parchment-dim); }
.stream-meta { color: var(--parchment-dim); font-size: 0.85rem; }
.stream-tags { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.stream-empty-note,
.stream-more {
  color: var(--parchment-dim);
  text-align: center;
  margin: 0.8rem 0 0;
}
.stream-more-count { color: var(--parchment-dim); font-size: 0.85em; }
.stream-channels {
  margin: 0.9rem 0 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.4rem;
}

/* --- Sections 1.1, 6.1, 13: the highlights carousel. Server-rendered
   track; without scripting every slide shows in a wrapping grid. With
   scripting the track is a row of slides sized by the SAME media
   query media-carousel.js reads (two side by side from 1000px, one
   below), so the first paint already holds the final geometry - no
   layout shift when the script binds. Slides pad to form the gap so
   the percentage math holds; the control row is a fixed-height slot. */
.media-carousel { margin: 0 0 0.25rem; min-width: 0; }
/* contain: inline-size - the nowrap track contributes NO intrinsic
   width to its ancestors (a flex row of 100%-basis slides would
   otherwise widen a grid track to n cards and overflow the page). */
.carousel-viewport { overflow: hidden; min-width: 0; contain: inline-size; }
.home-equal-cards > .panel { min-width: 0; }
/* W74 section 2: the W69 section 1.1 compact homepage preview (a 52rem
   cap on the viewport and controls) is RETIRED - the homepage carousel
   is the gallery carousel, same width rules, same cards. */
.carousel-track {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: 1.1rem;
}
.carousel-slide { min-width: 0; }
.js .carousel-track {
  display: flex;
  flex-wrap: nowrap;
  gap: 0;
  margin: 0 -0.55rem;
  transition: transform 0.45s ease;
  will-change: transform;
}
.js .carousel-track.no-transition { transition: none; }
.js .carousel-slide {
  flex: 0 0 100%;
  box-sizing: border-box;
  padding: 0 0.55rem;
}
@media (min-width: 1000px) {
  .js .carousel-slide { flex-basis: 50%; }
}
/* A lone slide (one highlight, the homepage placeholder) is one
   centered card at every width. */
.js .carousel-slide:only-child { margin: 0 auto; }
.js .carousel-slide .video-card { height: 100%; }
.carousel-controls {
  height: 2.6rem;
  margin-top: 0.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
}
.carousel-btn {
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 50%;
  border: 1px solid var(--edge-bright);
  background: var(--ink-raised);
  color: var(--parchment);
  font: inherit;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: border-color 0.2s ease, color 0.2s ease;
}
.carousel-btn:hover { border-color: var(--arcane-bright); color: var(--arcane-bright); }
.carousel-btn:focus-visible { outline: 2px solid var(--arcane); outline-offset: 2px; }
.carousel-btn[disabled] { opacity: 0.35; cursor: default; }
.media-carousel.is-single .carousel-btn { visibility: hidden; }
html:not(.js) .carousel-controls { display: none; }
.carousel-dots { display: flex; gap: 0.45rem; align-items: center; min-width: 1rem; }
.carousel-dot {
  width: 0.62rem;
  height: 0.62rem;
  border-radius: 50%;
  border: 1px solid var(--edge-bright);
  background: transparent;
  padding: 0;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.carousel-dot:hover { border-color: var(--arcane-bright); }
.carousel-dot.is-active { background: var(--arcane); border-color: var(--arcane-bright); }
.carousel-dot:focus-visible { outline: 2px solid var(--arcane); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .js .carousel-track { transition: none; }
  .live-dot { animation: none; }
  .video-play-glyph, .stream-card, .carousel-btn, .carousel-dot { transition: none; }
}

/* --- Section 3: the homepage Community Media preview - subheadings
   in the gold treatment, the carousel compact, the actions below. */
.home-media { text-align: center; }
.home-media-heading {
  color: var(--gold);
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  margin: 1.1rem 0 0.7rem;
}
.home-media .video-body, .home-media .stream-body { text-align: left; }
.home-media > .home-media-btn { margin-top: 1.1rem; }

/* --- Section 5: the Submit Media type row. */
.media-type-section { border-top: 0; margin-top: 0.2rem; }
.form-row.is-optional > label::after { content: " (optional)"; color: var(--parchment-faint); font-size: 0.8em; }

/* ==================================================================
   W69 (docs/specs/W69-HOMEPAGE-MEDIA-GALLERY-UI.md): Homepage & Media
   Gallery UI/UX updates - compact standard cards, the sitewide media
   empty-state rule, the tag row, the readability steps. Extends the
   W68 component set (section 22) - nothing forks.
   ================================================================== */

/* --- Sections 2 + 11: the compact standard card. The 16:9 preview
   stays; the metadata area tightens (padding, gap, type) and the
   title no longer reserves two lines when it needs one, so the card
   is the preview plus what it says and nothing else. Equal heights in
   a row hold through the grid stretch and the footer's margin-top:
   auto. */
.video-body { padding: 0.55rem 0.7rem 0.6rem; gap: 0.25rem; }
.video-title { font-size: 0.95rem; min-height: 0; }
.video-meta { font-size: 0.8rem; color: var(--parchment-soft); }
.video-date { color: var(--parchment-dim); }
.video-foot { padding-top: 0.35rem; }
.stream-body { padding: 0.55rem 0.7rem 0.6rem; gap: 0.25rem; }
.stream-title { font-size: 0.92rem; min-height: 0; }
.stream-meta { font-size: 0.8rem; color: var(--parchment-soft); }
.stream-viewers { color: var(--parchment-soft); }
.video-title-placeholder, .stream-title-placeholder { color: var(--parchment-soft); font-size: 0.88rem; }
.video-foot-placeholder { padding-top: 0.35rem; margin-top: auto; }

/* --- Fold-in (W68 batched question 2): the server-stored stream
   preview fills the 16:9 area; the platform mark rides its corner. */
.stream-preview-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.stream-preview-has-image .stream-preview-icon {
  position: absolute;
  right: 0.6rem;
  top: 0.6rem;
  width: 1.4rem;
  height: 1.4rem;
  padding: 0.25rem;
  border-radius: 4px;
  background: rgba(12, 8, 24, 0.82);
}

/* --- Sections 5, 12 and 23: the sitewide media empty-state rule. ONE
   centralized message per section (this note), uniform neutral
   placeholder cards under it. Retired here: the W68 .stream-empty-note
   second sentence and the .gallery-empty-line. */
.media-empty-note {
  color: var(--parchment-soft);
  font-size: 0.9rem;
  text-align: center;
  margin: 0 0 0.7rem;
}
.media-carousel > .media-empty-note { margin-bottom: 0.6rem; }

/* --- Section 18: small secondary text on the media surfaces steps to
   --parchment-soft (descriptions, labels, dates, empty notes); the
   dark theme itself is untouched. */
.gallery-note { color: var(--parchment-soft); }
.gallery-field-label { color: var(--parchment-soft); }
.stream-more-count { color: var(--parchment-soft); }
.gallery-sort select { color: var(--parchment); }

/* --- Sections 8-10: the tag row. ALL first; the active chip is
   UNMISTAKABLE - the filled arcane ground with ink text, the bright
   border, weight 600 (hover keeps the panel ground, so on and hover
   never look alike); every chip shares one height; the row wraps onto
   centered lines with compact vertical spacing, and can never widen
   the page. */
.gallery-tags { row-gap: 0.4rem; column-gap: 0.4rem; max-width: 100%; }
.tag-chip { white-space: nowrap; line-height: 1.2; min-height: 1.75rem; }
.tag-chip-on, .tag-chip-on:hover {
  background: var(--arcane);
  border-color: var(--arcane-bright);
  color: var(--ink);
  font-weight: 600;
}
.tag-chip-on.tag-chip-off { background: var(--arcane-deep); color: var(--arcane-bright); }


/* ------------------------------------------------------------------ */
/* W71 (owner spec: Header Alignment, Spacing, and Button Consistency, */
/* docs/specs/W71-HEADER-ALIGNMENT.md; decisions W71-DECISIONS.md).    */
/* The header is ONE centered run of INK-TRUE segments - wordmark,     */
/* primary nav, status chip, account group - separated by the ONE      */
/* 1.5rem group gap (the W24 token), so the clock -> first button gap  */
/* equals the logo -> HOME gap by construction and the group centers   */
/* as a unit on its ink, not on slack. Measured on rendered pixels by  */
/* scripts/w71-header-gap.py (the W65 proof standard), both auth       */
/* states, 1920/1366/768/390.                                          */
/* ------------------------------------------------------------------ */
/* 1. The chip box IS its ink: no horizontal padding (the border is
   transparent since W31, so the padding was dead space that pushed
   the buttons right), width follows its FIXED slots instead of the
   16.5rem centered slot (W13/W58/W61): the status label rides a
   fixed 3rem slot (Pending, the widest en state, measures 47.2px) and
   the Server + clock component its fixed slot (item 2), so the chip's
   width is constant in every state and at every tick - the W55
   zero-CLS wall by construction. A shorter state's slack sits between
   the label and the dot separator (left-aligned label); the
   hour-digit slack sits before "Server" (end-anchored component) -
   never after the offset, where it varied the gap to the buttons. */
.status-chip { width: auto; justify-content: flex-start; padding: 0.42rem 0; }
.status-chip [data-status-label] { display: inline-block; min-width: 3rem; text-align: left; }
/* 2. The clock END is the fixed edge (the spec measures from "the end
   of the server clock"): the WHOLE Server + clock component is
   END-anchored inside a fixed 9.9rem slot re-measured to its widest
   run ("Server" 37.88 + the 0.3rem word gap + "10:00:00 AM UTC-4"
   115.02px = 157.7px at the chip size; 9.9rem = 158.4px carries it with
   a hairline; the W64 precedent), and the W65 .cp-clock slot inside it
   becomes content-sized. So: the clock's end never moves; the W63
   Server-to-digit word space is untouched (the label and the run
   travel together); the W65 word-space proof lives inside the run,
   untouched; and the hour-digit slack (one tabular digit, ~7px at
   one-digit hours) lands in the status/clock separation before
   "Server" - the separation W61 section 16 wants WIDER than the inner
   gaps, so it only ever grows. Never before the buttons. */
.clock-pair-chip { width: 9.9rem; justify-content: flex-end; }
.clock-pair-chip .cp-clock { width: auto; text-align: left; }
/* 3. The chip -> account group boundary carries the SAME 1.5rem group
   gap as logo -> nav and nav -> chip (row gap stays 0.5rem for the
   wrapped burger layout, so the header height there is unchanged). */
.nav-actions { gap: 0.5rem 1.5rem; }
/* 4. The account group is ONE footprint in BOTH auth states, by slot
   construction instead of a padded 16rem box: a 5rem NARROW slot +
   0.5rem + a 9.5rem WIDE slot = 15rem, identical box, identical x,
   identical 2.5rem height. Signed out: SIGN IN (narrow, 77px natural)
   + CREATE ACCOUNT (wide, 149.6px natural); signed in: the username
   (wide - the W12 primary control, ellipsized at 9.5rem, its W12
   width) + LOG OUT (narrow). The slots are min-widths for the labels
   (a wider draft-locale label may grow, never clip; en is the geometry
   of record - question 2) and a fixed width for the ellipsized name.
   The cluster's own width is auto: the slots carry the contract, so no
   slack remains on either side and the run centers on ink. */
.account-cluster { width: auto; justify-content: flex-start; }
.nav-actions .account-btn { min-width: 5rem; }
.nav-actions .btn-elevated { min-width: 9.5rem; }
.nav-actions .account-name { width: 9.5rem; }
/* 5. Burger layout (sub-1100px): the chip + account row centers as one
   unit too (at 390 the two stack, each centered), the same 1.5rem
   between them - the rule applied consistently across widths. */
@media (max-width: 1100px) {
  .nav-actions { justify-content: center; }
}

/* ==========================================================================
   W72 (docs/specs/W72-MARKETPLACE-UI.md): the Marketplace side page.
   Every control below is the site's existing vocabulary (the standard
   Tab component, the segmented .seg-row pills, the ONE table skin, the
   panel cards, the callouts); this block only adds the marketplace's
   strips, the compact sub-lines inside table cells, the detail grid,
   the state chips and the action forms. Nothing here touches shared
   selectors - source order keeps every earlier rule intact.
   ========================================================================== */
.mk-cat-row { margin-bottom: 0.75rem; }
.mk-sub-row { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0 0 var(--block-gap); }
.mk-chip {
  display: inline-block;
  padding: 0.28rem 0.7rem;
  border: 1px solid var(--edge-bright);
  border-radius: 999px;
  font-size: 0.74rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--parchment);
  text-decoration: none;
  line-height: 1.3;
}
.mk-chip:hover { border-color: var(--arcane-bright); color: var(--arcane-bright); }
.mk-chip-on, .mk-chip-on:hover { background: var(--arcane); border-color: var(--arcane); color: var(--ink); }
.mk-filter {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.6rem 0.9rem;
  align-items: end;
  margin: 0 0 var(--block-gap);
}
.mk-filter label { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.8rem; color: var(--parchment-dim); }
.mk-filter .mk-search { grid-column: 1 / -1; }
.mk-filter input, .mk-filter select { width: 100%; }
.mk-range { display: grid; grid-template-columns: auto 1fr auto 1fr; gap: 0.25rem 0.35rem; align-items: center; font-size: 0.8rem; color: var(--parchment-dim); }
.mk-range .mk-range-label { grid-column: 1 / -1; }
.mk-range input { min-width: 0; width: 100%; }
.mk-filter .btn { justify-self: start; }
.mk-count { margin: 0 0 0.6rem; font-size: 0.85rem; color: var(--parchment-dim); }
.mk-count .label { color: var(--parchment); font-weight: 600; margin-right: 0.4rem; }
.mk-sub { display: block; font-size: 0.78rem; color: var(--parchment-dim); line-height: 1.35; }
.mk-sale { color: var(--parchment); }
.mk-flag { color: var(--arcane-bright); }
.mk-row-flag td:first-child { box-shadow: inset 3px 0 0 var(--arcane); }
.mk-row-on td { background: rgba(255, 255, 255, 0.04); }
.mk-item-link { color: var(--parchment); text-decoration: none; font-weight: 600; }
.mk-item-link:hover { color: var(--arcane-bright); }
.mk-remaining span { font-variant-numeric: tabular-nums; }
.mk-ref { font-family: var(--font-mono, monospace); font-size: 0.8rem; letter-spacing: 0.03em; }
.mk-state {
  display: inline-block;
  padding: 0.12rem 0.55rem;
  border: 1px solid var(--edge-bright);
  border-radius: 999px;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--parchment);
}
.mk-state-active { border-color: var(--signal-online); color: var(--signal-online); }
.mk-state-sold { border-color: var(--arcane); color: var(--arcane-bright); }
.mk-state-expired, .mk-state-canceled, .mk-state-removed, .mk-state-failed { color: var(--parchment-dim); }
.mk-state-closing, .mk-state-unsettled { border-color: var(--signal-offline); color: var(--signal-offline); }
.mk-limit { margin: 0 0 0.9rem; font-size: 0.85rem; color: var(--parchment-dim); }
.mk-limit .label { color: var(--parchment); font-weight: 600; margin-right: 0.4rem; }
.mk-h3 { margin: 1.1rem 0 0.5rem; font-size: 0.95rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--parchment); }
.mk-char-row { margin-bottom: 0.9rem; }
.mk-state-row { margin-bottom: 0.75rem; }
.mk-eligibility { margin-top: 0.8rem; font-size: 0.82rem; color: var(--parchment-dim); }
.mk-eligibility .label { color: var(--parchment); font-weight: 600; margin-right: 0.35rem; }
.mk-fee-preview { margin: 0.4rem 0 0; padding: 0.55rem 0.75rem; border-left: 3px solid var(--arcane); background: rgba(255, 255, 255, 0.03); font-size: 0.85rem; }
.mk-fee-preview .label { color: var(--arcane-bright); font-weight: 600; margin-right: 0.35rem; }
.mk-inline-form { display: inline; }
.mk-back { margin: 0 0 0.75rem; font-size: 0.85rem; }
.mk-back a { color: var(--parchment-dim); text-decoration: none; }
.mk-back a:hover { color: var(--arcane-bright); }
.mk-detail-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); gap: 0.9rem; }
.mk-kv th { white-space: nowrap; }
.mk-action-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 1rem 1.5rem; }
.mk-action-form { padding: 0.9rem 1rem; border: 1px solid var(--edge-soft); border-radius: 6px; }
.mk-action-form .mk-h3 { margin-top: 0; }
.mk-action-form .form-row { margin-bottom: 0.7rem; }
.mk-action-form select, .mk-action-form input { width: 100%; }
.mk-watch-form { display: block; margin-top: 1rem; }
.mk-snipe { margin-top: 0.8rem; }
.mk-leading .label { color: var(--signal-online); font-weight: 600; margin-right: 0.3rem; }
.mk-irreversible { margin: 1rem 0; }
.mk-confirm-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center; }
.mk-unavailable { margin-bottom: var(--block-gap); }
@media (max-width: 860px) {
  .mk-filter { grid-template-columns: 1fr 1fr; }
  .mk-filter .mk-search { grid-column: 1 / -1; }
}

/* ==========================================================================
   W73 (docs/specs/W73-MARKETPLACE-UIUX.md): the Marketplace UI/UX
   refinement. Everything below is scoped to the marketplace (.mk-*)
   over the W72 block: the item icon cell, the Local / Server time cell
   (server values in the site's server green, explicit caps, the dynamic
   UTC offset), the toolbar that carries the toggle, the row actions and
   the selectable row, the From / To range fieldsets, currency suffixes
   on money inputs, the split fee estimate, the item summary and
   breadcrumb on listing setup, the transaction badges, the purposeful
   empty states, the dismissible notice, comfortable click targets,
   higher contrast on muted text and sort indicators, and the narrow-
   width column priorities (secondary columns fold away below 768 / 560
   px; the detail page carries everything).
   ========================================================================== */
.mk-icon {
  display: inline-block; width: 32px; height: 32px; flex: 0 0 32px;
  border: 1px solid var(--edge); border-radius: 4px; background: rgba(0, 0, 0, 0.35);
  object-fit: contain; image-rendering: auto; vertical-align: middle;
}
.mk-icon-none { position: relative; }
.mk-icon-none::before {
  content: "?"; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: var(--parchment-faint); font-size: 0.85rem; font-weight: 600;
}
.mk-item-cell { min-width: 11rem; }
.mk-item { display: flex; align-items: center; gap: 0.6rem; }
.mk-item-text { display: flex; flex-direction: column; min-width: 0; }
.mk-item-name { color: var(--parchment); font-weight: 600; }
.mk-qty { color: var(--parchment-dim); font-size: 0.85em; }
.mk-table .mk-sub, .mk-sub { color: var(--parchment-dim); }
.mk-sub-inline { font-size: 0.78rem; color: var(--parchment-dim); }
.mk-na { color: var(--parchment-dim); font-size: 0.85em; }
.mk-price { font-variant-numeric: tabular-nums; color: var(--parchment); font-weight: 600; }
.mk-price-cell { white-space: nowrap; }
/* W97: the marketplace's own indicator colors are RETIRED - the ONE
   site-wide .sort-ind rule (the W65 block) serves this table too. */
/* Headers may wrap ("Buy Now / Current Bid", "Time Remaining") so the
   eleven columns share the panel column; the arrow stays glued to the
   last word through the sort-link's own nowrap on that run. */
.mk-table th { white-space: normal; }
.mk-table .sort-link { white-space: normal; }
.mk-row { cursor: pointer; }
.mk-row:hover td { background: rgba(255, 255, 255, 0.035); }
.mk-row-on td { background: rgba(155, 109, 255, 0.10); }
.mk-empty-actions .btn, .mk-notice-actions .btn { padding: 0.3rem 0.65rem; font-size: 0.72rem; letter-spacing: 0.06em; margin: 0.1rem 0.15rem 0.1rem 0; }
.mk-watching { border-color: var(--arcane); color: var(--arcane-bright); }
.mk-locked { color: var(--parchment-dim); font-size: 0.8rem; letter-spacing: 0.06em; text-transform: uppercase; }
.mk-copy {
  display: inline-flex; align-items: center; justify-content: center; min-width: 1.75rem; min-height: 1.75rem;
  padding: 0 0.35rem; border: 1px solid var(--edge); border-radius: 4px; background: transparent;
  color: var(--parchment-dim); font-size: 0.85rem; line-height: 1; cursor: pointer; vertical-align: middle;
}
.mk-copy:hover, .mk-copy:focus-visible { color: var(--arcane-bright); border-color: var(--arcane); }

/* The time cell + toggle (section 5). */
.mk-time { display: inline-block; white-space: nowrap; font-variant-numeric: tabular-nums; }
.mk-time-v { display: inline; }
.mk-time-cap { font-family: var(--font-sans); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--parchment-dim); margin-right: 0.15rem; }
.mk-time-server .mk-time-val, .mk-time-server .mk-time-cap { color: var(--time-server); }
.mk-time-local .mk-time-val { color: var(--time-local); }
.mk-toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.6rem 1rem; margin: 0 0 0.75rem; }
.mk-toolbar .mk-limit { margin: 0; }
.mk-basis-form { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; margin-left: auto; }
.mk-basis .seg-opt { padding: 0.36rem 0.8rem; min-height: 2rem; }
.mk-basis .seg-opt:has(input[value="server"]:checked) { background: var(--time-server); border-color: var(--time-server); color: var(--ink); }
/* W77 sections 1 + 3: EVERY Local Time / Server Time toggle on the site
   (the Marketplace / Server Schedule control, the Recruitment board's
   Time Display, the listing form's availability basis) fills its checked
   option with the mode's own color - Server in --time-server, Local in
   --time-local - so the control reads as the legend of the values. */
.mk-basis .seg-opt:has(input[value="local"]:checked),
.rc-basis .seg-opt:has(input[value="local"]:checked),
.seg-radio[data-time-basis] .seg-opt:has(input[value="local"]:checked) { background: var(--time-local); border-color: var(--time-local); color: var(--ink); }
.rc-basis .seg-opt:has(input[value="server"]:checked),
.seg-radio[data-time-basis] .seg-opt:has(input[value="server"]:checked) { background: var(--time-server); border-color: var(--time-server); color: var(--ink); }
/* W89 carried item A: the note's slot is RESERVED. Both notes occupy the
   SAME grid cell - the reservation copies hidden but laid out - so the
   cell is the widest of them from first paint and time-basis.js's swap
   cannot resize it. .mk-basis-form carries margin-left:auto, so before
   this the note's width change moved the whole control (measured in W88
   at 398.8 -> 449.8px: the layout shift W87c attributed to machine
   contention). visibility:hidden, not display:none - a display:none copy
   reserves nothing. */
.mk-basis-note {
  display: inline-grid;
  font-size: 0.78rem;
  color: var(--parchment-dim);
}
.mk-basis-note > span { grid-area: 1 / 1; }
.mk-note-slot { visibility: hidden; pointer-events: none; }

/* Filters: explicit From / To labels, the price unit, comfortable targets. */
.mk-filter fieldset.mk-range { border: 0; padding: 0; margin: 0; min-width: 0; display: grid; grid-template-columns: auto 1fr auto 1fr; gap: 0.25rem 0.4rem; align-items: center; }
.mk-filter fieldset.mk-range legend { padding: 0; font-size: 0.8rem; color: var(--parchment-dim); margin-bottom: 0.25rem; }
.mk-range label { font-size: 0.74rem; color: var(--parchment-dim); text-transform: uppercase; letter-spacing: 0.05em; }
.mk-range-unit { color: var(--parchment-faint); text-transform: none; letter-spacing: 0; }
.mk-filter input[type="date"] { min-width: 0; }
.mk-filter.is-loading ~ .mk-toolbar ~ [data-live-results], .mk-filter.is-loading ~ [data-live-results], .mk-filter.is-loading ~ [data-mk-inventory] { opacity: 0.6; transition: opacity 0.15s; }
.mk-filter .btn { min-height: 2.2rem; }
.mk-chip { min-height: 2rem; display: inline-flex; align-items: center; padding: 0.3rem 0.8rem; }
.mk-chip:focus-visible, .mk-row:focus-visible, .mk-item-link:focus-visible, .mk-x-actions .btn:focus-visible { outline: 2px solid var(--arcane); outline-offset: 2px; }
.mk-count { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem; margin: 0 0 0.6rem; }
.mk-count-n { color: var(--parchment); font-weight: 600; }
.mk-count-note { color: var(--parchment-dim); font-size: 0.85rem; }
.mk-cat-row .seg, .mk-state-row .seg, .mk-char-row .seg { min-height: 2.1rem; }
.mk-char-meta { display: block; font-size: 0.66rem; letter-spacing: 0.04em; text-transform: none; color: inherit; opacity: 0.85; }
.mk-char-row .seg { flex-direction: column; align-items: flex-start; gap: 0; line-height: 1.2; padding-top: 0.35rem; padding-bottom: 0.35rem; }

/* Empty states inside the structure-always tables (section 17). */
.mk-table .table-empty-row td { padding: 1.2rem 0.9rem; }
.mk-empty-text { display: block; color: var(--parchment); margin-bottom: 0.5rem; }
.mk-empty-actions { display: inline-flex; flex-wrap: wrap; gap: 0.4rem; }
.mk-empty-state { margin: 0.5rem 0 0; }

/* State + transaction badges: never color alone (the text names the state). */
.mk-state-completed, .mk-state-released { border-color: var(--signal-online); color: var(--signal-online); }
.mk-state-outbid, .mk-state-pending { border-color: var(--gold); color: var(--gold); }
.mk-state-bid-leading, .mk-state-bid-won { border-color: var(--signal-online); color: var(--signal-online); }
.mk-state-bid-outbid, .mk-state-bid-lost, .mk-state-bid-released, .mk-state-bid-rejected { color: var(--parchment-dim); }
.mk-badge {
  display: inline-block; padding: 0.12rem 0.55rem; border-radius: 4px; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--parchment); border: 1px solid var(--edge-bright);
}
.mk-badge-purchase, .mk-badge-auction_won { border-color: var(--arcane); color: var(--arcane-bright); }
.mk-badge-sale, .mk-badge-auction_sale { border-color: var(--signal-online); color: var(--signal-online); }
.mk-badge-fee { border-color: var(--gold); color: var(--gold); }
.mk-badge-expired, .mk-badge-canceled { color: var(--parchment-dim); }
.mk-badge-lost, .mk-badge-released { border-color: var(--edge-bright); color: var(--parchment); }
/* W74 section 5: the W73 History details fold (.mk-txn-details) is retired - the expanded row carries the transaction facts. */

/* Listing setup (section 8) + the confirmation (9-10). */
.mk-crumbs { font-size: 0.8rem; color: var(--parchment-dim); margin: 0 0 0.6rem; letter-spacing: 0.04em; }
.mk-crumbs a { color: var(--parchment-dim); text-decoration: none; }
.mk-crumbs a:hover { color: var(--arcane-bright); }
.mk-crumbs [aria-current] { color: var(--parchment); }
.mk-item-summary { display: flex; align-items: center; gap: 0.8rem; padding: 0.7rem 0.9rem; border: 1px solid var(--edge-soft); border-radius: 6px; margin: 0 0 0.9rem; background: rgba(255, 255, 255, 0.02); }
.mk-item-summary .mk-icon { width: 40px; height: 40px; flex-basis: 40px; }
.mk-item-summary-text { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.mk-item-summary-text strong { color: var(--parchment); }
.mk-money { display: inline-flex; align-items: center; max-width: 100%; }
.mk-money input { min-width: 0; border-top-right-radius: 0; border-bottom-right-radius: 0; }
.mk-money-suffix { padding: 0.45rem 0.7rem; border: 1px solid var(--edge); border-left: 0; border-radius: 0 4px 4px 0; font-size: 0.8rem; color: var(--parchment-dim); white-space: nowrap; background: var(--ink-panel); line-height: 1.2; align-self: stretch; display: inline-flex; align-items: center; }
.mk-fee-preview { margin: 0.6rem 0 0; padding: 0.7rem 0.9rem; border-left: 3px solid var(--arcane); background: rgba(255, 255, 255, 0.03); font-size: 0.85rem; }
.mk-fee-preview .label { color: var(--arcane-bright); font-weight: 600; display: block; margin-bottom: 0.35rem; }
.mk-fee-lines { display: grid; grid-template-columns: auto 1fr; gap: 0.2rem 1rem; margin: 0; }
.mk-fee-lines > div { display: contents; }
.mk-fee-lines dt { color: var(--parchment-dim); }
.mk-fee-lines dd { margin: 0; color: var(--parchment); font-variant-numeric: tabular-nums; }
.mk-fee-lines div:last-child dd { font-weight: 700; color: var(--gold-bright); }
.mk-fee-text { margin: 0.4rem 0 0; color: var(--parchment-dim); font-size: 0.8rem; }
.mk-fee-rule { margin: 0.3rem 0 0; color: var(--parchment-dim); font-size: 0.78rem; }
.mk-listing-form .btn-primary:disabled, .mk-listing-form .btn-primary[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; }
.mk-confirm-actions .btn.is-busy { opacity: 0.6; pointer-events: none; }
.mk-detail-head { display: flex; align-items: flex-start; gap: 0.9rem; }
.mk-detail-head .mk-icon { width: 48px; height: 48px; flex-basis: 48px; margin-top: 0.2rem; }
.mk-detail-head h2 { margin-bottom: 0.2rem; }
.mk-countdown { font-variant-numeric: tabular-nums; }

/* Notices (sections 11 + 17): dismissible, action-oriented. */
.mk-notice { margin-bottom: 1rem; }
.mk-notice-actions { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; margin-left: 0.6rem; vertical-align: middle; }
.mk-dismiss { min-width: 1.9rem; padding-left: 0.5rem; padding-right: 0.5rem; }

/* Narrow widths (section 16): secondary columns fold away, the row's
   essentials (item, price, time remaining, actions) stay reachable
   without a page-wide scrollbar; the table-wrap scrolls if needed. */
/* The tables fit the ~900px panel column at 1366 by construction: a
   compact type size, wrapping time cells and stacked row actions, and
   the column priorities below (visual check, 2026-08-29). */
.mk-table { font-size: 0.88rem; }
.mk-table td, .mk-table th { padding-left: 0.55rem; padding-right: 0.55rem; }
.mk-table .mk-time, .mk-kv .mk-time { white-space: normal; }
.mk-table .mk-time-cap, .mk-kv .mk-time-cap { display: block; }
.mk-time-d, .mk-time-c { white-space: nowrap; }
.mk-cur-inline { display: none; margin-left: 0.3rem; font-size: 0.78rem; color: var(--parchment-dim); font-weight: 400; }
/* Priority 2 (Grade / Ench. / Currency - all repeated in the item
   line or shown inline in the price cell) folds below 1500px, the
   width where eleven columns fit the panel column; priority 4 (Bids /
   Transaction ID) below 768px; priority 3 (Ends) below 560px. The
   essentials - item, price, time remaining, actions - always stay, and
   the detail page carries everything. */
@media (max-width: 1499px) {
  .mk-table .mk-col-2 { display: none; }
  .mk-cur-inline { display: inline; }
  .mk-item-cell { min-width: 9.5rem; }
}
@media (max-width: 559px) {
  table.arcanis.mk-kv th { white-space: normal; width: 40%; }
  table.arcanis.mk-kv td { overflow-wrap: anywhere; }
}
@media (max-width: 767px) {
  .mk-table .mk-col-4 { display: none; }
  .mk-toolbar { flex-direction: column; align-items: stretch; }
  .mk-basis-form { margin-left: 0; }
  .mk-filter fieldset.mk-range { grid-template-columns: auto 1fr; }
}
@media (max-width: 559px) {
  .mk-table .mk-col-3, .mk-table .mk-col-type { display: none; }
  .mk-item-cell { min-width: 9rem; }
}

/* ==========================================================================
   W74 (docs/specs/W74-CONSOLIDATED-UPDATES.md): the consolidated updates -
   the Server Schedule Local / Server toggle over ONE time cell (section
   9), uniform Drops controls (section 10), the site-wide server-time
   green (section 7), currency images (section 4), expandable rows
   (section 5) and the site-wide table standard (section 6).
   ========================================================================== */

/* Section 9: the boss tables carry the ONE site-wide time cell
   (includes/time-cell.html - the marketplace's .mk-time family, one
   basis shown at a time, the server value in the site's green). The
   cap sits on its own line so the date and the clock keep their
   nowrap runs inside the schedule column. */
.boss-sched .bt-time .mk-time { white-space: normal; }
.boss-sched .bt-time .mk-time-cap { display: block; }
/* The W64 .bt-wrow window rows and the W92b inline-label rule are
   RETIRED with the estimated window (W96): the Respawn Window cell is
   ONE span (.bt-window) on every boss table - the duration, then the
   "remaining" countdown - in the clock family the .bt-time cell already
   sets, with the same nowrap the countdown column keeps so a ticking
   value never re-wraps. */
.boss-table .bt-window { white-space: nowrap; font-variant-numeric: tabular-nums; }
/* W96: the "Respawn Window Starts In" header is the spec's exact string
   and wider than the countdown column its values need (the widest value,
   "15d 22h 13m 08s", is ~115px; the header ~222px in a 140px column).
   The ONE sort link is nowrap site-wide (W65), which made this header
   push the table 82px past its wrap (measured by
   scripts/w96-boss-rows-probe.py); this header alone wraps at its spaces
   - two lines, "Respawn Window" over "Starts In" with the indicator -
   so the column keeps the width its VALUES need and Name keeps its
   room. Header-only: the body cells stay nowrap. */
.boss-table .sort-link[data-sort-key="countdown"] { white-space: normal; }
.sched-basis-bar { margin: 0.7rem 0 0.5rem; }
/* Section 10's uniform Drops control box is GONE with its column (W92,
   item 7 / W91 Q7). */

/* Section 4: the currency image - the game's own item icon (Adena 57,
   Gold Coin 4037, Gold Bar 2807) beside the exact currency name, ONE
   include (market/_currency.html) wherever a currency renders: cells,
   the detail / confirmation rows, the price suffix, the setup control
   and the filter. */
.mk-cur { display: inline-flex; align-items: center; gap: 0.35rem; vertical-align: middle; white-space: nowrap; }
.mk-cur-icon { width: 20px; height: 20px; flex: 0 0 20px; border-radius: 3px; image-rendering: auto; }
.mk-cur-inline .mk-cur, .mk-money-suffix .mk-cur { gap: 0.3rem; }
.mk-cur-inline .mk-cur-icon, .mk-money-suffix .mk-cur-icon { width: 16px; height: 16px; flex-basis: 16px; }
.mk-cur-seg .seg-opt > span { display: inline-flex; align-items: center; }
.mk-cur-seg .mk-cur-icon { width: 18px; height: 18px; flex-basis: 18px; }
.mk-cur-field .mk-cur-pick { display: inline-flex; align-items: center; gap: 0.4rem; }
.mk-cur-pick-icons { display: inline-flex; align-items: center; gap: 0.15rem; }
.mk-cur-pick-icons .mk-cur-icon { width: 18px; height: 18px; flex-basis: 18px; }
.mk-cur-pick select { flex: 1 1 auto; min-width: 0; }

/* Section 5: clickable + expandable rows. The row is a real focusable
   control (tabindex, aria-expanded); the expanded row follows it,
   spans the table and stays visually attached (the pair shares one
   zebra shade, an inset accent along the left edge while open). Hidden
   until opened with scripting (the synchronous .js flag), always
   visible without. The expanded view is a three-column grid - item /
   listing facts / actions - collapsing to one column in the narrow
   panel column. */
.mk-row[data-row-expand] { cursor: pointer; }
.mk-row .mk-chev { display: inline-block; width: 0.55rem; margin-right: 0.3rem; color: var(--parchment-faint); font-size: 0.7rem; transition: transform 0.15s ease; }
.mk-row .mk-chev::before { content: "\25B8"; }
.mk-row.is-open .mk-chev { transform: rotate(90deg); color: var(--arcane-bright); }
.mk-row.is-open td { background: rgba(155, 109, 255, 0.10); }
.mk-row.is-open td:first-child, .mk-expand.is-open td { box-shadow: inset 3px 0 0 var(--arcane); }
.js .mk-expandable tr.mk-expand:not(.is-open) { display: none; }
.mk-expandable tr.mk-row td { border-bottom: none; }
.mk-expandable tr.mk-expand td { padding-top: 0; padding-bottom: 0.9rem; }
table.arcanis.mk-expandable tbody tr:nth-child(4n+1) td,
table.arcanis.mk-expandable tbody tr:nth-child(4n+2) td { background: var(--row-shade-a); }
table.arcanis.mk-expandable tbody tr:nth-child(4n+3) td,
table.arcanis.mk-expandable tbody tr:nth-child(4n+4) td { background: var(--row-shade-b); }
table.arcanis.mk-expandable tbody tr.mk-row.is-open td,
table.arcanis.mk-expandable tbody tr.mk-expand.is-open td { background: rgba(155, 109, 255, 0.08); }
.mk-expand-inner { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr) minmax(0, 1fr); gap: 0.6rem 1.6rem; margin-left: 1.6rem; padding: 0.55rem 0 0 0.9rem; border-left: 2px solid var(--arcane); font-size: 0.86rem; }
.mk-expand-history, .mk-expand-sell { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); }
.mk-x-head { display: flex; align-items: flex-start; gap: 0.6rem; margin-bottom: 0.5rem; }
.mk-x-head .mk-icon { width: 40px; height: 40px; flex-basis: 40px; }
.mk-x-title { display: flex; flex-direction: column; min-width: 0; }
.mk-x-title strong { color: var(--parchment); }
.mk-x-facts { margin: 0; display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 0.22rem 0.8rem; }
.mk-x-facts > div { display: contents; }
.mk-x-facts dt { color: var(--parchment-faint); font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.06em; padding-top: 0.15rem; }
.mk-x-facts dd { margin: 0; color: var(--parchment); min-width: 0; overflow-wrap: anywhere; }
.mk-x-facts .mk-time { white-space: normal; }
.mk-x-links { margin: 0.6rem 0 0; }
.mk-x-h { margin: 0 0 0.35rem; color: var(--gold); font-size: 0.82rem; text-transform: uppercase; letter-spacing: 0.08em; }
.mk-x-form { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 0.8rem; }
.mk-x-form select, .mk-x-form input { max-width: 100%; }
.mk-x-form .btn { align-self: flex-start; }
.mk-x-label { font-size: 0.74rem; color: var(--parchment-dim); }
.mk-x-actions .info-note, .mk-x-actions .help-msg { margin: 0 0 0.5rem; }
.mk-x-watch { margin-top: 0.25rem; }
@media (max-width: 1099px) {
  .mk-expand-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .mk-expand-history, .mk-expand-sell { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 767px) {
  .mk-expand-inner { grid-template-columns: minmax(0, 1fr); margin-left: 0.4rem; padding-left: 0.6rem; }
}

/* Section 7: the ONE class for a SERVER date and time value - the
   main server clock's green (--signal-online). Every |zoned render
   (the site display timezone is the server timezone), the game-data
   pages' siege / cycle / respawn strings (|server_time), the login
   history instants and the schedule / marketplace server cells carry
   it; local date/time values keep the standard ink.
   W77 section 1: the color is the ONE --time-server sky blue now. */
.t-server { color: var(--time-server); }
.t-server .lh-date, .t-server .lh-time { color: inherit; }

/* Section 6: the permanent site-wide table standard. Every data table
   renders through {% stacktable %} (core/templatetags/tablestack.py):
   the body cells carry their column label (data-label) and the table
   wrapper a breakpoint class from its column count. Above the breakpoint the
   table lays out normally with WRAPPING cells (nowrap only where a
   column is typed short); below it the table becomes a clearly
   structured stacked layout - one block per row, each cell a
   label / value pair, the header row folded into a "Sort by" chip strip
   (the SAME sort links - section 14 sorting keeps working when stacked),
   spanning cells (empty states, expanded rows, details rows) at full
   width without a label. Never an inner scrollbar, never clipped,
   never unreadably small. */
.tbl-stack > table.arcanis td { overflow-wrap: anywhere; }
.tbl-stack > table.arcanis td.col-num, .tbl-stack > table.arcanis td.col-fit { overflow-wrap: normal; }
/* Headers keep the table skin's nowrap above the breakpoint (the W50 ledger
   interiors are PROTECTED geometry); only cells wrap. */
@media (max-width: 599px) {
  .stack-lt-600 > table.arcanis { display: block; width: auto; min-width: 0; table-layout: auto; }
  .stack-lt-600 > table.arcanis thead { display: block; margin: 0 0 0.35rem; }
  .stack-lt-600 > table.arcanis thead tr { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; padding: 0.35rem 0.75rem; border-bottom: 1px solid var(--edge-soft); }
  .stack-lt-600 > table.arcanis thead tr::before { content: "Sort by"; font-family: var(--font-sans); font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--parchment-faint); margin-right: 0.2rem; }
  .stack-lt-600 > table.arcanis th { display: inline-block; width: auto !important; min-width: 0; padding: 0.18rem 0.55rem; border: 1px solid var(--edge); border-bottom: 1px solid var(--edge); border-radius: 999px; font-size: 0.72rem; letter-spacing: 0.04em; white-space: nowrap; }
  .stack-lt-600 > table.arcanis th:not(.th-sort) { display: none; }
  .stack-lt-600 > table.arcanis th.th-sort:has(.sort-link.is-active) { border-color: var(--arcane); }
  .stack-lt-600 > table.arcanis tbody, .stack-lt-600 > table.arcanis tr, .stack-lt-600 > table.arcanis td { display: block; width: auto; }
  .stack-lt-600 > table.arcanis colgroup { display: none; }
  .stack-lt-600 > table.arcanis tr { padding: 0.55rem 0.75rem; border-bottom: 1px solid var(--edge-soft); }
  .stack-lt-600 > table.arcanis td { display: grid; grid-template-columns: minmax(6.5rem, 38%) minmax(0, 1fr); gap: 0.25rem 0.7rem; align-items: baseline; padding: 0.22rem 0; border: 0; white-space: normal; text-align: left; background: transparent !important; box-shadow: none; }
  .stack-lt-600 > table.arcanis td::before { content: attr(data-label); color: var(--parchment-faint); font-family: var(--font-sans); font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase; line-height: 1.4; }
  .stack-lt-600 > table.arcanis td[colspan], .stack-lt-600 > table.arcanis td:not([data-label]) { grid-template-columns: minmax(0, 1fr); }
  .stack-lt-600 > table.arcanis td[colspan]::before, .stack-lt-600 > table.arcanis td:not([data-label])::before { content: none; }
  .stack-lt-600 > table.arcanis tbody tr:nth-child(odd) { background: var(--row-shade-a); }
  .stack-lt-600 > table.arcanis tbody tr:nth-child(even) { background: var(--row-shade-b); }
  .stack-lt-600 > table.arcanis.sticky-lead td:first-child, .stack-lt-600 > table.arcanis.sticky-lead th:first-child { position: static; }
}
@media (max-width: 899px) {
  .stack-lt-900 > table.arcanis { display: block; width: auto; min-width: 0; table-layout: auto; }
  .stack-lt-900 > table.arcanis thead { display: block; margin: 0 0 0.35rem; }
  .stack-lt-900 > table.arcanis thead tr { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; padding: 0.35rem 0.75rem; border-bottom: 1px solid var(--edge-soft); }
  .stack-lt-900 > table.arcanis thead tr::before { content: "Sort by"; font-family: var(--font-sans); font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--parchment-faint); margin-right: 0.2rem; }
  .stack-lt-900 > table.arcanis th { display: inline-block; width: auto !important; min-width: 0; padding: 0.18rem 0.55rem; border: 1px solid var(--edge); border-bottom: 1px solid var(--edge); border-radius: 999px; font-size: 0.72rem; letter-spacing: 0.04em; white-space: nowrap; }
  .stack-lt-900 > table.arcanis th:not(.th-sort) { display: none; }
  .stack-lt-900 > table.arcanis th.th-sort:has(.sort-link.is-active) { border-color: var(--arcane); }
  .stack-lt-900 > table.arcanis tbody, .stack-lt-900 > table.arcanis tr, .stack-lt-900 > table.arcanis td { display: block; width: auto; }
  .stack-lt-900 > table.arcanis colgroup { display: none; }
  .stack-lt-900 > table.arcanis tr { padding: 0.55rem 0.75rem; border-bottom: 1px solid var(--edge-soft); }
  .stack-lt-900 > table.arcanis td { display: grid; grid-template-columns: minmax(6.5rem, 38%) minmax(0, 1fr); gap: 0.25rem 0.7rem; align-items: baseline; padding: 0.22rem 0; border: 0; white-space: normal; text-align: left; background: transparent !important; box-shadow: none; }
  .stack-lt-900 > table.arcanis td::before { content: attr(data-label); color: var(--parchment-faint); font-family: var(--font-sans); font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase; line-height: 1.4; }
  .stack-lt-900 > table.arcanis td[colspan], .stack-lt-900 > table.arcanis td:not([data-label]) { grid-template-columns: minmax(0, 1fr); }
  .stack-lt-900 > table.arcanis td[colspan]::before, .stack-lt-900 > table.arcanis td:not([data-label])::before { content: none; }
  .stack-lt-900 > table.arcanis tbody tr:nth-child(odd) { background: var(--row-shade-a); }
  .stack-lt-900 > table.arcanis tbody tr:nth-child(even) { background: var(--row-shade-b); }
  .stack-lt-900 > table.arcanis.mk-expandable tbody tr:nth-child(4n+1), .stack-lt-900 > table.arcanis.mk-expandable tbody tr:nth-child(4n+2) { background: var(--row-shade-a); }
  .stack-lt-900 > table.arcanis.mk-expandable tbody tr:nth-child(4n+3), .stack-lt-900 > table.arcanis.mk-expandable tbody tr:nth-child(4n+4) { background: var(--row-shade-b); }
  .stack-lt-900 > table.arcanis.sticky-lead td:first-child, .stack-lt-900 > table.arcanis.sticky-lead th:first-child { position: static; }
  .stack-lt-900 > table.arcanis .mk-time-cap { display: inline; }
  .stack-lt-900 > table.arcanis .mk-expand-inner { margin-left: 0; }
}
@media (max-width: 1099px) {
  .stack-lt-1100 > table.arcanis,
  .layout-panel .stack-lt-900 > table.arcanis { display: block; width: auto; min-width: 0; table-layout: auto; }
  .stack-lt-1100 > table.arcanis thead,
  .layout-panel .stack-lt-900 > table.arcanis thead { display: block; margin: 0 0 0.35rem; }
  .stack-lt-1100 > table.arcanis thead tr,
  .layout-panel .stack-lt-900 > table.arcanis thead tr { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; padding: 0.35rem 0.75rem; border-bottom: 1px solid var(--edge-soft); }
  .stack-lt-1100 > table.arcanis thead tr::before,
  .layout-panel .stack-lt-900 > table.arcanis thead tr::before { content: "Sort by"; font-family: var(--font-sans); font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--parchment-faint); margin-right: 0.2rem; }
  .stack-lt-1100 > table.arcanis th,
  .layout-panel .stack-lt-900 > table.arcanis th { display: inline-block; width: auto !important; min-width: 0; padding: 0.18rem 0.55rem; border: 1px solid var(--edge); border-bottom: 1px solid var(--edge); border-radius: 999px; font-size: 0.72rem; letter-spacing: 0.04em; white-space: nowrap; }
  .stack-lt-1100 > table.arcanis th:not(.th-sort),
  .layout-panel .stack-lt-900 > table.arcanis th:not(.th-sort) { display: none; }
  .stack-lt-1100 > table.arcanis th.th-sort:has(.sort-link.is-active),
  .layout-panel .stack-lt-900 > table.arcanis th.th-sort:has(.sort-link.is-active) { border-color: var(--arcane); }
  .stack-lt-1100 > table.arcanis tbody,
  .layout-panel .stack-lt-900 > table.arcanis tbody,
  .stack-lt-1100 > table.arcanis tr,
  .layout-panel .stack-lt-900 > table.arcanis tr,
  .stack-lt-1100 > table.arcanis td,
  .layout-panel .stack-lt-900 > table.arcanis td { display: block; width: auto; }
  .stack-lt-1100 > table.arcanis colgroup,
  .layout-panel .stack-lt-900 > table.arcanis colgroup { display: none; }
  .stack-lt-1100 > table.arcanis tr,
  .layout-panel .stack-lt-900 > table.arcanis tr { padding: 0.55rem 0.75rem; border-bottom: 1px solid var(--edge-soft); }
  .stack-lt-1100 > table.arcanis td,
  .layout-panel .stack-lt-900 > table.arcanis td { display: grid; grid-template-columns: minmax(6.5rem, 38%) minmax(0, 1fr); gap: 0.25rem 0.7rem; align-items: baseline; padding: 0.22rem 0; border: 0; white-space: normal; text-align: left; background: transparent !important; box-shadow: none; }
  .stack-lt-1100 > table.arcanis td::before,
  .layout-panel .stack-lt-900 > table.arcanis td::before { content: attr(data-label); color: var(--parchment-faint); font-family: var(--font-sans); font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase; line-height: 1.4; }
  .stack-lt-1100 > table.arcanis td[colspan],
  .layout-panel .stack-lt-900 > table.arcanis td[colspan],
  .stack-lt-1100 > table.arcanis td:not([data-label]),
  .layout-panel .stack-lt-900 > table.arcanis td:not([data-label]) { grid-template-columns: minmax(0, 1fr); }
  .stack-lt-1100 > table.arcanis td[colspan]::before,
  .layout-panel .stack-lt-900 > table.arcanis td[colspan]::before,
  .stack-lt-1100 > table.arcanis td:not([data-label])::before,
  .layout-panel .stack-lt-900 > table.arcanis td:not([data-label])::before { content: none; }
  .stack-lt-1100 > table.arcanis tbody tr:nth-child(odd),
  .layout-panel .stack-lt-900 > table.arcanis tbody tr:nth-child(odd) { background: var(--row-shade-a); }
  .stack-lt-1100 > table.arcanis tbody tr:nth-child(even),
  .layout-panel .stack-lt-900 > table.arcanis tbody tr:nth-child(even) { background: var(--row-shade-b); }
  .stack-lt-1100 > table.arcanis.boss-sched tbody tr:nth-child(4n+1), .stack-lt-1100 > table.arcanis.boss-sched tbody tr:nth-child(4n+2),
  .stack-lt-1100 > table.arcanis.mk-expandable tbody tr:nth-child(4n+1),
  .layout-panel .stack-lt-900 > table.arcanis.mk-expandable tbody tr:nth-child(4n+1),
  .stack-lt-1100 > table.arcanis.mk-expandable tbody tr:nth-child(4n+2),
  .layout-panel .stack-lt-900 > table.arcanis.mk-expandable tbody tr:nth-child(4n+2) { background: var(--row-shade-a); }
  .stack-lt-1100 > table.arcanis.boss-sched tbody tr:nth-child(4n+3), .stack-lt-1100 > table.arcanis.boss-sched tbody tr:nth-child(4n+4),
  .stack-lt-1100 > table.arcanis.mk-expandable tbody tr:nth-child(4n+3),
  .layout-panel .stack-lt-900 > table.arcanis.mk-expandable tbody tr:nth-child(4n+3),
  .stack-lt-1100 > table.arcanis.mk-expandable tbody tr:nth-child(4n+4),
  .layout-panel .stack-lt-900 > table.arcanis.mk-expandable tbody tr:nth-child(4n+4) { background: var(--row-shade-b); }
  .stack-lt-1100 > table.arcanis.sticky-lead td:first-child, .stack-lt-1100 > table.arcanis.sticky-lead th:first-child,
  .layout-panel .stack-lt-900 > table.arcanis.sticky-lead td:first-child, .layout-panel .stack-lt-900 > table.arcanis.sticky-lead th:first-child { position: static; }
  .stack-lt-1100 > table.arcanis .mk-time-cap,
  .layout-panel .stack-lt-900 > table.arcanis .mk-time-cap { display: inline; }
  .stack-lt-1100 > table.arcanis .mk-expand-inner,
  .layout-panel .stack-lt-900 > table.arcanis .mk-expand-inner,
  .stack-lt-1100 > table.arcanis .bt-details-inner,
  .layout-panel .stack-lt-900 > table.arcanis .bt-details-inner { margin-left: 0; }
  .stack-lt-1100 > table.arcanis.boss-sched tr[data-boss-row] td,
  .layout-panel .stack-lt-900 > table.arcanis.boss-sched tr[data-boss-row] td { border-bottom: 0; }
  .stack-lt-1100 > table.arcanis .mk-row .mk-chev,
  .layout-panel .stack-lt-900 > table.arcanis .mk-row .mk-chev { display: none; }
}
/* The marketplace tables (10 / 9 / 8 / 6 columns) in a ~900px panel
   column: the priority-2 columns (Grade / Ench. / Currency - repeated
   in the item line and inline in the price cell) fold at EVERY width
   now (the panel column never widens with the viewport - W64), so the
   currency image rides the price cell inline; the stacked layout takes
   over below the breakpoint. */
.mk-table .mk-col-2 { display: none; }
.mk-cur-inline { display: inline; }

/* ==========================================================================
   W75 (docs/specs/SITE_MARKETPLACE_UI_W75_2026-08-29.md): the Marketplace
   UI refinement. Everything below is scoped to the marketplace (.mk-*)
   over the W72-W74 blocks: the larger, state-clear chevron (2), the
   expanded row without the repeated image (3-4), the row hover on every
   marketplace table (8), the Back link as a visible button (6), the
   Watch action in the listing header (O), the ONE Acting character
   block over the action panels (H), no per-row Local / Server caption
   in the table cells (I), themed date inputs (L), the clearly distinct
   disabled Review Listing (M), the currency filter toggles (J), the
   neutral sort affordance at readable contrast (Q / R) and the stacked
   labels lifted to AA (R).
   ========================================================================== */

/* Item 2: the chevron is a real 1.15rem glyph box in the accent color -
   right = collapsed, rotated to down = expanded; the row's hover and
   focus brighten it so the affordance is discovered by pointing. */
.mk-row .mk-chev {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.15rem; height: 1.15rem; margin-right: 0.35rem;
  color: var(--arcane-bright); font-size: 1.05rem; line-height: 1;
  transition: transform 0.15s ease, color 0.15s ease; transform-origin: 50% 50%;
}
.mk-row .mk-chev::before { content: "\25B8"; }
.mk-row:hover .mk-chev, .mk-row:focus-visible .mk-chev { color: var(--gold-bright); }
.mk-row.is-open .mk-chev { transform: rotate(90deg); color: var(--gold-bright); }
.mk-table td.col-num { white-space: nowrap; }

/* Item 8: ONE hover treatment for every marketplace table row - a
   clearly visible arcane tint the text stays readable on (parchment on
   the tinted shade keeps > 9:1), distinct from the open / selected row
   tint (which deepens further on hover) and never applied to the
   expanded detail row, the empty-state row or the flagged inset. */
:root { --mk-row-hover: rgba(201, 174, 255, 0.13); --mk-row-open: rgba(155, 109, 255, 0.10); --mk-row-open-hover: rgba(155, 109, 255, 0.2); }
table.arcanis.row-hover tbody tr:not(.mk-expand):not(.table-empty-row):not(.bt-details):hover > td,
table.arcanis.mk-table tbody tr:not(.mk-expand):not(.table-empty-row):hover > td { background: var(--mk-row-hover); }
table.arcanis.mk-table tbody tr.mk-row.is-open > td,
table.arcanis.mk-expandable tbody tr.mk-row.is-open td,
table.arcanis.mk-expandable tbody tr.mk-expand.is-open td { background: var(--mk-row-open); }
table.arcanis.mk-table tbody tr.mk-row.is-open:hover > td { background: var(--mk-row-open-hover); }
table.arcanis.mk-table tbody tr.mk-row:hover > td:first-child { box-shadow: inset 3px 0 0 var(--arcane-bright); }
.tbl-stack > table.arcanis.row-hover tbody tr:not(.mk-expand):not(.table-empty-row):not(.bt-details):hover,
.tbl-stack > table.arcanis.mk-table tbody tr:not(.mk-expand):not(.table-empty-row):hover { background: var(--mk-row-hover); }
.tbl-stack > table.arcanis.mk-table tbody tr.mk-row.is-open,
.tbl-stack > table.arcanis.mk-table tbody tr.mk-expand.is-open { background: var(--mk-row-open); }
.tbl-stack > table.arcanis.mk-table tbody tr.mk-row.is-open:hover { background: var(--mk-row-open-hover); }
.mk-row-on td { background: var(--mk-row-open); }

/* Items 3-4: the expanded row is the essentials + the actions - two
   columns, no image head. */
.mk-expand-inner { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); }
.mk-expand-sell { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.mk-expand-history { grid-template-columns: minmax(0, 1fr); }
.mk-x-item .mk-x-facts:empty { display: none; }

/* Item 6: the Back link is a visible button-styled control. */
.mk-back { margin: 0 0 0.9rem; }
.mk-back .mk-back-link { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.45rem 0.9rem; border-color: var(--edge-bright); color: var(--parchment); font-size: 0.78rem; }
.mk-back .mk-back-link:hover { color: var(--arcane-bright); border-color: var(--arcane); }

/* Item O: Watch sits in the header row beside the title and status. */
.mk-detail-head { align-items: flex-start; flex-wrap: wrap; }
.mk-detail-title { flex: 1 1 14rem; min-width: 0; }
.mk-head-watch { margin: 0.25rem 0 0; flex: 0 0 auto; }
.mk-head-watch .btn { white-space: nowrap; }

/* Item H: the ONE Acting character block above the action panels. */
.mk-acting { max-width: 28rem; margin-bottom: 1rem; }
.mk-acting select { width: 100%; }
.mk-act-form .mk-action-form { margin-top: 0; }
.mk-act-form .mk-action-form input { width: 100%; }

/* Item I: no per-row Local / Server caption inside the marketplace
   tables and detail cells - the toggle and its note establish the
   mode; the caption stays in the DOM for the switch script and for
   assistive technology (sr-only). */
.mk-table .mk-time-cap, .mk-kv .mk-time-cap, .mk-x-facts .mk-time-cap,
.tbl-stack > table.arcanis.mk-table .mk-time-cap,
.stack-lt-1100 > table.arcanis.mk-table .mk-time-cap,
.layout-panel .stack-lt-900 > table.arcanis.mk-table .mk-time-cap {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0; display: inline;
}
.mk-table .mk-time, .mk-kv .mk-time, .mk-x-facts .mk-time { position: relative; }

/* Item L: the History date inputs wear the site's control skin. */
.mk-filter input[type="date"] {
  width: 100%; max-width: 24rem; background: var(--ink-panel); border: 1px solid var(--edge); border-radius: 3px;
  color: var(--parchment); padding: 0.45rem 0.6rem; font: inherit; font-size: 0.85rem; color-scheme: dark; min-height: 2.2rem;
}
.mk-filter input[type="date"]:focus { border-color: var(--arcane); outline: none; }
.mk-filter input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; opacity: 0.75; filter: invert(0.85) sepia(0.2); }

/* Item M: Review Listing - the disabled state is an unmistakable
   dashed, dimmed outline; the enabled state is the filled primary. */
.mk-listing-form .mk-review:disabled, .mk-listing-form .mk-review[aria-disabled="true"] {
  opacity: 1; background: transparent; border: 1px dashed var(--edge-bright); color: var(--parchment-faint); box-shadow: none; cursor: not-allowed;
}
.mk-listing-form .mk-review:not(:disabled):not([aria-disabled="true"]) {
  background: var(--arcane); border-color: var(--arcane-bright); color: var(--ink); box-shadow: 0 0 0 1px rgba(201, 174, 255, 0.35), 0 0 14px rgba(155, 109, 255, 0.35);
}
.mk-listing-form .mk-review:not(:disabled):not([aria-disabled="true"]):hover { background: var(--arcane-bright); }

/* Item J: the currency toggles - real buttons around the images. */
.mk-cur-toggle {
  display: inline-flex; align-items: center; justify-content: center; width: 1.9rem; height: 1.9rem; padding: 0;
  border: 1px solid transparent; border-radius: 4px; background: transparent; cursor: pointer; opacity: 0.75;
}
.mk-cur-toggle:hover, .mk-cur-toggle:focus-visible { opacity: 1; border-color: var(--edge-bright); outline: none; }
.mk-cur-toggle.is-on { opacity: 1; border-color: var(--arcane); background: rgba(155, 109, 255, 0.18); box-shadow: inset 0 0 0 1px var(--arcane); }
.mk-cur-pick-icons { gap: 0.2rem; }

/* Items Q + R: the inactive sort affordance reads at AA (parchment-faint
   at full opacity inside the panel = 6.1:1) beside the bright arrow on
   the active column; the stacked-layout labels and the "Sort by" prefix
   lift from parchment-faint to parchment-dim site-wide (3.5:1 -> 7.3:1
   on the public ground at their 11px size). W97: the marketplace-only
   .sort-ind pair that carried item R is RETIRED - the same values are
   now the ONE site-wide rule (the W65 block, spec item 6). */
.stack-lt-600 > table.arcanis td::before, .stack-lt-900 > table.arcanis td::before,
.stack-lt-1100 > table.arcanis td::before, .layout-panel .stack-lt-900 > table.arcanis td::before,
.stack-lt-600 > table.arcanis thead tr::before, .stack-lt-900 > table.arcanis thead tr::before,
.stack-lt-1100 > table.arcanis thead tr::before, .layout-panel .stack-lt-900 > table.arcanis thead tr::before { color: var(--parchment-dim); }

/* The item details page shares the listing detail skin. */
.mk-item-detail-notes { display: flex; flex-direction: column; gap: 0.6rem; align-self: start; }
.mk-item-detail-notes .mk-eligibility, .mk-item-detail-notes .mk-limit { margin: 0; }

/* ==================================================================
   W76 (docs/specs/W76-RECRUITMENT-CENTER-REORG.md): the Recruitment
   Center reorganization - the three-tab strip, the Listings filter
   GRID (labels bound to their controls), the matching count, the
   compact mass-event card with ONE Register action, the My Listings
   management table, the sectioned form with its navigator, the
   persistent action area and the autosave status, the visual
   consistency pass (section 12: control heights, readable small text,
   AA statuses, sentence-case labels, hover and focus states, selected
   states never by color alone, larger click targets).
   ================================================================== */

/* --- Section 1: the primary tabs read as larger targets. --- */
.tab-row[aria-label="Recruitment Center"] .tab { min-height: 2.4rem; display: inline-flex; align-items: center; }

/* --- Section 2: the filter grid - four columns, ONE .rf-field per
   control (the label sits directly above it; a wrap moves the pair as
   a unit), the search field wide, the whole thing stacking cleanly on
   narrow screens. Supersedes the W63 inline label/select run. --- */
.recruit-filter.rf-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.75rem 1rem;
  align-items: end;
  margin: 0 0 var(--block-gap);
}
.rf-field { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
.rf-field > label, .rf-field > .rf-label {
  font-size: 0.78rem; letter-spacing: 0.04em; color: var(--parchment-dim); line-height: 1.2;
}
.rf-field > select, .rf-field > input[type="search"] {
  width: 100%; max-width: 100%; min-height: 2.35rem; box-sizing: border-box;
}
.recruit-filter.rf-grid select { max-width: 100%; }
.rf-field .seg-radio { display: inline-flex; min-height: 2.35rem; }
.rf-field .seg-radio .seg-opt { flex: 1 1 0; justify-content: center; padding: 0.3rem 0.5rem; }
.rf-clear .btn { min-height: 2.35rem; width: 100%; justify-content: center; }
.recruit-filter.rf-grid .rc-basis { margin-left: 0; }
@media (max-width: 1099px) {
  .recruit-filter.rf-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .recruit-filter.rf-grid { grid-template-columns: minmax(0, 1fr); }
}
.rc-count { margin: 0 0 0.7rem; font-size: 0.85rem; color: var(--parchment-dim); }
.rc-count strong { color: var(--parchment); }
.rc-auto-note { margin-bottom: 0.9rem; }

/* --- Section 2: the compact mass-event card. Distinct from the
   player-managed cards by its gold-tinted edge and the Automatic badge
   (its tooltip explains it); the facts read as label / value pairs,
   the server value in the site's green (W74), the countdown with
   seconds; the roster is a native details on the count. --- */
.rc-event-card { border-color: rgba(237, 217, 168, 0.35); }
.rc-event-card .rc-ev-head h3 { margin: 0; }
.rc-auto-badge { cursor: help; }
.rc-auto-badge:focus-visible { outline: 2px solid var(--arcane-bright); outline-offset: 1px; }
.rc-ev-facts { display: grid; gap: 0.25rem 1rem; margin: 0.5rem 0 0.2rem; font-size: 0.9rem; }
.rc-ev-facts > div { display: flex; flex-wrap: wrap; gap: 0.15rem 0.4rem; align-items: baseline; margin: 0; }
.rc-ev-facts dt, .rc-ev-facts dd { margin: 0; }
.rc-ev-cap { color: var(--parchment-dim); font-size: 0.82rem; letter-spacing: 0.02em; }
.rc-ev-time .mk-time-cap.rc-ev-cap { text-transform: none; letter-spacing: 0.02em; font-size: 0.82rem; }
.rc-ev-at { color: var(--parchment-dim); }
.rc-countdown { font-variant-numeric: tabular-nums; color: var(--time-local); font-weight: 600; } /* W77 section 2 */
.rc-reg-open { color: var(--signal-online); font-weight: 600; }
.rc-roster > summary { cursor: pointer; list-style: none; display: inline-block; color: var(--arcane-bright); border-bottom: 1px dotted var(--arcane-bright); min-height: 1.6rem; }
.rc-roster > summary::-webkit-details-marker { display: none; }
.rc-roster > summary::after { content: " \25BE"; }
.rc-roster[open] > summary::after { content: " \25B4"; }
.rc-roster-list { list-style: none; margin: 0.4rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.35rem; }
.rc-roster-list .badge.rc-roster-mine { border-color: var(--signal-online); color: var(--signal-online); }
.rc-roster-count { font-weight: 600; }
.rc-ev-actions { margin-top: 0.55rem; }
.rc-ev-more { margin-top: 0.35rem; }
.rc-ev-detail { display: grid; gap: 0.45rem; margin: 0.4rem 0 0.6rem; font-size: 0.88rem; }
.rc-ev-detail > div { display: grid; grid-template-columns: 7.5rem minmax(0, 1fr); gap: 0.4rem; }
.rc-ev-detail dt { color: var(--parchment-dim); }
.rc-ev-detail dd { margin: 0; }
.rc-group-code { font-family: var(--font-mono, monospace); letter-spacing: 0.12em; color: var(--gold-bright); }
.rc-register { display: block; }
.rc-reg-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0.6rem 1rem; align-items: start; }
.rc-reg-field { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.85rem; color: var(--parchment-dim); min-width: 0; }
.rc-reg-field select, .rc-reg-field input[type="text"] { max-width: 100%; width: 100%; box-sizing: border-box; }
.rc-reg-mode { border: 1px solid var(--edge); border-radius: 4px; padding: 0.45rem 0.7rem 0.55rem; margin: 0; }
.rc-reg-mode legend { padding: 0 0.3rem; font-size: 0.82rem; color: var(--parchment-dim); }
.rc-reg-mode .inline-check { display: flex; gap: 0.45rem; align-items: center; margin: 0.15rem 0; color: var(--parchment); min-height: 1.7rem; }
.rc-reg-mode input[type="text"] { margin-top: 0.35rem; }
.rc-reg-mode input[type="text"][hidden] { display: none; }
.rc-reg-note { grid-column: 1 / -1; }
.rc-register .btn-primary { margin-top: 0.6rem; }
@media (max-width: 640px) {
  .rc-ev-detail > div { grid-template-columns: minmax(0, 1fr); gap: 0.1rem; }
  .rc-reg-grid { grid-template-columns: minmax(0, 1fr); }
}

/* --- Section 9: My Listings - the states (AA on the panel ground),
   the row hover, the compact action cluster. --- */
.rc-state-active { color: var(--signal-online); }
.rc-state-draft, .rc-state-new { color: var(--parchment-dim); border: 1px dashed var(--edge-bright); border-radius: 999px; padding: 0 0.45rem; }
.rc-state-paused { color: var(--gold); }
.rc-state-expired, .rc-state-closed, .rc-state-unpublished, .rc-state-rejected, .rc-state-declined, .rc-state-withdrawn { color: #b9b1c9; }
.rc-state-pending-review, .rc-state-pending { color: #d7cfe6; }
.rc-state-accepted, .rc-state-registered, .rc-state-assigned { color: var(--signal-online); }
.rc-state-preview { color: var(--gold); border: 1px dashed var(--gold); border-radius: 999px; padding: 0 0.45rem; }
.rc-state-browser-draft::after { content: " \2022"; color: var(--gold); }
.rc-reason { color: #d7cfe6; font-size: 0.78rem; }
.rc-form-title { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.rc-form-title .rc-state { font-size: 0.68rem; }
.rc-switch .rc-state { font-size: 0.62rem; margin-left: 0.2rem; }
table.arcanis.rc-mine-table tbody tr:not(.table-empty-row):hover > td { background: rgba(201, 174, 255, 0.13); }
table.arcanis.rc-apps tbody tr:not(.table-empty-row):hover > td { background: rgba(201, 174, 255, 0.13); }
.rc-mine-table .rc-actions .btn, .rc-apps .rc-actions .btn { min-height: 1.9rem; padding: 0.25rem 0.6rem; font-size: 0.72rem; }
/* Eight columns inside the panel column: every cell wraps (the numeric
   No. stays on one line), the header labels wrap too, the action cluster
   wraps into a compact block - the table never outgrows its wrapper. */
.rc-mine-table th, .rc-mine-table td { white-space: normal; }
.rc-mine-table td.col-num { white-space: nowrap; }
.rc-mine-table th.th-sort .sort-link { display: inline; white-space: normal; }
.rc-mine-table td.rc-actions-cell { min-width: 11rem; }
.rc-mine-table .rc-actions { max-width: 16rem; flex-wrap: wrap; gap: 0.25rem; }
.rc-mine-table .rc-actions .btn { padding: 0.2rem 0.45rem; font-size: 0.68rem; }
.rc-mine-table .rc-actions form { display: inline-flex; flex-wrap: wrap; gap: 0.25rem; }
.rc-mine-table .badge { white-space: normal; }
.rc-view-stack { margin: 0.4rem 0 0.8rem; }
.rc-only-note { font-size: 0.85rem; color: var(--parchment-dim); margin: 0 0 0.6rem; }
.rc-create-section { margin-top: var(--block-gap); }
.rc-create-title { color: var(--gold); font-size: 1.15rem; margin: 0 0 0.5rem; }
.rc-preview { border-color: var(--gold); }

/* --- Section 11: the section navigator + progress, the persistent
   (sticky) action area, the autosave status, the missing list. --- */
.rc-form-nav { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1rem; margin: 0 0 1rem; padding: 0.55rem 0.8rem; border: 1px solid var(--edge); border-radius: 6px; background: var(--ink-panel); position: sticky; top: calc(72px + 0.5rem); z-index: 5; }
.rc-form-nav ol { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.3rem 0.6rem; counter-reset: rcnav; }
.rc-form-nav li { counter-increment: rcnav; }
.rc-form-nav a { display: inline-flex; align-items: center; gap: 0.4rem; min-height: 2rem; padding: 0.2rem 0.6rem; border: 1px solid transparent; border-radius: 999px; font-size: 0.8rem; color: var(--parchment-dim); text-decoration: none; }
.rc-form-nav a:hover, .rc-form-nav a:focus-visible { color: var(--arcane-bright); border-color: var(--arcane); outline: none; }
.rc-form-nav a.is-current { color: var(--parchment); border-color: var(--edge-bright); background: var(--ink-raised); }
.rc-form-nav .rc-nav-mark { display: inline-flex; align-items: center; justify-content: center; width: 1.15rem; height: 1.15rem; border-radius: 50%; border: 1px solid var(--edge-bright); font-size: 0.68rem; color: var(--parchment-dim); }
.rc-form-nav .rc-nav-mark::before { content: counter(rcnav); }
.rc-form-nav a.is-complete .rc-nav-mark { background: var(--signal-online); border-color: var(--signal-online); color: var(--ink); }
.rc-form-nav a.is-complete .rc-nav-mark::before { content: "\2713"; }
.rc-form-progress { margin-left: auto; font-size: 0.78rem; color: var(--parchment-dim); }
.rc-sticky-actions { position: sticky; bottom: 0; z-index: 4; margin: 1.2rem -0.2rem 0; padding: 0.7rem 0.6rem 0.6rem; background: var(--ink-panel); border-top: 1px solid var(--edge); box-shadow: 0 -8px 18px rgba(0, 0, 0, 0.35); display: block; }
.rc-submit-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 0.8rem; }
.rc-submit-row .btn { min-height: 2.35rem; }
.rc-submit .btn-submit { font-size: 0.85rem; padding: 0.6rem 1.4rem; }
.draft-status { font-size: 0.82rem; color: var(--parchment-dim); min-height: 1em; }
.draft-status.is-saved { color: var(--signal-online); font-weight: 600; }
.draft-status.is-saved::before { content: "\2713 "; }
.rc-missing { margin: 0.5rem 0 0; font-size: 0.82rem; color: var(--gold-bright); }
.rc-missing[hidden] { display: none; }
.rc-submit-help { margin: 0.35rem 0 0; }
.rc-banner-actions { display: inline-flex; gap: 0.4rem; margin-top: 0.4rem; }
.rc-banner-actions .btn[hidden] { display: none; }

/* --- Section 12: readable small text, standard control heights,
   selected states with a mark, hover and focus everywhere, larger
   click areas. The play-day chips already carry the W57 check mark;
   the preset buttons gain one while pressed; the Full Build switch and
   the Any Class check keep their own marks. --- */
form[data-rc-form] .help-msg, .recruit-card .rc-note, .recruit-card .rc-pending, .rc-table { font-size: 0.85rem; }
form[data-rc-form] .help-msg { color: #cdc4dd; }
.recruit-card .dull-span, .rc-table .dull-span { color: #b9b1c9; }
form[data-rc-form] select, form[data-rc-form] input[type="text"], form[data-rc-form] input[type="search"] { min-height: 2.35rem; box-sizing: border-box; }
form[data-rc-form] .btn { min-height: 2.1rem; }
.play-quick .btn.preset-on::before { content: "\2713 "; }
.seg-row .seg, .seg-radio .seg-opt { min-height: 2.1rem; }
.seg-row .seg.active::before, .seg-radio .seg-opt:has(input:checked)::before { content: "\2713"; font-size: 0.8em; }
.seg-row .seg:focus-visible { outline: 2px solid var(--arcane-bright); outline-offset: 1px; }
.rc-table th.th-sort .sort-link { min-height: 1.8rem; display: inline-flex; align-items: baseline; }
/* W97: the recruitment tables' own indicator colors (the gold active
   arrow) are RETIRED - the ONE site-wide .sort-ind rule serves them. */
.inline-check input[type="checkbox"], .inline-check input[type="radio"] { width: 1.05rem; height: 1.05rem; accent-color: var(--arcane-bright); }
.rc-apply .btn, .rc-actions .btn { min-height: 2.1rem; }

/* W77 (docs/specs/W77-SITE-UPDATES.md): the consolidated site updates.
   Section 5: inside a table that carries BOTH a date & time column and
   a countdown column, the two columns follow the selected time mode -
   the [data-time-basis] wrapper (server-rendered from the account's
   basis, switched in place by time-basis.js) colors every countdown
   value in it: Server Time -> --time-server, Local Time -> --time-local
   (the date cells already carry their own view colors). Carried by the
   Server Schedule boss tables, the Marketplace Browse / My Listings /
   Watchlist tables and the listing facts, and the public Boss Status
   pair (server time only - the wrapper is fixed to server there).
   Section 10: the ONE row-hover treatment is the marketplace rule above
   (the .row-hover class on every Rankings and Boss / Grand Boss table;
   rows with links or controls keep their own actions - the tint is a
   background only).
   Section 6: the Discord CTA's icon + label group. The W74 inline-flex
   button already centers the two as ONE group (align-items: center, the
   0.55rem gap); the residual is the display font's ascent bias - the
   uppercase glyphs sit 1.5px above the icon's optical center inside a
   line box no line-height value can shift them within (a line height
   only adds symmetric half-leading). So the label keeps its W74 line box
   (the button's rendered 213.98 x 44.84 at 1920 is unchanged) and takes
   a 0.12em relative shift down (scripts/w77-discord-fab.py, rendered
   ink: delta -1.5px -> 0 at 1920 / 1366 / 768); below 480px the ink was
   already centered (delta 0), so the shift applies from 480px up. No
   dimension, color, type, effect, position or behavior changes. */
[data-time-basis="server"] [data-countdown-until],
[data-time-basis="server"] .mk-countdown { color: var(--time-server); }
[data-time-basis="local"] [data-countdown-until],
[data-time-basis="local"] .mk-countdown { color: var(--time-local); }
/* W80 item 14 (docs/specs/W80-CONSOLIDATED-WEB.md): the countdown's
   LABEL takes the mode color with its value - the schedule entries'
   "Starts in" caption, the boss tables' "Window opens in" line, the
   cards' "Starts in:", the marketplace's Time Remaining term - so a
   Server Time page reads sky blue end to end and a Local Time page in
   the local ink (the wrappers are restamped by time-basis.js). The
   state word a countdown flips to at zero (.is-done) keeps the same
   color: it is the same fact, finished. */
[data-time-basis="server"] .cd-label, [data-time-basis="server"] .bt-timer .bt-sub, [data-time-basis="server"] .rc-ev-facts .cd-label,
[data-time-basis="server"] .se-count .se-cap { color: var(--time-server); }
[data-time-basis="local"] .cd-label, [data-time-basis="local"] .bt-timer .bt-sub, [data-time-basis="local"] .rc-ev-facts .cd-label,
[data-time-basis="local"] .se-count .se-cap { color: var(--time-local); }
[data-countdown-until].is-done { font-weight: 600; }
@media (min-width: 480px) { .discord-fab .df-label { position: relative; top: 0.12em; } }
.boss-table .bt-countdown { font-variant-numeric: tabular-nums; }
.boss-table .bt-countdown .bt-sub { display: block; font-size: 0.7rem; color: var(--parchment-faint); font-family: var(--font-sans); }

/* W77b (docs/specs/W77B-RULINGS.md, the owner's rulings on the W77
   questions). Ruling 1: the public Boss Status pair carries the site-
   wide Local / Server toggle and renders every respawn instant through
   the ONE time cell; the per-cell Local / Server caption stays sr-only
   here (the toggle and its note establish the mode - the W75 item I
   marketplace rule), so the W18 estimated window keeps its designed
   three-line block and the exact time stays one line at the floor.
   Ruling 2: the grand-opening countdown VALUE is a time value - it reads
   in --time-local (above); the cell ground, border and labels are the
   component's own accent and are untouched. */
.boss-table .mk-time-cap {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0; display: inline;
}
.boss-table .mk-time { position: relative; }
/* Stacked (below 900px): the cell wraps at its chunk boundary like the
   marketplace cells, so a narrow value column never overflows. */
.tbl-stack > table.arcanis.boss-table .mk-time { display: inline; white-space: normal; }
.tbl-stack > table.arcanis.boss-table .mk-time-d, .tbl-stack > table.arcanis.boss-table .mk-time-c { white-space: nowrap; }

/* ==========================================================================
   W78 (docs/specs/W78-MARKETPLACE-UI-UX.md): Marketplace UI round 2 - the
   EXPANDABLE ROW as the primary workflow. Everything below is scoped to
   the marketplace (.mk-*) over the W72-W75 blocks: the clearer chevron
   with a larger interaction area (1), the expanded row's action set -
   the ONE Acting character block, the Buy Now / Place Bid / Watch
   hierarchy, the per-row listing form (1, 3, 6), the narrow-width fold
   (5), the responsive filter grid with full-width controls and the muted
   Clear Filters (4), the wide date controls and the labeled copy control
   (9), the column sizing for the wrapping headings (5), the strengthened
   selected-character state (6), the balance lines and the disabled /
   busy states (3, 10-11), and the readability pass on the small text
   (10). The time colors stay the W77 tokens; the W75 hover / open tints
   stay as pinned.
   ========================================================================== */

/* Section 1: the chevron - a 1.5rem glyph box with a 2.1rem square hit
   area (the No. cell is part of the row's own click target too). */
.mk-row .mk-chev {
  width: 1.5rem; height: 1.5rem; margin: -0.3rem 0.35rem -0.3rem -0.2rem; padding: 0.3rem;
  box-sizing: content-box; font-size: 1.2rem; border-radius: 4px;
}
.mk-row:hover .mk-chev, .mk-row:focus-visible .mk-chev { background: rgba(155, 109, 255, 0.14); }
.mk-row.is-open .mk-chev { background: rgba(155, 109, 255, 0.2); }
.mk-table td.col-num { padding-right: 0.35rem; }

/* Section 4: the responsive filter grid - four columns in the panel
   column, one .mk-field per control (the label directly above it), every
   control the full width of its cell, the search and the currency pair
   two cells wide, the date range two cells wide; two columns below
   1100px, one below 560px. Every selected value stays whole. */
.mk-filter.mk-filter-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.75rem 1rem;
  align-items: end;
  margin: 0 0 var(--block-gap);
}
.mk-filter-grid > .mk-field { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; font-size: 0.82rem; color: var(--parchment-dim); }
.mk-filter-grid > .mk-field > .mk-field-label, .mk-filter-grid > fieldset.mk-range > legend { font-size: 0.8rem; letter-spacing: 0.04em; color: var(--parchment-dim); line-height: 1.2; }
.mk-filter-grid > .mk-span-2 { grid-column: span 2; }
.mk-filter-grid > .mk-field > input, .mk-filter-grid > .mk-field > select { width: 100%; max-width: 100%; min-height: 2.35rem; box-sizing: border-box; }
.mk-filter-grid > fieldset.mk-range { display: grid; grid-template-columns: auto minmax(0, 1fr) auto minmax(0, 1fr); gap: 0.3rem 0.4rem; align-items: center; }
.mk-filter-grid > fieldset.mk-range > legend { grid-column: 1 / -1; margin-bottom: 0.1rem; }
.mk-filter-grid > fieldset.mk-range input { min-height: 2.35rem; box-sizing: border-box; }
.mk-filter-grid .mk-cur-pick { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.45rem; align-items: center; }
.mk-filter-grid .mk-cur-pick select { width: 100%; min-width: 0; }
.mk-filter-grid .mk-field-clear { align-self: end; }
.mk-filter-grid .mk-clear { min-height: 2.35rem; width: 100%; justify-content: center; }
.mk-filter-grid .mk-clear:disabled, .mk-filter-grid .mk-clear[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; border-style: dashed; color: var(--parchment-faint); }
.mk-filter-grid .mk-clear:disabled:hover { border-color: var(--edge); color: var(--parchment-faint); }
/* Section 9: the date controls are as wide as their cell, never
   narrower than the placeholder; the format is stated in the legend. */
.mk-filter-grid > fieldset.mk-range-dates { grid-template-columns: auto minmax(9.5rem, 1fr) auto minmax(9.5rem, 1fr); }
.mk-filter-grid > fieldset.mk-range-dates input[type="date"] { width: 100%; max-width: 100%; min-width: 9.5rem; }
@media (max-width: 1099px) {
  .mk-filter.mk-filter-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mk-filter-grid > fieldset.mk-range-dates { grid-column: 1 / -1; }
}
@media (max-width: 560px) {
  .mk-filter.mk-filter-grid { grid-template-columns: minmax(0, 1fr); }
  .mk-filter-grid > .mk-span-2 { grid-column: auto; }
  .mk-filter-grid > fieldset.mk-range, .mk-filter-grid > fieldset.mk-range-dates { grid-template-columns: auto minmax(0, 1fr); }
}

/* Section 5: the wrapping headings get a floor so "Buy Now / Current
   Bid", "Time Remaining" and "Transaction ID" wrap at a word boundary
   at most once and never one word per line. */
.mk-table th.mk-col-price { min-width: 7.5rem; }
.mk-table th.mk-col-remaining { min-width: 6.5rem; }
.mk-table th.mk-col-ref { min-width: 9rem; }
.mk-table td.mk-col-ref { white-space: nowrap; }

/* Section 6: the selected character - strengthened (the accent edge,
   the tint, the check mark), not a filled primary button. */
.mk-char-row .seg.active {
  background: rgba(155, 109, 255, 0.16); border-color: var(--arcane-bright); color: var(--parchment);
  box-shadow: inset 0 0 0 1px var(--arcane-bright); font-weight: 600;
}
.mk-char-row .seg.active::before { content: "\2713"; color: var(--arcane-bright); font-size: 0.8rem; margin-right: 0.35rem; }
.mk-char-row .seg.active .mk-char-meta { color: var(--parchment-dim); opacity: 1; }
.mk-char-row .seg { flex-direction: row; align-items: center; gap: 0.35rem; flex-wrap: wrap; }
.mk-char-row .seg .mk-char-meta { display: inline; }

/* Sections 1 + 3: the expanded row's action set. The Acting character
   block heads the form; the Buy Now / Place Bid panels sit side by side
   (one column in the stacked layout); the hierarchy is the button skin:
   Buy Now filled primary, Place Bid outlined secondary, Watch quiet. */
.mk-x-actions .mk-act-form { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 0.6rem; }
.mk-x-acting { display: flex; flex-direction: column; gap: 0.3rem; max-width: 28rem; }
.mk-x-acting select { width: 100%; max-width: 100%; }
.mk-x-panels { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr)); gap: 0.6rem 1rem; }
/* W93: the panel's border, radius, background and padding are
   SUPERSEDED by the W93 block at the end of this file (the panels are
   grouped by their headings and the grid gap). Do not restore the box. */
.mk-x-panel { display: flex; flex-direction: column; gap: 0.35rem; padding: 0.7rem 0.8rem; border: 1px solid var(--edge-soft); border-radius: 6px; background: rgba(255, 255, 255, 0.02); }
.mk-x-panel .mk-x-h { margin-bottom: 0.1rem; }
.mk-x-panel input, .mk-x-panel .mk-money { max-width: 100%; }
.mk-x-panel .btn { align-self: flex-start; margin-top: 0.2rem; }
.mk-bid-btn { border-color: var(--arcane-bright); color: var(--arcane-bright); background: transparent; }
.mk-bid-btn:hover:not(:disabled) { background: rgba(155, 109, 255, 0.18); color: var(--parchment); }
.mk-x-actions .btn:disabled, .mk-x-actions .btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }
.mk-x-actions .btn.is-busy { opacity: 0.6; pointer-events: none; }
.mk-manage { margin-top: 0.2rem; }
.mk-x-watch .btn { margin-top: 0.1rem; }
.mk-x-actions .info-note .mk-sub-inline { color: var(--parchment-dim); }
[data-mk-bid-why]:not(:empty) { color: var(--signal-offline); font-weight: 600; }
.mk-locked-note { color: var(--gold); }

/* Section 5: the narrow-width fold - the folded columns' facts render
   in the expanded row only below 768px (the widths where Type / Bids /
   Ends / Date & Time fold away). The expanded row itself is ONE column
   there (the W75 two-column rule sits after the W74 media rule in
   source order and won at every width - the 390 overflow the probe
   measured, scripts/w78-overflow-probe.py). */
.mk-x-fold { display: none; }
@media (max-width: 767px) {
  .mk-x-fold { display: grid; margin-top: 0.3rem; padding-top: 0.3rem; border-top: 1px dashed var(--edge-soft); }
  .mk-expand-inner, .mk-expand-history, .mk-expand-sell { grid-template-columns: minmax(0, 1fr); }
}

/* Sections 1 + 6: the per-row listing form - one column under the row
   facts, three compact sections side by side (one column when stacked),
   the estimate and the actions at full width. */
.mk-expand-sell { grid-template-columns: minmax(0, 1fr); }
.mk-x-item-sell { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 1.2rem; }
.mk-x-facts-inline { display: flex; flex-wrap: wrap; gap: 0.2rem 1.2rem; }
.mk-x-facts-inline > div { display: inline-flex; align-items: baseline; gap: 0.4rem; }
.mk-x-item-sell .mk-x-links { margin: 0; }
.mk-listing-form-row { border-top: 1px solid var(--edge-soft); padding-top: 0.6rem; }
.mk-lf-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.6rem 1.2rem; align-items: start; }
.mk-lf-grid .form-section { margin: 0; padding: 0; border: 0; }
.mk-lf-grid .form-section-title { margin: 0 0 0.45rem; font-size: 0.8rem; }
.mk-lf-grid .form-row { margin-bottom: 0.55rem; }
.mk-lf-grid .seg-radio { flex-wrap: wrap; }
.mk-lf-grid .seg-radio .seg-opt { padding: 0.3rem 0.55rem; font-size: 0.72rem; }
.mk-lf-grid input[type="text"], .mk-lf-grid input[type="number"] { width: 100%; max-width: 100%; box-sizing: border-box; }
.mk-lf-grid .mk-money { width: 100%; }
.mk-lf-grid .mk-money input { flex: 1 1 auto; }
.mk-lf-grid .mk-fee-preview { margin-top: 0.2rem; }
.mk-lf-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.8rem; margin-top: 0.6rem; }
.mk-review-why { margin: 0; flex: 1 1 14rem; color: var(--parchment-dim); }
.mk-review-why.is-ready { color: var(--signal-online); }
.mk-auction-rules .mk-rule-line { display: block; margin-top: 0.15rem; }
.mk-fee-thresholds { color: var(--parchment); }
@media (max-width: 1099px) {
  .mk-lf-grid { grid-template-columns: minmax(0, 1fr); }
}

/* Section 9: the labeled copy control and the copied state. */
.mk-copy { gap: 0.3rem; padding: 0.15rem 0.5rem; font-size: 0.74rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--parchment); }
.mk-copy .mk-copy-text { font-family: var(--font-sans); }
.mk-copy.is-copied { border-color: var(--signal-online); color: var(--signal-online); }
.mk-x-events { margin: 0; padding: 0 0 0 1.1rem; display: flex; flex-direction: column; gap: 0.3rem; }
.mk-x-events li { display: grid; grid-template-columns: minmax(0, auto) minmax(0, 1fr); gap: 0.2rem 0.8rem; align-items: baseline; }
.mk-x-events .mk-ev-when { font-size: 0.8rem; white-space: nowrap; }
.mk-x-events .mk-ev-when .mk-time { white-space: nowrap; }
.mk-x-events .mk-ev-label { color: var(--parchment); }
.mk-expand-history { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); }
@media (max-width: 767px) {
  .mk-expand-history { grid-template-columns: minmax(0, 1fr); }
  .mk-x-events li { grid-template-columns: minmax(0, 1fr); }
}

/* Sections 2 + 3: the details page - the rules panel, the balance lines. */
.mk-rules-panel .mk-rules { grid-template-columns: max-content minmax(0, 1fr); gap: 0.4rem 1rem; }
.mk-rules-panel .mk-rules dt { padding-top: 0.2rem; }
.mk-short { color: var(--signal-offline); font-weight: 600; }
[data-mk-balance] td, [data-mk-balance-after] td { font-variant-numeric: tabular-nums; }

/* Section 10: the readability pass - the small helper, label, metadata
   and explanatory copy step up one size; the facts' labels lift from
   parchment-faint to parchment-dim; the buttons inside the marketplace
   share one height and padding. */
.mk-sub, .mk-table .mk-sub, .mk-sub-inline, .mk-na, .mk-x-label, .mk-basis-note, .mk-fee-text, .mk-fee-rule,
.mk-x-actions .help-msg, .mk-x-actions .info-note, .mk-listing-form .help-msg, .mk-eligibility, .mk-count-note { font-size: 0.82rem; }
.mk-x-facts dt { font-size: 0.76rem; color: var(--parchment-dim); }
.mk-x-h { font-size: 0.84rem; }
.mk-x-label { color: var(--parchment); }
.mk-time-cap { font-size: 0.7rem; }
.mk-x-actions .btn, .mk-x-links .btn, .mk-lf-actions .btn, .mk-head-watch .btn, .mk-actions-panel .btn, .mk-confirm-actions .btn {
  min-height: 2.35rem; padding-top: 0.45rem; padding-bottom: 0.45rem; display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;
}
.mk-x-actions .btn:focus-visible, .mk-copy:focus-visible, .mk-cur-toggle:focus-visible, .mk-row:focus-visible, .mk-filter-grid select:focus-visible, .mk-filter-grid input:focus-visible {
  outline: 2px solid var(--arcane-bright); outline-offset: 2px;
}
.mk-x-actions .field-error input, .mk-listing-form .field-error input { border-color: var(--signal-offline); }
.mk-x-actions input[aria-invalid="true"] { border-color: var(--signal-offline); }

/* W78b (docs/specs/W78B-RULINGS.md) ruling 2: the lazy-rendered Sell form -
   a collapsed row carries only the loading note (scripted) or the item
   details link (no script). */
.mk-lazy-form { display: flex; flex-direction: column; gap: 0.4rem; }
.mk-lazy-note { margin: 0; }
html:not(.js) .mk-lazy-note { display: none; }

/* W79 (docs/specs/W79-EVENT-CHANNEL.md): the mass-event channel writer +
   the unified same-instant siege card - the castle chip list inside the
   card, the engine phase line, the roster grouped per castle / side once
   the engine assigned, the engine words on a member (assigned /
   unavailable / released), the own-registration state note. The engine
   writes every word rendered here; nothing fakes. */
.rc-ev-castles { display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; margin: 0.35rem 0 0; }
.rc-ev-castles .rc-ev-cap { margin-right: 0.15rem; }
.rc-castle-chip { display: inline-block; border: 1px solid rgba(237, 217, 168, 0.45); border-radius: 999px; padding: 0.05rem 0.6rem; font-size: 0.8rem; color: var(--parchment); }
.rc-ev-head h3 .rc-ev-when { white-space: nowrap; }
.rc-ev-engine { margin-top: 0.3rem; font-size: 0.86rem; }
.rc-ev-engine .rc-ev-phase { font-weight: 600; }
.rc-ev-engine .rc-ev-phase-locked { color: var(--gold); }
.rc-roster-groups { list-style: none; margin: 0.4rem 0 0; padding: 0; display: grid; gap: 0.45rem; }
.rc-roster-group > .rc-roster-group-title { display: block; font-size: 0.82rem; color: var(--parchment-dim); letter-spacing: 0.02em; margin-bottom: 0.2rem; }
.rc-roster-list .badge .rc-member-state { color: var(--parchment-dim); }
.rc-roster-list .badge.rc-roster-unavailable { border-color: rgba(255, 140, 120, 0.6); color: rgba(255, 170, 150, 0.95); }
.rc-roster-list .badge.rc-roster-released { opacity: 0.7; }
.rc-ev-state { margin: 0.35rem 0 0; font-size: 0.86rem; }
.rc-ev-state.rc-ev-state-unavailable { color: rgba(255, 170, 150, 0.95); }
.rc-ev-state.rc-ev-state-late { color: var(--gold); }
.rc-ev-assign { display: grid; gap: 0.15rem 0.6rem; grid-template-columns: max-content minmax(0, 1fr); font-size: 0.86rem; margin: 0; }
.rc-ev-assign dt { color: var(--parchment-dim); }
.rc-ev-assign dd { margin: 0; }
@media (max-width: 767px) {
  .rc-ev-head h3 .rc-ev-when { white-space: normal; }
  .rc-ev-assign { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   W80 (docs/specs/W80-CONSOLIDATED-WEB.md): the consolidated web bundle -
   the Marketplace's three-button primary navigation with the My
   Marketplace secondary tabs (18), the expanded rows without the nested
   frame (3, 5, 6), the character selector grid (7), the filter rows and
   the currency-gated price range (8, 9), the baseline sort indicators
   (10), the concise fee thresholds (11), the sticky-header scroll
   clearance (2). The W75 hover / open tints and the W77 time tokens stay
   as pinned.
   ========================================================================== */

/* Item 2: everything a scroll TARGETS - a followed anchor, a row opened
   on arrival (?open= / #mk-expand-…), a details element scrolled into
   view - clears the sticky 72px header with room to spare: the W33
   jump-link clearance (scroll-margin-top, which hash navigation and
   scrollIntoView both honor) applied to every panel landmark, so a
   targeted title, tab strip, filter form, table or row is never hidden
   beneath the header. A document-level scroll-padding would ADD to the
   W33 anchors' own margin (the harness pins their landing), so the
   clearance stays per landmark - and it follows the header's OWN height:
   72px on the desktop row, ~97px on the wrapped burger row below 1100px,
   ~142px below 480px where the chip and the account cluster wrap too
   (measured by scripts/w80-header-probe.py). An expansion row targeted
   by a #mk-expand-… arrival clears one listing row more, so the row it
   belongs to stays in view above it. */
:root { --sticky-clear: 5.25rem; }
@media (max-width: 1099px) { :root { --sticky-clear: 7rem; } }
@media (max-width: 479px) { :root { --sticky-clear: 9.75rem; } }
.panel-section-header, .mk-tabs, .mk-subtabs, .mk-filter, .mk-toolbar, .table-wrap, .mk-row,
.recruit-card, .sched-entry, .panel { scroll-margin-top: var(--sticky-clear); }
.mk-expand { scroll-margin-top: calc(var(--sticky-clear) + 4rem); }

/* Item 18: the three primary buttons evenly spaced on one row; the
   secondary strip beneath reads as a different control (the segmented
   pills) and sits closer to its content than to the primary row. */
.mk-tabs-primary { gap: 0.75rem; }
.mk-tabs-primary .tab { min-width: 9rem; justify-content: center; text-align: center; }
.mk-subtabs { margin: calc(-1 * var(--block-gap) + 0.55rem) 0 var(--block-gap); }
@media (max-width: 479px) {
  .mk-tabs-primary .tab { min-width: 0; flex: 1 1 auto; }
}

/* Items 3, 5, 6: ONE unified expanded-row background (the W75 open tint
   the row and its expansion already share) - no inset accent bar, no
   left rule, no indentation: the whole row width carries the facts and
   the action area. Height comes from the content (align-items: start -
   no column stretches to the tallest), the vertical padding is tight,
   and the action area sits on the same top line as the facts so it is
   part of the listing, never detached. Browse / Watchlist / My Listings
   are two columns - the facts, then the actions with the Buy Now / Place
   Bid panels side by side inside them; History keeps facts + the event
   sequence; Sell is the one-column form. */
.mk-row.is-open td:first-child, .mk-expand.is-open td { box-shadow: none; }
.mk-expand-inner {
  margin-left: 0; padding: 0.4rem 0 0.05rem; border-left: 0;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr); gap: 0.35rem 1.4rem; align-items: start;
}
.mk-expand-history { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); }
.mk-expand-sell { grid-template-columns: minmax(0, 1fr); }
.mk-expandable tr.mk-expand td { padding-top: 0; padding-bottom: 0.65rem; }
.mk-x-links { margin: 0.4rem 0 0; }
.mk-x-actions { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
.mk-x-actions .mk-act-form { gap: 0.4rem; margin-bottom: 0.2rem; }
.mk-x-actions .info-note, .mk-x-actions .help-msg { margin: 0 0 0.3rem; }
.mk-x-panels { gap: 0.5rem 0.8rem; }
.mk-x-panel { padding: 0.55rem 0.7rem; gap: 0.3rem; }
.mk-x-watch { margin-top: 0; }
.mk-manage { margin-top: 0; align-self: flex-start; }
.mk-x-item-sell { padding-bottom: 0.1rem; }
.mk-listing-form-row { padding-top: 0.5rem; }
.mk-lf-grid { gap: 0.5rem 1.2rem; }
.mk-lf-grid .mk-lf-actions { margin-top: 0.45rem; }
@media (max-width: 1099px) {
  .mk-expand-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .mk-expand-history, .mk-expand-sell { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 767px) {
  .mk-expand-inner, .mk-expand-history, .mk-expand-sell { grid-template-columns: minmax(0, 1fr); margin-left: 0; padding-left: 0; }
}

/* Item 7: the Sell Item character selector - consistently sized cards
   on a balanced grid that wraps cleanly (never a run of uneven joined
   pills); the selected card keeps the W78 strengthened state (the accent
   edge, the tint, the check mark) - never a filled primary button. */
.mk-char-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 12.5rem), 1fr)); gap: 0.45rem; }
.mk-char-row .seg, .mk-char-row .seg + .seg, .mk-char-row .seg:first-child, .mk-char-row .seg:last-child {
  border: 1px solid var(--edge); border-radius: 6px; justify-content: flex-start; min-height: 2.7rem;
  padding: 0.4rem 0.75rem; min-width: 0;
}
.mk-char-row .seg:hover { border-color: var(--arcane); }
/* W87 item 5 (docs/specs/W87-CONSOLIDATED-WEBSITE.md) - THE DEFECT AND
   ITS ROOT CAUSE. This rule used to read
     .mk-char-row .seg.active, .mk-char-row .seg.active + .seg
   The `+ .seg` half belongs to the pre-W80 JOINED-PILL selector, where
   .seg-row collapsed adjacent borders (.seg + .seg { border-left: 0 })
   and the segment AFTER the active one had to carry the accent so the
   shared edge painted. W80 item 7 (the .mk-char-row grid above) gave
   every character its OWN complete bordered card, so from that moment
   the sibling half stopped painting a shared edge and started drawing a
   full second accent border around the next card - one selected
   character, two tiles that read as selected (and, because the grid
   wraps, sometimes on a different row entirely). It is a stale CSS
   selector left behind by a layout change, not a state bug: the server
   renders exactly one .active. The sibling half is removed; only the
   actively selected tile carries any active-state paint. */
.mk-char-row .seg.active { border-color: var(--arcane-bright); }
.mk-char-row .seg .mk-char-meta { min-width: 0; overflow-wrap: anywhere; }

/* Items 8 + 9: consistent filter rows - every control one height, the
   Currency dropdown alone in its field (no icon strip), the Clear
   Filters control in the grid's last column at the controls' height,
   the price range greyed while it waits for a currency (readable, not
   hidden - nothing moves when a currency is chosen). */
.mk-filter-grid > .mk-cur-field select { width: 100%; }
.mk-filter-grid .mk-field-clear { grid-column: -2 / -1; }
.mk-range-price[data-mk-price-waiting] input { opacity: 0.55; cursor: not-allowed; }
.mk-range-price[data-mk-price-waiting] .mk-range-unit { color: var(--parchment-faint); }
@media (max-width: 1099px) {
  .mk-filter-grid .mk-field-clear { grid-column: auto; }
}

/* Item 10: the sort indicator rides the header text's own line - an
   inline-flex link keeps the glyph beside the label on the first line's
   baseline, so a wrapping heading never stacks its arrow beneath itself
   (the recruitment tables' W66 precedent, applied to every marketplace
   table); the markup is unchanged. */
.mk-table th.th-sort .sort-link { display: inline-flex; flex-wrap: nowrap; align-items: baseline; gap: 0.3em; }
.mk-table th.th-sort .sort-ind { flex: 0 0 auto; }

/* Item 11: the concise fee thresholds - one line per currency, the
   selected currency's line lifted, the no-fee line and the confirmation
   note beneath. */
.mk-fee-rule-cap { display: block; color: var(--parchment); font-weight: 600; margin-bottom: 0.15rem; }
.mk-fee-thresholds { margin: 0; padding: 0 0 0 1.1rem; color: var(--parchment); }
.mk-fee-thresholds li { margin: 0.05rem 0; }
.mk-fee-thresholds li.is-current { color: var(--arcane-bright); }
.mk-fee-thresholds-inline { padding-left: 1.1rem; }
.mk-fee-confirm { margin: 0.25rem 0 0; color: var(--parchment-dim); }

/* ---------- W81 (docs/specs/W81-WEBSITE-AND-EVENT-SYSTEM.md): the ---------- */
/* ---------- siege trio toggle, the two-column events grid, the ---------- */
/* ---------- Fortresses / Clan Halls row hover, the Alerts & Messages inbox ---------- */

/* Items 1 + 15: the shared Server Time / Local Time toggle beneath the
   siege tab row - centered like the public tab row, on all three tabs
   (identical geometry across the siblings). */
.siege-basis-bar { justify-content: center; margin: 0 0 var(--block-gap); }
.siege-basis-bar .mk-basis-form { justify-content: center; }
/* The time cell inside a prose line (the castle's Next siege line, the
   Territory War note) reads inline with its sentence. */
.castle-info .mk-time, .info-note .mk-time { display: inline; }

/* Item 3: the Scheduled Events cards in two columns at desktop widths
   (a card bounds its own content - min-width 0 - so the page never
   scrolls sideways); one column below 900px. The fact strip inside a
   half-width card stacks its three regions below 1100px. */
@media (min-width: 900px) {
  .event-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 1.5rem; align-items: start; }
  .event-grid > .event-section { min-width: 0; }
}
/* The grid's cards keep the ONE 12.8px stack token between rows (the
   card's own top margin - no grid row-gap on top of it) and between
   stacked cards below 900px - the W50 CODEX gap. */
.event-grid > .event-section { margin-top: var(--block-gap); }
.event-grid { margin-top: var(--block-gap); }
/* Item 2: a time cell inside a STACKED table (the narrow-width label /
   value grid) wraps like text - the W77b boss-table rule generalized -
   so no panel page scrolls sideways at 390px. */
.tbl-stack > table.arcanis .mk-time { display: inline; white-space: normal; }
/* A time cell inside a shrink-to-content (.col-fit) column keeps the
   W16 equal-slack contract: the Server / Local caption goes sr-only
   there (the W77b Boss Status precedent) - the basis is the toggle's
   and the value's color. */
table.arcanis td.col-fit .mk-time-cap { position: absolute; width: 1px; height: 1px; padding: 0; margin: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
/* W81b (the owner-reported Dashboard defect): a time cell inside a
   key / value table (.table-kv - the Dashboard's Account Details) wraps
   like text between its date and its clock (the .mk-kv precedent), so
   the value column never widens the table past its card at any width
   or basis; the row backgrounds stay inside the card. */
table.arcanis.table-kv td.value .mk-time { white-space: normal; }
/* The inbox's category chip wraps inside a stacked table (390px). */
.tbl-stack > table.arcanis .ib-cat { white-space: normal; }
@media (min-width: 900px) and (max-width: 1099px) {
  .event-grid .fact-strip { grid-template-columns: minmax(0, 1fr); }
}

/* Item 13: the Alerts & Messages inbox. The site's notification
   indicator - a count badge on the header's account cluster (absolutely
   positioned: the W12 / W71 cluster geometry is untouched) and a count
   chip on the sidebar entry; the tab counters; the critical strip; the
   entry table (the shared .arcanis table + the W77 row hover), the
   unread state, the expanded entry with its actions. */
.account-cluster { position: relative; }
.hdr-inbox {
  position: absolute; top: -0.45rem; left: 8.9rem; z-index: 3;
  min-width: 1.35rem; height: 1.35rem; padding: 0 0.35rem; box-sizing: border-box;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px; border: 1px solid var(--arcane-bright);
  background: var(--arcane); color: #fff; font-size: 0.72rem; font-weight: 700; line-height: 1;
  text-decoration: none; box-shadow: 0 0 0 2px var(--ink);
}
.hdr-inbox:hover, .hdr-inbox:focus-visible { background: var(--arcane-bright); color: var(--ink); }
@media (max-width: 1099px) {
  .hdr-inbox { left: auto; right: 5.4rem; }
}
.side-count {
  display: inline-flex; align-items: center; justify-content: center; margin-left: 0.5rem;
  min-width: 1.3rem; height: 1.3rem; padding: 0 0.35rem; box-sizing: border-box; border-radius: 999px;
  background: var(--arcane); color: #fff; font-size: 0.7rem; font-weight: 700; line-height: 1;
}
.tab .ib-count {
  display: inline-flex; align-items: center; justify-content: center; margin-left: 0.4rem;
  min-width: 1.3rem; height: 1.3rem; padding: 0 0.35rem; box-sizing: border-box; border-radius: 999px;
  background: var(--arcane); color: #fff; font-size: 0.7rem; font-weight: 700; line-height: 1;
}
.tab.active .ib-count { background: var(--ink); color: var(--arcane-bright); }
.ib-critical { border-color: var(--danger, #d8585c); }
.ib-critical .label { color: var(--danger, #d8585c); }
.ib-critical-list { list-style: none; margin: 0.35rem 0 0; padding: 0; display: grid; gap: 0.45rem; }
.ib-critical-list li { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.75rem; }
.ib-critical-list .ib-crit-title { font-weight: 600; color: var(--parchment); }
.ib-critical-list .ib-crit-body { color: var(--parchment-dim); }
.ib-critical-list form { margin: 0; display: inline-flex; }
.ib-toolbar { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr) auto; gap: 0.6rem 0.75rem; align-items: end; margin: 0 0 0.75rem; }
.ib-toolbar .mk-field { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
.ib-toolbar .mk-field-label { font-size: 0.78rem; color: var(--parchment-dim); }
.ib-toolbar input, .ib-toolbar select { width: 100%; min-width: 0; }
.ib-bulk { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin: 0 0 0.75rem; }
.ib-bulk form { margin: 0; display: inline-flex; gap: 0.5rem; flex-wrap: wrap; }
.ib-bulk .ib-select-all { display: inline-flex; align-items: center; gap: 0.35rem; margin-right: 0.5rem; }
@media (max-width: 1099px) {
  .ib-toolbar { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
@media (max-width: 599px) {
  .ib-toolbar { grid-template-columns: minmax(0, 1fr); }
}
.ib-table td, .ib-table th { vertical-align: top; }
.ib-table .ib-col-pick { width: 2.2rem; }
.ib-table .ib-col-state { width: 1.6rem; }
.ib-table .ib-col-cat { width: 11rem; }
.ib-table .ib-col-char { width: 9rem; }
.ib-table .ib-col-when { width: 14rem; }
.ib-row { cursor: pointer; }
.ib-row.is-unread .ib-title { font-weight: 700; color: var(--parchment); }
.ib-row .ib-title { color: var(--parchment); }
.ib-row .ib-preview { display: block; color: var(--parchment-dim); font-size: 0.86rem; margin-top: 0.1rem; }
.ib-row .ib-sender { display: block; color: var(--gold); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; }
.ib-state { display: inline-block; width: 0.6rem; height: 0.6rem; border-radius: 50%; border: 1px solid var(--edge-bright); background: transparent; }
.ib-row.is-unread .ib-state { background: var(--arcane-bright); border-color: var(--arcane-bright); box-shadow: 0 0 6px var(--arcane); }
.ib-row.is-critical .ib-state { border-color: var(--danger, #d8585c); }
.ib-row.is-critical.is-unread .ib-state { background: var(--danger, #d8585c); box-shadow: none; }
.ib-row.is-open > td { background: var(--mk-row-open); }
.ib-cat { display: inline-block; font-size: 0.78rem; padding: 0.1rem 0.5rem; border: 1px solid var(--edge-soft); border-radius: 999px; color: var(--parchment); white-space: nowrap; }
.ib-expand > td { background: var(--mk-row-open); padding-top: 0.2rem; }
.ib-expand-inner { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.5rem 1.5rem; align-items: start; padding: 0.3rem 0 0.4rem; }
.ib-expand-body { margin: 0; color: var(--parchment); overflow-wrap: anywhere; }
.ib-expand-meta { margin: 0.35rem 0 0; color: var(--parchment-dim); font-size: 0.86rem; }
.ib-expand-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
.ib-expand-actions form { margin: 0; display: inline-flex; }
.ib-pick { margin: 0; }
@media (max-width: 1099px) {
  .ib-expand-inner { grid-template-columns: minmax(0, 1fr); }
}
.tbl-stack > table.ib-table .ib-row.is-open, .tbl-stack > table.ib-table .ib-expand { background: var(--mk-row-open); }
.ib-none { margin: 0.5rem 0 0; }

/* W82 scope 1 (docs/specs/W82-SORTING-AND-INSTANT-LISTINGS.md): the
   section-14 header-baseline rule W80 item 10 gave the marketplace tables
   is the site-wide rule - every sortable header lays its label and its
   indicator out as one inline-flex run on the label's baseline, so a
   wrapping heading never stacks its arrow beneath itself. Lower
   specificity than the marketplace / recruitment rules above, which keep
   their own wrapping exceptions. */
.th-sort .sort-link { display: inline-flex; flex-wrap: nowrap; align-items: baseline; gap: 0.3em; }
.th-sort .sort-ind { flex: 0 0 auto; }

/* ==================== W83 (docs/specs/W83-MY-RECRUITMENT-WORKFLOW.md) ==================== */
/* The My Recruitment three-page workflow, the four-level tab
   hierarchy, the MEASURED sticky offsets and the persistent panel
   side menu (the sticky rule itself lives in the W67/W68 block). */

/* Section 10: panel-ux.js initStickyMeasure sets --header-h and
   --rc-progress-h from the RENDERED header and indicator and rewrites
   --sticky-clear from them. These fallbacks keep the no-JS page (they
   mirror the W80 measured header heights per breakpoint). */
:root { --header-h: 73px; --rc-progress-h: 0px; }
@media (max-width: 1099px) { :root { --header-h: 97px; } }
@media (max-width: 479px) { :root { --header-h: 142px; } }
/* 73px = the 72px desktop row + its 1px bottom border (the RENDERED
   height, so the measured write is a no-op at standard widths and the
   first paint never shifts - initStickyMeasure only writes on a >= 2px
   real change). */

/* Section 2: level 2 - the three PRIMARY recruitment-type tabs, the
   strongest selection level under the Center tabs. */
.rc-primary-tabs { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 0.6rem; }
.rc-primary-tabs .rc-ptab { display: inline-flex; align-items: center; justify-content: center; min-height: 2.7rem; padding: 0.45rem 1.5rem; border: 1px solid var(--edge); border-radius: 6px; background: var(--ink-raised); color: var(--parchment-dim); font-family: var(--font-display); font-size: 0.95rem; letter-spacing: 0.08em; text-transform: uppercase; }
.rc-primary-tabs .rc-ptab:hover { color: var(--arcane-bright); border-color: var(--arcane); text-decoration: none; }
.rc-primary-tabs .rc-ptab:focus-visible { outline: 2px solid var(--arcane-bright); outline-offset: 1px; }
.rc-primary-tabs .rc-ptab.active { color: var(--gold-bright); border-color: var(--gold); background: var(--ink-panel); box-shadow: inset 0 -2px 0 var(--gold); }
.rc-primary-tabs .rc-ptab.active::before { content: "\2713"; font-size: 0.8em; margin-right: 0.45rem; }

/* Section 2.2: level 3 - the two listing-type tabs IMMEDIATELY beneath
   (nothing sits between the rows), clearly subordinate pills. */
.rc-kind-tabs { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0 0 1rem; }
.rc-kind-tabs .rc-ktab { display: inline-flex; align-items: center; gap: 0.45rem; min-height: 2.2rem; padding: 0.3rem 0.95rem; border: 1px solid var(--edge); border-radius: 999px; font-size: 0.82rem; color: var(--parchment-dim); }
.rc-kind-tabs .rc-ktab:hover { color: var(--arcane-bright); border-color: var(--arcane); text-decoration: none; }
.rc-kind-tabs .rc-ktab:focus-visible { outline: 2px solid var(--arcane-bright); outline-offset: 1px; }
.rc-kind-tabs .rc-ktab.active { color: var(--arcane-bright); border-color: var(--arcane); background: var(--ink-panel); }
.rc-kind-tabs .rc-ktab.active::before { content: "\2713"; font-size: 0.75em; }
.rc-kind-tabs .rc-state { font-size: 0.62rem; }

/* Section 3.1: the three-step progress indicator - sticky beneath the
   header at the MEASURED offset. */
.rc-progress { position: sticky; top: calc(var(--header-h, 72px) + 0.4rem); z-index: 5; margin: 0 0 1rem; padding: 0.5rem 0.8rem; border: 1px solid var(--edge); border-radius: 6px; background: var(--ink-panel); }
.rc-progress ol { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0; counter-reset: rcstep; }
.rc-progress li { display: inline-flex; align-items: center; }
.rc-progress li + li::before { content: "\2192"; color: var(--parchment-faint); margin: 0 0.45rem; }
.rc-progress .rc-step { display: inline-flex; align-items: center; gap: 0.45rem; min-height: 2.1rem; padding: 0.25rem 0.7rem; border: 1px solid transparent; border-radius: 999px; font-size: 0.82rem; color: var(--parchment-dim); text-decoration: none; counter-increment: rcstep; }
.rc-progress .rc-step:hover, .rc-progress .rc-step:focus-visible { color: var(--arcane-bright); border-color: var(--arcane); outline: none; }
.rc-progress .rc-step:focus-visible { outline: 2px solid var(--arcane-bright); outline-offset: 1px; }
.rc-progress .rc-step.is-current { color: var(--parchment); border-color: var(--edge-bright); background: var(--ink-raised); }
.rc-progress .rc-step[aria-disabled="true"] { opacity: 0.75; cursor: not-allowed; }
.rc-progress .rc-step-mark { display: inline-flex; align-items: center; justify-content: center; width: 1.2rem; height: 1.2rem; border-radius: 50%; border: 1px solid var(--edge-bright); font-size: 0.7rem; color: var(--parchment-dim); flex: none; }
.rc-progress .rc-step-mark::before { content: counter(rcstep); }
.rc-progress .rc-step.is-complete .rc-step-mark { background: var(--signal-online); border-color: var(--signal-online); color: var(--ink); }
.rc-progress .rc-step.is-complete .rc-step-mark::before { content: "\2713"; }
/* Section 15: the compact indicator below 640px - numbers always, the
   current page's name only. */
@media (max-width: 640px) {
  .rc-progress .rc-step:not(.is-current) .rc-step-label { display: none; }
  .rc-progress li + li::before { margin: 0 0.25rem; }
}

/* Section 3: the page containers. The SERVER stamps data-off and the
   synchronous html.js flag applies it at first paint (zero CLS);
   without scripting every page renders stacked and the classic
   .rc-actions-nojs row serves every server action. */
html.js [data-rc-paged] .rc-page[data-off] { display: none; }
.rc-actions-page { display: none; }
html.js .rc-actions-page:not([data-off]) { display: flex; }
.rc-actions-nojs { display: flex; }
html.js .rc-actions-nojs { display: none; }

/* Sections 8 + 10: every scroll target lands beneath the header AND
   the sticky indicator (both measured). */
.rc-page, .rc-page .form-section, .rc-page .form-row { scroll-margin-top: calc(var(--header-h, 72px) + var(--rc-progress-h, 0px) + 1rem); }

.rc-kind-line { margin: 0 0 0.8rem; color: var(--parchment-dim); font-size: 0.88rem; }
/* Section 4.2: the character names beside each class chip. */
.rc-char-names { color: var(--parchment-faint); font-size: 0.92em; }

/* Section 7.3: the restored-draft notice and its dismiss control. */
.rc-restored { margin: 0 0 0.9rem; }
.rc-restored-dismiss { background: none; border: none; color: var(--arcane-bright); font: inherit; font-size: 0.82rem; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; padding: 0 0 0 0.4rem; }
.rc-restored-dismiss:hover { color: var(--parchment); }

/* Section 7.2: newer unsaved entries vs the last manual save. */
.rc-dirty-note { display: block; font-size: 0.8rem; color: var(--gold-bright); margin: 0.2rem 0 0; }
.rc-dirty-note[hidden] { display: none; }

/* Section 8: the clickable missing-field items. */
.rc-missing a { color: var(--gold-bright); text-decoration: underline; text-underline-offset: 2px; }
.rc-missing a:hover, .rc-missing a:focus-visible { color: var(--parchment); }

/* Section 12: the restrained destructive treatment and readable gated
   forward actions (never so faint the label is unreadable). */
.rc-discard:hover, .rc-discard:focus-visible { color: var(--error-soft); border-color: var(--error-soft); background: rgba(190, 80, 80, 0.08); }
.rc-submit-row .btn[aria-disabled="true"] { opacity: 0.6; cursor: not-allowed; }

/* Section 4.4: rows HELD (disabled, preserved) while Any Class is on. */
.need-row.rows-held { opacity: 0.55; }

/* Section 5.5: the 3:1 banner frame and the reposition control. */
.banner-frame { aspect-ratio: 3 / 1; max-width: 36rem; overflow: hidden; border: 1px solid var(--edge); border-radius: 4px; }
.banner-frame img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; }
.banner-adjust { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.8rem; margin-top: 0.4rem; }
.banner-adjust[hidden] { display: none; }
.banner-pos-label { display: inline-flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--parchment-dim); }
.banner-adjust input[type="range"] { width: 12rem; }

/* Section 11: the small-screen collapsible panel navigation (the
   toggle is SERVER-rendered; the collapse applies only under html.js,
   so both first paints are deterministic and the no-JS sidebar stays
   fully expanded - zero layout shift either way). */
.side-toggle { display: none; }
@media (max-width: 860px) {
  html.js .side-toggle { display: flex; align-items: center; width: 100%; min-height: 2.7rem; padding: 0.55rem 0.8rem; background: none; border: none; border-radius: 4px; color: var(--parchment); font: inherit; cursor: pointer; text-align: left; }
  html.js .side-toggle::after { content: "\25BE"; margin-left: auto; color: var(--parchment-dim); }
  html.js .panel-sidebar.open .side-toggle::after { content: "\25B4"; }
  html.js .side-toggle:focus-visible { outline: 2px solid var(--arcane); outline-offset: -2px; }
  html.js .panel-sidebar > :not(.side-toggle) { display: none; }
  html.js .panel-sidebar.open > :not(.side-toggle) { display: block; }
}

/* ==================== W84 (docs/specs/W84-AUTOSAVE-DRAFTS.md) ==================== */
/* Server-side autosave + the Saved Drafts list. The W83 dirty note and
   its leave warning are RETIRED (the owner ruling: navigation away is
   silent and lossless); in their place sit the compact NON-BLOCKING
   preservation warning (shown only when an autosave attempt FAILED -
   a successful autosave is silent, never an "All changes saved"
   banner) and the compact conflict prompt (a newer version was saved
   from another session - rule 15). */
.rc-save-warn { display: block; margin-top: 0.5rem; font-size: 0.82rem; color: var(--gold-soft, #d8b96a); }
.rc-save-warn::before { content: "\26A0 "; }
.rc-conflict { display: block; margin-top: 0.5rem; font-size: 0.85rem; }
.rc-conflict .button-link { margin-left: 0.6rem; }
.rc-conflict-note { margin: 0 0 1rem; }
.rc-drafts { margin-top: 1.4rem; }
.rc-drafts .rc-actions form { display: inline; }
.rc-drafts .rc-actions { display: inline-flex; flex-wrap: wrap; gap: 0.35rem; align-items: center; }

/* ==================== W84b (docs/specs/W84B-RULINGS.md) ==================== */
/* Ruling 2: the visible one-draft-per-slot indication - a muted line
   in the action area stating that Save Draft updates the slot's
   existing saved draft (or, on a fresh form over one, replaces it). */
.rc-slot-note { display: block; margin-top: 0.4rem; font-size: 0.8rem; color: var(--parchment-dim); }

/* ==================== W85 (docs/specs/W85-SIDEBAR-SCROLLING.md) ==================== */
/* The User Panel side menu's internal scrolling, on EVERY side page
   (the W83 section 11 rule). The persistent sticky sidebar and its own
   scroll region are UNCHANGED (the W67/W68/W83 block above owns them -
   position: sticky at the measured header offset, max-height bounded
   by the viewport, overflow-y: auto so the region scrolls ONLY when
   the menu cannot fit). W85 adds three things and regresses none of
   that:

   1. The scroll region's scrollbar is never PAINTED - not the thumb,
      not the track - in any engine: scrollbar-width (Firefox), the
      legacy -ms-overflow-style (old Edge) and the ::-webkit-scrollbar
      display:none (Chromium/WebKit). Scrolling itself is untouched:
      overflow-y stays auto, so wheel, trackpad, touch drag and the
      keyboard all still scroll the region. Hiding a scrollbar must
      never become hiding the content.
   2. scroll-padding-block keeps NATIVE scroll-into-view (the Tab-focus
      walk, anchor jumps) from parking an item flush against the fade,
      so a keyboard-focused entry always lands fully legible inside the
      region - and, because the region starts below the fixed header,
      fully below the header too.
   3. The discoverability affordance for the hidden scrollbar: a soft
      edge fade, NOT a scrollbar imitation. panel-ux.js
      initSidebarFade stamps data-side-fade with the direction(s) that
      actually have hidden content (top / bottom / both / none), so the
      fade is absent when the menu fits and clears at each end of the
      travel.

      W85b RULING 1 (docs/specs/W85B-RULINGS.md) SUPERSEDES the W85
      mask: the fade was a mask-image on the region itself, which faded
      the card's horizontal BORDER CAP along with the content on
      whichever edge was fading. The owner ruled that a dissolving
      border reads as a rendering glitch rather than as "the menu
      continues", so the frame must stay intact while only the CONTENT
      fades beneath it. The fade is therefore two sticky gradient
      OVERLAYS (::before at the top of the scrollport, ::after at the
      bottom) painting the card's own ground colour down to
      transparent. Nothing is masked, so the border, the rounded
      corners and the background are painted in full at every scroll
      position.

      They cost NO layout: each overlay's height plus the flex gap it
      introduces is cancelled by an equal negative margin, so the
      region's scrollHeight is identical with and without them. They
      are sticky, so they pin to the scrollport edge instead of
      scrolling away; pointer-events: none, so they never intercept a
      click on the entry beneath; and z-index 1 keeps them above the
      non-positioned entries. Driving them by OPACITY (rather than by
      the W85 mask's gradient stops) also makes the transition a real,
      reliably interpolable one - still declared ONLY under
      prefers-reduced-motion: no-preference, so a reduced-motion viewer
      gets the same affordance instantly with no animation. */
.panel-sidebar {
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.panel-sidebar::-webkit-scrollbar { width: 0; height: 0; display: none; }
/* initSidebarFade makes the region itself a tab stop exactly while it
   overflows (see the JS); a keyboard user who lands on it must see it. */
.panel-sidebar:focus-visible { outline: 2px solid var(--arcane); outline-offset: -2px; }
@media (min-width: 861px) {
  .panel-sidebar { scroll-padding-block: 1.9rem; }
  /* The overlays exist ONLY once the script has measured the overflow
     (data-side-fade is stamped by initSidebarFade); the no-JS page and
     the pre-measure first paint carry no overlay at all - and, because
     each one's box is cancelled by its own negative margin, adding
     them shifts nothing either way. --side-fade is the ONE length:
     the overlay height and the margin that cancels it are both
     derived from it, so the two can never drift apart. */
  html.js .panel-sidebar[data-side-fade]::before,
  html.js .panel-sidebar[data-side-fade]::after {
    content: "";
    --side-fade: 1.5rem;
    position: sticky;
    z-index: 1;
    flex: 0 0 auto;
    height: var(--side-fade);
    opacity: 0;
    pointer-events: none;
  }
  html.js .panel-sidebar[data-side-fade]::before {
    top: 0;
    margin-bottom: calc(-1 * (var(--side-fade) + 0.1rem));
    background: linear-gradient(to bottom, var(--ink-raised), transparent);
  }
  html.js .panel-sidebar[data-side-fade]::after {
    bottom: 0;
    margin-top: calc(-1 * (var(--side-fade) + 0.1rem));
    background: linear-gradient(to top, var(--ink-raised), transparent);
  }
  html.js .panel-sidebar[data-side-fade="top"]::before,
  html.js .panel-sidebar[data-side-fade="both"]::before { opacity: 1; }
  html.js .panel-sidebar[data-side-fade="bottom"]::after,
  html.js .panel-sidebar[data-side-fade="both"]::after { opacity: 1; }
  @media (prefers-reduced-motion: no-preference) {
    html.js .panel-sidebar[data-side-fade]::before,
    html.js .panel-sidebar[data-side-fade]::after { transition: opacity 160ms linear; }
  }
}

/* ==================== W85b (docs/specs/W85B-RULINGS.md) ==================== */
/* RULING 4, the STANDING RULE for every future round: the hidden
   scrollbar is SCOPED to .panel-sidebar and future User Panel scroll
   regions do NOT inherit it. Hiding a scrollbar is right for a short
   navigation list, whose every entry is one glance away and whose
   overflow is announced by the edge fade; it is WRONG for a long
   content region, where the scrollbar is a position indicator the
   reader needs (how far in am I, how much is left). Any new panel
   scroll region therefore keeps its native scrollbar unless a round
   deliberately opts it in - never by inheriting a blanket rule. There
   is deliberately no `.panel [data-hide-scrollbar]`-style blanket
   selector in this sheet: opting a region in means adding it, by
   name, beside the .panel-sidebar rule above, with the reason
   recorded. */

/* ==================== W86 (docs/specs/W86-SEQUENTIAL-WORKFLOW.md) ==================== */
/* Sequential completion enforced, the progress menu at the top of the
   listing box with three states, Saved Drafts in its own tab, and the
   four-tier action-button hierarchy. APPENDED: every W83 / W84 / W85
   declaration above is byte-unchanged, so their pins and measurements
   still hold and this block only adds. */

/* Item 3: the internal pane switch - a NEW hierarchy level between the
   Recruitment Center nav and the primary Player / Party-CP / Clan
   tabs. It deliberately wears a THIRD control vocabulary so the three
   rows can never read as interchangeable: the Center row above is
   underlined tabs, the primary row below is separated uppercase
   blocks, and this is ONE bordered segmented switch whose two halves
   share an internal divider with no gap between them. */
.rc-pane-switch { display: inline-flex; flex-wrap: wrap; margin: 0 0 0.9rem; border: 1px solid var(--edge-bright); border-radius: 6px; overflow: hidden; background: var(--ink-raised); }
.rc-pane-switch .rc-pane { display: inline-flex; align-items: center; gap: 0.45rem; min-height: 2.5rem; padding: 0.4rem 1.15rem; font-size: 0.86rem; color: var(--parchment-dim); text-decoration: none; }
.rc-pane-switch .rc-pane + .rc-pane { border-left: 1px solid var(--edge-bright); }
.rc-pane-switch .rc-pane:hover { color: var(--arcane-bright); background: var(--ink-panel); text-decoration: none; }
.rc-pane-switch .rc-pane:focus-visible { outline: 2px solid var(--arcane-bright); outline-offset: -2px; }
/* The selected half is carried by ground, ink, weight AND a leading
   marker - never by colour alone (the W83 section 14 rule). */
.rc-pane-switch .rc-pane.active { color: var(--gold-bright); background: var(--ink-panel); font-weight: 600; box-shadow: inset 0 -2px 0 var(--gold); }
.rc-pane-switch .rc-pane.active::before { content: "\2713"; font-size: 0.8em; }
/* Item 3c: the saved-draft count. */
.rc-pane-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 1.35rem; height: 1.35rem; padding: 0 0.35rem; border: 1px solid var(--edge-bright); border-radius: 999px; background: var(--ink); color: var(--parchment); font-size: 0.72rem; font-variant-numeric: tabular-nums; }
.rc-pane-switch .rc-pane.active .rc-pane-badge { border-color: var(--gold); color: var(--gold-bright); }
/* Item 3a: the drafts table is the whole pane now, not a tail section
   under the form, so its W84 top offset goes. */
.rc-create-section > .rc-drafts { margin-top: 0; }

/* Item 2a: the progress menu is the FIRST child of the listing form
   box, above the dynamic title. Its sticky top is the W83 measured
   offset, unchanged; the panel's own top padding is what it now sits
   in, so the pinned frame gives it a little breathing room below. */
.rc-form-panel > .rc-progress { margin: 0 0 0.9rem; }
.rc-form-panel > .rc-progress + .rc-form-title { margin-top: 0; }

/* Item 2b: THREE states, each legible without colour.
   - available: the step number in an outlined mark, quiet ink;
   - completed: a checkmark replaces the number (the W83 green mark
     stands) and the label takes the parchment ink;
   - locked: a padlock replaces the number, the ink drops to faint and
     the mark loses its ground - and, structurally, the anchor has no
     href, so there is nothing to click or focus (item 2c);
   - current: the W83 filled ground and bright border plus the 600
     label weight added here, so it reads as "you are here" in
     greyscale. (It can also be COMPLETE at the same time - the two
     states stack, and the checkmark is what carries completeness.) */
.rc-progress .rc-step.is-complete .rc-step-label { color: var(--parchment); }
.rc-progress .rc-step.is-current { font-weight: 600; }
.rc-progress .rc-step.is-locked { color: var(--parchment-faint); cursor: default; opacity: 1; }
.rc-progress .rc-step.is-locked:hover { color: var(--parchment-faint); border-color: transparent; }
.rc-progress .rc-step.is-locked .rc-step-mark { border-color: var(--edge); background: none; color: var(--parchment-faint); }
.rc-progress .rc-step.is-locked .rc-step-mark::before { content: "\1F512"; font-size: 0.62rem; }
/* A locked step must never look like the mere absence of a hover. */
.rc-progress .rc-step.is-locked .rc-step-label { text-decoration: line-through; text-decoration-thickness: 1px; text-underline-offset: 2px; }

/* Item 1c + 1d: the per-page action block wraps a row and its
   explanation, so the flex row belongs to .rc-submit-row alone. */
html.js .rc-actions-page:not([data-off]) { display: block; }
.rc-gate-note { margin: 0.45rem 0 0; font-size: 0.82rem; color: var(--parchment-dim); line-height: 1.5; }
.rc-gate-note[hidden] { display: none; }
.rc-gate-lead { color: var(--gold-bright); }
.rc-gate-note a { color: var(--gold-bright); text-decoration: underline; text-underline-offset: 2px; }
.rc-gate-note a:hover, .rc-gate-note a:focus-visible { color: var(--parchment); }
/* Item 3d: the plain statement a partially valid reopened draft lands
   with. */
.rc-landing-note { margin: 0 0 0.9rem; }

/* Item 4: the action hierarchy. Four tiers, each separated by shape,
   border weight, label weight, padding and POSITION as well as
   colour, so the order survives greyscale:
     PRIMARY  (Continue / Preview / Publish) - filled accent ground,
              600 label, the widest padding, always last in its
              cluster;
     SECONDARY (Save Draft) - no fill, a clearly drawn 1px border on
              the panel ground;
     TERTIARY (Back / Back to Edit) - quiet ink, but a VISIBLE border
              (the borderless .btn-quiet is not used on these rows);
     ASIDE    (Start New Listing / Discard Draft) - bordered, and
              lifted out of the navigation cluster behind a divider
              (item 4d: they interrupt the workflow rather than move
              through it). */
.rc-submit-row { row-gap: 0.6rem; }
.rc-act-nav, .rc-act-apart { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; }
.rc-act-apart { margin-left: auto; padding-left: 1rem; border-left: 1px solid var(--edge); }
/* Two-class selectors throughout, for two reasons found by measuring
   rather than by reading: `button.btn { font-weight: inherit; font:
   inherit }` (0,1,1) outranks any single-class tier rule and would
   flatten the primary's heavier label, and `.rc-submit .btn-submit`
   (0,2,0) owns the forward action's padding. ONE vertical padding and
   ONE min-height across all four tiers make the equal button height of
   item 4e a measured fact rather than an intention. */
/* 2.9rem clears the TALLEST natural tier (the 0.8rem-label secondary
   at 45px measured), so all four clamp to ONE height while keeping
   their different label sizes - the size difference stays a hierarchy
   signal and the dimensions stay consistent (item 4e).
   The three-class selector is required, not decorative: the panel's
   own `form[data-rc-form] .btn { min-height: 2.1rem }` scores (0,2,1)
   and silently outranked a two-class `.rc-submit-row .btn`, leaving
   every tier at its natural height. Found by measuring the rendered
   pixels, not by reading the sheet. */
.rc-sticky-actions .rc-submit-row .btn { min-height: 2.9rem; box-sizing: border-box; padding: 0.55rem 1.1rem; display: inline-flex; align-items: center; justify-content: center; }
.rc-sticky-actions .rc-submit-row .rc-act-primary { padding: 0.55rem 1.6rem; font-weight: 600; }
.rc-submit-row .rc-act-secondary { border-color: var(--edge-bright); background: var(--ink-panel); }
.rc-submit-row .rc-act-secondary:hover { border-color: var(--arcane); }
.rc-submit-row .rc-act-ghost { border-color: var(--edge); background: none; color: var(--parchment-dim); }
.rc-submit-row .rc-act-ghost:hover { border-color: var(--edge-bright); color: var(--parchment); background: var(--ink-raised); }
.rc-sticky-actions .rc-submit-row .rc-act-aside { border-color: var(--edge); background: none; color: var(--parchment-dim); font-size: 0.76rem; padding: 0.55rem 0.95rem; }
.rc-submit-row .rc-act-aside:hover { border-color: var(--edge-bright); color: var(--parchment); }
/* Item 4e: four DISTINCT interaction states on every tier. Hover and
   focus are already separate above; active presses the control and
   focus-visible draws a ring that never depends on the border. */
.rc-submit-row .btn:focus-visible { outline: 2px solid var(--arcane-bright); outline-offset: 2px; }
.rc-submit-row .btn:active:not(:disabled) { transform: translateY(1px); }
.rc-act-primary:active:not(:disabled) { background: var(--arcane); color: var(--ink); }
/* A disabled action stays READABLE and stays obviously a button (the
   W29 section-13 rule, extended to the real disabled attribute the
   gate now sets), and its reason rides both the title and the
   near-button explanation below it. */
.rc-submit-row .btn:disabled, .rc-submit-row .btn[aria-disabled="true"] { opacity: 1; cursor: not-allowed; }
.rc-submit-row .rc-act-primary:disabled { background: var(--ink-panel); border-color: var(--edge-bright); border-style: dashed; color: var(--parchment-dim); font-weight: 600; }
/* Below 640px the clusters stack and the divider becomes a rule above
   the interrupting pair, which keeps the separation without a
   left-hand border on a full-width block. */
@media (max-width: 640px) {
  .rc-submit-row { flex-direction: column; align-items: stretch; }
  .rc-act-nav, .rc-act-apart { width: 100%; }
  .rc-act-apart { margin-left: 0; padding: 0.6rem 0 0; border-left: none; border-top: 1px solid var(--edge); }
  .rc-submit-row .btn { width: 100%; }
}


/* ==========================================================================
   W87 (docs/specs/W87-CONSOLIDATED-WEBSITE.md) - items 2, 3, 4 and the
   dimension half of item 5.
   ========================================================================== */

/* --- Item 2: the ONE character/class selection tile ----------------------
   Every button, tile or box that displays character or class information
   in a selection interface is the same box: one width track, one height,
   one padding, one internal layout. Nothing about the CONTENT changes the
   box - a long character name, a long class name, a note, or the selected
   state all render inside the same rectangle. Two things used to break
   that and are handled here by construction:
     1. the selected state added `font-weight: 600` and INSERTED a check
        mark, both of which re-flow the text and widen the tile;
     2. the tile grew to its content, so a long name made one tile wider
        than its neighbours.
   The mark now has a RESERVED slot that is always present and only
   changes color, the weight no longer changes, and the text rides its own
   truncating span. The full text stays in the DOM (so assistive
   technology reads the whole label) and on `title` (so it is available on
   hover); only the paint is clipped. */
:root {
  --rc-tile-min: 13.5rem;   /* the narrowest a tile track may be */
  --rc-tile-h: 2.5rem;      /* the ONE tile height */
  --rc-tile-mark: 0.95rem;  /* the reserved selected-mark slot */
}

/* The tile tracks: equal-width columns that fill the row, so equivalent
   tiles are identical at every width and a partly filled last row keeps
   the same track width as a full one. */
.chip-grid.rc-tile-grid,
[data-class-field] .chip-grid:not(.class-selected),
[data-pos-picker] .chip-grid,
.class-results {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--rc-tile-min)), 1fr));
  gap: 0.45rem;
  align-items: stretch;
}

.rc-tile {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  width: 100%;
  min-width: 0;
  min-height: var(--rc-tile-h);
  padding: 0.32rem 0.7rem;
  border-radius: 6px;
  text-align: left;
  overflow: hidden;
}
/* The reserved mark slot - always laid out, only its color changes, so
   selecting a tile moves nothing. */
.rc-tile::before {
  content: "¹3";
  flex: 0 0 var(--rc-tile-mark);
  width: var(--rc-tile-mark);
  font-size: 0.8em;
  line-height: 1;
  text-align: center;
  color: transparent;
}
.chip-grid .rc-tile:has(input:checked)::before,
.class-result.rc-tile.cr-on::before { color: var(--arcane-bright); }
/* The weight is the same in every state (a bolder run re-flows the text
   inside a fixed box and can flip a tile into truncation on selection);
   the selected state is carried by the background, the border and the
   mark, which cost no space. */
.chip-grid .rc-tile,
.chip-grid .rc-tile:has(input:checked) { font-weight: 400; }
/* The text truncates instead of growing the tile. */
.rc-tile-text {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rc-tile .rc-char-names, .rc-tile .cg-note { white-space: nowrap; }
/* The search-result and position-add tiles share the same box. */
.class-result.rc-tile { border-radius: 6px; padding: 0.32rem 0.7rem; }
.class-results .cr-none { grid-column: 1 / -1; }

/* Item 2 applies to the Marketplace character tiles too (item 5's own
   control): one height whatever the name and class run to, and a
   reserved mark slot so selecting never shifts the row. */
/* ONE fixed height whatever the character name and class run to: the
   mark column, then the name over the meta line, each truncating on its
   own. Before W87 the tile was a wrapping flex row, so a long name
   spilled onto a second line and that card grew taller than its
   neighbours (48.02 px against 66.41 px in the same row). */
.mk-char-row .seg {
  display: grid;
  grid-template-columns: var(--rc-tile-mark) minmax(0, 1fr);
  grid-template-rows: auto auto;
  align-content: center;
  column-gap: 0.35rem;
  row-gap: 0;
  height: 3.3rem;
  min-height: 3.3rem;
  align-items: center;
}
.mk-char-row .seg::before {
  content: "¹3";
  grid-column: 1;
  grid-row: 1 / span 2;
  width: var(--rc-tile-mark);
  font-size: 0.8rem;
  line-height: 1;
  text-align: center;
  color: transparent;
  margin-right: 0;
}
.mk-char-row .seg.active::before { color: var(--arcane-bright); }
.mk-char-row .seg, .mk-char-row .seg.active { font-weight: 400; }
.mk-char-row .seg .mk-char-name,
.mk-char-row .seg .mk-char-meta {
  grid-column: 2;
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The selected-position rows are boxes that display class information
   too: they are full-width by layout, so only the height could move -
   a long class name must truncate rather than wrap the row taller. */
.needs-rows .need-row { min-height: var(--rc-tile-h); }
.need-row .pos-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- Item 3: the item icon on every Raid Boss drop entry ----------------
   The W73 pipeline's icon (gamedb/itemicons.py -> the extracted PNG, or
   its neutral placeholder), one size, one alignment, one gap, for every
   row of the list including the placeholder rows. */
.bt-drop-list .mk-icon {
  flex: 0 0 24px;
  width: 24px;
  height: 24px;
  border-radius: 3px;
}
.bt-drop-name { display: inline-flex; align-items: center; gap: 0.5rem; min-width: 0; }

/* --- Item 4: the expandable Raid Boss row (Bosses tab only) -------------
   The whole row is the control; the chevron states it, the hover tint
   answers the pointer, and the expanded details row stays welded to its
   parent (no gap, no separating border, one shared accent edge). */
.boss-sched tr[data-row-expand] { cursor: pointer; }
/* The hover tint is the ONE marketplace row-hover treatment the table
   already carries through .row-hover; the row adds the accent edge that
   the marketplace rows use to say "this row is the control". */
.boss-sched tr[data-row-expand]:hover > td:first-child { box-shadow: inset 3px 0 0 var(--arcane-bright); }
.boss-sched tr[data-row-expand].is-open:hover > td { background: var(--mk-row-open-hover); }
.boss-sched tr[data-row-expand]:focus-visible {
  outline: 2px solid var(--arcane-bright);
  outline-offset: -2px;
}
.bt-chev {
  display: inline-flex;
  flex: 0 0 auto;
  width: 0.85rem;
  justify-content: center;
  margin-right: 0.4rem;
  color: var(--parchment-dim);
  transition: transform 0.15s ease;
}
/* W97 (docs/specs/W97-BOSS-TABLES-AND-SORT-INDICATORS.md, spec item 3):
   the chevron sits BESIDE the boss name, never on its own line, and the
   location sub-line sits BENEATH the name. The cell's content is ONE
   flex run (.bt-namewrap: the chevron, then the identity block) on
   BOTH the panel tabs and the public Boss Status tables, so the four
   boss tables share one name-cell structure; the identity block
   (.bt-ident) keeps the W64 name-and-location stack, which is why the
   panel's block-level .bt-name used to push the chevron onto a line of
   its own. Baseline alignment puts the glyph on the name's first line
   whatever the name's font; min-width 0 lets a long name wrap inside
   the column instead of growing the cell. */
.boss-table .bt-namewrap { display: flex; align-items: baseline; min-width: 0; }
.boss-table .bt-ident { display: block; flex: 1 1 auto; min-width: 0; }
.boss-sched tr[data-row-expand].is-open .bt-chev { transform: rotate(90deg); color: var(--arcane-bright); }
/* Open: the row and its details row share one tint and one accent edge,
   and the details row keeps no top padding, so the pair reads as one
   block welded together. */
.boss-sched tr[data-row-expand].is-open > td,
.boss-sched tr.bt-details.is-open > td { background: var(--mk-row-open); }
/* W93: the inset accent edge is SUPERSEDED (the W93 block); the tint
   alone marks the open pair, and the tint rules there carry the
   table's own specificity because these two never outranked the zebra. */
.boss-sched tr[data-row-expand].is-open > td:first-child,
.boss-sched tr.bt-details.is-open > td:first-child { box-shadow: inset 3px 0 0 var(--arcane-bright); }
@media (prefers-reduced-motion: reduce) { .bt-chev { transition: none; } }

/* ==========================================================================
   W88 Part C - the site-wide class emblem
   (docs/specs/W88-OWNER-DIRECTIVE.md). ONE component, one square box, one
   gap, one vertical centering rule, three sizes. The emblem SUPPLEMENTS
   the name, so .cls is an inline flex pair that never wraps between the
   image and the first word and never grows the row it sits in.
   ========================================================================== */
.cls {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  min-width: 0;
  max-width: 100%;
  vertical-align: middle;
}
.cls-emblem {
  display: inline-block;
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  border: 1px solid var(--edge);
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.35);
  object-fit: contain;
  vertical-align: middle;
}
/* The neutral placeholder, the same treatment W73 gave a missing item
   icon: a quiet box with a question mark, never a broken image. */
.cls-emblem-none { position: relative; }
.cls-emblem-none::before {
  content: "?";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--parchment-faint);
  font-size: 0.75rem;
  font-weight: 600;
}
.cls-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cls-sm .cls-emblem { width: 18px; height: 18px; }
.cls-lg .cls-emblem { width: 32px; height: 32px; }
.cls-xl .cls-emblem { width: 32px; height: 32px; }
.cls-muted .cls-name { color: var(--parchment-dim); }
/* Inside a table cell the pair may wrap to two lines rather than force a
   horizontal scrollbar; the emblem still leads the first line. */
td .cls-name, th .cls-name { white-space: normal; }

/* ==========================================================================
   W88 Part A - the ONE character selection control
   (docs/specs/W88-OWNER-DIRECTIVE.md). Every picker in the Recruitment
   Center - both listing forms, the Apply control on all four boards and
   the mass-event Register control - renders the same rows through
   includes/_character_option.html, so the three code paths cannot drift.

   Only ELIGIBLE characters are ever in the control (item 1). When none
   is, the control is present and EMPTY and the reason sits BELOW it
   (item 3) - .charpick-msg is that message, and it is never inside the
   control.

   Geometry rides the W87 item 2 tile tokens (--rc-tile-h, --rc-tile-min,
   --rc-tile-mark), so a character row is exactly as tall as a class
   tile, a long name or class truncates instead of growing the box, and
   selecting only repaints - it never resizes and never moves a
   neighbour.
   ========================================================================== */
.charpick-field { display: block; }
.charpick-legend {
  display: block;
  margin-bottom: 0.35rem;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--parchment-dim);
}
.charpick {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--rc-tile-min)), 1fr));
  gap: 0.45rem;
  align-items: stretch;
}
.charpick-opt {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  width: 100%;
  min-width: 0;
  min-height: var(--rc-tile-h);
  margin: 0;
  border: 1px solid var(--edge-bright);
  border-radius: 6px;
  padding: 0.32rem 0.7rem;
  font-size: 0.8rem;
  color: var(--parchment);
  background: var(--ink-panel);
  cursor: pointer;
  overflow: hidden;
}
.charpick-opt input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  pointer-events: none;
}
/* The reserved mark slot, exactly as the class tiles reserve theirs:
   always laid out, only its color changes, so selecting moves nothing. */
.charpick-face {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex: 1 1 auto;
  min-width: 0;
}
.charpick-face::before {
  content: "\2713";
  flex: 0 0 var(--rc-tile-mark);
  width: var(--rc-tile-mark);
  font-size: 0.8em;
  line-height: 1;
  text-align: center;
  color: transparent;
}
.charpick-opt:hover { border-color: var(--arcane); color: var(--arcane-bright); }
.charpick-opt:has(input:checked) {
  background: var(--arcane-deep);
  border-color: var(--arcane);
  color: var(--arcane-bright);
}
.charpick-opt:has(input:checked) .charpick-face::before { color: var(--arcane-bright); }
.charpick-opt:has(input:focus-visible) { outline: 2px solid var(--arcane); outline-offset: 1px; }
/* The weight never changes between states: a bolder run re-flows the
   text inside a fixed box and can flip a row into truncation. */
.charpick-opt, .charpick-opt:has(input:checked) { font-weight: 400; }
.charpick-note {
  flex: 0 0 auto;
  font-size: 0.72rem;
  color: var(--gold);
  white-space: nowrap;
}
.charpick-msg { display: block; margin-top: 0.45rem; }
.charpick-msg strong { color: var(--parchment); }
/* An empty control still occupies its slot, so the message below it
   reads as "this field has nothing to offer" rather than as a gap. */
.charpick[data-charpick-empty] { min-height: 0; }

/* ==========================================================================
   W88 Part B - the Class Progression Tree
   (docs/specs/W88-OWNER-DIRECTIVE.md, spec items 7-24). ONE component
   set for all six races: one race-tab row (the existing .seg-row), one
   class tile, one connector system, one details panel.

   The tile box is FIXED (item 4): --ct-tile-w by --ct-tile-h, the emblem
   at one size, one padding, one type scale. A long class name truncates
   inside the shared emblem component truncating span, so it can neither
   grow a tile nor disturb the hierarchy, and the selected state is
   carried by background, border and edge - none of which costs space, so
   selecting moves nothing.
   ========================================================================== */
:root {
  --ct-tile-max: 11rem;    /* the widest a class tile is ever drawn */
  --ct-tile-min: 7rem;     /* the narrowest, below which the tree indents */
  --ct-tile-w: var(--ct-tile-max);  /* the ONE class tile width */
  --ct-tile-h: 3rem;       /* the ONE class tile height */
  --ct-stem: 1.5rem;       /* the connector run between two tiers */
  --ct-gap: 0.5rem;        /* the gap between sibling tiles */
  --ct-leaves: 6;          /* a branch's final professions; set per branch */
  --ct-nodes: 16;          /* every class in that branch; set per branch */
}

.ct-races { margin-bottom: 1.4rem; }
.ct-branch { margin: 0 0 2rem; }
.ct-branch-title {
  margin: 0 0 0.7rem;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--gold);
}
/* W89 item 3: NOTHING scrolls here, in either direction, and nothing is
   hidden. Each branch element carries its own widest tier as --ct-cols
   (gamedata/classtree.py widest_tier), and every tile in that branch
   takes an equal share of the width the branch actually has - capped at
   --ct-tile-max so a narrow branch is not stretched, floored at
   --ct-tile-min so a wide one stays readable. The chart is only drawn
   above the breakpoint where the widest branch still clears that floor;
   below it the layout becomes the indented tree, where the tile width is
   no longer what has to fit. */
.ct-branch { container-type: inline-size; }
@supports (width: 1cqi) {
  /* 100cqi is the BRANCH's own inline size wherever the value is used, so
     the share is computed against the width the branch actually has - a
     percentage here would resolve against the tile's own parent instead,
     which is circular.

     The drawn width of a nested chart is leaves x tile + nodes x gap:
     every node carries the sibling gap as its own padding, and a node's
     box is the row of its children. Sizing from the widest TIER alone
     undercounts by one gap per internal node - which is exactly how the
     Human tree still overflowed by 20px at a 125% zoom. */
  .ct-branch {
    --ct-tile-w: clamp(
      var(--ct-tile-min),
      calc((100cqi - var(--ct-nodes) * var(--ct-gap)) / var(--ct-leaves)),
      var(--ct-tile-max));
  }
}
.ct-scroll {
  overflow: visible;
  padding-bottom: 0.5rem;
}

/* ---- the connector system -------------------------------------------
   A nested list drawn as an advancement hierarchy: each tier is a row,
   each child hangs from its parent on a real drawn line, and the branch
   point is visible. Physical proximity alone is never the only signal
   (item 2). */
.ct-level {
  display: flex;
  justify-content: center;
  position: relative;
  margin: 0;
  padding: var(--ct-stem) 0 0;
  list-style: none;
}
.ct-root { padding-top: 0; }
/* The stem falling from a parent tile into the row of its children. It
   is drawn INSIDE the row's own padding (top: 0), never above it - a
   negative offset here would run the line back up over the parent tile
   itself, which is exactly what the W88 proof screenshots caught. */
.ct-level .ct-level::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: 0;
  height: var(--ct-stem);
  border-left: 1px solid var(--edge-bright);
}
.ct-node {
  position: relative;
  padding: var(--ct-stem) calc(var(--ct-gap) / 2) 0;
  text-align: center;
  list-style: none;
}
.ct-root > .ct-node { padding-top: 0; }
/* the two halves of the horizontal rule above a row of siblings */
.ct-node::before,
.ct-node::after {
  content: "";
  position: absolute;
  top: 0;
  width: 50%;
  height: var(--ct-stem);
  border-top: 1px solid var(--edge-bright);
}
.ct-node::before { right: 50%; }
.ct-node::after { left: 50%; border-left: 1px solid var(--edge-bright); }
/* an only child needs no crossbar - just the stem its ::after draws */
.ct-node:only-child::before { display: none; }
.ct-node:only-child::after { left: 50%; width: 0; border-top: 0 none; }
/* the run ends at the outermost siblings */
.ct-node:first-child::before,
.ct-node:last-child::after { border-top-color: transparent; }
.ct-root > .ct-node::before,
.ct-root > .ct-node::after { display: none; }

/* ---- the class tile -------------------------------------------------- */
.ct-tile {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  width: var(--ct-tile-w);
  height: var(--ct-tile-h);
  margin: 0 auto;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--edge-bright);
  border-radius: 6px;
  background: var(--ink-panel);
  color: var(--parchment);
  font-size: 0.8rem;
  font-weight: 400;
  text-align: left;
  text-decoration: none;
  overflow: hidden;
}
/* W89b (owner styling requirement): the emblem and the class name are
   ONE group, centered inside the node's full width. Two declarations do
   it and nothing else changes - the tile keeps its box, its padding, its
   emblem size, its typography, its colors, its borders and its
   connectors. `width: auto` lets the pair shrink to its own content so
   there is something to center; `justify-content: center` on the tile
   centers it. A long name and a short name each centre independently,
   because each group is measured from its own content. */
.ct-tile { justify-content: center; }
.ct-tile .cls { width: auto; gap: 0.4rem; }
/* W89 item 3: at a narrow share the class name WRAPS inside the tile's
   own box (two lines fit the 3rem height at this line-height) instead of
   truncating to an ellipsis. The box itself never changes size. */
.ct-tile .cls-name {
  white-space: normal;
  overflow: hidden;
  line-height: 1.12;
  text-overflow: clip;
}
@media (max-width: 1150px) {
  .ct-tile { font-size: 0.74rem; padding: 0.3rem 0.45rem; }
  .ct-tile .cls-emblem { width: 26px; height: 26px; }
}
.ct-tile:hover {
  border-color: var(--arcane);
  color: var(--arcane-bright);
  text-decoration: none;
}
.ct-tile.is-selected {
  background: var(--arcane-deep);
  border-color: var(--arcane);
  color: var(--arcane-bright);
}
.ct-tile:focus-visible { outline: 2px solid var(--arcane); outline-offset: 2px; }
/* The weight never changes between states, so a selected tile cannot
   re-flow its own text into truncation. */
.ct-tile, .ct-tile.is-selected { font-weight: 400; }
.ct-loading .ct-tile { cursor: progress; }

/* ---- the details panel ----------------------------------------------- */
.ct-details-wrap { margin-top: 1.2rem; }
.ct-details {
  border: 1px solid var(--edge);
  border-radius: 8px;
  background: var(--ink-raised);
  padding: 1.2rem 1.3rem 1.4rem;
}
.ct-detail-head {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-bottom: 1rem;
}
.ct-detail-head .cls-emblem { width: 48px; height: 48px; }
.ct-detail-title h2 { margin: 0; font-size: 1.35rem; color: var(--gold-bright); }
.ct-detail-sub { margin: 0.2rem 0 0; color: var(--parchment-dim); font-size: 0.85rem; }
/* W89 item 9: the summary was two very wide columns with most of each
   row empty. The tracks are now sized to the facts themselves, the row
   gap is tightened, and the label sits ABOVE a value that no longer
   needs a 15rem column to hold "Fighter". */
.ct-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.45rem 1.1rem;
  margin: 0 0 1rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--edge);
  border-radius: 6px;
  background: var(--ink-panel);
}
.ct-facts > div { min-width: 0; }
/* One fact reads as a sentence rather than a word; it takes the row. */
.ct-facts .ct-fact-wide { grid-column: 1 / -1; }
.ct-facts dt {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--parchment-faint);
}
.ct-facts dd {
  margin: 0.1rem 0 0;
  color: var(--parchment);
  font-size: 0.85rem;
  line-height: 1.25;
}
.ct-advance h3, .ct-skills h3 {
  margin: 0 0 0.6rem;
  font-size: 0.95rem;
  color: var(--gold);
}
/* W89 item 14: PRECEDED BY and ADVANCES TO are the SAME component
   rendered twice, side by side, so progression reads in both directions
   and neither side can drift from the other. */
.ct-progress {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 0.9rem 1.6rem;
  margin-bottom: 1.4rem;
}
.ct-prog { min-width: 0; }
.ct-prog h3 {
  margin: 0 0 0.5rem;
  font-size: 0.95rem;
  color: var(--gold);
}
.ct-prog p.dull-span { margin: 0; font-size: 0.82rem; }
/* A starting class and a final profession keep the element - it states
   the fact - but carry no clickable option, so nothing misleads. */
.ct-prog.is-empty h3 { color: var(--parchment-faint); }
.ct-advance-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.ct-advance-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.2rem;
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--edge-bright);
  border-radius: 999px;
  color: var(--parchment);
  font-size: 0.8rem;
  text-decoration: none;
}
.ct-advance-link:hover { border-color: var(--arcane); color: var(--arcane-bright); text-decoration: none; }
.ct-advance-lv { color: var(--parchment-dim); font-size: 0.72rem; white-space: nowrap; }

/* W89 item 13: each skill category is a headed section in the page's
   own flow - never an independently scrolling container. */
.ct-skill-group { margin-bottom: 1.6rem; }
.ct-skill-heading {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0 0 0.35rem;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--gold);
}
.ct-skill-count {
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--parchment-faint);
}
/* W89 item 11: when every skill in the section comes from the same
   place, the section says it ONCE - at a readable contrast, not the
   faint per-row caption that made "CLASS TRAINER" unreadable. */
.ct-skill-source {
  margin: 0 0 0.5rem;
  color: var(--parchment-dim);
  font-size: 0.78rem;
}
.ct-skill-icon-col { width: 3rem; }
.ct-skill-icon {
  display: inline-block;
  width: 32px;
  height: 32px;
  border: 1px solid var(--edge);
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.35);
  object-fit: contain;
  vertical-align: middle;
}
.ct-skill-icon-none { position: relative; }
.ct-skill-icon-none::before {
  content: "?";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--parchment-faint);
  font-size: 0.85rem;
  font-weight: 600;
}
.ct-skill-name { text-align: left; font-weight: 600; color: var(--parchment); }
/* W89 item 11: the per-skill source label only appears in a section
   whose skills DO differ, and when it does it is readable - the faint
   ink is reserved for the level's second line. */
.ct-skill-how {
  display: block;
  font-size: 0.72rem;
  font-weight: 400;
  color: var(--parchment-dim);
}
.ct-skill-learn {
  display: block;
  font-size: 0.72rem;
  font-weight: 400;
  color: var(--parchment-faint);
}
/* W89 item 8: both lines name their ladder ("Skill Lv. 13-49" /
   "Character Lv. 40-74"), so the cell must hold two labelled lines
   without wrapping either of them mid-phrase. */
.ct-skill-level {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  vertical-align: top;
}
/* W89 item 12: a long description grows its ROW. Nothing is clipped,
   nothing is truncated, and the three columns stay aligned at the top
   when it wraps to several lines. */
.ct-skill-desc {
  color: var(--parchment-dim);
  vertical-align: top;
  overflow-wrap: anywhere;
}
.ct-skill-table td, .ct-skill-table th { vertical-align: top; }
.ct-skill-table .ct-skill-icon-col { vertical-align: middle; }
/* The skill table follows the W74 stacking standard, so the
   description column only claims a minimum ABOVE the stacking
   breakpoint; below it the table stacks and never scrolls. */
@media (min-width: 601px) { .ct-skill-desc { min-width: 16rem; } }

/* ---- W89 items 5-7: the skill row is a control ----------------------
   The site's own row-expansion vocabulary, reused rather than restated:
   the .bt-chev glyph that the boss tables use, a pointer cursor, and the
   table-hover tint. Striping is EXPLICIT (.ct-row-a / .ct-row-b from the
   loop counter) rather than :nth-child, because an expanded detail row
   is inserted between two skill rows and would otherwise re-stripe the
   whole rest of the table under the reader. */
table.arcanis.ct-skill-table tbody tr.ct-row-a > td,
table.arcanis.ct-skill-table tbody tr.ct-row-a > th,
table.arcanis.ct-sk-levels tbody tr.ct-row-a > td,
table.arcanis.ct-sk-levels tbody tr.ct-row-a > th,
table.arcanis.ct-sk-routes tbody tr.ct-row-a > td,
table.arcanis.ct-sk-routes tbody tr.ct-row-a > th,
table.arcanis.ct-sk-steps tbody tr.ct-row-a > td,
table.arcanis.ct-sk-steps tbody tr.ct-row-a > th { background: var(--row-shade-a); }
table.arcanis.ct-skill-table tbody tr.ct-row-b > td,
table.arcanis.ct-skill-table tbody tr.ct-row-b > th,
table.arcanis.ct-sk-levels tbody tr.ct-row-b > td,
table.arcanis.ct-sk-levels tbody tr.ct-row-b > th,
table.arcanis.ct-sk-routes tbody tr.ct-row-b > td,
table.arcanis.ct-sk-routes tbody tr.ct-row-b > th,
table.arcanis.ct-sk-steps tbody tr.ct-row-b > td,
table.arcanis.ct-sk-steps tbody tr.ct-row-b > th { background: var(--row-shade-b); }
/* Item 6: the site's existing table-hover tint, on every class-related
   table - the skill tables, the enchant-route table and the per-level
   tables alike. Written above the explicit striping so hover wins. */
table.arcanis.ct-skill-table tbody tr.ct-skill-row:hover > td,
table.arcanis.ct-skill-table tbody tr.ct-skill-row:hover > th,
table.arcanis.ct-sk-levels tbody tr:hover > td,
table.arcanis.ct-sk-levels tbody tr:hover > th,
table.arcanis.ct-sk-routes tbody tr.ct-route-row:hover > td,
table.arcanis.ct-sk-routes tbody tr.ct-route-row:hover > th,
table.arcanis.ct-sk-steps tbody tr:hover > td,
table.arcanis.ct-sk-steps tbody tr:hover > th { background: var(--arcane-deep); }
/* W89 item 4: the class tables under the W74 stacking standard.

   The stacked layout folds the header row into a "Sort by" chip strip
   and hides every body <th> that is not a sort chip. Neither fits a
   table that has no sorting and whose ROW HEADER is its subject: below
   600px the skill name simply vanished, under a stray "Sort by" label.
   These two rules are scoped to the class tables - the shared mechanism
   is untouched - so the name reads as the row's title and the empty
   chip strip is gone. */
@media (max-width: 599px) {
  /* No sortable header here, so the chip strip has nothing to hold -
     only the stacked layout drops it; the desktop table keeps its real
     column headers. */
  .stack-lt-600 > table.arcanis.ct-skill-table thead { display: none; }
  .stack-lt-600 > table.arcanis.ct-skill-table tbody th.ct-skill-name {
    display: block;
    width: auto !important;
    padding: 0.22rem 0 0.1rem;
    border: 0;
    border-radius: 0;
    font-size: 0.92rem;
    letter-spacing: 0;
    white-space: normal;
    background: transparent;
  }
}
.ct-skill-row, .ct-route-row { cursor: pointer; }
.ct-skill-row:focus-visible, .ct-route-row:focus-visible {
  outline: 2px solid var(--arcane);
  outline-offset: -2px;
}
.ct-skill-row.is-open .bt-chev,
.ct-route-row.is-open .bt-chev { transform: rotate(90deg); color: var(--arcane-bright); }
.ct-skill-link { color: inherit; text-decoration: none; }
.ct-skill-row:hover .ct-skill-link { color: var(--arcane-bright); }
/* The open pair - the row and its detail - shares one tint and one
   accent edge, exactly as the boss tables' open pair does. */
table.arcanis.ct-skill-table tbody tr.ct-skill-row.is-open > td,
table.arcanis.ct-skill-table tbody tr.ct-skill-row.is-open > th,
table.arcanis.ct-skill-table tbody tr.ct-skill-detail.is-open > td {
  background: var(--mk-row-open);
}
/* W93: the inset accent edge is SUPERSEDED (the W93 block); the shared
   tint alone marks the open pair. */
table.arcanis.ct-skill-table tbody tr.ct-skill-row.is-open > td:first-child,
table.arcanis.ct-skill-table tbody tr.ct-skill-detail.is-open > td:first-child {
  box-shadow: inset 3px 0 0 var(--arcane-bright);
}
.ct-skill-detail > td { padding: 0 0.8rem 0.9rem; }
/* W89b: the backstop. class-tree.js REMOVES a collapsed detail row, so
   this should never match - but a detail row that somehow loses its
   open state must not keep painting a full-height panel, which is
   exactly the defect the owner hit. The specificity clears the stacked
   layout's `tr { display: block }`, which is what a plain
   `[hidden]`-style rule would have lost to below 600px. */
table.arcanis.ct-skill-table tbody tr.ct-skill-detail:not(.is-open) {
  display: none;
}
.ct-skill-loading { min-height: 2rem; }

/* ---- W89 item 5: the expanded skill detail --------------------------- */
.ct-sk { padding-top: 0.2rem; }
.ct-sk-head { display: flex; align-items: center; gap: 0.6rem; }
.ct-sk-icon {
  width: 32px;
  height: 32px;
  border: 1px solid var(--edge);
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.35);
  object-fit: contain;
}
.ct-sk-name { margin: 0; font-weight: 600; color: var(--gold-bright); }
.ct-sk-sub { margin: 0.1rem 0 0; font-size: 0.76rem; color: var(--parchment-dim); }
.ct-sk-desc { margin: 0.6rem 0 0; color: var(--parchment); font-size: 0.85rem; }
.ct-sk-block { margin-top: 0.9rem; }
.ct-sk-block h5 {
  margin: 0 0 0.35rem;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--gold);
}
.ct-sk-note { margin: 0 0 0.4rem; font-size: 0.76rem; color: var(--parchment-dim); }
.ct-sk-range, .ct-sk-step { white-space: nowrap; font-variant-numeric: tabular-nums; }
/* W89b, owner ruling on Q3: the collapsed level list. The extra rows are
   hidden ONLY when scripting is available - html.js - so a reader
   without it gets the complete table instead of three rows and a control
   that cannot do anything. The same flag hides the control itself when
   it would be inert. */
html.js .ct-sk-levelbox.is-collapsed tr[data-ct-extra] { display: none; }
html:not(.js) .ct-sk-more,
html:not(.js) .ct-sk-levelnote { display: none; }
.ct-sk-levelnote {
  margin: 0.35rem 0 0.3rem;
  color: var(--parchment-faint);
  font-size: 0.74rem;
}
.ct-sk-more { font-size: 0.74rem; padding: 0.2rem 0.6rem; }
.ct-route-detail > td { padding: 0 0 0.6rem; }
.ct-sk-levels, .ct-sk-routes, .ct-sk-steps { font-size: 0.82rem; }
.ct-empty { margin-top: 0.4rem; }

/* ---- responsive (item 13) --------------------------------------------
   Below 900px the hierarchy turns from a top-down chart into an indented
   tree: each child hangs from its parent on a drawn elbow, so the
   parent-child relationship survives at every width. Nothing overlaps,
   nothing is dropped, and no connector becomes unreadable - it changes
   direction rather than disappearing. */
@media (max-width: 900px) {
  .ct-level {
    display: block;
    padding: 0 0 0 1.3rem;
  }
  .ct-root { padding-left: 0; }
  .ct-level .ct-level::before {
    top: 0;
    left: 0;
    height: 100%;
  }
  .ct-node {
    padding: 0.35rem 0 0 1.3rem;
    text-align: left;
  }
  .ct-root > .ct-node { padding-left: 0; }
  .ct-node::before {
    top: 0;
    right: auto;
    left: 0;
    width: 1.3rem;
    height: 1.85rem;
    border-top: 0 none;
    border-left: 1px solid var(--edge-bright);
    border-bottom: 1px solid var(--edge-bright);
    border-bottom-left-radius: 6px;
  }
  .ct-node::after {
    top: 1.85rem;
    left: 0;
    width: 0;
    height: calc(100% - 1.85rem);
    border-top: 0 none;
    border-left: 1px solid var(--edge-bright);
  }
  .ct-node:only-child::before { display: block; }
  .ct-node:only-child::after { display: none; }
  .ct-node:last-child::after { display: none; }
  .ct-root > .ct-node::before,
  .ct-root > .ct-node::after { display: none; }
  /* W89 item 3: the indented layout is not width-constrained the way
     the chart is - each row holds ONE tile - so the branch's fitting
     formula is switched off here and the tile takes its full width
     back. Leaving the chart's share in place squeezed a name into a
     112px box for no reason. */
  .ct-branch { --ct-tile-w: var(--ct-tile-max); }
  .ct-tile { margin: 0; width: 100%; max-width: var(--ct-tile-w); }
  .ct-tile .cls-emblem { width: 32px; height: 32px; }
}
@media (max-width: 560px) {
  .ct-branch { --ct-tile-w: 10rem; }
  .ct-details { padding: 1rem; }
}

/* W88 Part A: the character control inside the two hosts that used to
   hold a <select> - the board Apply form (a wrapping flex row) and the
   mass-event Register grid (a two-column grid). The control takes a
   full row in both, so its rows never fight the message beside them. */
.rc-apply .charpick-field { flex: 1 1 100%; max-width: 34rem; }
.rc-apply .charpick,
.rc-reg-field .charpick {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr));
}
.rc-reg-field .charpick-legend { margin-bottom: 0.25rem; }
.rc-actions .charpick-msg { max-width: 42rem; }

/* W88 Part C item 4 - context-appropriate sizing. The emblem scales to
   the type it sits beside, so it supplements the name rather than
   dominating a small chip: the uppercase micro-type of a badge and the
   marketplace character chip's meta line both take the smallest box. */
.badge .cls-emblem,
.mk-char-meta .cls-emblem { width: 14px; height: 14px; border-radius: 3px; }
.badge .cls,
.mk-char-meta .cls { gap: 0.3rem; }
.badge .cls-name { text-transform: uppercase; letter-spacing: 0.12em; }
/* Inside a rc-tile the emblem rides the tile's own truncating span, so
   the name still ellipsises and the tile box never grows. */
.rc-tile-text .cls { max-width: 100%; }
.rc-tile-text .cls-emblem { width: 16px; height: 16px; }

/* W88 Part C - the emblem must not change the geometry of the table it
   joins. The W50 LEDGER interiors are a protected capture, and adding an
   emblem to a class column raised every ranking row from 42.9px to 45px:
   .cls is an inline-flex box, so it inherits the cell line-height (24.3px)
   and its line box then needs more room than the cell reserves.

   Pinning the flex container to line-height 1 makes its box exactly the
   emblem box (20px with its border), which fits the cell content box that
   already existed - so the rows measure 42.9px again, unchanged from
   before this round. The NAME keeps a normal line-height, so a class name
   that wraps to a second line still reads correctly. */
td .cls, th .cls { line-height: 1; }
td .cls-name, th .cls-name { line-height: normal; }

/* ==========================================================================
   W90 item 1 - the site-wide live-table layer
   ==========================================================================
   ONE RULE, and it is not about the layer being subtle - it is about the
   layer being INVISIBLE. After the owner's W90 rulings
   (docs/specs/W90-RULINGS.md) this whole section styles exactly one
   thing, and everything else it used to style is ruled out permanently:

   - a region adds NO ELEMENT at all, so it cannot change a measurement
      or break a selector (see the note below);
   - there is NO update highlight (ruling Q2) - a changed cell gets its
      new text and nothing else;
   - there is NO live indicator (ruling Q1) - no dot, pill or badge, and
      nothing equivalent under another name.

   What remains is the one state a reader must be able to see: a row
   whose data is gone while they still have it open.
   -------------------------------------------------------------------- */

/* THERE IS NO REGION ELEMENT, so there is no rule for one. A region is a
   pair of HTML comments (core/templatetags/livetable.py). The first cut
   of this round did wrap each region in a div with display:contents, on
   the reasoning that an element generating no box cannot change a
   layout - and the layout harness proved that wrong on this round's own
   tree: `.section-content > .class-filter` is a CHILD COMBINATOR, and the
   wrapper made the class filter a grandchild, so Olympiad and Tournament
   lost its styling and measured a real cumulative layout shift of
   0.00100 against a standard of zero. A comment has no box, no styling,
   no selector surface, and no place in any combinator. */

/* NO UPDATE HIGHLIGHT. Owner ruling Q2, STANDING: values change
   SILENTLY. W90 first shipped a 2.4-second violet background flash on a
   changed cell; the owner ruled it out with the reasoning that it is a
   subtler version of the same "look, something updated" signal that
   ruling Q1 had already declined - if a value changes while the reader
   is looking at it, SEEING THE NEW VALUE IS THE SIGNAL.

   So a patched cell receives new TEXT and nothing else: no class, no
   background, no animation, no transition. There is nothing here to
   suppress under prefers-reduced-motion either, because there is no
   motion to begin with.

   A future round must not reintroduce one without a new ruling. */

/* A row whose data is gone but which the visitor still has open: it is
   kept until they close it (never yanked away mid-read) and marked so
   they can see it is no longer current. Opacity only. */
tr[data-live-gone] { opacity: 0.55; }

/* NO VISIBLE INDICATOR SHIPS IN W90, and the first cut of this block is
   why the decision is written down rather than merely taken.

   It defined a `.live-dot` - and `.live-dot` ALREADY EXISTS on this site:
   it is the pulsing red badge on a live stream card (W68,
   pages/_stream_card.html). A later rule in the same stylesheet wins, so
   the new one set `visibility: hidden` on every stream card's LIVE badge
   across the site.

   NOTHING THIS PROJECT OWNS WOULD HAVE CAUGHT IT. `visibility: hidden`
   leaves the box in place, so the layout harness - which measures
   geometry - compared identical boxes and reported green on every page.
   The Django suite asserts markup, and the markup was unchanged. The
   HTTP smoke reads bytes, and the bytes were unchanged. It reached
   PRODUCTION, and the thing that finally caught it was the W90 live
   verifier reading the deployed stylesheet for the property directly.

   Two things follow, and both are kept:

   1. The rules are REMOVED rather than renamed, because a visible "live"
      indicator is NEW VISUAL VOCABULARY on surfaces whose look is
      owner-ruled (the f110 gates). It is a Gate A question, not
      something to add beside a mechanism, and it is asked as one in the
      round report.
   2. Requirement 5's actual demand - "no false live claim when nothing
      is connected" - is met the strongest way available: nothing on the
      site claims to be live at all, so no claim can be false. The
      client's setIndicator hook stays wired for the day it is ruled on. */

/* ==========================================================================
   W93 - the expandable interface rule (owner spec item 2,
   docs/specs/W93-WEBSITE-AND-SERVER-INSTRUCTIONS.md), site-wide and
   STANDING for every expandable component, existing and future.

   THE RULE. When an element expands, the revealed content is a
   CONTINUATION of its parent, never a window inside a window: no inner
   accent line, no secondary border, no inset outline, no boxed section
   inside the already-bounded parent. Spacing, indentation, typography,
   alignment, a subtle background difference and content hierarchy
   organise the revealed information. ONE boundary, where one is
   needed, says which parent is open, and the revealed content stays
   attached to the row, card or section it came from.

   THE SHARED TREATMENT (future components inherit it; nothing is
   re-implemented per surface):
     .x-reveal   on the revealed container - it paints no border, no
                 shadow, no outline, no radius, no indent of its own.
     .x-section  on a grouped subsection inside a reveal (the Buy Now
                 and Place Bid panels, a fee preview) - grouped by its
                 heading and the grid gap, never by a box.
     --open-tint the ONE parent boundary for expandable table rows: the
                 open row and its revealed row share it, a closed row
                 never paints it. Card and section expanders keep the
                 parent's own edge (a card border, a callout) as theirs.

   SUPERSESSION, recorded so a later round does not restore the framing:
     - W78 (Marketplace expandable rows): the bordered, rounded, tinted
       .mk-x-panel action boxes, the dashed .mk-x-fold rule and the
       .mk-listing-form-row top rule are RETIRED. (W80 items 3/5/6 had
       already retired the row's inset bar, left rule and indent.)
     - W87 item 4 (Raid Boss rows): the .bt-details-inner 2px accent
       left rule with its 3.5rem indent, the inset accent bar on the
       open pair and the dotted rules between drop entries are RETIRED.
       W87b's Grand tab rows inherit the same retirement.
     - W89 item 5 (class skill rows): the inset accent bar on the open
       pair is RETIRED and the nested level / route / step tables lose
       their own chrome; W89's .ct-facts box inside the .ct-details
       panel is RETIRED (the panel is the one boundary).
     - W60 (Alerts categories): the per-row .alert-row cards inside an
       open category, the .boss-alert-area box and the .ba-list box are
       RETIRED; the category head keeps the one boundary.
     - W67 (Full Build note): the bordered gold-edged .fb-rule box is
       RETIRED; the note indents beneath its label.
   The rule of record for each retirement is the comment at the
   original rule, which now points here.

   The measured shape of the earlier "one tint" claim, stated so the
   fix is understood: on the boss and class tables the open tint was
   written at (0,3,2) and the global zebra at (0,3,3), so the tint NEVER
   painted - only the inset bar said which pair was open. The tint rules
   below carry the table's own specificity and paint. */
:root { --open-tint: var(--mk-row-open); }

.x-reveal { border: 0; box-shadow: none; outline: 0; border-radius: 0; }
.x-reveal .x-section { border: 0; border-radius: 0; background: transparent; padding: 0; }

/* Boss Drops (both boss tabs): the revealed facts and drops align with
   the boss name (the chevron column's width, no rule, no bar), the drop
   entries are separated by their own line height, and the ONE boundary
   is the shared open tint on the pair. */
.boss-sched .bt-details-inner { padding: 0.2rem 0 0; border-left: 0; }
/* W93b, owner ruling W93 Q2: KEEP FLAT - the two-column line spacing
   separates the entries; a fainter separator is the framing rule 1
   removes. Do not restore a rule between drop entries. */
.boss-sched .bt-drop-list li { border-top: 0; padding: 0.14rem 0; }
table.arcanis.boss-sched tbody tr[data-row-expand].is-open > td,
table.arcanis.boss-sched tbody tr.bt-details.is-open > td { background: var(--open-tint); }
table.arcanis.boss-sched tbody tr[data-row-expand].is-open:hover > td { background: var(--mk-row-open-hover); }
.boss-sched tr[data-row-expand].is-open > td:first-child,
.boss-sched tr[data-row-expand].is-open:hover > td:first-child,
.boss-sched tr.bt-details.is-open > td:first-child { box-shadow: none; }

/* Marketplace details (Browse / Watchlist / My Listings / Sell /
   History): the action panels are grouped by their headings and the
   panel gap, the narrow-width fold and the Sell form join their facts
   with spacing, and the fee preview inside a row keeps only its
   subtle background. */
.mk-x-panel { border: 0; border-radius: 0; background: transparent; padding: 0; }
.mk-x-panels { gap: 0.6rem 1.6rem; }
.mk-x-fold { border-top: 0; padding-top: 0; margin-top: 0.45rem; }
.mk-listing-form-row { border-top: 0; padding-top: 0.3rem; }
.mk-expand-inner .mk-fee-preview { border-left: 0; padding: 0.5rem 0.7rem; }

/* Class pages: the skill row's open pair shares the tint alone; the
   level, route and step tables inside the revealed detail are the
   detail's own lists - no header rule, no zebra of their own, no cell
   rules - and the facts strip inside the class panel is a strip, not a
   second box. */
table.arcanis.ct-skill-table tbody tr.ct-skill-row.is-open > td:first-child,
table.arcanis.ct-skill-table tbody tr.ct-skill-detail.is-open > td:first-child { box-shadow: none; }
/* W93b, owner ruling W93 Q4: CONFIRMED flat - the faint alternate shade
   stays; the header underline and the cell rules do not return ("a header
   underline inside an expanded skill is the window-inside-a-window
   pattern in miniature"). */
.ct-sk table.arcanis th { border-bottom: 0; }
.ct-sk table.arcanis td { border-bottom: 0; }
.ct-sk table.arcanis tbody tr:nth-child(odd) td,
.ct-sk table.arcanis tbody tr:nth-child(even) td,
.ct-sk table.arcanis tbody tr.ct-row-a > td, .ct-sk table.arcanis tbody tr.ct-row-b > td,
.ct-sk table.arcanis tbody tr.ct-row-a > th, .ct-sk table.arcanis tbody tr.ct-row-b > th { background: transparent; }
.ct-sk table.arcanis tbody tr:nth-child(even) td,
.ct-sk table.arcanis tbody tr:nth-child(even) th { background: rgba(255, 255, 255, 0.025); }
.ct-facts { border: 0; border-radius: 0; background: transparent; padding: 0 0 0.9rem; }

/* Alerts (account information): the rows inside an open category sit
   on a subtle shared ground, separated by the list gap; the boss
   selection area indents beneath its master toggle and its bounded
   list scrolls without a frame. */
.alert-group .alert-row { border: 0; border-radius: 0; background: rgba(255, 255, 255, 0.025); }
.boss-alert-area { border: 0; border-radius: 0; padding: 0.35rem 0 0 0.9rem; }
.ba-list { border: 0; border-radius: 0; padding: 0.3rem 0.1rem 0.3rem 0; }

/* The Full Build note: an indented note beneath its label. */
.fb-rule { border: 0; border-radius: 0; padding: 0.4rem 0.7rem; background: rgba(255, 255, 255, 0.03); }

/* Messages (account information): the open pair's tint, at the table's
   own specificity (the W81 rules sat beneath the zebra and never
   painted - measured by scripts/w93-proofs.py). */
table.arcanis.ib-table tbody tr.ib-row.is-open > td,
table.arcanis.ib-table tbody tr.ib-expand > td { background: var(--open-tint); }

/* Recruitment Center mass-event cards: the registration mode inside
   Details & Register is a labelled group, not a bordered box. */
.rc-ev-more .rc-reg-mode { border: 0; border-radius: 0; padding: 0; }
.rc-ev-more .rc-reg-mode legend { padding: 0; }

/* ==========================================================================
   W94 - the Item database, the NPC / Monster database and the drop links
   (owner spec items 4-6, docs/specs/W94-WEBSITE-AND-SERVER-INSTRUCTIONS.md).

   THE DROP LIST. templates/gamedata/_drop_list.html is the ONE drop-list
   include on every drop surface (panel boss rows, public Boss Status rows,
   NPC index rows, the NPC fragment). It carries BOTH the panel's
   .bt-drop-list class (so the W87 / W93 rules keep painting it there,
   unchanged) and .x-drops, whose rules below are the SAME values as the
   W93 end state of .bt-drop-list, written once for the surfaces outside
   .boss-sched. The icon and the name are links to the item's page; the
   icon link is tabindex=-1 so the keyboard meets one link per entry.

   THE EXPANDABLE ROW (site-wide, static/js/row-expand.js). A table with
   .x-table carries rows that are their own expansion control and one
   .x-details row each. Under the W93 rule the revealed row is a
   continuation: .x-reveal (no chrome), the pair's shared --open-tint as
   the ONE boundary, written at the table's own specificity (0,3,3)+ so
   it outranks the W51 zebra (the trap W93 measured). Hidden until
   opened only WITH scripting (the .js flag rule); visible in full without.

   THE TABLES. .db-table is table.arcanis with typed columns; the icon
   column is fixed to the 32px icon; the name column is the flexible
   one. The facts grid (.db-facts) is a plain grid - spacing and
   typography, no box - because it is page content, not a reveal. */
.x-drops { margin: 0; padding: 0; list-style: none; font-size: 0.78rem; color: var(--parchment-dim); columns: 2; column-gap: 2rem; }
.x-drops li { display: flex; justify-content: space-between; gap: 0.8rem; padding: 0.14rem 0; border-top: 0; break-inside: avoid; }
.x-drops .bt-chance { flex: none; font-variant-numeric: tabular-nums; color: var(--parchment-faint); }
.x-drop-icon { display: inline-flex; flex: none; line-height: 0; }
.x-drop-icon .mk-icon { display: block; }
.x-drop-tag { display: inline-block; padding: 0 0.35rem; border-radius: 3px; font-size: 0.64rem; text-transform: uppercase; letter-spacing: 0.06em; background: rgba(155, 109, 255, 0.16); color: var(--arcane-bright); vertical-align: 0.1em; }
.x-drops-more { margin-left: 0.5rem; font-size: 0.72rem; text-transform: none; letter-spacing: 0; color: var(--parchment-faint); text-decoration: underline dotted; text-underline-offset: 0.15em; }
.x-drops-more:hover { color: var(--arcane-bright); }
@media (max-width: 767px) {
  .x-drops { columns: 1; }
}

/* The site-wide expandable row: the W87 boss-row shape on a public table. */
.js table.arcanis.x-table tr.x-details:not(.is-open) { display: none; }
table.arcanis.x-table tr.x-details > td { padding-top: 0; padding-bottom: 0.7rem; }
table.arcanis.x-table tr[data-x-row] { cursor: pointer; }
table.arcanis.x-table tr[data-x-row].is-open .bt-chev { transform: rotate(90deg); color: var(--arcane-bright); }
table.arcanis.x-table tbody tr[data-x-row].is-open > td,
table.arcanis.x-table tbody tr.x-details.is-open > td { background: var(--open-tint); }
table.arcanis.x-table tbody tr[data-x-row].is-open:hover > td { background: var(--mk-row-open-hover); }
table.arcanis.x-table tbody tr[data-x-row].is-open > td:first-child,
table.arcanis.x-table tbody tr.x-details.is-open > td:first-child { box-shadow: none; }
.x-details-inner { padding: 0.2rem 0 0 1.35rem; }
.x-details-inner .bt-drops-title { margin: 0 0 0.15rem; font-size: 0.76rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--gold); }
.x-table .bt-cell-name { position: relative; }
.x-table .bt-sub { display: block; margin-top: 0.15rem; font-size: 0.7rem; color: var(--parchment-faint); font-family: var(--font-sans); letter-spacing: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tbl-stack > table.arcanis.x-table tbody tr[data-x-row].is-open,
.tbl-stack > table.arcanis.x-table tbody tr.x-details.is-open { background: var(--open-tint); }
@media (max-width: 1099px) {
  .stack-lt-1100 > table.arcanis .x-details-inner,
  .stack-lt-900 > table.arcanis .x-details-inner,
  .stack-lt-600 > table.arcanis .x-details-inner { padding-left: 0; }
}
/* The public Boss Status tables: their details rows use the panel's
   .bt-details name so the W91 client's detail-row handling (which
   matches by attribute, not class) and the harness read them alike. */
table.arcanis.boss-table.x-table tr.bt-details > td { padding-top: 0; padding-bottom: 0.7rem; }
.js table.arcanis.boss-table.x-table tr.bt-details:not(.is-open) { display: none; }
table.arcanis.boss-table.x-table tbody tr.bt-details.is-open > td { background: var(--open-tint); }
table.arcanis.boss-table.x-table tbody tr.bt-details.is-open > td:first-child { box-shadow: none; }
.boss-table .bt-details-inner { padding: 0.2rem 0 0 1.35rem; border-left: 0; }
.boss-table .bt-drops-title { margin: 0 0 0.15rem; font-size: 0.76rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--gold); }

/* The database tables. */
table.arcanis.db-table col.c-icon { width: 3.25rem; }
table.arcanis.db-table col.c-grade { width: 6.5rem; }
table.arcanis.db-table col.c-type { width: 8.5rem; }
table.arcanis.db-table col.c-cat { width: 12rem; }
table.arcanis.db-table col.c-weight { width: 6rem; }
table.arcanis.db-table col.c-lv { width: 4.5rem; }
table.arcanis.db-table col.c-race { width: 9rem; }
table.arcanis.db-table col.c-hp { width: 7.5rem; }
table.arcanis.db-table col.c-drops { width: 5.5rem; }
table.arcanis.db-table col.c-qty { width: 7rem; }
table.arcanis.db-table col.c-chance { width: 7rem; }
table.arcanis.db-table col.c-kind { width: 6rem; }
table.arcanis.db-table col.c-region { width: 12rem; }
table.arcanis.db-table col.c-count { width: 5.5rem; }
table.arcanis.db-table col.c-respawn { width: 8rem; }
table.arcanis.db-table col.c-name { width: auto; }
.db-icon-cell { line-height: 0; }
.db-icon-cell a { display: inline-block; }
.db-th-icon { width: 3.25rem; }
.db-name-link { color: inherit; text-decoration: none; border-bottom: 1px dotted var(--edge); }
.db-name-link:hover { color: var(--arcane-bright); border-bottom-color: var(--arcane-bright); }
.db-grade { font-family: var(--font-display); letter-spacing: 0.06em; }
.db-grade-s, .db-grade-s80, .db-grade-s84 { color: var(--gold); }
.db-grade-a { color: var(--arcane-bright); }
.db-count { margin: 0 0 0.6rem; font-size: 0.8rem; }
.db-aggro { background: rgba(224, 92, 92, 0.16); color: #e39a9a; }
.db-boss-link { margin-left: 0.4rem; font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--gold); text-decoration: underline dotted; text-underline-offset: 0.15em; }
.db-boss-link:hover { color: var(--gold-bright); }

/* The record pages. */
.db-hero-icon { display: inline-flex; vertical-align: middle; margin-right: 0.55rem; line-height: 0; }
.db-hero-icon .mk-icon { width: 32px; height: 32px; }
.db-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: 0.55rem 1.2rem; margin: 0; padding: 0; }
.db-facts > div { min-width: 0; }
.db-facts dt { font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--parchment-faint); }
.db-facts dd { margin: 0.1rem 0 0; font-size: 0.92rem; color: var(--parchment); overflow-wrap: anywhere; }
.db-raw { font-size: 0.68rem; color: var(--parchment-faint); font-family: var(--font-clock); letter-spacing: 0; }
.db-stats dd { font-family: var(--font-clock); font-variant-numeric: tabular-nums; }
.db-flags dd.db-yes { color: var(--signal-online); }
.db-flags dd.db-no { color: var(--parchment-faint); }
.db-h3 { margin: 1.1rem 0 0.5rem; font-size: 0.76rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--gold); }
.db-list { margin: 0; padding: 0 0 0 1.1rem; font-size: 0.86rem; color: var(--parchment-dim); }
.db-list li { padding: 0.1rem 0; }
.db-list-cols { columns: 2; column-gap: 2rem; }
.db-inline { margin: 0; font-size: 0.9rem; }
.db-source { margin: 0.9rem 0 0; font-size: 0.72rem; }
.db-boss-callout p { margin: 0 0 0.6rem; }
.db-boss-callout p:last-child { margin-bottom: 0; }
@media (max-width: 767px) {
  .db-list-cols { columns: 1; }
}


/* ==================================================================
   W95 (docs/specs/W95-WEBSITE-AND-SERVER-INSTRUCTIONS.md items 8-9):
   the Website Administration interface. It rides the User Panel's
   shell (.panel-shell / .panel-columns / .panel-sidebar / .panel-content
   above) and the site table skin; this block adds only what the
   administration pages need - the stat cards, the inline SVG charts
   (one hue each, thin bars anchored to the baseline, the table beside
   the chart is the readable form), the two-column blocks, the code
   editor rows and the sandboxed HTML preview frame. Expanded article,
   message and media rows are W94 x-table rows under the W93 rule
   (.x-reveal, no inner framing). Nothing here reaches a public page:
   every selector is scoped by .wa-layout or a .wa- class.
   ================================================================== */
.wa-layout .wa-bar { justify-content: flex-start; }
.wa-layout .wa-bar-name { color: var(--gold); letter-spacing: 0.08em; text-transform: uppercase; font-size: 0.78rem; }
.wa-layout .wa-bar-links { margin-left: auto; }
.wa-lead { margin: 0 0 1.1rem; color: var(--parchment-dim); font-size: 0.92rem; line-height: 1.55; max-width: 62rem; }
.wa-h2 { margin: 1.6rem 0 0.7rem; font-size: 1.05rem; }
.wa-h2:first-child { margin-top: 0; }
.wa-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr)); gap: 0.75rem; margin: 0 0 1rem; }
.wa-card { background: var(--ink-raised); border: 1px solid var(--edge-soft); border-radius: 6px; padding: 0.75rem 0.9rem; display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; }
.wa-num { font-family: var(--font-display, inherit); font-size: 1.55rem; line-height: 1.1; color: var(--parchment); font-variant-numeric: tabular-nums; }
.wa-cap { font-size: 0.74rem; line-height: 1.35; color: var(--parchment-dim); }
.wa-chart { margin: 0 0 1rem; background: var(--ink-raised); border: 1px solid var(--edge-soft); border-radius: 6px; padding: 0.6rem 0.9rem 0.5rem; min-width: 0; }
.wa-chart figcaption { font-size: 0.78rem; color: var(--parchment-dim); margin-bottom: 0.4rem; }
.wa-chart svg { display: block; width: 100%; height: 7.5rem; }
.wa-axis { stroke: var(--edge); stroke-width: 1; }
.wa-bar { fill: var(--arcane); }
.wa-bar:hover { fill: var(--arcane-bright); }
.wa-line { fill: none; stroke: var(--gold); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.wa-charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); gap: 0.75rem; }
.wa-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr)); gap: 0.5rem 1.25rem; align-items: start; }
/* The site's .table-wrap never scrolls (W74: tables stack, never clip);
   the administration grids therefore keep a 26rem column floor so a
   table inside a column is never narrower than its content, and cells
   with long keys break rather than push the page. */
.wa-table td, .wa-table th { overflow-wrap: anywhere; }
.wa-grid-form { grid-template-columns: minmax(18rem, 26rem) minmax(20rem, 1fr); }
.wa-grid-editor { grid-template-columns: minmax(22rem, 1fr) minmax(20rem, 1fr); }
@media (max-width: 1100px) { .wa-grid-form, .wa-grid-editor { grid-template-columns: 1fr; } }
.wa-block { min-width: 0; }
.wa-block .wa-h2 { margin-top: 1rem; }
.wa-more { margin: 0 0 0.5rem; display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wa-table .wa-path { font-family: var(--font-mono, monospace); font-size: 0.82rem; word-break: break-all; }
.wa-table .wa-when { font-size: 0.82rem; color: var(--parchment-dim); max-width: 32rem; }
.wa-bad { color: var(--signal-offline); }
.wa-window .wa-window-note { align-self: center; font-size: 0.8rem; }
.wa-excerpt { margin: 0 0 0.4rem; max-width: 60rem; line-height: 1.5; }
.wa-meta { margin: 0 0 0.6rem; font-size: 0.8rem; }
.wa-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin: 0.4rem 0 0.2rem; }
.wa-inline { display: inline-flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; margin: 0; }
.wa-cell-actions { white-space: nowrap; }
.wa-cell-actions .btn, .wa-cell-actions .wa-inline { vertical-align: middle; }
.wa-cell-actions .btn { padding: 0.2rem 0.6rem; font-size: 0.74rem; }
.wa-status-form .wa-reason { display: flex; flex-direction: column; gap: 0.25rem; max-width: 30rem; font-size: 0.78rem; color: var(--parchment-dim); margin-bottom: 0.4rem; }
.wa-code-row textarea { font-family: var(--font-mono, monospace); font-size: 0.8rem; line-height: 1.45; white-space: pre; overflow-wrap: normal; overflow-x: auto; tab-size: 2; }
.wa-vars code, .wa-meta code { font-family: var(--font-mono, monospace); font-size: 0.8rem; color: var(--gold); }
.wa-vars td, .wa-vars th { overflow-wrap: anywhere; }
.wa-vars td:first-child { overflow-wrap: normal; white-space: nowrap; }
.wa-vars .wa-sample { font-size: 0.8rem; color: var(--parchment-dim); word-break: break-all; }
.wa-subject { margin: 0 0 0.5rem; }
.wa-frame { display: block; width: 100%; height: 34rem; border: 1px solid var(--edge-soft); border-radius: 6px; background: #f4efe4; }
.wa-textpreview { margin: 0.6rem 0; }
.wa-textpreview summary { cursor: pointer; color: var(--parchment-dim); font-size: 0.85rem; }
.wa-pre { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 0.8rem; line-height: 1.45; background: var(--ink-raised); border: 1px solid var(--edge-soft); border-radius: 6px; padding: 0.7rem 0.9rem; max-height: 30rem; overflow: auto; }
.wa-test { margin-top: 0.6rem; }
.wa-note { margin-top: 1.2rem; }

/* W95 (found by the harness's new shift-source diagnostics): the class
   filter's combobox button inherited the label's 0.75rem font and its
   own 0.5rem vertical padding, so when combobox.js replaced the native
   select (height 2.2rem) the label grew by 2px at script time - a
   measured cumulative layout shift of 0.00100 on /rankings/tournament/
   whenever the filter sat inside the first viewport. The button now
   takes exactly the select's box (height, font size, widths), so the
   enhancement changes no geometry. Fixed by construction. */
.class-filter .cbx-btn {
  height: 2.2rem;
  padding-top: 0;
  padding-bottom: 0;
  font-size: 0.88rem;
  text-transform: none;
  letter-spacing: normal;
  min-width: 11rem;
  max-width: 14rem;
  box-sizing: border-box;
}

/* W94b (owner ruling W94 Q2): the Marketplace rows' icon and name open the
   item's database page through the ONE item-link include; the Sell row
   keeps a quiet text link to its full-page item view, and the item link
   inside a Marketplace row wears the row's own name styling. */
.mk-item > .x-item-icon { display: inline-flex; flex: none; line-height: 0; }
.mk-item > .x-item-link { text-decoration: none; }
.mk-item > .x-item-link:hover { text-decoration: underline dotted; text-underline-offset: 0.15em; }
.mk-detail-link { display: inline-block; font-size: 0.7rem; color: var(--parchment-faint); text-decoration: underline dotted; text-underline-offset: 0.15em; }
.mk-detail-link:hover { color: var(--arcane-bright); }
.mk-detail-icon { display: inline-flex; line-height: 0; }
.mk-detail-title h2 .x-item-link { color: inherit; }

/* W94c (owner ruling W94b Q2): a drop entry the server team changed at
   runtime wears a small tag; the override line above a list is a quiet
   sentence, not a box. */
.db-override-tag { background: rgba(224, 178, 92, 0.18); color: var(--gold); }
.db-override-note { margin: 0 0 0.6rem; font-size: 0.78rem; }

/* ==========================================================================
   W98 - site-wide dates, tables and the Marketplace review
   (docs/specs/W98-MARKETPLACE-SITEWIDE-REVIEW.md and
   docs/specs/W98-SITE-WIDE-CONSISTENCY-AUDIT.md sections 1, 12-14, 17,
   21-23, 27). Everything below is an amendment to a rule above; the
   rule it amends is named.
   ========================================================================== */

/* Section 23 - metadata contrast. The faint parchment step lifts one
   modest shade on BOTH grounds (public :root 3.3:1 -> 4.6:1 against
   --ink-panel; the W59/W60 panel block 5.8:1 -> 6.6:1), and the small
   UPPERCASE metadata labels the owner named - SERVER TIME / LOCAL TIME
   / STARTS IN (.se-cap), RECURRING / SCHEDULED (.se-status), the
   expanded-row facts (.mk-x-facts dt), the boss sub-lines (.bt-sub),
   the "Detected from your device" note and the dull placeholders -
   read from the ONE --meta-ink token: brighter than faint, still
   clearly below body ink and the gold headings, so secondary labels
   never compete with primary content. Measured by
   scripts/w98-audit.py (the contrast walk) on the reference pages. */
:root {
  --parchment-faint: #877e6c;
  --meta-ink: #938a78;
}
.layout-panel {
  --parchment-faint: #a39a87;
  --meta-ink: #aba28e;
}
.se-cap, .se-detect, .se-status, .mk-x-facts dt, .dull-span, .mk-detail-link,
.boss-sched .bt-sub, .boss-sched .bt-timer .bt-sub, .boss-table .bt-countdown .bt-sub, .x-table .bt-sub {
  color: var(--meta-ink);
}
.se-status.ses-in-progress, .se-status.ses-alive { color: var(--signal-online); }

/* Section 1 / ruling R1 - the ONE date-with-time form inside a time
   cell: "6 September 2026 at 4:00 PM UTC−4". The word "at" rides the
   clock span (includes/time-cell.html) so a wrap never separates it
   from its time; it is set no differently from the clock. */
.mk-time-at { font: inherit; color: inherit; }

/* Marketplace item 12 - slightly larger item icons in the listing and
   inventory rows (32 -> 36px; the detail heads keep their own sizes),
   and a clear hover state on every clickable item name (the arcane
   bright ink AND an underline - the W94b dotted underline alone read
   as decoration). Alternating rows and row hover are untouched. */
.mk-table .mk-icon { width: 36px; height: 36px; flex: 0 0 36px; }
.mk-item > .x-item-link:hover, .mk-item > .x-item-link:focus-visible, .mk-item-link:hover {
  color: var(--arcane-bright);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* Marketplace item 4 - the expanded Sell Item row, substantially more
   compact: the three-column structure (Listing Type & Currency | Price
   | Duration) stands; the label sits directly on its control (the
   template's <br> after a label is retired here), the helper lines
   are one size down and tighter, the estimate block and the actions
   row sit closer. */
.mk-expand-sell .mk-x-item-sell { padding-bottom: 0; gap: 0.2rem 1rem; }
.mk-expand-sell .mk-x-facts-inline { gap: 0.1rem 1rem; }
.mk-expand-inner .mk-listing-form-row { padding-top: 0.15rem; }
.mk-lf-grid { gap: 0.35rem 1rem; }
.mk-lf-grid .form-section-title { margin-bottom: 0.3rem; }
.mk-lf-grid .form-row { margin-bottom: 0.4rem; }
.mk-lf-grid .form-row > label { display: block; margin-bottom: 0.15rem; }
.mk-lf-grid .form-row > label + br { display: none; }
.mk-lf-grid .help-msg { margin-top: 0.15rem; font-size: 0.78rem; line-height: 1.35; }
.mk-lf-grid .mk-auction-rules .mk-rule-line { margin-top: 0.05rem; }
.mk-lf-grid .seg-radio .seg-opt { padding: 0.28rem 0.5rem; }
.mk-lf-grid .mk-fee-preview, .mk-expand-inner .mk-fee-preview { margin-top: 0.1rem; padding: 0.4rem 0.6rem; }
.mk-lf-grid .mk-lf-actions { margin-top: 0.35rem; }

/* Marketplace item 9 - the estimate: ONE compact summary (Sale Price,
   Service Fee, You Receive - the strongest line, larger and gold) and
   the full fee rules folded behind a quiet disclosure, one click away.
   The <details> keeps the W80 threshold list and the selected
   currency's marked line (market.js) inside it. */
.mk-fee-lines { gap: 0.1rem 0.8rem; }
.mk-fee-lines .mk-fee-net dt { color: var(--parchment); font-weight: 600; }
.mk-fee-lines .mk-fee-net dd { font-size: 1.05em; }
.mk-fee-text { margin-top: 0.25rem; }
details.mk-fee-rule { margin: 0.3rem 0 0; }
details.mk-fee-rule > summary.mk-fee-rule-cap {
  display: inline-flex; align-items: center; gap: 0.35rem; cursor: pointer;
  color: var(--parchment-dim); font-weight: 500; font-size: 0.78rem; margin: 0;
  list-style: none;
}
details.mk-fee-rule > summary::-webkit-details-marker { display: none; }
details.mk-fee-rule > summary.mk-fee-rule-cap::before {
  content: ""; width: 0.4em; height: 0.4em; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg); transition: transform 0.15s ease; flex: none;
}
details.mk-fee-rule[open] > summary.mk-fee-rule-cap::before { transform: rotate(45deg); }
details.mk-fee-rule > summary.mk-fee-rule-cap:hover { color: var(--arcane-bright); }
details.mk-fee-rule > .mk-fee-thresholds { margin-top: 0.25rem; }
@media (prefers-reduced-motion: reduce) {
  details.mk-fee-rule > summary.mk-fee-rule-cap::before { transition: none; }
}

/* Section 22 - a header sort swaps the table in place (live-filter.js);
   the unit is marked while the fetch is in flight so a slow answer is
   visible without moving a pixel (no size change, no animation). */
.table-wrap.is-loading, [data-live-results].is-loading { opacity: 0.85; }

/* Section 22 / marketplace item 12 - the listing tables fit their 900px
   column again with the full-month "Ends" cell (measured by
   scripts/w98-measure.py: Browse overflowed by 42px, My Listings by
   71px). The listing tables take the dense cell-padding token the boss
   and clan tables already ride (--cell-pad-x 0.9 -> 0.55rem across ten
   columns, ~112px), the Type value may wrap ("Auction &" / "Buy Now"),
   and the price header - "Price / Bid"
   on every listing table (section 21: the W73 "Buy Now / Current Bid"
   and "Price / Current Bid" wrapped to two and three lines) - keeps a
   floor that holds it on one line in an EMPTY table, as does the
   History date column ("Date & Time" wrapped to three lines with zero
   rows). */
table.arcanis.mk-table { --cell-pad-x: 0.55rem; }
table.arcanis.mk-table td.mk-col-type { white-space: normal; }
table.arcanis.mk-table th.mk-col-price { min-width: 6.5rem; }
table.arcanis.mk-history-table th.mk-col-3 { min-width: 9rem; }
/* The Website Administration tables carry the ONE time cell now (W81's
   rule: a staff date rides {% time_cell %}, never a private format), so
   the members table takes the same dense token and the cell's Server /
   Local cap sits on its own line as it does in the listing tables. */
table.arcanis.wa-table { --cell-pad-x: 0.6rem; }
.wa-table .mk-time-cap { display: block; }
/* Section 21: the two shortened My Listings headings ("Open Positions",
   "Created / Published") wrap to at most two lines under the W76 wrap
   rule that stands (a nowrap cut overflowed the harness's wider fixture
   by 74px); the three-line "Positions Still Needed" / "Created or
   Published Date" are gone. */
.wa-grid .wa-block-wide { grid-column: 1 / -1; }
/* Marketplace item 12 (found on the rendered Browse table): with the
   Item column at its 1366 floor, the W94b icon + name + sub-line ran in
   ONE flex row and the name broke letter by letter ("Lo / ng / Sw /
   ord") under the stacked layout's overflow-wrap. The item cell is a
   two-column grid now - the icon spans both rows, the name sits over
   its sub-line - and the name wraps at its spaces only. */
.mk-table .mk-item { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 0.6rem; row-gap: 0.05rem; align-items: center; }
.mk-table .mk-item > .x-item-icon { grid-row: 1 / span 2; align-self: center; }
.mk-table .mk-item > .x-item-link { grid-column: 2; overflow-wrap: normal; min-width: 0; }
.mk-table .mk-item > .mk-item-text { grid-column: 2; overflow-wrap: normal; min-width: 0; }
/* The My Listings table takes the denser padding token as well (38px
   across its eight columns) so its content-rich rows keep clear of the
   847px column at 1366. */
table.arcanis.rc-mine-table { --cell-pad-x: 0.75rem; }

/* W99 (docs/specs/W99-SCHEDULE-RECRUITMENT-AUDIT.md) section 11: ONE
   Register action per state on a mass-event card - the collapsed
   card's Register (the opener) hides while the details are open, where
   the form's own final Register is the one submission. Pure CSS on the
   native details state, so the no-JS baseline behaves the same. */
.rc-event-card:has(.rc-ev-more[open]) .rc-ev-register { display: none; }
/* Section 15: the site's own confirmation modal (panel-ux.js
   confirmModal) - the dark purple ground, the gold heading, the
   destructive action in the danger tier beside a secondary Cancel. */
.arc-modal { position: fixed; inset: 0; z-index: 1200; display: flex; align-items: center; justify-content: center; padding: 1rem; }
.arc-modal[hidden] { display: none; }
.arc-modal-backdrop { position: absolute; inset: 0; background: rgba(6, 3, 14, 0.72); }
.arc-modal-dialog { position: relative; width: min(100%, 30rem); background: var(--ink-panel); border: 1px solid var(--edge-elevated); border-radius: 4px; padding: 1.35rem 1.5rem 1.25rem; box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55); }
.arc-modal-title { margin: 0 0 0.6rem; font-size: 1.05rem; }
.arc-modal-body { margin: 0 0 1.1rem; color: var(--parchment); line-height: 1.5; }
.arc-modal-actions { display: flex; justify-content: flex-end; gap: 0.6rem; flex-wrap: wrap; }
.arc-modal-destructive { background: rgba(224, 133, 133, 0.1); }
body.arc-modal-open { overflow: hidden; }
