/* ═══ HELHEIMRS — components ═══
   Every faceted shape draws its own border with --bd and puts var(--facet-edges)
   first in its background, so the chamfer is a closed line on all six sides
   (see tokens.css). States change --bd and --*-bg, never `background` itself. */

/* ── Buttons: stone slabs that press in ──
   Rest / hover / press / focus / disabled are five different looks: hover
   lifts and brightens, press sinks and darkens, focus is a ring inside the cut,
   disabled is a dashed empty socket — not a faded copy of the live button. */
.btn {
  --btn-bg: var(--stone-grad-2);
  --bd: var(--line-strong);
  --facet: 8px;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: 44px; padding: 0 var(--s-5);
  font-family: var(--font-body); font-weight: 700; font-size: var(--fs-sm); line-height: var(--lh-ui);
  color: var(--fg-strong);
  background: var(--facet-edges), var(--btn-bg);
  border: 1px solid var(--bd);
  clip-path: var(--clip-facet);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.07), inset 0 -2px 0 rgba(0,0,0,.45);
  transition: transform var(--t-fast) var(--ease-press), filter var(--t-fast) var(--ease-stone), box-shadow var(--t-fast) var(--ease-stone), border-color var(--t-fast) var(--ease-stone), color var(--t-fast) var(--ease-stone);
  position: relative; white-space: nowrap; text-decoration: none; user-select: none;
}
:where(.btn) .ico { --n: 20; }
/* a small button carries the small glyph */
:where(.btn--sm) .ico { --n: 16; }
.btn:hover { text-decoration: none; --bd: var(--line-glow); filter: brightness(1.14); box-shadow: inset 0 1px 0 rgba(255,255,255,.14), inset 0 -2px 0 rgba(0,0,0,.45); }
.btn:active { transform: translateY(1px); filter: brightness(.94); box-shadow: inset 0 2px 6px rgba(0,0,0,.6); transition-duration: 90ms; }
:root[data-theme="light"] .btn:not(.btn--primary, .btn--ghost, .btn--danger, .btn--danger-text):hover { filter: none; --btn-bg: linear-gradient(180deg, #FFFFFF, #F1F3EF); box-shadow: inset 0 1px 0 #fff, inset 0 -2px 0 rgba(24,36,34,.08); }
:root[data-theme="light"] .btn:not(.btn--primary, .btn--ghost, .btn--danger, .btn--danger-text):active { filter: none; --btn-bg: linear-gradient(180deg, #E9ECE6, #F4F5F1); box-shadow: inset 0 2px 5px rgba(24,36,34,.16); }

.btn--primary { --btn-bg: var(--emerald-grad); --bd: rgba(47,211,154,.6); color: var(--accent-fg); box-shadow: inset 0 1px 0 rgba(255,255,255,.42), inset 0 -2px 0 rgba(0,0,0,.25); }
.btn--primary:hover { --bd: rgba(123,230,192,.9); filter: brightness(1.08); transform: translateY(-1px); box-shadow: inset 0 1px 0 rgba(255,255,255,.6), inset 0 -2px 0 rgba(0,0,0,.25), inset 0 0 0 1px rgba(255,255,255,.16); }
/* pressed: the light falls inside — the ramp flips and the top goes into shadow */
.btn--primary:active { --btn-bg: linear-gradient(180deg, #1A9E73, #2BC892); transform: translateY(1px); filter: none; box-shadow: inset 0 2px 6px rgba(0,0,0,.35); }
:root[data-theme="light"] .btn--primary { --bd: rgba(15,107,79,.75); }
:root[data-theme="light"] .btn--primary:hover { --btn-bg: linear-gradient(180deg, #13865F 0%, #0A4C3C 100%); filter: none; }
:root[data-theme="light"] .btn--primary:active { --btn-bg: linear-gradient(180deg, var(--jade-deep), #0F6B4F); box-shadow: inset 0 2px 6px rgba(0,0,0,.35); }

/* ghost: a neutral edge at rest, so the page's emerald is spent on what is live */
.btn--ghost { --btn-bg: transparent; --bd: var(--line-strong); color: var(--accent-text); box-shadow: none; }
.btn--ghost:hover { --btn-bg: rgba(47,211,154,.08); --bd: var(--line-glow); filter: none; box-shadow: none; }
.btn--ghost:active { --btn-bg: rgba(47,211,154,.14); box-shadow: none; }
:root[data-theme="light"] .btn--ghost:hover { --btn-bg: rgba(15,107,79,.07); box-shadow: none; }
:root[data-theme="light"] .btn--ghost:active { --btn-bg: rgba(15,107,79,.13); box-shadow: none; }

/* destructive: filled ember, or an ember outline — never the same ghost as «ویرایش» */
.btn--danger { --btn-bg: linear-gradient(180deg, #8A2A1E, #5A1912); --bd: rgba(234,122,102,.55); color: #FFF1EE; box-shadow: inset 0 1px 0 rgba(255,255,255,.14), inset 0 -2px 0 rgba(0,0,0,.45); }
.btn--danger:hover { --bd: rgba(234,122,102,.9); filter: brightness(1.12); }
.btn--danger-text { --btn-bg: transparent; --bd: var(--danger-edge); color: var(--danger); box-shadow: none; }
.btn--danger-text:hover { --btn-bg: var(--danger-bg); --bd: var(--danger); filter: none; box-shadow: none; }
:root[data-theme="light"] .btn--danger-text:hover { --btn-bg: var(--danger-bg); }

.btn--sm { min-height: 36px; padding: 0 var(--s-4); font-size: var(--fs-xs); --facet: 6px; }
.btn--lg { min-height: 56px; padding: 0 var(--s-7); font-size: var(--fs-md); --facet: 12px; }
.btn--icon { padding: 0; width: 44px; }
/* disabled: an empty socket, unmistakably not a button that is merely loading */
:root .btn:is([disabled], [aria-disabled="true"]):not(:where(form[aria-busy="true"] [type="submit"][disabled])), :root .btn:is([disabled], [aria-disabled="true"]):not(:where(form[aria-busy="true"] [type="submit"][disabled])):is(:hover, :active) {
  --btn-bg: var(--bg-elev-2); --bd: var(--control-edge);
  color: var(--fg-faint); border-style: dashed; box-shadow: none; filter: none; transform: none; cursor: not-allowed;
}
.btn[aria-disabled="true"] { pointer-events: none; }
/* busy: the label stays, a small ring of six rune-cuts turns beside it.
   A form that waits on the server and disables its own submit meanwhile (sign
   in, register, the password forms) is saying the same thing, so that submit
   is drawn busy, not as the empty socket: «ورود» pressed used to go dashed and
   grey, which reads "not available here", and turned nothing. The :where keeps
   these selectors as light as the one-attribute rules they extend. */
.btn:is([aria-busy="true"], :where(form[aria-busy="true"] [type="submit"][disabled])) { cursor: progress; pointer-events: none; }
.btn:is([aria-busy="true"], :where(form[aria-busy="true"] [type="submit"][disabled]))::before {
  content: ""; flex: none; width: 14px; height: 14px; border-radius: 50%;
  background: var(--busy-ring); forced-color-adjust: none;
  -webkit-mask: radial-gradient(farthest-side, transparent 52%, #000 56%); mask: radial-gradient(farthest-side, transparent 52%, #000 56%);
  animation: btn-busy .9s steps(6) infinite;
}
@keyframes btn-busy { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .btn:is([aria-busy="true"], :where(form[aria-busy="true"] [type="submit"][disabled]))::before { animation: none; } }
:root[data-motion="off"] .btn:is([aria-busy="true"], :where(form[aria-busy="true"] [type="submit"][disabled]))::before { animation: none; }
/* Contrast Themes set every gradient to none: sign in, save, pay and cancel
   showed no progress at all while the button quietly swallowed a second press.
   The ring opts out of the adjustment (above) and tokens.css cuts it in system
   colours there; the mask still shapes it, and the rules above still hold it
   still without motion (A11Y-TYPE-R9-4). */

/* ── Inputs: carved grooves with an edge you can find (≥ 3:1) ── */
.field { display: flex; flex-direction: column; gap: var(--s-2); min-width: 0; }
.field label, .label { font-size: var(--fs-sm); font-weight: 700; color: var(--fg-strong); line-height: var(--lh-ui); }
.field .hint { font-size: var(--fs-xs); color: var(--fg-muted); line-height: var(--lh-ui); }
.input, .select, .textarea {
  width: 100%; min-width: 0; min-height: 46px; padding: var(--s-2) var(--s-4);
  background: var(--bg); color: var(--fg-strong);
  border: 1px solid var(--control-edge); border-radius: var(--r-1);
  box-shadow: inset 0 2px 6px rgba(0,0,0,.55), inset 0 -1px 0 rgba(255,255,255,.04);
  line-height: var(--lh-ui);
  transition: border-color var(--t-fast) var(--ease-stone), box-shadow var(--t-fast) var(--ease-stone);
}
/* background-color, not the shorthand: the shorthand would reset the select's chevron image */
:root[data-theme="light"] :is(.input, .select, .textarea) { background-color: #F4F5F2; box-shadow: inset 0 2px 4px rgba(24,36,34,.1); }
:is(.input, .select, .textarea):hover { border-color: var(--fg-muted); }
.input::placeholder, .textarea::placeholder { color: var(--fg-faint); opacity: 1; }
/* Free text a member types takes the direction of what is typed, paragraph by
   paragraph, and while empty the page's. dir="auto" gave the first and not
   the second: an empty field resolves left to right, so on a Persian page the
   placeholders hung on the left of right-aligned forms, «جست‌وجو در عنوان‌ها…»
   put its ellipsis first, «برچسب را بنویس و Enter بزن» read «بزن Enter برچسب
   را بنویس و», and the first Persian letter threw the caret across the box.
   plaintext takes the direction from the text itself (a Latin title still runs
   left to right) and the page's when there is none; a field that still says
   dir="auto" is held to the page's direction, so this one rule reaches it.
   Always-Latin fields (an address, a time, a handle) say dir="ltr" and keep it;
   a number field (inputmode) keeps the page's: digits alone have no direction,
   and plaintext would set «۱۲» against the left edge of a Persian form. */
:is(input:not([type]), input[type="text"], input[type="search"], textarea):not([dir="ltr"], [dir="rtl"], [inputmode="numeric"], [inputmode="decimal"]) { unicode-bidi: plaintext; text-align: start; }
:is(input, textarea)[dir="auto"] { direction: inherit; }
/* The clear × of a search field. The browser's own was Chrome's blue on Frost,
   and in a Persian field a Latin query (an email, an id) ran from the left
   edge straight into it, 0–2px away. It is drawn here instead, in the palette:
   a 16px × in --fg-muted (the accent under the pointer), 8px clear of the
   text, at the field's end, opposite the magnifier, on a 32px target that adds
   nothing to the field's height. The browser still shows it only while there
   is something to clear, and Escape still clears. The palette's field keeps
   none (well.css): Escape closes the palette there. */
input[type="search"]:not(.palette__input)::-webkit-search-cancel-button {
  -webkit-appearance: none; appearance: none;
  flex: none; box-sizing: content-box; width: 16px; height: 16px; padding: 8px;
  margin-block: -8px; margin-inline: var(--s-2) -8px;
  background: var(--fg-muted); cursor: pointer;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M3.5 3.5 12.5 12.5M12.5 3.5 3.5 12.5' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'/></svg>") center / 16px 16px no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M3.5 3.5 12.5 12.5M12.5 3.5 3.5 12.5' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'/></svg>") center / 16px 16px no-repeat;
}
input[type="search"]:not(.palette__input)::-webkit-search-cancel-button:hover { background: var(--accent-text); }
/* Hidden, the browser's × still kept its box: 40px of a 195px phone field went
   to a control that was not there, and the placeholder was cut mid-word
   («ایمیل، نام کاربری یا ن»). While the hint shows, it takes no room at all. */
input[type="search"]:not(.palette__input):placeholder-shown::-webkit-search-cancel-button { width: 0; padding: 0; margin: 0; }
@media (forced-colors: active) { input[type="search"]:not(.palette__input)::-webkit-search-cancel-button { background: ButtonText; } }
:is(.input, .select, .textarea):focus-visible { outline: none; border-color: var(--accent); box-shadow: inset 0 2px 6px rgba(0,0,0,.55), 0 0 0 1px var(--accent), var(--glow-soft); border-radius: var(--r-1); }
:root[data-theme="light"] :is(.input, .select, .textarea):focus-visible { box-shadow: inset 0 2px 4px rgba(24,36,34,.1), 0 0 0 1px var(--accent), 0 0 0 4px rgba(15,107,79,.14); }
:is(.input, .select, .textarea):disabled { border-style: dashed; color: var(--fg-muted); cursor: not-allowed; box-shadow: none; }
.textarea { min-height: 120px; resize: vertical; line-height: 1.8; }
/* the caret belongs on the inline-end side, which is the left only while the
   document is RTL; background-position has no logical keyword, so English says so */
.select { appearance: none; background-image: var(--select-caret); background-repeat: no-repeat; background-position: left 14px center; padding-inline-end: 36px; }
[dir="ltr"] .select { background-position: right 14px center; }
:is(.input, .select, .textarea):is(.is-error, [aria-invalid="true"]) { border-color: var(--danger); box-shadow: inset 0 2px 6px rgba(0,0,0,.4), 0 0 0 1px var(--danger); }
.error-msg { color: var(--danger); font-size: var(--fs-xs); line-height: var(--lh-ui); }

/* A tick box and its sentence. One flowing paragraph beside a box: as a flex
   row, every text node and the link inside became its own column ("I | accept
   the community | rules and …"). The box hangs at the start of the first line. */
.check { display: inline-block; position: relative; max-width: 100%; padding-inline-start: calc(22px + var(--s-2)); line-height: 1.6; cursor: pointer; vertical-align: top; }
.check > input {
  position: absolute; inset-inline-start: 0; top: .2em; top: calc((1lh - 22px) / 2);
  width: 22px; height: 22px; margin: 0; padding: 0; border: 0; border-radius: 0;
  appearance: none; -webkit-appearance: none; flex: none; cursor: pointer;
  background: var(--check-off) center / 100% 100% no-repeat;
}
.check > input:hover { background-image: var(--check-hover); }
.check > input:checked { background-image: var(--check-on); }
.check > input[type="radio"]:checked { background-image: var(--radio-on); }
.check > input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 50%; box-shadow: none; }
.check > input:disabled { opacity: .5; cursor: not-allowed; }
/* unticked when it had to be: only danger colours — the rim, and the row */
.check > input:is([aria-invalid="true"], .is-error):not(:checked) { background-image: var(--check-bad); }
.check:is(.is-error, :has(> input[aria-invalid="true"]:not(:checked))) { background: var(--danger-bg); box-shadow: 0 0 0 6px var(--danger-bg); }
.switch { position: relative; flex: none; width: 44px; height: 24px; background: var(--bg-elev-2); border: 1px solid var(--control-edge); border-radius: var(--r-pill); cursor: pointer; padding: 0; transition: background var(--t-fast) var(--ease-press), border-color var(--t-fast) var(--ease-press); }
.switch::after { content: ""; position: absolute; top: 3px; inset-inline-start: 3px; width: 16px; height: 16px; border-radius: 50%; background: var(--fg-muted); transition: transform var(--t-ui) var(--ease-press), background var(--t-fast) var(--ease-press); }
.switch:hover { border-color: var(--fg-muted); }
.switch[aria-checked="true"] { background: rgba(47,211,154,.16); border-color: var(--accent); }
.switch[aria-checked="true"]::after { transform: translateX(-20px); background: var(--emerald); box-shadow: 0 0 8px rgba(47,211,154,.55); }
[dir="ltr"] .switch[aria-checked="true"]::after { transform: translateX(20px); }
.switch:is(:disabled, [aria-disabled="true"]) { border-style: dashed; cursor: not-allowed; opacity: .55; }
.switch:is(:disabled, [aria-disabled="true"]):hover { border-color: var(--control-edge); }
:root[data-theme="light"] .switch[aria-checked="true"] { background: rgba(15,107,79,.12); }
:root[data-theme="light"] .switch[aria-checked="true"]::after { background: var(--jade); box-shadow: none; }

/* ── The pointer choice (HX.cursor.html): three chips, one on ──
   The house's own chip (below), not a tile of its own: the one chosen is
   filled, ticked and heavier, its edge and tick 5.4:1 and more against both
   the chip and the menu in either theme. One function draws it in the header
   menu and on the settings page. A chip never wraps its label, so the names
   are short, and each chip keeps one width on or off: off, the room the tick
   takes is padding split between its two sides, so choosing one moves
   nothing, and the label stays centred. The menu stretches the row to its
   width (shell.css); a page's wider column keeps the chips at their size. */
.cursor-pick { border: 0; padding: 0; margin: 0; min-width: 0; }
.cursor-pick__legend { display: grid; gap: 2px; padding: 0; margin-bottom: var(--s-2); font-size: var(--fs-sm); font-weight: 600; color: var(--fg); }
.cursor-pick__legend b { font-weight: inherit; }
.cursor-pick__legend small { font-size: var(--fs-2xs); font-weight: 500; color: var(--fg-muted); }
.cursor-pick__opts { display: grid; grid-auto-flow: column; grid-template-columns: repeat(3, auto); justify-content: start; gap: var(--s-2); }
.chip.cursor-pick__opt { --pad: var(--s-2); position: relative; justify-content: center; min-width: 0; padding-inline: calc(var(--pad) + 10px); }
.chip.cursor-pick__opt.is-on { padding-inline: var(--pad); }   /* the tick: 14px and its 6px gap */

/* ── Eyebrow / kicker: subordinate to its heading at a glance, even in greyscale ── */
.kicker { color: var(--fg-muted); font-size: var(--fs-sm); font-weight: 400; line-height: var(--lh-ui); margin: 0 0 var(--s-2); }
.sec-head .kicker { margin: 0; }

/* ── Chips / tags ──
   Three looks that cannot be confused: hover lights the edge, the chip that is
   ON is filled, ticked and heavier (so it is the strongest chip in the row, not
   the dimmest), and keyboard focus is a ring inside the cut on top of either. */
.chip {
  --bd: var(--line-strong); --chip-bg: var(--bg-elev); --facet: 6px;
  display: inline-flex; align-items: center; gap: 6px; min-height: 40px; padding: 0 var(--s-4);
  font-size: var(--fs-sm); font-weight: 600; line-height: var(--lh-ui); color: var(--fg-muted);
  background: var(--facet-edges), var(--chip-bg);
  border: 1px solid var(--bd); clip-path: var(--clip-facet);
  cursor: pointer; white-space: nowrap;
  transition: color var(--t-fast) var(--ease-stone), border-color var(--t-fast) var(--ease-stone), box-shadow var(--t-fast) var(--ease-stone);
}
.chip:hover { color: var(--fg-strong); --bd: var(--line-glow); text-decoration: none; }
/* a chip that is a link says where you are with aria-current; one that is a
   control says what it is with aria-pressed. Both look the same when they are on. */
.chip:is([aria-pressed="true"], [aria-checked="true"], [aria-current="page"], .is-on) {
  --chip-bg: rgba(47,211,154,.14); --bd: var(--accent);
  color: var(--fg-strong); font-weight: 800; box-shadow: var(--glow-inset-soft);
}
.chip:is([aria-pressed="true"], [aria-checked="true"], [aria-current="page"], .is-on)::before {
  content: ""; flex: none; width: 14px; height: 14px; background: var(--accent-text);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M2.5 8.6 6.2 12.2 13.5 4.3' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M2.5 8.6 6.2 12.2 13.5 4.3' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/></svg>") center / contain no-repeat;
}
:root[data-theme="light"] .chip:is([aria-pressed="true"], [aria-checked="true"], [aria-current="page"], .is-on) { --chip-bg: rgba(15,107,79,.1); }
.chip:focus-visible { outline: 2px solid var(--accent); outline-offset: -5px; }
/* a chip that is a <label> round a hidden radio or box takes the ring for it */
.chip:has(> input:focus-visible) { outline: 2px solid var(--accent); outline-offset: -5px; }
:where(.chip) .ico { --n: 16; }
.tag { display: inline-flex; align-items: center; gap: 4px; min-height: 24px; padding: 0 8px; font-size: var(--fs-xs); line-height: var(--lh-ui); color: var(--fg-muted); border: 1px solid var(--line-strong); border-radius: var(--r-1); background: rgba(255,255,255,.02); white-space: nowrap; }
.tag--accent { color: var(--accent-text); border-color: var(--line-glow); }
.tag--danger { color: var(--danger); border-color: var(--danger-edge); background: var(--danger-bg); }
.tag--stone { background: var(--bg-elev-2); color: var(--fg); }
/* waiting is not yet a state: a dashed edge tells it apart in greyscale */
.tag--pending, .tag--dashed { border-style: dashed; border-color: var(--control-edge); }
:where(.tag) .ico { --n: 16; }
/* A chip and a tag are flex rows, so the break helper HX.ct puts after each
   ZWNJ (.wb, base.css) became a flex item of its own: «تازه‌واردها» was laid
   out as two words with two gaps between them. Neither ever wraps (nowrap),
   so the break chance is dropped and the word is one run again. */
:is(.tag, .chip) .wb { display: none; }

/* ── Notification badge: a count pinned to the top end corner of its control ── */
.badge {
  display: inline-grid; place-items: center; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 9px; background: var(--accent); color: var(--accent-fg);
  font: 700 var(--fs-2xs)/1 var(--font-body); font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 2px var(--bg); white-space: nowrap; unicode-bidi: isolate;
}

/* ── Cards: carved slabs ── */
.slab {
  --bd: var(--line); --facet: 14px;
  position: relative; background: var(--facet-edges), var(--stone-grad); border: 1px solid var(--bd);
  clip-path: var(--clip-facet); padding: var(--s-5);
  box-shadow: var(--shadow-stone);
  transition: transform var(--t-slow) var(--ease-stone), border-color var(--t-ui) var(--ease-stone);
}
.slab--glow { --bd: var(--line-glow); box-shadow: var(--shadow-stone), var(--glow-inset); }
.slab--pad-0 { padding: 0; }
:root[data-theme="light"] .slab { --bd: rgba(24,36,34,.16); }
:root[data-theme="light"] .slab--glow { --bd: var(--line-glow); }
/* emerald light tracing the edge on hover (uses two gradient borders) */
.slab-trace { position: relative; }
.slab-trace::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 2;
  clip-path: var(--clip-facet);
  background: linear-gradient(90deg, transparent 0%, var(--emerald) 50%, transparent 100%) top / 200% 1px no-repeat,
              linear-gradient(90deg, transparent 0%, var(--emerald) 50%, transparent 100%) bottom / 200% 1px no-repeat,
              linear-gradient(180deg, transparent 0%, var(--emerald) 50%, transparent 100%) left / 1px 200% no-repeat,
              linear-gradient(180deg, transparent 0%, var(--emerald) 50%, transparent 100%) right / 1px 200% no-repeat;
  opacity: 0; transition: opacity var(--t-ui) var(--ease-stone);
}
.slab-trace:hover::before, .slab-trace:focus-within::before { opacity: 1; animation: trace 1.6s linear infinite; }
@keyframes trace { 0% { background-position: 200% top, -100% bottom, left -100%, right 200%; } 100% { background-position: -100% top, 200% bottom, left 200%, right -100%; } }
@media (prefers-reduced-motion: reduce) { .slab-trace:hover::before { animation: none; background: none; box-shadow: var(--glow-inset); } }

/* pointed-arch frame (portal echo) for featured media: the roof is cut by
   width (--mask-arch, tokens.css), so it is 32° on a wide hero, a tall
   portrait and a phone alike. The frame's 2px rim runs on round the roof: the
   media inside is cut by the same roof, 2px lower. */
.arch { position: relative; -webkit-mask: var(--mask-arch); mask: var(--mask-arch); }
.arch-frame { position: relative; padding: 2px; background: linear-gradient(180deg, var(--emerald), var(--jade) 70%, transparent); -webkit-mask: var(--mask-arch); mask: var(--mask-arch); }
.arch-frame > * { -webkit-mask: var(--mask-arch); mask: var(--mask-arch); }

/* ── Avatars ──
   The hex is the mark; the letter or emblem sits in it. Its ground used to be
   --bg-elev-2, the colour of the very stones and cards it sits on, and there
   the hex vanished: the DM's «آ» on a session stone floated as a stray green
   letter. So every hex has a 1px carved rim (the avatar's own background)
   round a ground a step off the surface (::before, 1px in). A picture covers
   both edge to edge; a ringed avatar has the ring for an edge and keeps its
   plain ground. It is a size container so its initial can be held to it. */
.avatar { position: relative; isolation: isolate; container-type: size; width: 40px; height: 40px; clip-path: var(--clip-hex); background: var(--avatar-rim); display: inline-grid; place-items: center; font-family: var(--font-display); font-weight: 800; line-height: 1; color: var(--accent-text); font-size: var(--fs-sm); flex: none; overflow: hidden; }
.avatar::before { content: ""; position: absolute; inset: 1px; z-index: -1; clip-path: var(--clip-hex); background: var(--avatar-ground); }
.avatar-ring > .avatar { --avatar-ground: var(--bg-elev-2); }
.avatar-ring > .avatar::before { inset: 0; }
.avatar:has(> .avatar__img) { background: var(--bg-elev-2); }
.avatar:has(> .avatar__img)::before { content: none; }
/* The initial is a mark, like the glyphs: it grows with the reader's text only
   as far as its shape holds it whole. At 200% a 28px hex held a 24px letter in
   a 38px line and «ب» lost its dot, «س» kept only its teeth. 57% of the hex
   (or seat) keeps dots above and below inside the cut; at 100% every initial
   is under that, so nothing moves. */
:is(.avatar, .shield) > .avatar__letter { font-size: min(1em, 57cqmin); }
.avatar--lg { width: 96px; height: 96px; font-size: var(--fs-2xl); }
/* a picture fills the hex; an emblem sits inside it with room to breathe */
.avatar__img { width: 100%; height: 100%; object-fit: cover; display: block; }
.avatar__rune { display: grid; place-items: center; width: 58%; height: 58%; color: var(--accent-text); }
.avatar__rune svg:not(.ico) { width: 100%; height: 100%; }
/* an interface glyph standing in for an emblem keeps to the glyph sizes */
.avatar__rune .ico { --n: 20; }
.avatar--sm .avatar__rune .ico { --n: 16; }
.avatar--lg .avatar__rune .ico { --n: 32; }
.avatar--sm { width: 28px; height: 28px; font-size: var(--fs-2xs); }
.avatar-ring { display: inline-grid; place-items: center; padding: 2px; background: var(--emerald-grad); clip-path: var(--clip-hex); }

/* ── Tabs ──
   The current tab is the one the page is on — a link says so with
   aria-current, a tab with aria-selected — and on a phone the strip fades at
   its far end, so a strip that scrolls says that it does. */
.tabs { display: flex; gap: var(--s-1); border-bottom: 1px solid var(--line); overflow-x: auto; scrollbar-width: none; padding-bottom: 1px; }
.tab { position: relative; flex: none; padding: var(--s-3) var(--s-4); font-weight: 600; line-height: var(--lh-ui); color: var(--fg-muted); background: none; border: 0; white-space: nowrap; transition: color var(--t-fast) var(--ease-stone); }
.tab:hover { color: var(--fg-strong); text-decoration: none; }
.tab:is(:disabled, [aria-disabled="true"]), .tab:is(:disabled, [aria-disabled="true"]):hover { color: var(--fg-faint); cursor: not-allowed; }
.tab:is([aria-selected="true"], [aria-current="page"]) { color: var(--accent-text); font-weight: 700; }
.tab:is([aria-selected="true"], [aria-current="page"])::after { content: ""; position: absolute; inset-inline: var(--s-3); bottom: -1px; height: 2px; background: var(--emerald); }
:root[data-theme="light"] .tab:is([aria-selected="true"], [aria-current="page"])::after { background: var(--jade); }
:where(.tab) .ico { --n: 20; }
@media (max-width: 56.25em) {
  .tabs { -webkit-mask-image: linear-gradient(to right, transparent 0, #000 28px); mask-image: linear-gradient(to right, transparent 0, #000 28px); }
  [dir="ltr"] .tabs { -webkit-mask-image: linear-gradient(to left, transparent 0, #000 28px); mask-image: linear-gradient(to left, transparent 0, #000 28px); }
}

/* ── Modal (stone window) ── */
/* The veil is a layer of its own (core.js puts it just under the backdrop), so
   the backdrop can stay transparent and unclipped and lend the cut window its
   lift: a clip-path clips the window's own outer shadow. */
.modal-veil { position: fixed; inset: 0; z-index: var(--z-modal); background: var(--bg-veil); backdrop-filter: blur(6px); pointer-events: none; animation: fade var(--t-ui) var(--ease-out) both; }
.modal-backdrop { position: fixed; inset: 0; z-index: var(--z-modal); display: grid; place-items: center; padding: var(--gutter); filter: var(--lift); animation: fade var(--t-ui) var(--ease-out) both; }
/* the registration stone is no window: it is unclipped and casts its own shadow,
   and it leaves with its veil */
.modal-backdrop:has(> .modal--stone) { filter: none; }
.modal-veil:has(+ .modal-backdrop.is-leaving) { opacity: 0; transition: opacity 240ms var(--ease-out) 80ms; }
/* a toast that arrives while a dialog is open is set into the dialog's top, in
   the flow, clear of the close button: never over the dialog */
.modal__toasts { display: grid; gap: var(--s-2); }
.modal__toasts:not(:empty) { margin-block-end: var(--s-4); margin-inline-end: 40px; }
.modal {
  --facet: 14px; --bd: var(--line-strong);
  position: relative; width: min(100%, 640px); max-height: calc(100vh - 2 * var(--gutter)); max-height: calc(100dvh - 2 * var(--gutter)); overflow: auto;
  background: var(--facet-edges), var(--stone-grad); border: 1px solid var(--bd); clip-path: var(--clip-facet);
  padding: var(--s-6); box-shadow: var(--shadow-stone), var(--glow-inset), inset 0 0 0 1px rgba(255,255,255,.03);
  animation: rise var(--t-slow) var(--ease-out) both;
}
:root[data-theme="light"] .modal { box-shadow: var(--shadow-stone), inset 0 0 0 1px rgba(15,107,79,.18); }
.modal--wide { width: min(100%, 1040px); }
/* a question is a small window: two lines and two buttons do not need 640px */
.modal:has(> .modal__body > .confirm) { width: min(100%, 440px); padding: var(--s-5); }
/* close: a quiet glyph, lighter than the choice the dialog is asking for */
.modal .modal__close { position: absolute; top: var(--s-2); inset-inline-end: var(--s-2); z-index: 1; width: 32px; min-height: 32px; --btn-bg: transparent; --bd: transparent; color: var(--fg-muted); box-shadow: none; }
.modal .modal__close:hover { --btn-bg: rgba(255,255,255,.06); --bd: transparent; color: var(--fg-strong); filter: none; }
:root[data-theme="light"] .modal .modal__close:hover { --btn-bg: rgba(24,36,34,.06); }
@media (pointer: coarse) { .modal .modal__close { width: 44px; min-height: 44px; top: var(--s-1); inset-inline-end: var(--s-1); } }
.modal :where(.modal__close) .ico { --n: 20; }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes rise { from { opacity: 0; transform: translateY(24px) scale(.98); } to { opacity: 1; transform: none; } }

/* ── Tooltip / glossary ──
   The tip points at its word: a notch on the edge that faces the term (core.js
   sets data-side="bottom" and --notch-x when the tip is flipped or clamped).
   The rim is the outer shape and the face an inset copy of it, so the chamfers
   and the notch are outlined all the way round. It can be hovered: a definition
   you cannot move onto, select or enlarge fails WCAG 1.4.13. */
.term { border-bottom: 1px dotted var(--line-glow); color: var(--accent-text); cursor: help; position: relative; }
/* The English run is isolated LTR, so a logical margin would resolve against
   ITS direction (the left) — on a Persian page that put the gap on the wrong
   side and jammed the scripts together («قدرSTR»). The gap is physical, on the
   side that faces the Persian word, keyed on the page's own direction. */
.term-en { font-family: var(--font-body); font-style: normal; font-weight: 500; font-size: max(.78em, var(--fs-2xs)); letter-spacing: .01em; color: var(--fg-faint); direction: ltr; unicode-bidi: isolate; }
[dir="rtl"] .term-en { margin-right: .45em; }
[dir="ltr"] .term-en { margin-left: .45em; }
/* in a heading the English name is a quiet second line, not a second title */
[dir] :is(h1, h2, h3, h4) .term-en { display: block; margin: 6px 0 0; font: 500 var(--fs-md)/1.4 var(--font-body); color: var(--fg-muted); letter-spacing: .01em; }
.tip {
  --facet: 8px; --n: 8px; --nx: var(--notch-x, 50%);
  --tip-shape: polygon(0 0, calc(100% - var(--facet)) 0, 100% var(--facet), 100% calc(100% - var(--n)), calc(var(--nx) + var(--n)) calc(100% - var(--n)), var(--nx) 100%, calc(var(--nx) - var(--n)) calc(100% - var(--n)), var(--facet) calc(100% - var(--n)), 0 calc(100% - var(--n) - var(--facet)));
  position: fixed; left: 0; top: 0; z-index: var(--z-modal); isolation: isolate;
  max-width: min(320px, calc(100vw - 24px)); padding: 12px 14px calc(12px + var(--n));
  font-size: var(--fs-sm); line-height: 1.7; color: var(--fg);
  background: var(--line-glow); clip-path: var(--tip-shape);
  pointer-events: auto; animation: fade var(--t-fast) var(--ease-out) both;
}
.tip::before { content: ""; position: absolute; inset: 1px; z-index: -1; --nx: calc(var(--notch-x, 50% + 1px) - 1px); background: var(--bg-elev-2); clip-path: var(--tip-shape); }
.tip[data-side="bottom"] {
  --tip-shape: polygon(0 var(--n), calc(var(--nx) - var(--n)) var(--n), var(--nx) 0, calc(var(--nx) + var(--n)) var(--n), calc(100% - var(--facet)) var(--n), 100% calc(var(--n) + var(--facet)), 100% 100%, var(--facet) 100%, 0 calc(100% - var(--facet)));
  padding: calc(12px + var(--n)) 14px 12px;
}
:root[data-theme="light"] .tip { background: rgba(15,107,79,.55); }
:root[data-theme="light"] .tip::before { background: var(--bg-elev-3); }
.tip b { color: var(--fg-strong); display: block; font-size: var(--fs-sm); font-weight: 700; }
[dir] .tip .term-en { display: block; margin: 0 0 2px; font-size: var(--fs-2xs); color: var(--fg-muted); }
/* an English line set as its own block still lines up with the Persian around it */
:is(h1, h2, h3, h4, .tip) .term-en { text-align: match-parent; }

/* ── Toast (fallback when ravens are off; ravens reuse it) ──
   Three kinds you can tell apart without colour: a leading glyph (raven rune,
   tick, the ᚾ need-rune for an error), a rail in the kind colour, and an
   error that is framed in ember. They float off busy art by their stack's
   drop shadow — the stack is unclipped, the toast is not. */
.toasts { position: fixed; bottom: var(--s-6); inset-inline-start: var(--s-6); z-index: var(--z-raven); display: flex; flex-direction: column; gap: var(--s-2); width: min(380px, calc(100vw - 2 * var(--s-4))); filter: var(--lift); pointer-events: none; }
.toasts > * { pointer-events: auto; }
@media (max-width: 56.25em) {
  .toasts { top: calc(var(--hdr-h) + var(--s-2)); bottom: auto; inset-inline: var(--s-3); width: auto; flex-direction: column-reverse; }
}
.toast {
  --bd: var(--line-strong); --facet: 8px; --kind: var(--fg-muted);
  --glyph: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path d='M6 2.5v15M14 2.5v15M6 7.5l8 5' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'/></svg>");
  position: relative; display: grid; grid-template-columns: 20px minmax(0, 1fr) auto; column-gap: var(--s-3); align-items: start;
  padding: var(--s-3) var(--s-3) var(--s-3) var(--s-4);
  font-size: var(--fs-sm); line-height: var(--lh-ui); color: var(--fg);
  background: var(--facet-edges), var(--bg-elev-2);
  border: 1px solid var(--bd); border-inline-start: 3px solid var(--kind);
  clip-path: var(--clip-facet); box-shadow: var(--shadow-stone);
  animation: toast-in var(--t-ui) var(--ease-out) both;
}
:root[data-theme="light"] .toast { background: var(--facet-edges), var(--bg-elev-3); }
.toast::before { content: ""; grid-column: 1; grid-row: 1 / span 2; width: 20px; height: 20px; margin-top: 1px; background: var(--kind); -webkit-mask: var(--glyph) center / contain no-repeat; mask: var(--glyph) center / contain no-repeat; }
.toast > b { grid-column: 2; display: block; color: var(--fg-strong); font-weight: 700; }
.toast > span { grid-column: 2; }
/* a rune the markup brings (the raven's note) takes the glyph's place; an
   error's ᚾ is the masked glyph itself, drawn, never the rune font's */
.toast > i.rune { grid-column: 1; grid-row: 1 / span 2; float: none; margin: 0; font-family: var(--font-rune); font-size: 1.125rem; line-height: 1.2; letter-spacing: 0; color: var(--kind); font-style: normal; text-align: center; }
.toast:has(> i.rune)::before { display: none; }
.toast__close { grid-column: 3; grid-row: 1; position: static; display: grid; place-items: center; width: 28px; height: 28px; margin: -4px; padding: 0; border: 0; background: none; color: var(--fg-muted); font-size: 0.875rem; line-height: 1; cursor: pointer; border-radius: var(--r-1); }
.toast__close:hover { color: var(--fg-strong); background: rgba(255,255,255,.06); }
@media (pointer: coarse) { .toast__close { width: 44px; height: 44px; margin: -12px; } }
.toast--success { --kind: var(--accent-text); --bd: var(--line-glow);
  --glyph: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path d='M3.5 10.5 8 15 16.5 5.5' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/></svg>"); }
.toast--success > b { color: var(--accent-text); }
.toast:is(.toast--danger, .toast--error) { --kind: var(--danger); --bd: var(--danger-edge);
  --glyph: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path d='M10 2v16M5.5 7.5l9 5' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round'/></svg>");
  background: var(--facet-edges), linear-gradient(var(--danger-bg), var(--danger-bg)), var(--bg-elev-2); }
:root[data-theme="light"] .toast:is(.toast--danger, .toast--error) { background: var(--facet-edges), linear-gradient(var(--danger-bg), var(--danger-bg)), var(--bg-elev-3); }
@keyframes toast-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@media (max-width: 56.25em) { .toast { animation-name: toast-in-top; } }
@keyframes toast-in-top { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

/* ── Loading: a rune ring that FILLS with emerald ──
   Each rune lights in turn and stays lit (12 × 120 ms), then the whole ring
   dims and starts again. --fill is a registered number animated on the ring;
   each rune reads how far past its own index the fill has reached. The loader
   only appears after 200 ms, so a fast page never flashes it. */
@property --fill { syntax: '<number>'; inherits: true; initial-value: 0; }
.rune-ring { --n: 12; --fill: 0; position: relative; width: 96px; height: 96px; display: grid; place-items: center; animation: rune-fill 2.2s linear infinite; }
.rune-ring span {
  --lit: clamp(0, var(--fill) - var(--i), 1);
  position: absolute; inset: 0; display: grid; place-items: start center;
  font-family: var(--font-rune); font-size: 1.0625rem; line-height: 1;
  color: color-mix(in srgb, var(--emerald) calc(var(--lit) * 100%), var(--ornament));
  text-shadow: 0 0 calc(var(--lit) * 10px) rgba(47,211,154,.6);
  transform: rotate(calc(var(--i) * (360deg / var(--n))));
}
:root[data-theme="light"] .rune-ring span { color: color-mix(in srgb, var(--jade) calc(var(--lit) * 100%), var(--ornament)); text-shadow: none; }
@keyframes rune-fill { 0% { --fill: 0; opacity: 1; } 66% { --fill: 13; opacity: 1; } 84% { --fill: 13; opacity: 1; } 100% { --fill: 13; opacity: .25; } }
.rune-ring::after { content: ""; width: 12px; height: 12px; background: var(--accent); clip-path: var(--clip-hex); }
.loading { display: grid; place-items: center; gap: var(--s-4); padding: var(--s-9) 0; color: var(--fg-muted); animation: fade var(--t-ui) var(--ease-out) 200ms both; }
.skeleton { background: linear-gradient(90deg, var(--bg-elev) 25%, var(--bg-elev-2) 50%, var(--bg-elev) 75%); background-size: 200% 100%; animation: shimmer 1.6s infinite; min-height: 1em; }
@keyframes shimmer { to { background-position: -200% 0; } }
/* still: a ring half lit, which says "working" without moving */
@media (prefers-reduced-motion: reduce) { .rune-ring { animation: none; --fill: 7; } .skeleton { animation: none; } }
:root[data-motion="off"] .rune-ring { animation: none; --fill: 7; }

/* ── Empty state with the Gatekeeper ──
   He stands in a lit niche, rim-lit against a backlight, so the default dark
   theme shows his whole outline instead of two eye dots. He arrives once (rises,
   then his eyes light, then the action) and is still afterwards. */
.empty {
  --bd: var(--line); --facet: 14px;
  display: grid; justify-items: center; text-align: center; gap: var(--s-2);
  padding: var(--s-6) var(--s-5);
  background: var(--facet-edges), radial-gradient(ellipse 46% 58% at 50% 26%, rgba(47,211,154,.13), rgba(47,211,154,.03) 60%, transparent 75%), var(--bg-elev);
  border: 1px solid var(--bd); clip-path: var(--clip-facet);
}
:root[data-theme="light"] .empty { --bd: rgba(24,36,34,.16); background: var(--facet-edges), radial-gradient(ellipse 40% 52% at 50% 24%, rgba(28,44,52,.05), transparent 72%), var(--bg-elev); }
/* At 80px the vignettes' props were 8–12px — a bowl lost in the foot glow, a
   lantern that was a dot — and four scenes read as one standing keeper. The
   figure is a scene, so it is shown at a scene's size: 160px on a desktop,
   136px on a phone, where the prop is the size of a glyph and reads first. */
.empty .keeper { position: relative; width: clamp(136px, 32vw, 160px); animation: keeper-rise var(--t-slow) var(--ease-out) both; }
.empty .keeper svg { width: 100%; height: auto; }
/* the body goes darker than the niche, and a two-step emerald rim draws its edge */
:root:not([data-theme="light"]) .empty .keeper svg { --ink: #050506; filter: drop-shadow(0 0 .7px rgba(123,230,192,.95)) drop-shadow(0 0 .7px rgba(123,230,192,.9)) drop-shadow(0 0 16px rgba(47,211,154,.32)); }
.empty .keeper::after { content: ""; position: absolute; inset: auto 8% -6px; height: 14px; background: radial-gradient(ellipse, rgba(47,211,154,.35), transparent 70%); pointer-events: none; }
:root[data-theme="light"] .empty .keeper::after { background: radial-gradient(ellipse, rgba(28,44,52,.2), transparent 70%); }
.empty .keeper-eye { animation: eye-light 400ms var(--ease-out) 780ms both; }
.empty > :is(.btn, .row, a, button) { margin-top: var(--s-2); animation: fade var(--t-ui) var(--ease-out) 930ms both; }
/* the whole-page error writes its title as the page's h1 (core.js errorHTML);
   it is the same card, so it keeps the card's size and weight, not the page's */
.empty :is(h1, h2, h3) { margin: 0; font-size: var(--fs-xl); font-weight: 800; }
.empty p { color: var(--fg-muted); margin: 0 auto; max-width: 60ch; line-height: 1.7; text-wrap: balance; }
/* a page that did not open (core.js paintError): the card in the page's frame,
   at a set width, with the room round it of any page's first block */
.page-error { padding-block: var(--s-8) var(--s-9); }
.page-error > .empty { max-width: 40rem; margin-inline: auto; }
@keyframes keeper-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes eye-light { from { opacity: 0; } to { opacity: 1; } }

/* ── Stat / rune-glow strip ── */
.glow-strip { --bd: var(--line-glow); --facet: 10px; display: flex; align-items: center; gap: var(--s-5); flex-wrap: wrap; padding: var(--s-4) var(--s-5); border: 1px solid var(--bd); background: var(--facet-edges), linear-gradient(90deg, rgba(47,211,154,.08), transparent 60%); clip-path: var(--clip-facet); box-shadow: var(--glow-inset); }
.glow-strip .num { font-family: var(--font-display); font-weight: 900; font-size: var(--fs-2xl); color: var(--emerald); text-shadow: 0 0 14px rgba(47,211,154,.6); line-height: 1; }
:root[data-theme="light"] .glow-strip .num { color: var(--jade); text-shadow: none; }

/* ── Skull difficulty ── */
/* difficulty is drawn, not typed. An unlit skull is a quiet grey that still
   clears 3:1 (opacity took it to 1.7:1, and difficulty is the data here); a lit
   one is bone white. No emerald: on a card the free seats are the live thing. */
.skulls { display: inline-flex; gap: 3px; align-items: center; }
.skulls i { display: inline-grid; width: 16px; height: 16px; color: var(--control-edge); }
.skulls i svg { --n: 16; display: block; }
.skulls i.on { color: var(--fg-strong); }

/* ── Shield seats ──
   The brief: "taken shields bear player avatars, empty ones glow faintly
   green". A seat is the brand's own shield, round — the pointed heater it used
   to be was a second shield shape for the same idea, beside a round mark and a
   round shield glyph. The open seat is the lit one: an emerald hairline rim
   round a face tinted 16% emerald. A taken seat steps back into the stone, a
   boss ring round its initial or emblem. «+N» is the same disc, empty.
   One size, --seat (32px), so a page sets one number and the disc stays round;
   one row, never wrapping. */
.shields { display: flex; gap: 6px; flex-wrap: nowrap; }
.shield {
  --seat: 32px;
  position: relative; flex: none; display: grid; place-items: center; overflow: hidden; container-type: size;
  width: var(--seat); height: var(--seat); border-radius: 50%; clip-path: circle(50%);
  background: var(--bg-elev-2); color: var(--fg-muted);
  font-size: var(--fs-2xs); font-weight: 800; line-height: 1;
  box-shadow: inset 0 0 0 1px var(--line-strong);
}
.shield--free { background: color-mix(in srgb, var(--emerald) 16%, var(--bg-elev)); box-shadow: inset 0 0 0 1.5px rgba(47,211,154,.85), inset 0 0 calc(var(--seat) / 3) rgba(47,211,154,.28); }
:root[data-theme="light"] .shield--free { background: color-mix(in srgb, var(--emerald) 16%, #FFFFFF); box-shadow: inset 0 0 0 1.5px var(--jade); }
/* the boss ring: 2px, a third of the way in, round whatever the seat holds */
.shield--taken::after { content: ""; position: absolute; inset: 18%; border-radius: 50%; box-shadow: 0 0 0 2px var(--line-strong); pointer-events: none; }
:root[data-theme="light"] .shield--taken { background: #E6E9E4; }
/* «+N» holds a count, not a letter: it stays out of the size containment the
   letter needs, or the list's small pill (quests.css .seats--sm) had no content
   width to grow to and cut «+۵» to a «+» and half a digit in a 16px ring */
.shield--more { container-type: normal; background: transparent; font-variant-numeric: tabular-nums; }
/* a player's own face fills the disc; the ring then rims the disc, not the face */
.shield .avatar__img { width: 100%; height: 100%; object-fit: cover; }
.shield:has(> .avatar__img)::after { inset: 0; box-shadow: inset 0 0 0 2px rgba(10,10,11,.55); }
.shield .avatar__rune { width: 50%; height: 50%; }

/* ── Stone divider with runes ── */
.divider { display: flex; align-items: center; gap: var(--s-4); color: var(--ornament); margin: var(--s-7) 0; }
.divider::before, .divider::after { content: ""; flex: 1; height: 1px; background: linear-gradient(90deg, transparent, var(--line-strong), transparent); }
.divider .rune { font-size: var(--rune-sm); }

/* ── The separator between facts (HX.SEP) ──
   Drawn, not set. Any round dot beside a Persian digit reads as a zero, and at
   1x the rune font draws ᛫ as exactly that dot: «۲۰:۰۰ ᛫ ۴ ساعته» read
   «۲۰:۰۰ ۰ ۴». A 5px lozenge in the ornament colour, which no digit resembles.
   The glyph stays in the markup at size 0, for whoever copies the line, and is
   isolated, so the runic letter (a strong left-to-right character) no longer
   pulls a Persian number across it. The margins are in rem: at font-size 0 an
   em is nothing; in a flex row (where vertical-align does not apply and an
   empty box sits on the baseline) it centres itself. .q-sep is the sessions
   pages' name for the same mark. */
.sep, .q-sep {
  display: inline-block; flex: none; align-self: center; width: 5px; height: 5px; margin-inline: .375rem; vertical-align: middle;
  background: var(--ornament); transform: rotate(45deg);
  font-size: 0; line-height: 0; overflow: hidden; color: transparent; font-style: normal; unicode-bidi: isolate;
}

/* ── Breadcrumbs ── */
.crumbs { display: flex; flex-wrap: wrap; gap: var(--s-2); font-size: var(--fs-sm); line-height: var(--lh-ui); color: var(--fg-muted); margin-bottom: var(--s-5); }
.crumbs a { color: var(--fg-muted); }
.crumbs a:hover { color: var(--accent-text); }
.crumbs li { list-style: none; display: flex; gap: var(--s-2); min-width: 0; }
/* the separator is drawn, not read: the same lozenge as .sep, and no text to
   find its way into the crumb's name */
.crumbs li:not(:last-child)::after { content: ""; align-self: center; flex: none; width: 5px; height: 5px; margin-inline: 2px; background: var(--ornament); transform: rotate(45deg); }
.crumbs ul { display: contents; padding: 0; margin: 0; }

/* ── Progress (emerald fissure) ── */
.fissure { position: fixed; top: 0; inset-inline-end: 0; width: 3px; height: 100vh; z-index: var(--z-nav); background: rgba(255,255,255,.04); pointer-events: none; }
.fissure > i { display: block; width: 100%; height: var(--p, 0%); background: linear-gradient(180deg, var(--emerald), var(--jade)); box-shadow: var(--glow-strong); transition: height 120ms linear; }

/* ── Key hint: keys are read letter by letter, so they are set in the mono ── */
kbd { font-family: var(--font-mono); font-size: var(--fs-2xs); font-weight: 500; line-height: 1; padding: 3px 6px; border: 1px solid var(--line-strong); border-bottom-width: 2px; border-radius: var(--r-1); background: var(--bg-elev); color: var(--fg-muted); direction: ltr; unicode-bidi: isolate; white-space: nowrap; }

/* ── Table ── */
/* position: relative makes the box the containing block of what is absolutely
   placed inside it — the .sr-only words in a cell above all. Without it they
   escape the scroll box's clip, and a cell scrolled out of view to the side
   still widens the page: on a phone the DM's payment grid made the whole page
   scroll sideways by the width of its hidden columns. */
.table-wrap { overflow-x: auto; max-width: 100%; position: relative; }
table.tbl { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); line-height: var(--lh-ui); }
.tbl th, .tbl td { padding: var(--s-2) var(--s-3); border-bottom: 1px solid var(--line); text-align: start; }
.tbl th { color: var(--fg-strong); font-weight: 700; background: rgba(255,255,255,.02); overflow-wrap: normal; }
.tbl td { font-variant-numeric: tabular-nums; }
/* digits run left-to-right in both languages, so a number column aligns on
   its units only when it is set to the right — in Persian and in English */
.tbl :is(th, td):is(.num, [data-num]) { text-align: right; }

/* ── Stat blocks (bestiary) ──
   The creature's name is the largest type in the block; a section header
   (Actions, Reactions) is a small rule-bound caption, as in the 5e block. */
.statblock { --facet: 14px; --bd: var(--line-strong); background: var(--facet-edges), var(--stone-grad); border: 1px solid var(--bd); clip-path: var(--clip-facet); padding: var(--s-5); position: relative; }
.statblock::before, .statblock::after { content: ""; position: absolute; inset-inline: 0; height: 3px; background: linear-gradient(90deg, transparent, var(--emerald), transparent); opacity: .7; }
.statblock::before { top: 0; } .statblock::after { bottom: 0; }
/* the creature's name, by its class: it is an h2 in the style guide and an h1
   on its own page. A bare h1/h2 in this list (and the class inside :is(),
   which lifts the whole list to (0,2,0)) also caught the page's h2 section
   heads, «اقدام‌ها», and set them as a 28px headline with no room round them. */
.statblock .statblock__name { font-size: var(--fs-2xl); margin: 0; }
.statblock .sub, :lang(fa) .statblock .sub { color: var(--fg-muted); font-style: normal; font-size: var(--fs-sm); }
.statblock .abil { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--s-2); text-align: center; margin: var(--s-4) 0; border-block: 1px solid var(--line-glow); padding: var(--s-3) 0; }
.statblock .abil b { display: block; color: var(--accent-text); font-family: var(--font-body); font-weight: 700; font-size: var(--fs-xs); }
[dir] .statblock .abil .term-en { display: block; margin: 0; font-size: var(--fs-2xs); }
.statblock .abil span { font-family: var(--font-display); font-weight: 800; font-size: var(--fs-lg); color: var(--fg-strong); }
.statblock .line { margin: 2px 0; font-size: var(--fs-sm); }
.statblock .line b { color: var(--fg-strong); }
.statblock .act { margin-top: var(--s-4); }
.statblock .act > :is(h3, h4), .statblock .act :is(h3, h4) { font-size: var(--fs-lg); font-weight: 800; margin: var(--s-5) 0 var(--s-2); padding-bottom: 4px; border-bottom: 1px solid var(--line-glow); color: var(--accent-text); }
/* the challenge rating: the number alone in the hex, its label set outside
   it — the hex's top vertex left 20px for «چالش», and English read "hallenge" */
.cr { position: relative; isolation: isolate; display: inline-grid; grid-template-rows: auto 64px; justify-items: center; align-items: center; min-width: 56px; color: var(--accent-fg); font-family: var(--font-display); font-weight: 900; font-size: 1.5rem; line-height: 1; font-variant-numeric: tabular-nums; }
.cr::after { content: ""; position: absolute; inset-inline: 0; bottom: 0; height: 64px; z-index: -1; background: var(--emerald); clip-path: var(--clip-hex); box-shadow: inset 0 -8px 12px -6px rgba(0,0,0,.35); }
:root[data-theme="light"] .cr::after { background: var(--jade); }
.cr > small, .cr__label { grid-row: 1; margin-bottom: var(--s-1); color: var(--fg-muted); font-family: var(--font-body); font-size: var(--fs-2xs); font-weight: 700; line-height: 1.2; white-space: nowrap; }
.cr:focus-visible { outline-offset: 2px; box-shadow: none; }

/* ── Speech (gatekeeper guide) ── */
.speech { --bd: var(--line-glow); --facet: 8px; position: relative; background: var(--facet-edges), var(--bg-elev-2); border: 1px solid var(--bd); padding: var(--s-3) var(--s-4); font-size: var(--fs-sm); line-height: var(--lh-ui); clip-path: var(--clip-facet); max-width: 280px; box-shadow: var(--glow-inset-soft); }

/* ── Ornamental rune band: whole words, fading out at both ends ── */
/* contain: inline-size — an unbreakable line of ornament must never be the
   thing that sets its column's width (it widened the auth page past 320px) */
.rune-band { font-family: var(--font-rune); letter-spacing: .6em; color: var(--ornament); font-size: .8125rem; white-space: nowrap; overflow: hidden; text-align: center; user-select: none; direction: ltr; max-width: none; contain: inline-size; -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent); mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent); }
.rune-band--lit { color: var(--rune-lit); }

/* ── Coarse pointers: every control reaches the 44px target the style guide promises ── */
@media (pointer: coarse) {
  /* a <summary> is a tap target like any other — the realm's "why this name?" and
     the tray's rune legend were both under 44px */
  .btn--sm, .chip, .tab, .sound-btn, .realm-why summary { min-height: 44px; }
  .btn--icon { width: 44px; min-height: 44px; }
  .switch { width: 56px; height: 32px; }
  .switch::after { width: 24px; height: 24px; top: 3px; }
  .switch[aria-checked="true"]::after { transform: translateX(-24px); }
  [dir="ltr"] .switch[aria-checked="true"]::after { transform: translateX(24px); }
  .tag { min-height: 28px; }
  .check { min-height: 44px; padding-block: 10px; }
  .check > input { top: calc(10px + (1lh - 22px) / 2); }
}

/* ── realm intro: what a place is, in plain words, and where its name comes from ── */
.realm-plain { display: grid; gap: var(--s-1); margin-top: var(--s-3); max-width: 56ch; font-size: var(--fs-sm); }
.realm-plain p { margin: 0; line-height: 1.9; color: var(--fg); }
.realm-plain b { color: var(--accent-text); }
.realm-why summary { cursor: pointer; width: fit-content; min-height: 32px; display: flex; align-items: center; color: var(--fg-muted); font-size: var(--fs-xs); text-decoration: underline dotted; text-underline-offset: 4px; }
.realm-why summary:hover { color: var(--accent-text); }
.realm-why[open] summary { color: var(--accent-text); }
.realm-why p { margin: var(--s-1) 0 0; padding-inline-start: var(--s-3); border-inline-start: 2px solid var(--line-glow); color: var(--fg-muted); line-height: 1.9; font-size: var(--fs-sm); }
/* the realm lines continue the heading's reading edge; a centred page (Niflheim)
   centres the column, never the lines inside it */
.pg-niflheim .realm-plain { margin-inline: auto; text-align: start; }

/* ── notices and confirmations ── */
.notice { padding: var(--s-3) var(--s-4); border: 1px solid var(--line-strong); border-inline-start: 3px solid var(--accent-text); background: rgba(255,255,255,.02); margin-block: var(--s-3); line-height: 1.8; }
.notice--warn { border-inline-start-color: var(--danger); background: var(--danger-bg); }
.confirm h2 { font-size: var(--fs-lg); margin: 0 0 var(--s-2); padding-inline-end: var(--s-6); }
.confirm p { margin: 0; }
.confirm__actions { margin-top: var(--s-5); gap: var(--s-2); }
/* how to pay, when there is credit (core.js HX.payWith): two whole-width
   choices, each naming what it does with the money on a line of its own */
.paywith__opts { display: grid; gap: var(--s-2); margin-top: var(--s-4); }
.paywith__opt { flex-direction: column; align-items: flex-start; gap: 2px; padding-block: var(--s-3); white-space: normal; text-align: start; }
.paywith__opt small { font-weight: 400; font-size: var(--fs-xs); line-height: var(--lh-ui); opacity: .88; }
.paywith .confirm__actions { margin-top: var(--s-3); }

/* ── Segmented choice (two or three options in one control) ── */
.seg { --bd: var(--control-edge); --facet: 6px; display: inline-flex; padding: 3px; gap: 3px; border: 1px solid var(--bd); background: var(--facet-edges), var(--bg); clip-path: var(--clip-facet); }
.seg > button { min-height: 36px; padding: 0 var(--s-3); border: 0; background: none; color: var(--fg-muted); font-weight: 600; font-size: var(--fs-sm); cursor: pointer; clip-path: var(--clip-facet); --facet: 4px; }
.seg > button:hover { color: var(--fg-strong); }
.seg > button[aria-pressed="true"] { background: var(--bg-elev-2); color: var(--fg-strong); font-weight: 800; box-shadow: var(--glow-inset-soft); }

/* ── coming soon: a closed page, drawn but blurred, under a card (HX.gate) ── */
.soon { position: relative; min-height: 70vh; overflow: hidden; }
.soon__under { filter: blur(7px) saturate(.7); opacity: .55; pointer-events: none; user-select: none; }
.soon__ghost { display: grid; gap: var(--s-5); padding-block: var(--s-8); }
.soon__ghost-row { height: 88px; background: var(--stone-grad); clip-path: var(--clip-facet); }
.soon__ghost-row:nth-child(odd) { width: 78%; }
.soon__card { --bd: var(--line-glow); position: absolute; inset-inline: var(--gutter); top: clamp(48px, 16vh, 160px); margin-inline: auto; max-width: 520px;
  display: grid; justify-items: center; gap: var(--s-3); text-align: center; padding: var(--s-8) var(--s-6);
  background: var(--facet-edges), var(--bg-veil); backdrop-filter: blur(4px); border: 1px solid var(--bd); box-shadow: var(--glow-soft); clip-path: var(--clip-facet); }
.soon__card h1 { margin: 0; font-size: var(--fs-3xl); }
.soon__card p { margin: 0; }
/* jēra drawn (HX.gate.rune: a 32px stave on a page's card, 24px on a panel's),
   lit in --rune-lit; a stroke takes no text-shadow, so its light is a
   drop-shadow, quieter on Frost, where a glow reads as a smudge on paper */
.soon__rune { display: inline-flex; line-height: 0; color: var(--rune-lit); filter: drop-shadow(0 0 10px rgba(47,211,154,.45)); }
:root[data-theme="light"] .soon__rune { filter: drop-shadow(0 0 6px rgba(15,122,90,.25)); }
.soon__actions { margin-top: var(--s-3); gap: var(--s-2); justify-content: center; flex-wrap: wrap; }
@media (prefers-reduced-transparency: reduce) { .soon__under { opacity: .2; } }
.soon--panel { min-height: 280px; }
.soon--panel .soon__card { top: var(--s-6); padding: var(--s-6) var(--s-5); }
.soon--panel .soon__card h3 { margin: 0; }

/* ── Forced colours: state that was carried by a tint gets a system colour ── */
@media (forced-colors: active) {
  .btn, .chip, .slab, .modal, .toast, .empty, .tip, .statblock, .speech, .seg { border-color: CanvasText; }
  .chip:is([aria-pressed="true"], [aria-checked="true"], [aria-current="page"], .is-on) { border: 2px solid Highlight; }
  .chip:is([aria-pressed="true"], [aria-checked="true"], [aria-current="page"], .is-on)::before { forced-color-adjust: none; background: Highlight; }
  .tab:is([aria-selected="true"], [aria-current="page"])::after { forced-color-adjust: none; background: Highlight; }
  .check > input { appearance: auto; -webkit-appearance: auto; background: none; }
  .switch { border-color: CanvasText; }
  .switch[aria-checked="true"]::after { forced-color-adjust: none; background: Highlight; }
  .shield--free { forced-color-adjust: none; background: Canvas; box-shadow: inset 0 0 0 2px Highlight; }
  .skulls i.on { color: Highlight; }
  .cr::after { forced-color-adjust: none; background: CanvasText; }
  .cr { forced-color-adjust: none; color: Canvas; }
  .cr > small, .cr__label { color: CanvasText; }
  .badge { forced-color-adjust: none; background: Highlight; color: HighlightText; box-shadow: none; }
  /* the lozenge is a background, which forced colours would paint over */
  .sep, .q-sep, .crumbs li:not(:last-child)::after { forced-color-adjust: none; background: CanvasText; }
  .toast::before { forced-color-adjust: none; background: CanvasText; }
  .tip { forced-color-adjust: none; background: CanvasText; }
  .tip::before { forced-color-adjust: none; background: Canvas; }
  .tip, .tip b { color: CanvasText; }
}
