/* =========================================================================
   CriticalTarkov — dark-first, military-inspired UI.

   Direction handling: this file uses CSS *logical* properties
   (margin-inline-start, padding-block, inset-inline-end, text-align: start …)
   almost everywhere, so the whole layout mirrors automatically under
   `dir="rtl"`. Only genuinely directional artwork — chevrons, arrows, progress
   fills — needs the explicit [dir="rtl"] rules near the bottom.
   ========================================================================= */

/* --- Theme tokens: dark is the default, light is opt-in ------------------ */
:root,
:root[data-theme="dark"] {
  color-scheme: dark;

  --bg:            #0d0f0d;
  --bg-elevated:   #141814;
  --bg-panel:      #171c17;
  --bg-inset:      #0a0c0a;
  --bg-hover:      #1e241d;

  --line:          #2a322a;
  --line-strong:   #3b453a;

  --text:          #dfe4dc;
  --text-strong:   #f2f5ef;
  --text-muted:    #8d978a;

  --accent:        #a8b545;   /* olive tracer */
  --accent-strong: #c3d152;
  --accent-ink:    #12160c;
  --accent-soft:   rgba(168, 181, 69, 0.14);

  /* The logo's crit marker. Close to --danger but deliberately its own token:
     the logo must not shift if the error red is ever retuned. */
  --brand-crit:    #e0624a;

  --danger:        #d9614b;
  --danger-soft:   rgba(217, 97, 75, 0.14);
  --warn:          #d9a441;
  --warn-soft:     rgba(217, 164, 65, 0.14);
  --ok:            #6fae6b;
  --ok-soft:       rgba(111, 174, 107, 0.14);
  --info:          #6f9dbd;
  --info-soft:     rgba(111, 157, 189, 0.14);

  --shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
  --grain: rgba(255, 255, 255, 0.02);
}

:root[data-theme="light"] {
  color-scheme: light;

  --bg:            #eceee7;
  --bg-elevated:   #f7f8f4;
  --bg-panel:      #ffffff;
  --bg-inset:      #e3e6dd;
  --bg-hover:      #e6e9df;

  --line:          #cdd2c4;
  --line-strong:   #b2b9a6;

  --text:          #2c322a;
  --text-strong:   #171b16;
  --text-muted:    #626a5e;

  --accent:        #6b7a1f;
  --accent-strong: #56631a;
  --accent-ink:    #ffffff;
  --accent-soft:   rgba(107, 122, 31, 0.12);

  --brand-crit:    #b83c26;   /* the dark-theme red goes pink on a pale page */

  --danger:        #b23b26;
  --danger-soft:   rgba(178, 59, 38, 0.10);
  --warn:          #9a6d10;
  --warn-soft:     rgba(154, 109, 16, 0.12);
  --ok:            #3f7a3b;
  --ok-soft:       rgba(63, 122, 59, 0.12);
  --info:          #2f6280;
  --info-soft:     rgba(47, 98, 128, 0.12);

  --shadow: 0 8px 24px rgba(30, 36, 26, 0.10);
  --grain: rgba(0, 0, 0, 0.015);
}

/* --- Reset --------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background:
    linear-gradient(180deg, var(--bg-elevated) 0%, var(--bg) 340px),
    var(--bg);
  color: var(--text);
  font-family: "Inter", "Segoe UI", system-ui, -apple-system, "Noto Sans Arabic",
               "Tahoma", "Helvetica Neue", Arial, sans-serif;
  font-size: 16px;
  line-height: 1.65;
  overflow-wrap: break-word;
}

:root[lang="ar"] body,
[dir="rtl"] body {
  font-family: "Noto Sans Arabic", "Segoe UI", "Tahoma", "Dubai", system-ui,
               "Arial", sans-serif;
  line-height: 1.85;   /* Arabic script needs more room between lines */
}

/* Han characters are not in Inter, so without this the browser falls back per
   glyph — on Windows usually to a serif, which reads as a different typeface
   mid-sentence. Naming the platform UI fonts keeps it consistent. */
:root[lang="zh"] body {
  font-family: "Inter", "PingFang SC", "Microsoft YaHei", "Hiragino Sans GB",
               "Source Han Sans SC", "Noto Sans CJK SC", system-ui, sans-serif;
  line-height: 1.8;    /* CJK sets denser; give the lines room */
}

img, svg, video { max-width: 100%; height: auto; display: block; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: var(--accent-strong); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3, h4 {
  color: var(--text-strong);
  line-height: 1.25;
  margin-block: 0 0.5em;
  font-weight: 650;
  letter-spacing: -0.01em;
}
[dir="rtl"] h1, [dir="rtl"] h2, [dir="rtl"] h3, [dir="rtl"] h4 { letter-spacing: 0; }

h1 { font-size: clamp(1.7rem, 1.2rem + 2vw, 2.5rem); }
h2 { font-size: clamp(1.35rem, 1.1rem + 1vw, 1.7rem); }
h3 { font-size: 1.15rem; }
p { margin-block: 0 1em; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

/* Clipped rather than pushed off-canvas: a large negative inline offset lands
   outside the viewport on the wrong side under `dir="rtl"`. */
.skip-link {
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0;
  z-index: 100;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  padding: 0;
  background: var(--accent);
  color: var(--accent-ink);
  white-space: nowrap;
}
.skip-link:focus {
  width: auto;
  height: auto;
  padding: 0.6rem 1rem;
  clip-path: none;
}

/* --- Layout -------------------------------------------------------------- */
.wrap { width: min(1160px, 100% - 2rem); margin-inline: auto; }
.wrap-narrow { width: min(820px, 100% - 2rem); margin-inline: auto; }
main { flex: 1 0 auto; padding-block-end: 4rem; }

.section-block { padding-block: 2.5rem; }
.stack > * + * { margin-block-start: 1rem; }
.grid { display: grid; gap: 1.1rem; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); }
/* Drips are portrait character shots, so they tile smaller and denser. */
.grid-4 { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.row { display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: center; }
.row-between { display: flex; gap: 1rem; flex-wrap: wrap;
               align-items: center; justify-content: space-between; }
.push-end { margin-inline-start: auto; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.muted { color: var(--text-muted); }
.small { font-size: 0.86rem; }
.tiny  { font-size: 0.78rem; }
.mono  { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
         font-variant-numeric: tabular-nums; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }

/* --- Header -------------------------------------------------------------- */
.site-header {
  position: sticky;
  inset-block-start: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--bg-elevated) 92%, transparent);
  backdrop-filter: blur(10px);
  border-block-end: 1px solid var(--line);
}
/* Row one: identity on one side, account controls on the other. Never wraps —
   these are the two things that must stay findable at any width. */
.header-top {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-height: 58px;
}

/* The bilingual lockup. The mark is the axis: Latin reads inward toward it
   from one side and Arabic inward from the other, so TARKOV and تاركوف always
   sit against the marker and the thing mirrors itself.

   `direction: ltr` is load-bearing, not a leftover. A flex row reverses its
   children under [dir="rtl"], which would swap the two halves and hand the
   Arabic pages a visibly different logo — the whole point here is that there
   is one lockup, identical in both directions. Only its position in the header
   flips, which the logical properties already handle. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  direction: ltr;
  color: var(--text-strong);
  min-width: 0;
  /* No `margin-inline-end: auto` here, deliberately. Logical properties resolve
     against the element's *own* direction, and this one is forced to ltr — so
     under [dir="rtl"] that auto margin lands on the leading side and drags the
     logo inward off the edge. `.nav-actions` already claims the free space with
     `margin-inline-start: auto`, and it inherits the page direction, so it
     resolves correctly both ways. */
}
.brand:hover { text-decoration: none; }
.brand-mark {
  flex: none;
  width: 1.6rem;
  height: 1.6rem;
  color: var(--brand-crit);
}
.brand-text { font-size: 1.02rem; line-height: 1; white-space: nowrap; }
.brand-text {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  /* Tracking is applied after every letter, including the last one. Left in,
     that trailing sliver makes the gap to the mark wider on the Latin side
     than on the Arabic side and the lockup stops looking centred. */
  margin-right: -0.13em;
}
.brand-text strong { color: var(--accent); font-weight: inherit; }
.brand-text .brand-word { margin-inline-end: 0.3em; }

.nav-toggle {
  display: none;
  width: 42px; height: 38px;
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  cursor: pointer;
  padding: 9px 10px;
  flex-direction: column;
  justify-content: space-between;
}
.nav-toggle span { display: block; height: 2px; background: var(--text); border-radius: 2px; }

/* Row two: the sections, on a line of their own. */
.primary-nav { border-block-start: 1px solid var(--line); }

/* Scrolls sideways instead of wrapping. Wrapping was what broke the old header
   — the second line of links pushed the account chips down and the whole bar
   changed height depending on how many sections existed. */
.nav-scroll {
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scrollbar-width: none;          /* the links are the affordance, not a bar */
}
.nav-scroll::-webkit-scrollbar { display: none; }

/* Edge fades, added by the script only while there is more to scroll to.
   `mask-image` rather than an overlay so the fade works on whatever the sticky
   header's translucent background happens to be sitting over. */
.nav-scroll.has-end {
  mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
}
.nav-scroll.has-start {
  mask-image: linear-gradient(to right, transparent, #000 2.5rem);
}
.nav-scroll.has-start.has-end {
  mask-image: linear-gradient(to right, transparent, #000 2.5rem,
                              #000 calc(100% - 2.5rem), transparent);
}

.nav-list {
  display: flex;
  gap: 0.15rem;
  list-style: none;
  margin: 0;
  padding: 0;
  flex-wrap: nowrap;
}
.nav-link {
  display: block;
  padding: 0.6rem 0.7rem;
  color: var(--text-muted);
  font-size: 0.92rem;
  white-space: nowrap;
  border-block-end: 2px solid transparent;
}
.nav-link:hover { color: var(--text-strong); text-decoration: none; }
/* Underline rather than a pill: it sits on the header's own bottom edge, so
   the current section reads as a tab without adding another boxed shape. */
.nav-link.is-active {
  color: var(--accent-strong);
  border-block-end-color: var(--accent);
}

.nav-actions { display: flex; align-items: center; gap: 0.45rem; margin-inline-start: auto; }

/* --- Chips & buttons ----------------------------------------------------- */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.65rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--bg-panel);
  color: var(--text);
  font-size: 0.85rem;
  cursor: pointer;
  white-space: nowrap;
}
.chip:hover { background: var(--bg-hover); text-decoration: none; border-color: var(--accent); }
.chip-theme { padding-inline: 0.5rem; }

.icon { width: 1em; height: 1em; fill: none; stroke: currentColor;
        stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
        font-size: 1.05rem; flex: none; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  padding: 0.6rem 1.1rem;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  background: var(--bg-panel);
  color: var(--text-strong);
  font-weight: 550;
  font-size: 0.94rem;
  cursor: pointer;
  text-align: center;
}
.btn:hover { background: var(--bg-hover); text-decoration: none; border-color: var(--accent); }
.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
.btn-primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); }
.btn-danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, var(--line)); }
.btn-danger:hover { background: var(--danger-soft); border-color: var(--danger); }
.btn-sm { padding: 0.38rem 0.7rem; font-size: 0.86rem; }
.btn-block { width: 100%; }
.btn[disabled], .btn:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-link {
  background: none; border: none; padding: 0;
  color: var(--accent-strong); cursor: pointer; text-decoration: underline;
}

/* --- Account menu -------------------------------------------------------- */
.account { position: relative; }
.account-name { max-width: 9rem; overflow: hidden; text-overflow: ellipsis; }
.menu {
  position: absolute;
  inset-block-start: calc(100% + 0.4rem);
  inset-inline-end: 0;
  min-width: 190px;
  background: var(--bg-panel);
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  box-shadow: var(--shadow);
  padding: 0.35rem;
  z-index: 50;
}
.menu a, .menu button {
  display: block;
  width: 100%;
  padding: 0.5rem 0.65rem;
  border: none;
  background: none;
  border-radius: 5px;
  color: var(--text);
  text-align: start;
  font-size: 0.9rem;
  cursor: pointer;
}
.menu a:hover, .menu button:hover { background: var(--bg-hover); text-decoration: none; }
.menu-admin { color: var(--accent-strong); }
.menu-signout { color: var(--danger); }

/* --- Avatars ------------------------------------------------------------- */
.avatar {
  border-radius: 50%;
  object-fit: cover;
  background: var(--bg-inset);
  border: 1px solid var(--line-strong);
  flex: none;
}
.avatar-xs { width: 24px; height: 24px; }
.avatar-sm { width: 36px; height: 36px; }
.avatar-md { width: 56px; height: 56px; }
.avatar-lg { width: 96px; height: 96px; }
.avatar-fallback {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  font-weight: 700;
  text-transform: uppercase;
  line-height: 1;
}
.avatar-lg.avatar-fallback { font-size: 2.2rem; }
.avatar-md.avatar-fallback { font-size: 1.4rem; }

/* --- Flash --------------------------------------------------------------- */
.flash-stack { margin-block-start: 1rem; display: grid; gap: 0.5rem; }
.flash {
  padding: 0.7rem 1rem;
  border-radius: 6px;
  border-inline-start: 3px solid var(--info);
  background: var(--info-soft);
  font-size: 0.92rem;
}
.flash-success { border-color: var(--ok); background: var(--ok-soft); }
.flash-error   { border-color: var(--danger); background: var(--danger-soft); }
.flash-warning { border-color: var(--warn); background: var(--warn-soft); }

/* --- Cards / panels ------------------------------------------------------ */
.panel {
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 1.25rem;
}
.panel-tight { padding: 0.9rem 1rem; }

.card {
  display: flex;
  flex-direction: column;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.card:hover { border-color: var(--line-strong); transform: translateY(-2px); }

/* --- Whole-card links -------------------------------------------------------
   One real anchor per card, stretched over the whole thing with a pseudo
   element. Not a click handler on the card: this way the card is a link in
   every sense a link is one — middle-click opens a tab, the status bar shows
   where it goes, ctrl-click works, a screen reader announces one link rather
   than a region full of them, and none of it needs JavaScript.

   Everything in the footer sits above the overlay, so upvoting, opening the
   comments and visiting the author's profile all still do their own thing.
   ------------------------------------------------------------------------- */
.card-link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  /* No background: this is a hit area, not a scrim. */
}
/* On memes, drips and rat spots there is no title to stretch, so the picture is
   the link — and `.card-media` is normally `position: relative`, which would
   make the overlay stop at the bottom of the image. Dropping it back to static
   hands the job to `.card`, which is the element we actually want covered.
   `.card-flag` re-anchors to the card as a result and lands in the same place,
   because the media sits flush at the top of the card. */
.card-media.card-link { position: static; }
/* Above the overlay. `position` is what puts them in the same stacking
   context — z-index alone does nothing on a static element. */
.card-foot,
.card .card-pending { position: relative; z-index: 2; }

/* The title reacts to hovering anywhere on the card, because the whole card is
   now the thing you are about to click. */
.card:hover .card-title a { color: var(--accent-strong); }

/* The cost of a stretched link is that dragging across the card selects
   nothing. Card text is a truncated preview of the page it links to, so there
   is nothing here worth copying — but the footer, which holds a real username,
   stays selectable because it sits above the overlay. */
.card-body { padding: 1.05rem 1.15rem; display: flex; flex-direction: column; gap: 0.5rem; flex: 1; }
.card-title { margin: 0; font-size: 1.08rem; }
.card-title a { color: var(--text-strong); }
.card-title a:hover { color: var(--accent-strong); text-decoration: none; }
.card-media {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  background: var(--bg-inset);
  border-block-end: 1px solid var(--line);
  overflow: hidden;
}
/* A PMC is taller than they are wide. */
.card-media-tall { aspect-ratio: 3 / 4; }
.card-media-tall img { object-fit: cover; padding: 0; }
.card-flag {
  position: absolute;
  inset-block-start: 0.5rem;
  inset-inline-start: 0.5rem;
}
/* `contain`, not `cover`: build previews are a mix of wide weapon art and
   16:9 screenshots, and cropping either one loses the point of the picture. */
.card-media img { width: 100%; height: 100%; object-fit: contain; padding: 0.4rem; }
.card-media-empty {
  display: grid;
  place-items: center;
  color: var(--line-strong);
  font-size: 2rem;
}
.card-foot {
  padding: 0.65rem 1.15rem;
  border-block-start: 1px solid var(--line);
  background: var(--bg-elevated);
  display: flex;
  gap: 0.6rem;
  align-items: center;
  font-size: 0.84rem;
  /* Pinned to the bottom of the card, whatever is above it.
     `.card-body` carries `flex: 1`, which is what used to do this job — but
     drips and memes only render a body when the post has notes or a caption.
     Without one there was no flexible element, so in a grid row sized by a
     taller neighbour the footer rode up under the image and left the bottom
     of the card empty. An auto margin on the last flex child does the same
     job without depending on a sibling that may not exist. */
  margin-block-start: auto;
}

/* Two zones, stacked: who posted it, then what you can do about it.
   ┌───────────────────────────────┐
   │ (J) Justkhan69  ADMIN         │  the byline
   │ [▲ 1]   [💬 0]                │  the actions
   └───────────────────────────────┘

   They shared one row until now, and the room ran out. The name was competing
   with the controls for the same horizontal space, so on the four-column grid
   it got about 43px — enough to render "Justkhan69" as "Just…". Letting the
   row wrap when it was tight fixed the truncation but produced something
   worse: a grid where some cards were one line and their neighbours two,
   depending on nothing more interesting than how long a name happened to be.

   Stacking removes the competition entirely rather than rationing it. Every
   card gets the same shape whatever the name, the name gets the full card
   width, and the controls keep their size. */
.card-foot { flex-direction: column; align-items: stretch; gap: 0.55rem; }

.card-by {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  min-width: 0;
}
.card-by > .utag,
.card-by > .avatar,
.card-by > .avatar-fallback { flex: none; }

.card-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.card-actions > .inline-form,
.card-actions > .vote,
.card-actions > .card-replies { flex: none; }

.card-replies {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  /* Tabular figures so a count going 9 → 10 does not shift the row. */
  font-variant-numeric: tabular-nums;
}

.card-author {
  /* `min-width: 0` is the load-bearing half: a flex item will not shrink below
     its content's intrinsic width without it, so the ellipsis would never
     engage and the name would push the badge and avatar off the card.
     The ellipsis is now a backstop rather than the mechanism — with a line of
     its own a name has the whole card width, and only a genuinely enormous
     one still runs out. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- Badges -------------------------------------------------------------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  border: 1px solid transparent;
  white-space: nowrap;
}
[dir="rtl"] .badge { letter-spacing: 0; }
.badge-meta   { background: var(--accent-soft); color: var(--accent-strong);
                border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.badge-budget { background: var(--info-soft); color: var(--info);
                border-color: color-mix(in srgb, var(--info) 40%, transparent); }
.badge-neutral{ background: var(--bg-inset); color: var(--text-muted); border-color: var(--line); }
.badge-warn   { background: var(--warn-soft); color: var(--warn);
                border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
.badge-ok     { background: var(--ok-soft); color: var(--ok);
                border-color: color-mix(in srgb, var(--ok) 40%, transparent); }
.badge-danger { background: var(--danger-soft); color: var(--danger);
                border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
.badge-map    { background: var(--info-soft); color: var(--info);
                border-color: color-mix(in srgb, var(--info) 35%, transparent);
                text-transform: none; letter-spacing: 0; }
.badge-featured { background: var(--warn-soft); color: var(--warn);
                  border-color: color-mix(in srgb, var(--warn) 40%, transparent); }

/* Confidence labels for cultist entries */
.badge-unconfirmed      { background: var(--danger-soft); color: var(--danger);
                          border-color: color-mix(in srgb, var(--danger) 35%, transparent); }
.badge-community_tested { background: var(--warn-soft); color: var(--warn);
                          border-color: color-mix(in srgb, var(--warn) 35%, transparent); }
.badge-high_confidence  { background: var(--ok-soft); color: var(--ok);
                          border-color: color-mix(in srgb, var(--ok) 35%, transparent); }

/* --- Hero ---------------------------------------------------------------- */
.hero {
  position: relative;
  padding-block: clamp(2.5rem, 6vw, 4.5rem);
  border-block-end: 1px solid var(--line);
  background-image:
    radial-gradient(1200px 400px at 50% -10%, var(--accent-soft), transparent 70%),
    repeating-linear-gradient(135deg, var(--grain) 0 2px, transparent 2px 9px);
  overflow: hidden;
}
.hero-inner { display: grid; gap: 1.5rem; }
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--accent);
  font-size: 0.8rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  margin-block-end: 0.4rem;
}
[dir="rtl"] .eyebrow { letter-spacing: 0; }
.hero h1 { max-width: 20ch; }
.hero-lead { font-size: 1.08rem; color: var(--text-muted); max-width: 62ch; }

.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 0.75rem;
  margin-block-start: 0.5rem;
}
.stat {
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0.8rem 1rem;
}
a.stat { color: inherit; }
a.stat:hover { text-decoration: none; border-color: var(--accent); background: var(--bg-hover); }
.stat-value { font-size: 1.5rem; font-weight: 700; color: var(--accent-strong); line-height: 1.2; }
.stat-label { font-size: 0.78rem; color: var(--text-muted); text-transform: uppercase;
              letter-spacing: 0.08em; }
[dir="rtl"] .stat-label { letter-spacing: 0; }

/* --- Page header --------------------------------------------------------- */
.page-head {
  padding-block: 2rem 1.25rem;
  border-block-end: 1px solid var(--line);
  margin-block-end: 1.75rem;
}
.breadcrumbs { font-size: 0.84rem; color: var(--text-muted); margin-block-end: 0.5rem; }
.breadcrumbs a { color: var(--text-muted); }
.breadcrumbs .sep { margin-inline: 0.4rem; }

/* --- Meta bar (last updated / patch) ------------------------------------- */
.meta-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  align-items: center;
  font-size: 0.85rem;
  color: var(--text-muted);
}
.meta-bar .icon { font-size: 1rem; }
.meta-item { display: inline-flex; align-items: center; gap: 0.35rem; }

.stale-notice {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  padding: 0.75rem 1rem;
  border-radius: 8px;
  background: var(--warn-soft);
  border-inline-start: 3px solid var(--warn);
  font-size: 0.9rem;
  margin-block: 1rem;
}
/* A guide written ahead of a patch is news, not rot. Amber says "distrust
   this"; for the one page explaining the update everyone is waiting for, that
   is precisely the wrong instruction. */
.stale-notice.is-ahead {
  background: var(--info-soft);
  border-inline-start-color: var(--info);
}

.disclaimer {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  padding: 1rem 1.15rem;
  border-radius: 10px;
  background: var(--danger-soft);
  border: 1px solid color-mix(in srgb, var(--danger) 35%, transparent);
  margin-block-end: 1.5rem;
}
.disclaimer .icon { font-size: 1.35rem; color: var(--danger); margin-block-start: 0.15rem; }
.disclaimer p:last-child { margin-block-end: 0; }

/* --- Prose (guide bodies) ------------------------------------------------ */
.prose { font-size: 1.02rem; }
.prose > :first-child { margin-block-start: 0; }
.prose h2 {
  margin-block: 2rem 0.75rem;
  padding-block-end: 0.4rem;
  border-block-end: 1px solid var(--line);
}
.prose h3 { margin-block: 1.5rem 0.5rem; color: var(--accent-strong); }
.prose h4 { margin-block: 1.2rem 0.4rem; }
.prose ul, .prose ol { padding-inline-start: 1.4rem; margin-block: 0 1.1rem; }
.prose li { margin-block-end: 0.4rem; }
.prose img { border-radius: 8px; border: 1px solid var(--line); margin-block: 1rem; }
.prose figure { margin: 1.5rem 0; }
.prose figcaption { font-size: 0.85rem; color: var(--text-muted); margin-block-start: 0.5rem;
                    text-align: center; }
.prose blockquote {
  margin: 1.25rem 0;
  padding: 0.75rem 1.1rem;
  border-inline-start: 3px solid var(--accent);
  background: var(--bg-elevated);
  border-radius: 0 8px 8px 0;
  color: var(--text-muted);
}
[dir="rtl"] .prose blockquote { border-radius: 8px 0 0 8px; }
.prose code {
  background: var(--bg-inset);
  padding: 0.12em 0.4em;
  border-radius: 4px;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 0.9em;
}
.prose pre {
  background: var(--bg-inset);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 1rem;
  overflow-x: auto;
}
.prose hr { border: none; border-block-start: 1px solid var(--line); margin-block: 2rem; }

/* Tables scroll inside their own box rather than the page */
.prose table, .data-table {
  width: 100%;
  border-collapse: collapse;
  margin-block: 1.25rem;
  font-size: 0.94rem;
}
.prose th, .prose td, .data-table th, .data-table td {
  border: 1px solid var(--line);
  padding: 0.55rem 0.75rem;
  text-align: start;
  vertical-align: top;
}
.prose th, .data-table th {
  background: var(--bg-elevated);
  color: var(--text-strong);
  font-weight: 600;
}
.prose tbody tr:nth-child(even), .data-table tbody tr:nth-child(even) {
  background: color-mix(in srgb, var(--bg-elevated) 55%, transparent);
}
.table-wrap, .table-scroll { overflow-x: auto; margin-block: 1.25rem; }
.table-wrap table, .table-scroll table { margin-block: 0; }

/* Callouts available to admin-authored content */
.prose .note, .prose .tip, .prose .warning, .prose .danger, .prose .callout {
  padding: 0.85rem 1.1rem;
  border-radius: 8px;
  margin-block: 1.25rem;
  border-inline-start: 3px solid var(--info);
  background: var(--info-soft);
}
.prose .tip     { border-color: var(--ok);     background: var(--ok-soft); }
.prose .warning { border-color: var(--warn);   background: var(--warn-soft); }
.prose .danger  { border-color: var(--danger); background: var(--danger-soft); }
.prose .note > :last-child, .prose .tip > :last-child,
.prose .warning > :last-child, .prose .danger > :last-child,
.prose .callout > :last-child { margin-block-end: 0; }

/* Numbered walkthroughs */
.prose ol.steps { list-style: none; counter-reset: step; padding-inline-start: 0; }
.prose ol.steps > li {
  counter-increment: step;
  position: relative;
  padding-inline-start: 2.75rem;
  margin-block-end: 1.1rem;
  min-height: 2rem;
}
.prose ol.steps > li::before {
  content: counter(step);
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0;
  width: 1.9rem;
  height: 1.9rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  color: var(--accent-strong);
  font-weight: 700;
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}

/* Interactive checklists — JS turns these into persisted checkboxes */
.prose ul.checklist { list-style: none; padding-inline-start: 0; }
.prose ul.checklist > li {
  border: 1px solid var(--line);
  border-radius: 8px;
  margin-block-end: 0.5rem;
  background: var(--bg-elevated);
}
.checklist-item { display: flex; gap: 0.7rem; align-items: flex-start;
                  padding: 0.65rem 0.9rem; cursor: pointer; }
.checklist-item input { margin-block-start: 0.28rem; accent-color: var(--accent);
                        width: 1.05rem; height: 1.05rem; flex: none; cursor: pointer; }
.prose ul.checklist > li:has(input:checked) { opacity: 0.6; background: var(--ok-soft);
                                              border-color: color-mix(in srgb, var(--ok) 30%, var(--line)); }
.prose ul.checklist > li:has(input:checked) .checklist-text { text-decoration: line-through; }
.checklist-progress {
  display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
  padding: 0.7rem 0.9rem; margin-block: 1rem;
  background: var(--bg-elevated); border: 1px solid var(--line); border-radius: 8px;
  font-size: 0.88rem;
  position: sticky; inset-block-start: 70px; z-index: 5;
}
.checklist-bar { flex: 1; min-width: 6rem; height: 7px; border-radius: 99px;
                 background: var(--bg-inset); overflow: hidden; }
.checklist-bar > span { display: block; height: 100%; background: var(--accent);
                        border-radius: 99px; transition: width 0.2s ease; }
.checklist-status:empty { display: none; }
.checklist-saved { color: var(--ok); }
.checklist-failed { color: var(--danger); }

/* Item artwork under a checklist entry */
.prose .checklist-text .item-icon {
  display: block;
  width: auto;
  max-width: 96px;
  max-height: 72px;
  margin-block: 0.4rem 0.1rem;
  margin-inline: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--bg-inset);
  padding: 0.2rem;
  object-fit: contain;
  image-rendering: -webkit-optimize-contrast;
}
.prose ul.checklist > li:has(input:checked) .item-icon { filter: grayscale(1); }

/* "Sign in to save your progress" prompt above an anonymous reader's checklist */
.checklist-signin {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  flex-wrap: wrap;
  padding: 0.85rem 1rem;
  margin-block: 1.25rem 0.75rem;
  border-radius: 8px;
  background: var(--info-soft);
  border: 1px solid color-mix(in srgb, var(--info) 35%, transparent);
  border-inline-start: 3px solid var(--info);
}
.checklist-signin > .icon { font-size: 1.3rem; color: var(--info); flex: none; }
.checklist-signin > div { flex: 1; min-width: 12rem; }
.checklist-signin p { margin: 0.15rem 0 0; color: var(--text-muted); }
.checklist-signin .btn { flex: none; }

/* --- Task chains ---------------------------------------------------------
   A numbered sequence of quests, each linking out to the wiki. The connector
   line uses inset-inline-start so it moves to the right-hand side under RTL.
   ------------------------------------------------------------------------- */
.prose ol.task-chain {
  list-style: none;
  counter-reset: task;
  padding-inline-start: 0;
  margin-block: 1.5rem;
  position: relative;
}
.prose ol.task-chain > li {
  counter-increment: task;
  position: relative;
  padding-inline-start: 3rem;
  padding-block: 0.35rem 1.1rem;
  margin: 0;
}
/* connector running between the node markers */
.prose ol.task-chain > li::after {
  content: "";
  position: absolute;
  inset-inline-start: 1.03rem;
  inset-block-start: 2.3rem;
  bottom: -0.35rem;
  width: 2px;
  background: linear-gradient(180deg, var(--accent) 0%, var(--line-strong) 100%);
  opacity: 0.55;
}
.prose ol.task-chain > li:last-child::after { display: none; }
.prose ol.task-chain > li::before {
  content: counter(task);
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.15rem;
  width: 2.1rem;
  height: 2.1rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--bg-panel);
  border: 2px solid var(--accent);
  color: var(--accent-strong);
  font-weight: 700;
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  z-index: 1;
}
.prose ol.task-chain > li > a:first-child {
  display: inline-block;
  font-weight: 650;
  font-size: 1.02rem;
  color: var(--text-strong);
  border-block-end: 1px dashed color-mix(in srgb, var(--accent) 55%, transparent);
}
.prose ol.task-chain > li > a:first-child:hover {
  color: var(--accent-strong);
  text-decoration: none;
  border-block-end-style: solid;
}
.prose ol.task-chain > li > a:first-child::after {
  content: "↗";
  font-size: 0.75em;
  margin-inline-start: 0.3rem;
  opacity: 0.65;
}
[dir="rtl"] .prose ol.task-chain > li > a:first-child::after { content: "↖"; }

.task-meta {
  display: block;
  font-size: 0.86rem;
  color: var(--text-muted);
  margin-block-start: 0.15rem;
}
.task-note {
  display: block;
  font-size: 0.86rem;
  margin-block-start: 0.3rem;
  padding: 0.4rem 0.7rem;
  border-radius: 6px;
  background: var(--bg-inset);
  border-inline-start: 2px solid var(--line-strong);
}
/* A hard prerequisite between two steps. */
.prose .task-gate {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-block: 1.25rem;
  padding: 0.55rem 0.9rem;
  border-radius: 6px;
  background: var(--warn-soft);
  border: 1px dashed color-mix(in srgb, var(--warn) 50%, transparent);
  font-size: 0.9rem;
}
.prose .task-gate::before { content: "⚑"; color: var(--warn); }

/* --- Branching flow chart (the endings decision tree) --------------------- */
.prose .flow { margin-block: 1.75rem; }
.prose .flow-node {
  position: relative;
  padding: 0.75rem 1rem;
  margin-block-end: 1.9rem;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--bg-panel);
}
/* the arrow joining one node to the next */
.prose .flow-node::after {
  content: "▼";
  position: absolute;
  inset-block-end: -1.5rem;
  inset-inline-start: 1.4rem;
  color: var(--accent);
  font-size: 0.8rem;
  opacity: 0.8;
}
.prose .flow-node:last-child::after { display: none; }
.prose .flow-node > :last-child { margin-block-end: 0; }

.prose .flow-start {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.prose .flow-decision {
  border-style: dashed;
  border-color: var(--warn);
  background: var(--warn-soft);
}
.prose .flow-decision > strong { color: var(--warn); }
.prose .flow-end { border-color: var(--ok); background: var(--ok-soft); }

.prose .flow-branches {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  margin-block-end: 1.5rem;
}
.prose .flow-branch {
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  padding: 0.9rem 1rem;
  background: var(--bg-elevated);
  border-block-start: 3px solid var(--line-strong);
}
.prose .flow-branch-head {
  font-weight: 700;
  font-size: 1.02rem;
  margin-block-end: 0.4rem;
  color: var(--text-strong);
}
.prose ol.flow-steps, .prose ul.flow-steps {
  padding-inline-start: 1.15rem;
  margin-block-end: 0;
  font-size: 0.92rem;
}
.prose .flow-steps > li { margin-block-end: 0.35rem; }

/* Ending emblems: inline with the branch label, larger beside the section heading. */
.prose .ending-logo {
  display: inline-block;
  width: 2.6rem;
  height: 2.6rem;
  margin: 0;
  margin-inline-end: 0.55rem;
  vertical-align: -0.8rem;
  object-fit: contain;
  border: none;
  border-radius: 0;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.4));
}
.prose .flow-branch-head {
  display: flex;
  align-items: center;
  gap: 0;
}
.prose .flow-branch-head .ending-logo { vertical-align: middle; flex: none; }

.prose h3.ending-heading {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  margin-block-start: 2.25rem;
}
.prose h3.ending-heading .ending-logo {
  width: 3.4rem;
  height: 3.4rem;
  vertical-align: middle;
  flex: none;
}

/* Each ending gets its own accent so the four routes are tellable apart. */
.prose .outcome-savior   { border-block-start-color: var(--ok); }
.prose .outcome-savior   .flow-branch-head { color: var(--ok); }
.prose .outcome-survivor { border-block-start-color: var(--info); }
.prose .outcome-survivor .flow-branch-head { color: var(--info); }
.prose .outcome-debtor   { border-block-start-color: var(--warn); }
.prose .outcome-debtor   .flow-branch-head { color: var(--warn); }
.prose .outcome-fallen   { border-block-start-color: var(--danger); }
.prose .outcome-fallen   .flow-branch-head { color: var(--danger); }

/* Responsive video embeds */
.prose iframe, .video-embed iframe {
  width: 100%;
  aspect-ratio: 16 / 9;
  height: auto;
  border: 1px solid var(--line);
  border-radius: 8px;
  margin-block: 1.25rem;
  background: var(--bg-inset);
}

/* --- Table of contents / side rail --------------------------------------- */
.guide-layout { display: grid; gap: 2rem; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1000px) {
  .guide-layout { grid-template-columns: minmax(0, 1fr) 250px; align-items: start; }
  .guide-rail { position: sticky; inset-block-start: 82px; }
}
.rail-title { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.1em;
              color: var(--text-muted); margin-block-end: 0.6rem; }
[dir="rtl"] .rail-title { letter-spacing: 0; }
.rail-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.15rem; }
.rail-list a { display: block; padding: 0.35rem 0.6rem; border-radius: 5px;
               font-size: 0.88rem; color: var(--text); border-inline-start: 2px solid transparent; }
.rail-list a:hover { background: var(--bg-hover); text-decoration: none; }
.rail-list a.is-active { color: var(--accent-strong); border-inline-start-color: var(--accent);
                         background: var(--accent-soft); }

/* --- Forms --------------------------------------------------------------- */
.form-grid { display: grid; gap: 1.1rem; }
.form-row { display: grid; gap: 1.1rem; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.field { display: grid; gap: 0.35rem; }
.field > label, .field-label { font-size: 0.88rem; font-weight: 600; color: var(--text-strong); }
.field .hint { font-size: 0.8rem; color: var(--text-muted); }
.req { color: var(--danger); }
.opt { color: var(--text-muted); font-weight: 400; font-size: 0.82em; }

input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="date"], input[type="search"], select, textarea {
  width: 100%;
  padding: 0.6rem 0.75rem;
  background: var(--bg-inset);
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  color: var(--text);
}
input:focus, select:focus, textarea:focus {
  border-color: var(--accent);
  outline: 2px solid var(--accent-soft);
  outline-offset: 0;
}
textarea { min-height: 120px; resize: vertical; line-height: 1.6; }
textarea.code { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
                font-size: 0.9rem; min-height: 300px; }
input[type="file"] { padding: 0.45rem; background: var(--bg-inset);
                     border: 1px dashed var(--line-strong); border-radius: 6px; width: 100%; }
select { appearance: none; background-image: none; }
/* A field sitting inside a table row next to a .btn-sm, rather than stacked in
   a form. The width override is the point: the rule above stretches every
   input to fill its container, which in a row of controls means one field
   pushing the button it belongs to onto the next line. */
.input-sm { padding: 0.35rem 0.5rem; font-size: 0.86rem; width: auto; }

.check { display: flex; gap: 0.55rem; align-items: center; font-size: 0.92rem; cursor: pointer; }
.check input { accent-color: var(--accent); width: 1.05rem; height: 1.05rem; cursor: pointer; }

.radio-cards { display: flex; gap: 0.6rem; flex-wrap: wrap; }
.radio-card {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--bg-inset);
  cursor: pointer;
}
.radio-card input { accent-color: var(--accent); }
.radio-card:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--text-strong);
}

.errors {
  padding: 0.8rem 1rem 0.8rem 1rem;
  border-radius: 8px;
  background: var(--danger-soft);
  border-inline-start: 3px solid var(--danger);
  margin-block-end: 1.25rem;
}
.errors ul { margin: 0; padding-inline-start: 1.1rem; }
.form-actions { display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: center;
                padding-block-start: 0.5rem; }

/* Bilingual editor: EN and AR fields side by side */
.bilingual { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.lang-field { border: 1px solid var(--line); border-radius: 8px; padding: 0.85rem; background: var(--bg-elevated); }
.lang-field > .field-label { display: flex; align-items: center; gap: 0.45rem; margin-block-end: 0.4rem; }
.lang-tag { font-size: 0.7rem; padding: 0.1rem 0.4rem; border-radius: 4px;
            background: var(--accent-soft); color: var(--accent-strong); font-weight: 700; }
.lang-field[data-lang="ar"] textarea, .lang-field[data-lang="ar"] input { direction: rtl; text-align: right; }
.lang-field[data-lang="ar"] textarea.code { direction: ltr; text-align: left; }
.lang-field[data-lang="en"] textarea, .lang-field[data-lang="en"] input { direction: ltr; text-align: left; }

/* --- Toolbar for the rich-content editor --------------------------------- */
.editor-toolbar { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-block-end: 0.4rem; }
.editor-toolbar button {
  padding: 0.25rem 0.55rem;
  font-size: 0.78rem;
  border: 1px solid var(--line-strong);
  border-radius: 5px;
  background: var(--bg-panel);
  cursor: pointer;
}
.editor-toolbar button:hover { background: var(--bg-hover); border-color: var(--accent); }

/* --- Toolbar / filters --------------------------------------------------- */
.toolbar {
  display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center;
  padding: 0.75rem 1rem;
  background: var(--bg-elevated);
  border: 1px solid var(--line);
  border-radius: 10px;
  margin-block-end: 1.5rem;
}
.filter-group { display: flex; gap: 0.3rem; flex-wrap: wrap; }
.filter {
  padding: 0.35rem 0.75rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  font-size: 0.85rem;
  color: var(--text);
  background: var(--bg-panel);
}
.filter:hover { border-color: var(--accent); text-decoration: none; }
.filter.is-active { background: var(--accent); border-color: var(--accent);
                    color: var(--accent-ink); font-weight: 600; }

/* --- Vote button --------------------------------------------------------- */
.vote {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1rem;
  min-width: 3.1rem;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--bg-inset);
  color: var(--text);
  cursor: pointer;
  line-height: 1.15;
}
.vote:hover { border-color: var(--accent); background: var(--bg-hover); text-decoration: none; }
.vote .icon { font-size: 1.1rem; }
.vote-count { font-weight: 700; font-variant-numeric: tabular-nums; font-size: 0.95rem; }
.vote.is-voted { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-strong); }
.vote[disabled] { cursor: default; opacity: 0.75; }
/* Own build: the count still shows, but nothing here is clickable. */
.vote.is-disabled { cursor: default; opacity: 0.6; }
.vote.is-disabled:hover { border-color: var(--line-strong); background: var(--bg-inset); }

/* --- Build specifics ----------------------------------------------------- */
.build-head { display: flex; gap: 1rem; align-items: flex-start; flex-wrap: wrap; }
.spec-grid { display: grid; gap: 0.6rem; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
.spec {
  background: var(--bg-inset);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0.6rem 0.8rem;
}
.spec-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.07em;
              color: var(--text-muted); }
[dir="rtl"] .spec-label { letter-spacing: 0; }
.spec-value { font-size: 1.15rem; font-weight: 700; color: var(--text-strong);
              font-variant-numeric: tabular-nums; }

/* --- Zoomable build screenshot ------------------------------------------- */
.zoomable {
  display: block;
  position: relative;
  border-radius: 10px;
  border: 1px solid var(--line);
  overflow: hidden;
  cursor: zoom-in;
}
.zoomable:hover { border-color: var(--accent); text-decoration: none; }
.zoomable img { width: 100%; display: block; }
.zoom-cue {
  position: absolute;
  inset-block-end: 0.6rem;
  inset-inline-end: 0.6rem;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.6rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  border: 1px solid var(--line-strong);
  color: var(--text);
  font-size: 0.78rem;
  backdrop-filter: blur(4px);
  opacity: 0;
  transition: opacity 0.15s ease;
}
.zoomable:hover .zoom-cue,
.zoomable:focus-visible .zoom-cue { opacity: 1; }
/* Touch devices have no hover, so keep the hint permanently visible there. */
@media (hover: none) {
  .zoom-cue { opacity: 1; }
}

/* --- Lightbox ------------------------------------------------------------ */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  background: rgba(0, 0, 0, 0.88);
  backdrop-filter: blur(3px);
  overflow: auto;
  overscroll-behavior: contain;
}
.lightbox img {
  max-width: 100%;
  max-height: calc(100vh - 3rem);
  width: auto;
  border-radius: 6px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
  cursor: zoom-in;
}
/* Actual-size mode: let the image overflow so it can be scrolled and read. */
.lightbox.is-actual-size { align-items: flex-start; justify-content: flex-start; }
.lightbox.is-actual-size img {
  max-width: none;
  max-height: none;
  width: auto;
  cursor: zoom-out;
  margin: auto;
}
.lightbox-close {
  position: fixed;
  inset-block-start: 1rem;
  inset-inline-end: 1rem;
  width: 2.6rem;
  height: 2.6rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: var(--bg-panel);
  color: var(--text-strong);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  z-index: 1;
}
.lightbox-close:hover { border-color: var(--accent); color: var(--accent-strong); }
.lightbox-hint {
  position: fixed;
  inset-block-end: 1rem;
  inset-inline: 0;
  text-align: center;
  color: var(--text-muted);
  font-size: 0.8rem;
  pointer-events: none;
}
body.lightbox-open { overflow: hidden; }

/* Mini profile card shown next to user content */
.mini-profile {
  display: flex;
  gap: 0.75rem;
  align-items: center;
  padding: 0.8rem 1rem;
  background: var(--bg-inset);
  border: 1px solid var(--line);
  border-radius: 10px;
}
.mini-profile .name { font-weight: 600; color: var(--text-strong); }
.mini-profile .meta { font-size: 0.8rem; color: var(--text-muted); }

/* --- Cultist helper ------------------------------------------------------ */
.combobox { position: relative; }
.combobox-list {
  position: absolute;
  inset-block-start: calc(100% + 4px);
  inset-inline: 0;
  z-index: 30;
  max-height: 260px;
  overflow-y: auto;
  background: var(--bg-panel);
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  box-shadow: var(--shadow);
  list-style: none;
  margin: 0;
  padding: 0.25rem;
}
.combobox-list li { border-radius: 5px; }
.combobox-list button {
  display: flex; justify-content: space-between; gap: 0.75rem; width: 100%;
  padding: 0.5rem 0.6rem; background: none; border: none; border-radius: 5px;
  text-align: start; cursor: pointer; font-size: 0.9rem;
}
.combobox-list button:hover, .combobox-list li.is-highlighted button {
  background: var(--accent-soft); color: var(--accent-strong);
}
.combobox-empty { padding: 0.6rem; color: var(--text-muted); font-size: 0.88rem; }

.entry {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-panel);
  padding: 1.1rem 1.2rem;
}
.entry + .entry { margin-block-start: 0.9rem; }
.entry-head { display: flex; gap: 0.75rem; align-items: flex-start;
              justify-content: space-between; flex-wrap: wrap; }
.item-tags { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-block: 0.6rem; }
.item-tag {
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--bg-inset);
  font-size: 0.82rem;
  color: var(--text);
}
.item-tag:hover { border-color: var(--accent); color: var(--accent-strong); text-decoration: none; }
.entry-outcome {
  padding: 0.6rem 0.85rem;
  border-radius: 6px;
  background: var(--accent-soft);
  border-inline-start: 3px solid var(--accent);
  margin-block: 0.6rem;
}
.source-note { font-size: 0.82rem; color: var(--text-muted); }

/* --- Empty state --------------------------------------------------------- */
.empty {
  text-align: center;
  padding: 3rem 1.5rem;
  border: 1px dashed var(--line-strong);
  border-radius: 10px;
  color: var(--text-muted);
  background: var(--bg-elevated);
}
.empty .icon { font-size: 2.25rem; margin-block-end: 0.5rem; color: var(--line-strong); }

/* --- Admin --------------------------------------------------------------- */
.admin-layout { display: grid; gap: 1.5rem; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 900px) {
  .admin-layout { grid-template-columns: 210px minmax(0, 1fr); align-items: start; }
  .admin-nav { position: sticky; inset-block-start: 82px; }
}
.admin-nav { display: grid; gap: 0.2rem; }
.admin-nav a {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.5rem 0.75rem; border-radius: 6px;
  color: var(--text); font-size: 0.92rem;
  border-inline-start: 2px solid transparent;
}
.admin-nav a:hover { background: var(--bg-hover); text-decoration: none; }
.admin-nav a.is-active { background: var(--accent-soft); color: var(--accent-strong);
                         border-inline-start-color: var(--accent); }
.admin-nav-label {
  margin: 0.7rem 0 0.15rem; padding-inline: 0.75rem;
  color: var(--text-muted); font-size: 0.7rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.09em;
}
/* Arabic has no upper case, and letter-spacing pulls a joined script apart. */
[dir="rtl"] .admin-nav-label { text-transform: none; letter-spacing: 0; font-size: 0.76rem; }

.admin-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.admin-table th, .admin-table td {
  padding: 0.6rem 0.7rem;
  border-block-end: 1px solid var(--line);
  text-align: start;
  vertical-align: middle;
}
.admin-table th { color: var(--text-muted); font-size: 0.76rem; text-transform: uppercase;
                  letter-spacing: 0.07em; font-weight: 600; }
[dir="rtl"] .admin-table th { letter-spacing: 0; }
.admin-table tbody tr:hover { background: var(--bg-hover); }
.admin-table .actions { display: flex; gap: 0.3rem; flex-wrap: wrap; justify-content: flex-end; }
.inline-form { display: inline; }

.dash-grid { display: grid; gap: 0.9rem; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }

/* The restore panel. A red edge because it is the one control on the site that
   cannot be undone by clicking it again — it should not look like the panel
   above it, which only downloads a file. */
.danger-panel { border-color: color-mix(in srgb, var(--danger) 45%, var(--line)); }
.danger-panel h2 { color: var(--danger); }

/* --- Footer ---------------------------------------------------------------
   Just the brand line now: the language switcher and section links both live in
   the header, and duplicating them down here earned nothing.
   ------------------------------------------------------------------------- */
.site-footer {
  margin-block-start: auto;
  padding-block: 1.75rem;
  border-block-start: 1px solid var(--line);
  background: var(--bg-elevated);
}
.footer-inner {
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap; gap: 0.5rem 1.5rem;
}
.footer-meta { display: flex; align-items: center; gap: 0.6rem; margin: 0; }
.footer-brand { display: flex; align-items: center; gap: 0.75rem; }
.footer-brand .brand-mark { width: 1.9rem; height: 1.9rem; }
.footer-title {
  margin: 0;
  color: var(--text-strong);
  font-size: 0.95rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
[dir="rtl"] .footer-title { letter-spacing: 0; }
.footer-brand .muted { margin: 0.1rem 0 0; }

@media (max-width: 480px) {
  .footer-brand { flex-direction: column; text-align: center; gap: 0.5rem; }
}

/* Sits above a post written in the other language. Quiet on purpose — it is a
   note about the text, not a warning about it. */
.lang-note {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-block: 0 0.75rem;
  padding: 0.5rem 0.7rem;
  border: 1px dashed var(--line-strong);
  border-radius: 8px;
  color: var(--text-muted);
}
.lang-note .icon { flex: none; inline-size: 1rem; block-size: 1rem; }

/* --- Comments on a profile ------------------------------------------------
   A list, not a grid of cards: a comment is a line of text, and giving each one
   a card would say it carries the same weight as a build with a screenshot.
   ------------------------------------------------------------------------- */
.profile-comments { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.profile-comment {
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--line);
  border-inline-start: 3px solid var(--line-strong);
  border-radius: 8px;
  background: var(--bg-panel);
}
.profile-comment:hover { border-inline-start-color: var(--accent); }
.profile-comment .eyebrow { margin: 0 0 0.3rem; }
.profile-comment-body {
  display: block;
  color: var(--text);
  font-size: 0.94rem;
  /* The whole quote is the link to the comment in context. */
  text-decoration: none;
}
.profile-comment-body:hover { color: var(--text-strong); text-decoration: none; }

/* --- My submissions -------------------------------------------------------
   The page a rejection needs, and the mark that tells you to come to it.
   ------------------------------------------------------------------------- */
.submission-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.submission {
  display: flex; align-items: flex-start; gap: 1rem; flex-wrap: wrap;
  padding: 0.75rem 0.85rem;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg-elevated);
}
.submission-main { flex: 1; min-width: 0; }
.submission-title { margin: 0; font-size: 1rem; }
.submission-title a { color: var(--text-strong); }
.submission-actions { flex: none; }
.profile-own-note { margin-block: 0.75rem 0; }

/* A dot on the account chip. Deliberately not a number: the count is one click
   away in the menu, and a badge with a digit in the header reads as urgent in a
   way that "an admin has a note for you" is not. */
.attention-dot {
  inline-size: 0.5rem; block-size: 0.5rem;
  border-radius: 50%;
  background: var(--danger);
  flex: none;
  /* A ring so it stays visible against the chip in either theme. */
  box-shadow: 0 0 0 2px var(--bg-panel);
}
.menu-attention { color: var(--danger); font-weight: 600; }

/* --- About --------------------------------------------------------------- */
.about-card + .about-card { margin-block-start: 1.25rem; }
.about-card h2 { font-size: 1.1rem; margin-block: 0 0.75rem; }
.about-list { display: grid; gap: 0.6rem; margin: 0; }
.about-list > div {
  display: grid; grid-template-columns: 8rem 1fr; gap: 0.5rem 1rem; align-items: baseline;
}
.about-list dt { color: var(--text-muted); font-size: 0.86rem; }
.about-list dd { margin: 0; }
@media (max-width: 30rem) {
  .about-list > div { grid-template-columns: 1fr; gap: 0.1rem; }
}

/* --- Pagination ---------------------------------------------------------- */
.pagination { display: flex; gap: 0.5rem; justify-content: center; align-items: center;
              margin-block-start: 2rem; }

/* =========================================================================
   Directional corrections — everything else mirrors via logical properties.
   ========================================================================= */
[dir="rtl"] .icon-flip,
[dir="rtl"] .pagination .icon,
[dir="rtl"] .breadcrumbs .icon { transform: scaleX(-1); }

[dir="rtl"] .admin-table .actions { justify-content: flex-start; }

/* Numbers stay LTR inside RTL text so "1,250,000 ₽" reads correctly. */
[dir="rtl"] .mono,
[dir="rtl"] .spec-value,
[dir="rtl"] .stat-value,
[dir="rtl"] .vote-count { direction: ltr; unicode-bidi: isolate; }

/* --- Landing-page section directory --------------------------------------
   Every section in one glance. Deliberately compact — the previews below it
   are where the actual content lives. */
.section-grid {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
}

.section-tile {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.9rem 1rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-panel);
  color: inherit;
}
.section-tile:hover {
  border-color: var(--accent);
  background: var(--bg-elevated);
  text-decoration: none;
}

.section-tile-icon {
  display: grid;
  place-items: center;
  flex: none;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 8px;
  background: var(--accent-soft);
  color: var(--accent-strong);
}
.section-tile-icon .icon { width: 1.25rem; height: 1.25rem; }

.section-tile-body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.section-tile-title { font-weight: 600; color: var(--text-strong); }
.section-tile-desc {
  font-size: 0.82rem;
  color: var(--text-muted);
  /* Two lines then ellipsis, so tiles in a row stay the same height. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.section-tile-count {
  margin-inline-start: auto;
  flex: none;
  align-self: flex-start;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  background: var(--bg-inset);
  border: 1px solid var(--line);
  color: var(--text-muted);
  font-size: 0.78rem;
}

/* --- /post chooser -------------------------------------------------------
   Taller than the landing-page directory tiles, because each one carries the
   list of what its form will ask for. */
.post-grid { display: grid; gap: 0.75rem; }

.post-tile {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1.15rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-panel);
  color: inherit;
}
.post-tile:hover {
  border-color: var(--accent);
  background: var(--bg-elevated);
  text-decoration: none;
}
.post-tile-icon {
  display: grid;
  place-items: center;
  flex: none;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 9px;
  background: var(--accent-soft);
  color: var(--accent-strong);
}
.post-tile-icon .icon { width: 1.4rem; height: 1.4rem; }
.post-tile-body { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; }
.post-tile-title { font-weight: 600; font-size: 1.05rem; color: var(--text-strong); }
.post-tile-needs { font-size: 0.85rem; color: var(--text-muted); }
/* `icon-flip` already mirrors the chevron in RTL; this only parks it at the end. */
.post-tile-go { margin-inline-start: auto; flex: none; color: var(--text-muted); }
.post-tile:hover .post-tile-go { color: var(--accent-strong); }

/* --- Landing-page code strip --------------------------------------------- */
.code-strip { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.code-chip {
  padding: 0.25rem 0.6rem;
  border-radius: 6px;
  border: 1px solid var(--line-strong);
  background: var(--bg-inset);
  color: var(--accent-strong);
  font-weight: 700;
  letter-spacing: 0.05em;
  font-size: 0.88rem;
}
.code-chip:hover { border-color: var(--accent); text-decoration: none; }

.join-panel { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }

/* --- Scav's Eye ----------------------------------------------------------
   The whole game is two submit buttons, so the card *is* the button. */
.streak { text-align: center; flex: none; }
.streak-value { display: block; font-size: 2.4rem; font-weight: 700; color: var(--accent-strong); line-height: 1; }
.streak-label {
  font-size: 0.72rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* The starting gate. Nothing on it is timed, which is the point of it. */
.start-panel { padding-block: 2.25rem; }
.start-clock {
  font-size: 3.4rem;
  font-weight: 700;
  line-height: 1;
  color: var(--accent-strong);
  margin-block: 0.4rem 0.75rem;
}
.start-lead { font-size: 1.05rem; margin-block-end: 1.25rem; }
.start-rules {
  list-style: none;
  padding: 0;
  margin-block-end: 1.5rem;
  display: grid;
  gap: 0.45rem;
  font-size: 0.94rem;
  color: var(--text-muted);
}
.btn-lg { padding: 0.7rem 1.8rem; font-size: 1.05rem; }

/* The round clock.
   A bar and a number: the bar for the glance, the number for the last couple
   of seconds when the difference between 1.4 and 0.4 decides whether to
   commit. Both are driven from the same deadline, so they cannot disagree. */
.round-clock { margin-block-start: 1.5rem; text-align: center; }
.round-clock-bar {
  block-size: 6px;
  border-radius: 3px;
  background: var(--bg-inset);
  overflow: hidden;
}
.round-clock-fill {
  display: block;
  block-size: 100%;
  inline-size: 100%;
  background: var(--accent);
  /* No transition. The fill is repainted every frame from the real remaining
     time, and easing between frames would put the bar behind the number. */
}
.round-clock-value {
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1;
  margin-block: 0.5rem 0;
  color: var(--text-muted);
  /* Tabular figures: without them the whole readout jitters sideways every
     tenth of a second as the digits change width. */
  font-variant-numeric: tabular-nums;
}
.round-clock.is-urgent .round-clock-fill { background: var(--danger); }
.round-clock.is-urgent .round-clock-value { color: var(--danger); }
.round-clock.is-out .round-clock-value { opacity: 0.5; }

.pick-grid {
  display: grid;
  gap: 0.9rem;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  margin-block-start: 1.5rem;
}
.pick { margin: 0; }
.pick-versus {
  font-size: 0.8rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.pick-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.7rem;
  width: 100%;
  padding: 1.4rem 1rem;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: var(--bg-panel);
  color: var(--text);
  cursor: pointer;
  text-align: center;
}
.pick-card:hover { border-color: var(--accent); background: var(--bg-elevated); }
.pick-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.pick-media { display: grid; place-items: center; height: 120px; }
.pick-media img { max-height: 120px; max-width: 100%; object-fit: contain; }
.pick-name { font-weight: 600; font-size: 1.02rem; color: var(--text-strong); }
.pick-cta {
  font-size: 0.78rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.pick-card:hover .pick-cta { color: var(--accent-strong); }

.shared-player {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  margin-block-end: 0.5rem;
  font-weight: 600;
  color: var(--text-strong);
}
.shared-player:hover { color: var(--accent-strong); text-decoration: none; }

/* --- Landing-page game panel --------------------------------------------- */
.game-promo {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  align-items: center;
}
.game-promo-copy { min-width: 0; }
.game-promo-board { min-width: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.game-promo-board .podium-list { border-color: var(--line-strong); }
.game-promo-board .podium-row { background: var(--bg-inset); }

@media (max-width: 720px) {
  .game-promo { grid-template-columns: 1fr; }
}

/* --- Top players, shown under the game ----------------------------------- */
.podium { margin-block-start: 2.5rem; }
.podium-title { font-size: 1.05rem; margin: 0; }
.podium-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
}
.podium-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.5rem 0.85rem;
  background: var(--bg-panel);
  border-block-start: 1px solid var(--line);
  font-size: 0.9rem;
}
.podium-row:first-child { border-block-start: none; }
.podium-row.is-me { background: var(--accent-soft); }
.podium-place {
  width: 1.6rem;
  flex: none;
  color: var(--text-muted);
  font-size: 0.82rem;
}
/* The three that actually get awarded. */
.podium-row:nth-child(1) .podium-place { color: var(--accent-strong); font-weight: 700; }
.podium-row:nth-child(2) .podium-place,
.podium-row:nth-child(3) .podium-place { color: var(--text); font-weight: 600; }

.podium-player { display: inline-flex; align-items: center; gap: 0.45rem; color: inherit; min-width: 0; }
.podium-player span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.podium-streak {
  margin-inline-start: auto;
  font-weight: 700;
  color: var(--accent-strong);
}
.podium-time { flex: none; width: 3.2rem; text-align: end; color: var(--text-muted); font-size: 0.82rem; }
/* The role badge is a sibling of the name link rather than a child, so the
   row's 0.7rem gap applies to it and it drifts away from the name it belongs
   to. Pulling it back leaves roughly the badge's own 0.4em margin, which is
   the spacing it has everywhere else. `flex: none` stops it being squeezed
   when a long display name is competing for the same room. */
.podium-row > .utag { flex: none; margin-inline-start: -0.35rem; }
/* On the landing-page rail there is no room for the word as well as the mark,
   and the same reasoning as `.card-foot .utag`: the shape carries it. */
.game-promo-board .utag-text { display: none; }
.game-promo-board .utag { padding-inline: 0.3em; }

/* How old the prices are, picked out of the line it sits in. It is the one
   part of that sentence that changes, and the one worth checking before
   trusting a round. */
.synced-age { color: var(--accent-strong); font-weight: 600; }

.claim-panel {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  flex-wrap: wrap;
  margin-block-start: 1.25rem;
  border-color: var(--accent);
}
.claim-panel h2 { font-size: 1.15rem; }
.claim-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-inline-start: auto; }

.result-rank { margin-block-start: 0.75rem; font-weight: 600; color: var(--accent-strong); }
.result-rank.muted { color: var(--text-muted); font-weight: 400; font-size: 0.9rem; }

.lb-table td, .lb-table th { vertical-align: middle; }
.lb-table .rank { width: 3rem; color: var(--text-muted); }
.lb-table .lb-streak { font-size: 1.05rem; font-weight: 700; color: var(--accent-strong); }
.lb-player { display: inline-flex; align-items: center; gap: 0.5rem; }
.lb-table tr.is-me { background: var(--accent-soft); }

.result-panel { padding-block: 2rem; }
.result-score { font-size: 4rem; font-weight: 700; line-height: 1; color: var(--accent-strong); }
.result-label { color: var(--text-muted); margin-block-start: 0.35rem; }

.answer-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.answer {
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-panel);
  text-align: center;
}
.answer img { max-height: 90px; object-fit: contain; }
.answer-name { font-weight: 600; margin-block: 0.5rem 0.25rem; }
.answer-price { font-size: 1.2rem; color: var(--text-strong); }
/* Green for the right answer, red outline for what they actually chose. */
.answer.is-dearer { border-color: var(--ok); background: var(--ok-soft); }
.answer.is-picked:not(.is-dearer) { border-color: var(--danger); background: var(--danger-soft); }

@media (max-width: 640px) {
  .pick-grid { grid-template-columns: 1fr; }
  .pick-versus { justify-self: center; }
  .pick-media { height: 90px; }
  .pick-media img { max-height: 90px; }
}

/* --- Share bar ----------------------------------------------------------- */
.share {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  padding-block-start: 1.25rem;
  margin-block-start: 1.25rem;
  border-block-start: 1px solid var(--line);
}
.share-label {
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-inline-end: 0.15rem;
}
.share-link {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border: 1px solid var(--line-strong);
  border-radius: 7px;
  background: var(--bg-inset);
  color: var(--text-muted);
}
.share-link:hover {
  border-color: var(--accent);
  color: var(--accent-strong);
  text-decoration: none;
}
.share-link .icon { width: 1.05rem; height: 1.05rem; }

/* --- Memes ---------------------------------------------------------------
   Memes come in every aspect ratio, so the tile is a fixed 4:3 window and the
   image is `contain`ed inside it — cropping a meme can cut off the punchline. */
.card-media-meme { aspect-ratio: 4 / 3; }
.card-media-meme img { object-fit: contain; padding: 0.3rem; }
.credit-line { font-style: italic; }

/* --- Comments ------------------------------------------------------------ */
.comments { margin-block-start: 2.5rem; }
.comment-list { list-style: none; margin: 0 0 1.5rem; padding: 0; }
.comment {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  padding-block: 1rem;
  border-block-start: 1px solid var(--line);
}
.comment:first-child { border-block-start: none; }
.comment-body { flex: 1; min-width: 0; }
.comment-head {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-wrap: wrap;
  margin: 0 0 0.35rem;
}
.comment-head .name { font-weight: 600; color: var(--text-strong); }
.comment .prose { font-size: 0.94rem; }
.comment .prose p { margin-block: 0 0.5rem; }
.comment .prose p:last-child { margin-block-end: 0; }
/* Hidden comments are only rendered for moderators; make that obvious. */
.comment.is-removed { opacity: 0.6; }
.comment.is-removed .prose { text-decoration: line-through; }

.comment-form { display: flex; flex-direction: column; gap: 0.6rem; }
.comment-form textarea { width: 100%; }

/* Reply and Delete used to sit at opposite ends of the row via `push-end`.
   Now they are one group under the comment, because they belong to it rather
   than to the layout. */
.comment-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
                   margin-block-start: 0.5rem; }
.comment-actions:empty { display: none; }

/* --- Post formatting editor ------------------------------------------------
   The bar sits on top of the textarea and shares its border, so the two read as
   one control rather than a strip of buttons that happens to be nearby.
   ------------------------------------------------------------------------- */
.editor { display: flex; flex-direction: column; }
.editor-bar {
  display: flex; align-items: center; gap: 0.15rem; flex-wrap: wrap;
  padding: 0.3rem 0.4rem;
  border: 1px solid var(--line); border-block-end: none;
  border-start-start-radius: 8px; border-start-end-radius: 8px;
  background: var(--bg-elevated);
}
/* Nothing claimed by script yet: the bar would be an empty grey strip, so it
   collapses instead of announcing a feature that is not there. */
.editor-bar:not(:has(.editor-btn:not([hidden]))) { display: none; }
.editor-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.9rem; height: 1.9rem; padding: 0 0.4rem;
  border: 1px solid transparent; border-radius: 5px;
  background: transparent; color: var(--text-muted);
  font: inherit; font-size: 0.86rem; line-height: 1; cursor: pointer;
}
.editor-btn:hover { background: var(--bg-hover); color: var(--text-strong); }
.editor-btn .icon { width: 1.05rem; height: 1.05rem; }
.editor-sep {
  width: 1px; height: 1.1rem; margin-inline: 0.25rem; background: var(--line);
}
.editor-preview-toggle { font-size: 0.8rem; font-weight: 600; }
/* The bar is glued to the top of whatever the caller put inside. */
.editor > textarea,
.editor .editor-preview {
  border-start-start-radius: 0; border-start-end-radius: 0;
  margin-block-start: -1px;
}
.editor-preview {
  border: 1px solid var(--line); border-radius: 0 0 8px 8px;
  padding: 0.7rem 0.85rem; min-height: 6rem; background: var(--bg);
}
.editor-preview > :first-child { margin-block-start: 0; }
.editor-preview > :last-child { margin-block-end: 0; }
.editor-help { margin-block-start: 0.4rem; }

/* Character countdown. Sits under its field, aligned to the end so it reads as
   a gauge rather than as another line of copy — and mirrors in RTL for free. */
.char-count {
  margin: 0.2rem 0 0;
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;   /* stops the width jittering per keystroke */
  color: var(--text-muted);
  text-align: end;
}
.char-count.is-close { color: var(--warn); }
.char-count.is-out { color: var(--danger); font-weight: 600; }

.comment-sort-note { margin-block: -0.4rem 0.8rem; }

/* The header is sticky, so an anchored comment would otherwise land underneath
   it. Only matters on the no-script path — posting a reply, or upvoting without
   JavaScript, both come back to `#c<id>` — but landing half-hidden under the
   header is exactly the kind of "the page moved oddly" this was reported as. */
.comment, .comments { scroll-margin-block-start: 5.5rem; }
/* Mid-vote. Not `pointer-events: none` — the cursor should still say button. */
.comment-vote:disabled { opacity: 0.6; cursor: default; }
.edited-mark { font-style: italic; }

/* Upvote on a comment. Deliberately smaller and quieter than the post vote
   button: it ranks a remark, not the thing the page is about. */
.comment-vote {
  display: inline-flex; align-items: center; gap: 0.3rem;
  padding: 0.22rem 0.5rem; border-radius: 999px;
  border: 1px solid var(--line); background: transparent;
  color: var(--text-muted); font: inherit; font-size: 0.82rem; font-weight: 600;
  cursor: pointer; line-height: 1;
}
.comment-vote .icon { width: 0.95rem; height: 0.95rem; }
.comment-vote:hover { border-color: var(--accent); color: var(--accent-strong); text-decoration: none; }
.comment-vote.is-voted {
  border-color: var(--accent); background: var(--accent-soft); color: var(--accent-strong);
}
/* Signed out, or unconfirmed: the count still reads, the control still leads
   somewhere useful, and it does not pretend to be pressable. */
.comment-vote.is-locked { cursor: pointer; opacity: 0.75; }
/* Your own comment: the count still reads, nothing invites a press. */
.comment-vote.is-own { cursor: default; opacity: 0.55; border-style: dashed; }

/* The reply and edit forms live inside a `details`, so this styles the toggle
   to look like the quiet text button it is standing in for. */
.reply-box > summary,
.edit-box > summary {
  display: inline-flex; align-items: center; gap: 0.35rem;
  cursor: pointer; list-style: none;
  font-size: 0.85rem; font-weight: 600; color: var(--text-muted);
  padding: 0.25rem 0.5rem; border-radius: 6px;
}
.reply-box > summary::-webkit-details-marker,
.edit-box > summary::-webkit-details-marker { display: none; }
.reply-box > summary::marker,
.edit-box > summary::marker { content: ""; }
.reply-box > summary:hover,
.edit-box > summary:hover { color: var(--accent-strong); background: var(--bg-hover); }
.reply-box > summary .icon,
.edit-box > summary .icon { width: 1rem; height: 1rem; }
.reply-box[open] > summary,
.edit-box[open] > summary { color: var(--accent-strong); }
.comment-form-reply, .comment-form-edit { margin-block-start: 0.6rem; }
/* The edit box replaces what you are reading, so it gets the margin the action
   row would otherwise sit in. */
.edit-box { margin-block-start: 0.5rem; }

/* Replies: indented, and joined to the comment they answer by a rule down the
   side. `margin-inline-start` and `border-inline-start` rather than left, so
   Arabic indents the other way without a second rule. */
.comment-replies-row { list-style: none; }
.comment-replies {
  margin: 0 0 0 0; padding: 0;
  margin-inline-start: 2.6rem;
  border-inline-start: 2px solid var(--line);
  padding-inline-start: 0.9rem;
}
.comment-replies > .comment:first-child { border-block-start: none; padding-block-start: 0.25rem; }
@media (max-width: 34rem) {
  .comment-replies { margin-inline-start: 1rem; padding-inline-start: 0.7rem; }
}

/* --- Ratting spots ------------------------------------------------------- */
.map-filter { flex-wrap: wrap; margin-block-end: 1.5rem; }
/* Still clickable — it is honest about being empty, not disabled. */
.map-filter .filter.is-empty { opacity: 0.5; }
.map-filter .filter .mono { color: var(--text-muted); }

.shot-stack { display: flex; flex-direction: column; gap: 1rem; }
.shot-stack img { width: 100%; border-radius: 10px; border: 1px solid var(--line); }

.shot-picker {
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.85rem;
  margin-block-start: 0.75rem;
}
.shot-grid {
  display: grid;
  gap: 0.6rem;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
}
.shot-thumb { display: flex; flex-direction: column; gap: 0.3rem; }
.shot-thumb img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--line);
}
.shot-thumb .check { font-size: 0.78rem; }

/* --- Feedback ------------------------------------------------------------
   A pile of messages, not a table: each one is a paragraph somebody wrote,
   and a table row would cut it off at the first interesting sentence. */
.feedback-list { list-style: none; padding: 0; display: grid; gap: 0.9rem; }
.feedback-item { display: grid; gap: 0.75rem; }
/* Read ones stay in the list but stop competing for attention. */
.feedback-item.is-handled { opacity: 0.62; }
.feedback-item.is-handled:hover { opacity: 1; }
.feedback-head { gap: 0.75rem; flex-wrap: wrap; }
.feedback-body {
  margin: 0;
  /* The one place on the site that renders user text verbatim — newlines and
     all — because a bug report is usually a list of steps and flattening it
     into a paragraph loses the steps. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  line-height: 1.6;
}
.feedback-shots {
  display: grid;
  gap: 0.5rem;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}
.feedback-shots img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--line);
}
.feedback-actions { gap: 0.5rem; align-items: center; flex-wrap: wrap; }

/* The member's own copy on /feedback. Deliberately plainer than the admin
   view — it is a receipt, not a thread. */
.feedback-mine { display: grid; gap: 0.4rem; }
.feedback-mine p { margin: 0; overflow-wrap: anywhere; }

/* --- Redeem codes -------------------------------------------------------- */
.redeem-how { margin-block-start: 1.5rem; }
.redeem-columns {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  margin-block: 1rem;
}

.code-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  margin-block-start: 1rem;
}

.code-card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-panel);
  overflow: hidden;
}
.code-card.is-expired { opacity: 0.72; }

.code-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  padding: 0.75rem 1rem;
  background: var(--bg-inset);
  border-block-end: 1px solid var(--line);
}
.code-value {
  font-family: ui-monospace, "SFMono-Regular", "Consolas", monospace;
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--accent-strong);
  /* Selectable in one gesture: double-clicking grabs the whole code. */
  user-select: all;
  /* Most codes are one short word, but BSG also publishes dashed ones like
     W2TX-6NU9-UFK1-89RW-M96B. Those are wide enough to push the copy button
     out of a grid column, so they are allowed to break — `anywhere` rather
     than `break-word` because there are no spaces to break at. */
  overflow-wrap: anywhere;
  min-width: 0;
}
.code-card.is-expired .code-value { color: var(--text-muted); text-decoration: line-through; }
.code-copy { margin-inline-start: auto; }
.code-copy.is-copied { border-color: var(--ok); color: var(--ok); }

.code-body { padding: 1rem; display: flex; flex-direction: column; gap: 0.6rem; flex: 1; }
.code-title { margin: 0; font-size: 1rem; }
.code-body .prose { font-size: 0.92rem; }
.code-body .prose p { margin-block: 0 0.4rem; }
.code-figure { margin: 0; }
.code-figure img { width: 100%; border-radius: 8px; border: 1px solid var(--line); }

.code-foot {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  padding: 0.6rem 1rem;
  border-block-start: 1px solid var(--line);
}

/* --- Twitch box ----------------------------------------------------------
   Fixed to the trailing corner, which `inset-inline-end` mirrors to the left
   on the Arabic pages without a second rule. Deliberately small: it is a
   presence on the page, not the page. */
.twitch-box {
  position: fixed;
  inset-block-end: 1rem;
  inset-inline-end: 1rem;
  z-index: 60;
  width: 288px;
  max-width: calc(100vw - 2rem);
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--bg-panel);
  box-shadow: var(--shadow);
  overflow: hidden;
}

.twitch-bar {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.6rem;
  background: var(--bg-elevated);
  border-block-end: 1px solid var(--line);
  font-size: 0.82rem;
}
.twitch-dot {
  width: 0.5rem;
  height: 0.5rem;
  flex: none;
  border-radius: 50%;
  background: #e91916;
  box-shadow: 0 0 0 0 rgba(233, 25, 22, 0.55);
  animation: twitch-pulse 2.4s ease-out infinite;
}
@keyframes twitch-pulse {
  70%  { box-shadow: 0 0 0 6px rgba(233, 25, 22, 0); }
  100% { box-shadow: 0 0 0 0 rgba(233, 25, 22, 0); }
}
/* The global prefers-reduced-motion rule below already stops this pulsing. */

.twitch-name {
  font-weight: 600;
  color: var(--text-strong);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.twitch-toggle {
  margin-inline-start: auto;
  padding: 0.15rem 0.45rem;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  background: var(--bg-inset);
  color: var(--text-muted);
  font-size: 0.75rem;
  cursor: pointer;
}
.twitch-toggle:hover { color: var(--text-strong); border-color: var(--accent); }

.twitch-frame { aspect-ratio: 16 / 9; background: #000; }
.twitch-frame iframe { display: block; width: 100%; height: 100%; border: 0; }

/* Collapsed: the bar stays as a handle, the player is unloaded by the script. */
.twitch-box.is-collapsed .twitch-frame { display: none; }

/* --- Responsive ---------------------------------------------------------- */
@media (max-width: 900px) {
  /* The section row collapses behind the toggle; the account controls stay in
     row one, so language, theme and sign-in never go behind a tap. */
  .nav-toggle { display: flex; }
  .primary-nav { display: none; padding-block-end: 0.5rem; }
  .primary-nav.is-open { display: block; }
  .nav-scroll { overflow-x: visible; }
  .nav-list { flex-direction: column; gap: 0.1rem; }
  .nav-link {
    padding: 0.65rem 0.75rem;
    border-block-end: none;
    border-inline-start: 2px solid transparent;
    border-radius: 0 6px 6px 0;
  }
  .nav-link.is-active {
    border-inline-start-color: var(--accent);
    background: var(--accent-soft);
  }
  .account-name { display: none; }   /* the avatar is enough at this width */
  .chip-lang span { display: none; }
}

/* Below this the top row runs out of room, so it sheds weight in order of what
   matters least: first the brand's first word, then the second auth button —
   registration is still one tap away from the sign-in page.

   Dropping "Critical" and "كريتيكال" together keeps the lockup bilingual on
   the way down — TARKOV ✕ تاركوف still reads as both languages. Cutting one
   script to save room would quietly demote it. */
@media (max-width: 640px) {
  .header-top { gap: 0.5rem; }
  .brand-text { font-size: 0.95rem; }
  .brand-mark { width: 1.45rem; height: 1.45rem; }
  .brand-word { display: none; }
  .nav-actions { gap: 0.35rem; }
  .chip { padding: 0.3rem 0.5rem; }
}

@media (max-width: 430px) {
  .btn-join { display: none; }
}

/* Last resort: the mark alone. It carries no script, so this is still the same
   logo rather than a language having been picked for the visitor. */
@media (max-width: 380px) {
  .brand-text { display: none; }
  .brand-mark { width: 1.7rem; height: 1.7rem; }
}

@media (max-width: 640px) {
  body { font-size: 15.5px; }
  .wrap, .wrap-narrow { width: calc(100% - 1.5rem); }
  .panel { padding: 1rem; }
  .card-foot { padding-inline: 1rem; }
  .toolbar { padding: 0.65rem 0.75rem; }
  .admin-table { font-size: 0.84rem; }
  .admin-table th, .admin-table td { padding: 0.5rem 0.4rem; }
  .admin-table .actions { justify-content: flex-start; }
  .form-actions .btn { flex: 1 1 auto; }
  .stat-row { grid-template-columns: repeat(2, 1fr); }
  /* Smaller and tucked into the corner so it never covers a form control. */
  .twitch-box { width: 208px; inset-block-end: 0.6rem; inset-inline-end: 0.6rem; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .card:hover { transform: none; }
}

@media print {
  .site-header, .site-footer, .toolbar, .guide-rail, .flash-stack,
  .twitch-box { display: none; }
  body { background: #fff; color: #000; }
}

/* --------------------------------------------------------------------------
   Role insignia
   Everyone holds exactly one role, so a name carries one mark. The shapes are
   distinguishable at 14px on their own; colour is a second signal, never the
   only one.
   -------------------------------------------------------------------------- */
.utag {
  display: inline-flex;
  align-items: center;
  gap: 0.25em;
  margin-inline-start: 0.4em;
  padding: 0.1em 0.5em 0.1em 0.35em;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 0.68em;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  vertical-align: middle;
  white-space: nowrap;
  line-height: 1.5;
}
.role-mark {
  inline-size: 1.15em;
  block-size: 1.15em;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* The founder star and the PMC tags read better filled than outlined. */
.utag-founder .role-mark path { fill: currentColor; stroke-width: 1.2; }
.utag-pmc .role-mark path { fill: color-mix(in srgb, currentColor 22%, transparent); }

.utag-admin {
  background: var(--warn-soft);
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 40%, transparent);
}
.utag-founder {
  background: var(--accent-soft);
  color: var(--accent-strong);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.utag-streamer {
  background: var(--info-soft);
  color: var(--info);
  border-color: color-mix(in srgb, var(--info) 40%, transparent);
}
/* The default role is the quiet one — most people have it, so it must not
   shout on every card. */
/* The honour tag an admin pins on a profile: gold, beside the role. */
.utag-tag {
  color: #f5c542;
  border-color: color-mix(in srgb, #f5c542 55%, transparent);
  background: color-mix(in srgb, #f5c542 12%, transparent);
  text-transform: none;
  letter-spacing: 0;
}
.utag-pmc {
  background: transparent;
  color: var(--text-muted);
  border-color: var(--line-strong);
}

/* On a card byline the label is noise; the mark alone carries it. */
.card-foot .utag,
.comment-head .utag { padding-inline: 0.3em; }

/* Every role is shown, PMC included.
   It used to be hidden here on the grounds that the default role is on most
   accounts and should not shout from every card. The effect was the opposite
   of quiet: admins were the only people with a badge, so the byline read as a
   list of staff and everybody else looked like they had no standing at all.
   Showing it costs nothing — `.utag-pmc` is already the muted variant, with no
   fill and a plain border — and it means the badge answers "who is this" the
   same way for everyone. */
@media (max-width: 30rem) {
  .utag-text { display: none; }
  .utag { padding-inline: 0.3em; }
}

/* A bigger version for the profile header, where the role is the point. */
.role-hero {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.75rem;
  border-radius: 999px;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.role-hero .role-mark { inline-size: 1.3rem; block-size: 1.3rem; }

/* The current role in the admin users table. Between the tiny inline `utag`
   and the profile's `role-hero`: it has to out-weigh the row of change buttons
   under it, which it did not when it was the same size as them. */
.role-current {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-block-end: 0.5rem;
  padding: 0.28rem 0.65rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
[dir="rtl"] .role-current { letter-spacing: 0; }
.role-current .role-mark { inline-size: 1.1rem; block-size: 1.1rem; }
/* This used to force `utag-pmc` back into view, because card footers hid it.
   They no longer do — every role is shown everywhere — so the override has
   nothing left to override and is kept only to state that plainly. */
.role-current.utag-pmc { display: inline-flex; }

/* The admin picker. */
.role-choices { display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; }
.role-choice { display: inline-flex; }
.role-choice .btn { gap: 0.3rem; }
.role-choice .role-mark { inline-size: 1rem; block-size: 1rem; }

/* --------------------------------------------------------------------------
   Streamer card on a profile
   -------------------------------------------------------------------------- */
.streamer-card {
  display: flex;
  align-items: center;
  gap: 1rem;
  text-decoration: none;
  color: inherit;
  border-inline-start: 3px solid var(--info);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.streamer-card:hover { background: var(--bg-elevated); border-inline-start-color: var(--accent); }
.streamer-glyph {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 10px;
  background: var(--info-soft);
  color: var(--info);
}
.streamer-glyph .icon { width: 1.5rem; height: 1.5rem; }
.streamer-body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.streamer-name { font-size: 1.05rem; word-break: break-all; }

@media (max-width: 34rem) {
  .streamer-card { flex-wrap: wrap; }
  .streamer-card .btn { inline-size: 100%; }
}

/* --------------------------------------------------------------------------
   Stats bar chart
   Pure CSS: one number per day, has to work without JavaScript, and the site
   ships no third-party scripts. Bars are sized with an inline height so the
   markup carries the data rather than a canvas hiding it.
   -------------------------------------------------------------------------- */
.bar-chart {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  block-size: 160px;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
}
.bar-col {
  flex: 1 1 0;
  min-inline-size: 4px;
  block-size: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.bar-stack {
  position: relative;
  inline-size: 100%;
  block-size: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.bar {
  position: absolute;
  inset-block-end: 0;
  inline-size: 100%;
  border-radius: 2px 2px 0 0;
  min-block-size: 1px;
}
/* Views sit behind visitors, so the pair reads as one bar with a darker core
   rather than two competing series. */
.bar-views    { background: color-mix(in srgb, var(--accent) 35%, transparent); }
.bar-visitors { background: var(--accent); inline-size: 55%; inset-inline-start: 22.5%; }
.bar-stack:hover .bar-views { background: color-mix(in srgb, var(--accent) 55%, transparent); }

.legend-key {
  display: inline-block;
  inline-size: 0.7rem;
  block-size: 0.7rem;
  border-radius: 2px;
  vertical-align: middle;
  margin-inline-end: 0.3rem;
}
.legend-views    { background: color-mix(in srgb, var(--accent) 35%, transparent); }
.legend-visitors { background: var(--accent); }

ul.plain { list-style: none; }

/* --------------------------------------------------------------------------
   Review queue and the "waiting for review" notice
   -------------------------------------------------------------------------- */
/* A rejection is a different message than "we have not looked yet", so it does
   not wear the same amber. */
.review-notice-rejected {
  border-color: color-mix(in srgb, var(--danger) 40%, transparent);
  border-inline-start-color: var(--danger);
  align-items: center;
}
.review-notice-rejected .icon { color: var(--danger); }
.rejection-reason {
  padding: 0.5rem 0.7rem;
  border-radius: 6px;
  background: var(--bg-inset);
}
@media (max-width: 40rem) {
  .review-notice-rejected { flex-wrap: wrap; }
}

.review-notice {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  padding: 0.85rem 1rem;
  margin-block-end: 1.25rem;
  border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent);
  border-inline-start: 3px solid var(--warn);
  border-radius: 10px;
  background: var(--warn-soft);
}
.review-notice .icon {
  flex: 0 0 auto;
  inline-size: 1.25rem;
  block-size: 1.25rem;
  color: var(--warn);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
}

.review-list { display: grid; gap: 1rem; }
.review-item {
  display: grid;
  grid-template-columns: 12rem 1fr 11rem;
  gap: 1.25rem;
  align-items: start;
}
.review-media img {
  inline-size: 100%;
  block-size: 8rem;
  object-fit: cover;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg-inset);
}
.review-nomedia {
  display: grid;
  place-items: center;
  inline-size: 100%;
  block-size: 8rem;
  border: 1px dashed var(--line-strong);
  border-radius: 8px;
  color: var(--text-muted);
  font-size: 0.8rem;
}
.review-body { min-width: 0; }
/* Scrolls rather than clamping to three lines. The queue is where you decide
   whether something publishes, and a decision made on the first three lines of
   a write-up is not really a decision — the part worth catching is as likely to
   be at the bottom. Capped so one long post cannot push the Approve button off
   the screen. */
.review-blurb {
  max-block-size: 16rem;
  overflow-y: auto;
  font-size: 0.9rem;
  padding-inline-end: 0.4rem;
}
.review-blurb > :first-child { margin-block-start: 0; }
.review-blurb > :last-child { margin-block-end: 0; }
.review-blurb h2, .review-blurb h3 { font-size: 1rem; margin-block: 0.6rem 0.3rem; }
.review-actions { display: grid; gap: 0.5rem; }

@media (max-width: 52rem) {
  .review-item { grid-template-columns: 1fr; }
  .review-media img, .review-nomedia { block-size: 11rem; }
}

/* The one nav item with work waiting on it. */
.nav-count {
  display: inline-block;
  min-inline-size: 1.4em;
  margin-inline-start: 0.4em;
  padding: 0.05em 0.4em;
  border-radius: 999px;
  background: var(--warn);
  color: var(--accent-ink);
  font-size: 0.72em;
  font-weight: 700;
  text-align: center;
}

/* --------------------------------------------------------------------------
   Role picker on the create-user form
   Radio buttons rather than a <select>, because the choice carries a mark and
   a sentence of consequence — neither of which fits in a dropdown.
   -------------------------------------------------------------------------- */
.role-picker { display: grid; gap: 0.5rem; }
.role-option {
  display: grid;
  grid-template-columns: auto auto 1fr;
  align-items: center;
  gap: 0.6rem;
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
}
.role-option:hover { background: var(--bg-elevated); }
.role-option:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.role-option:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.role-option .utag { margin-inline-start: 0; }
.role-option-blurb { color: var(--text-muted); font-size: 0.85rem; }

@media (max-width: 36rem) {
  .role-option { grid-template-columns: auto 1fr; }
  .role-option-blurb { grid-column: 1 / -1; }
}

/* --------------------------------------------------------------------------
   Profile: posts by section
   Every section is listed whether or not the person has posted in it, so a
   profile reads as "here is everything they do" rather than as a builds page
   with occasional extras. Sections with something in them are links to the
   heading below; empty ones are inert and dimmed.
   -------------------------------------------------------------------------- */
.post-tally {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-block: 1.5rem 1.75rem;
}
.tally {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  padding: 0.5rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  text-decoration: none;
  color: inherit;
  background: var(--bg-panel);
}
.tally-count { font-size: 1.15rem; font-weight: 700; line-height: 1; }
.tally-label { font-size: 0.85rem; color: var(--text-muted); }

.tally-has { border-color: var(--line-strong); }
.tally-has .tally-count { color: var(--accent-strong); }
.tally-has:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
}
/* Dimmed rather than hidden: a zero is information too. */
.tally-empty { opacity: 0.5; background: transparent; }

@media (max-width: 30rem) {
  .post-tally { gap: 0.35rem; }
  .tally { padding: 0.4rem 0.6rem; }
  .tally-label { font-size: 0.78rem; }
}

/* --------------------------------------------------------------------------
   "Awaiting review" ribbon on a card
   Only ever rendered for a post the viewer is entitled to see — their own, or
   an admin opening it from the queue — so it marks rather than reveals.
   -------------------------------------------------------------------------- */
.card { position: relative; }
.card-pending {
  position: absolute;
  inset-block-start: 0.5rem;
  inset-inline-start: 0.5rem;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.2rem 0.55rem;
  border: 1px solid color-mix(in srgb, var(--warn) 55%, transparent);
  border-radius: 999px;
  background: var(--bg);
  color: var(--warn);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;
}
.card-pending .icon {
  inline-size: 0.85rem;
  block-size: 0.85rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
}
/* A card waiting on somebody is dimmed, so a listing reads at a glance as
   "these are live, that one is not". */
.card:has(.card-pending) .card-media { opacity: 0.55; }

/* --------------------------------------------------------------------------
   Grouped navigation
   Nine sections became four entries: two links and two menus. Built on
   <details>, so it opens with no JavaScript at all — the script only closes it
   on an outside click or Escape, which <details> cannot do alone.
   -------------------------------------------------------------------------- */
.nav-menu { position: relative; }

/* The arrow that says "this opens", and no default triangle. */
.nav-summary {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.nav-summary::-webkit-details-marker { display: none; }
.nav-summary::marker { content: ""; }

.nav-caret {
  inline-size: 0.9rem;
  block-size: 0.9rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.15s ease;
}
.nav-menu[open] .nav-caret { transform: rotate(180deg); }

.nav-submenu {
  position: absolute;
  inset-block-start: calc(100% + 0.4rem);
  /* Logical, so the panel hangs from the correct edge under RTL. */
  inset-inline-start: 0;
  z-index: 60;
  min-inline-size: 14rem;
  margin: 0;
  padding: 0.35rem;
  list-style: none;
  background: var(--bg-elevated);
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.45);
}
.nav-submenu a {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.5rem 0.6rem;
  border-radius: 7px;
  color: var(--text);
  text-decoration: none;
  font-size: 0.92rem;
  white-space: nowrap;
}
.nav-submenu a:hover,
.nav-submenu a:focus-visible { background: var(--bg-panel); color: var(--text-strong); }
.nav-submenu a.is-active { color: var(--accent-strong); background: var(--accent-soft); }
.nav-submenu .icon {
  inline-size: 1.05rem;
  block-size: 1.05rem;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  color: var(--text-muted);
}
.nav-submenu a:hover .icon,
.nav-submenu a.is-active .icon { color: inherit; }

/* The nav row scrolls sideways on narrow screens, and `overflow` on the
   scroller would clip an absolutely-positioned panel. Once a menu is open the
   row stops clipping, so the panel can hang below it. */
.nav-scroll:has(.nav-menu[open]) { overflow: visible; }

@media (max-width: 40rem) {
  /* No room to hang a panel beside a scrolling row — open it inline instead,
     which also removes any chance of it running off the edge. */
  .nav-submenu {
    position: static;
    min-inline-size: 0;
    box-shadow: none;
    border: 0;
    padding-inline: 0;
    background: transparent;
  }
  .nav-menu[open] { inline-size: 100%; }
}

/* --------------------------------------------------------------------------
   Google sign-in
   -------------------------------------------------------------------------- */
.oauth-split {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-block: 1.1rem;
  color: var(--text-muted);
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.oauth-split::before,
.oauth-split::after {
  content: "";
  flex: 1;
  block-size: 1px;
  background: var(--line);
}

.btn-google {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
}
/* Google's own mark, so it stays recognisable rather than following the site
   palette — the whole value of the button is that people spot it instantly. */
.btn-google .icon {
  inline-size: 1.1rem;
  block-size: 1.1rem;
  fill: currentColor;
  stroke: none;
  flex: 0 0 auto;
}

/* --------------------------------------------------------------------------
   Development-only hint
   Rendered only when CT_ENV is not production. Styled to read as scaffolding
   rather than as site content, so it is never mistaken for a real message.
   -------------------------------------------------------------------------- */
.dev-hint {
  padding: 0.8rem 1rem;
  border: 1px dashed var(--line-strong);
  border-radius: 10px;
  background: var(--bg-inset);
  color: var(--text-muted);
  font-size: 0.85rem;
  line-height: 1.7;
}
.dev-hint strong { color: var(--text); }
.dev-hint .mono { color: var(--accent-strong); }
.dev-hint .tiny { font-size: 0.75rem; opacity: 0.75; }

/* A recipe's item list. Plain bullets inside a panel — the sacrifice is a list
   of things, and anything more decorative competes with the screenshots. */
.plain-list          { margin: 0; padding-inline-start: 1.1rem; font-size: 0.92rem; }
.plain-list li       { margin-block-end: 0.2rem; }
.plain-list li:last-child { margin-block-end: 0; }

/* ---------------------------------------------------------------------------
   A community run inside the reference list.

   Same shell as an editorial entry so the page reads as one list, with three
   additions an entry has no use for: a byline, the before/after evidence, and
   a footer carrying the upvote and the comment count.

   The whole block is a link. `position: relative` is what the stretched
   `.card-link::after` resolves against — without it the overlay would size
   itself to the page. The byline and the footer are lifted above that overlay
   so upvoting, opening the thread and visiting the author still do their own
   thing.
   --------------------------------------------------------------------------- */
.entry-recipe                 { position: relative; }
.entry-recipe .entry-head h3 a { color: inherit; text-decoration: none; }
.entry-recipe:hover .entry-head h3 a { color: var(--accent-strong); }
.entry-by,
.entry-foot                   { position: relative; z-index: 2; }
.entry-by {
  display: flex; align-items: center; gap: 0.4rem;
  margin-block: 0.5rem 0.2rem;
}
.entry-foot {
  display: flex; align-items: center; gap: 0.9rem;
  margin-block-start: 0.8rem; padding-block-start: 0.7rem;
  border-block-start: 1px solid var(--line);
}

/* Two shots side by side, cropped to a strip. The pair is the evidence, so
   neither is allowed to dominate the row it sits in. */
.recipe-thumbs        { display: flex; gap: 0.5rem; margin-block: 0.7rem; }
.recipe-thumbs img {
  flex: 1 1 0; min-width: 0; height: 120px; object-fit: cover;
  border-radius: 8px; border: 1px solid var(--line);
}
@media (max-width: 30rem) {
  .recipe-thumbs img  { height: 88px; }
}

/* ---------------------------------------------------------------------------
   Events.

   Cards that are not links. No hover lift, no pointer cursor, no stretched
   overlay — every affordance the post cards use to say "open me" is absent
   here on purpose, because there is nothing to open.

   A finished event is dimmed rather than hidden. Somebody who half-remembers
   an event needs to be able to confirm it happened and ended.
   --------------------------------------------------------------------------- */
.event-list          { display: grid; gap: 1rem; }
.event-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg-panel);
  overflow: hidden;
}
@media (min-width: 46rem) {
  .event-card:has(.event-media) { grid-template-columns: 15rem minmax(0, 1fr); }
}
.event-media         { background: var(--bg-inset); }
.event-media img     { display: block; width: 100%; height: 100%;
                       min-height: 9rem; object-fit: cover; }
.event-body          { padding: 1.1rem 1.25rem; }
.event-head          { display: flex; align-items: flex-start; gap: 0.75rem;
                       justify-content: space-between; flex-wrap: wrap; }
.event-title         { margin: 0; font-size: 1.15rem; }
.event-state         { flex: none; }
.event-when {
  display: flex; align-items: center; gap: 0.4rem;
  margin-block: 0.5rem 0.75rem;
  font-size: 0.9rem; color: var(--text-muted);
}
.event-when .icon    { width: 1rem; height: 1rem; flex: none; }

/* Dimmed, not greyscale: the banner should still read as the event it was. */
.event-card.is-ended            { opacity: 0.62; }
.event-card.is-ended .event-title { font-weight: 500; }

/* The one link an event card carries — outward, at whoever announced it. */
.event-source        { margin-block: 0.9rem 0; font-size: 0.85rem; }
.event-source a      { display: inline-flex; align-items: center; gap: 0.3rem;
                       color: var(--text-muted); }
.event-source a:hover { color: var(--accent-strong); }
.event-source .icon  { width: 0.85rem; height: 0.85rem; }

/* --------------------------------------------------------------------------
   Language menu
   Was a single chip toggling between two locales. With four there is no "the
   other", so it opens a panel. A <details> rather than script, to match the
   nav menus and keep the header working with JavaScript off.
   -------------------------------------------------------------------------- */
.lang-menu { position: relative; }
.lang-menu > summary { list-style: none; cursor: pointer; user-select: none; }
.lang-menu > summary::-webkit-details-marker { display: none; }
.lang-menu > summary::marker { content: ""; }

.lang-menu-panel {
  position: absolute;
  inset-block-start: calc(100% + 0.4rem);
  /* Logical, so the panel hangs from the correct edge under RTL. */
  inset-inline-end: 0;
  z-index: 60;
  min-inline-size: 12rem;
  padding: 0.35rem;
  background: var(--bg-elevated);
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.45);
}
.lang-menu-panel a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.5rem 0.6rem;
  border-radius: 7px;
  color: var(--text);
  text-decoration: none;
}
.lang-menu-panel a:hover,
.lang-menu-panel a:focus-visible { background: var(--accent-soft); }

/* Quiet by design. It marks a translation as unreviewed without shouting that
   the reader has picked the second-class option. */
.lang-beta {
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.05rem 0.4rem;
}
/* Scoped to the menu: `.lang-note` is also used on member posts to say which
   language they are written in, and that one must not be 15rem wide. */
.lang-menu-panel .lang-note {
  /* Back to normal text flow. The base .lang-note is a flex row because the
     notice on a member's post pairs an icon with a line of text; here there is
     no icon, and leaving it flex makes the sentence and the link separate flex
     items that will not wrap into each other. */
  display: block;
  margin: 0.35rem 0.25rem 0.15rem;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--text-muted);
  max-inline-size: 15rem;
}
/* Every other anchor in this panel is a language row — a padded flex line with
   the name at one end and the beta pill at the other. This one is a word in
   the middle of a sentence, so it has to opt out of all of that. */
.lang-menu-panel .lang-note a {
  display: inline;
  padding: 0;
  border-radius: 0;
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.lang-menu-panel .lang-note a:hover,
.lang-menu-panel .lang-note a:focus-visible {
  background: none;
  color: var(--accent-strong);
}

/* Which languages a record has been translated into, for the admin lists.
   Replaced a per-language table column, which stopped fitting at four. */
.lang-dots { display: inline-flex; gap: 0.25rem; }
.lang-dot {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 0.08rem 0.32rem;
  border-radius: 4px;
  border: 1px solid var(--line);
  color: var(--text-muted);
  opacity: 0.55;
}
.lang-dot.is-on {
  background: var(--accent-soft);
  border-color: transparent;
  color: var(--accent-strong);
  opacity: 1;
}

/* Profile picture, clickable to enlarge. `no-save` pairs with the JS that
   blocks the context menu and dragging — a deterrent, not protection, since
   the file is a public URL either way. */
.avatar-zoom { display: inline-block; border-radius: 50%; line-height: 0; cursor: zoom-in; }
.avatar-zoom:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.avatar-zoom img { transition: filter 0.15s ease; }
.avatar-zoom:hover img { filter: brightness(1.08); }

.no-save,
.no-save img,
img.no-save {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;   /* stops the iOS long-press save sheet */
}

/* --- /admin/links: the picture on the standalone links page ----------------
   Shown at the size the page itself renders it, so what an admin approves here
   is what a visitor sees rather than a thumbnail that flatters it. */
.links-avatar-row {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  flex-wrap: wrap;
}
.links-avatar-preview {
  flex: none;
  inline-size: 7rem;
  block-size: 7rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  overflow: hidden;
  border: 1px solid var(--line-strong);
  background: var(--bg-inset);
}
.links-avatar-preview img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.links-avatar-letter {
  font-family: ui-monospace, "SFMono-Regular", "Consolas", monospace;
  font-size: 3rem;
  font-weight: 700;
  color: var(--accent);
}
/* Takes the rest of the row, but not below a width where the sentences turn
   into one word per line. */
.links-avatar-facts { flex: 1 1 16rem; }
.links-avatar-facts p { margin-block: 0 0.5rem; }
.links-avatar-facts p:last-child { margin-block-end: 0; }

/* --- Cultist Circle: what you give, what you get --------------------------
   Two labelled columns with an arrow between them, collapsing to stacked rows
   when there is not width for both. The arrow is decorative and goes with the
   columns — once they stack, a rightward arrow between them is pointing the
   wrong way. `flex-direction` handles RTL on its own here because the columns
   are laid out in document order. */
.swap {
  display: flex;
  align-items: flex-start;
  gap: 0.9rem;
  margin-block: 0.7rem;
  flex-wrap: wrap;
}
.swap-arrow {
  flex: none;
  align-self: center;
  color: var(--text-muted);
  /* Logical, so it points from the giving side to the getting side under RTL
     as well — the icon itself is mirrored by the transform. */
  transform: scaleX(1);
}
[dir="rtl"] .swap-arrow { transform: scaleX(-1); }
.swap-arrow .icon { inline-size: 1.4rem; block-size: 1.4rem; }

.parts { flex: 1 1 12rem; min-inline-size: 0; }
.parts-label {
  margin: 0 0 0.35rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 0.68rem;
}
.part-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.part {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.25rem 0.55rem 0.25rem 0.25rem;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg-inset);
  color: var(--text);
  font-size: 0.82rem;
  max-inline-size: 100%;
}
.part:hover { border-color: var(--accent); color: var(--accent-strong); text-decoration: none; }

.part-icon {
  position: relative;
  flex: none;
  inline-size: 2.1rem;
  block-size: 2.1rem;
  display: grid;
  place-items: center;
  border-radius: 6px;
  /* Tarkov's own item-slot colour, so a transparent icon has something to sit
     on rather than floating on the card. */
  background: #22211f;
  overflow: hidden;
}
.part-icon img { inline-size: 100%; block-size: 100%; object-fit: contain; display: block; }
/* Shown when tarkov.dev has no row for the item — a quest item that never
   reached the flea. Better than an empty square, which reads as a broken
   image rather than a deliberate gap. */
.part-noicon { color: var(--text-muted); font-size: 0.9rem; }

.part-qty {
  position: absolute;
  inset-block-end: 0;
  inset-inline-end: 0;
  padding: 0 0.2rem;
  border-start-start-radius: 4px;
  background: rgba(0, 0, 0, 0.72);
  color: #fff;
  font-size: 0.62rem;
  font-weight: 700;
  line-height: 1.4;
}
/* The name can be long — "Maska-1SCh bulletproof helmet (Killa Edition)" — and
   a tile that grows to fit it pushes the row into one item per line. */
.part-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-inline-size: 11rem;
}

/* --------------------------------------------------------------------------
   Quest tracking: the Kappa page and a signed-in player's checklist on it.

   Logical properties throughout (inline-start, margin-inline) rather than
   left/right, so Arabic gets a correct mirror without a single [dir="rtl"]
   override. The one thing that must NOT mirror is a task name: those stay
   Latin-script and carry dir="ltr" in the markup.
   -------------------------------------------------------------------------- */

/* Shared figure strip — the same four numbers on /quests and on a profile,
   so they are one component rather than two that drift apart. */
.quest-figures {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-block: 0.9rem;
}
.quest-figures .figure {
  flex: 1 1 7rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-panel);
}
.figure-n {
  display: block;
  font-size: 1.6rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--accent-strong);
  font-variant-numeric: tabular-nums;
}
/* The denominator is context, not the number itself. */
.figure-n .of { font-size: 0.95rem; font-weight: 600; color: var(--text-muted); }
.figure-l { display: block; margin-block-start: 0.2rem; font-size: 0.82rem; color: var(--text-muted); }
.figure-l a { color: inherit; }

.profile-quests { margin-block: 1.4rem 0.5rem; }

.quest-summary .row-gap {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-block-start: 0.9rem;
}

/* --- task lists ---------------------------------------------------------- */
.quest-trader { margin-block-start: 1.6rem; }
.quest-trader h2 { display: flex; align-items: baseline; gap: 0.6rem; font-size: 1.05rem; }
.quest-trader h2 .muted { font-size: 0.85rem; font-variant-numeric: tabular-nums; }

.quest-list, .kappa-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
}
.quest-item, .kappa-task {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.8rem;
  background: var(--bg-panel);
  border-block-start: 1px solid var(--line);
}
.quest-list > :first-child, .kappa-list > :first-child { border-block-start: 0; }
.quest-item a, .kappa-task a { color: var(--text); text-decoration: none; }
.quest-item a:hover, .kappa-task a:hover { color: var(--accent-strong); text-decoration: underline; }

/* A done row is dimmed, not struck through: the point of the list is what is
   left, and strikethrough on 188 finished tasks is unreadable noise. */
.quest-item.is-done, .kappa-task.is-done { background: var(--bg-inset); }
.quest-item.is-done a, .kappa-task.is-done a { color: var(--text-muted); }

/* Fixed width so the names line up whether or not a row has a tick. */
.quest-tick, .kappa-tick {
  flex: 0 0 1.1rem;
  color: var(--ok);
  font-weight: 700;
  text-align: center;
}

.kappa-task-main { flex: 1 1 auto; min-inline-size: 0; }
.kappa-meta { display: block; font-size: 0.8rem; color: var(--text-muted); }
.kappa-state { flex: 0 0 auto; font-size: 0.8rem; color: var(--text-muted); }
.kappa-task.is-done .kappa-state { color: var(--ok); }

.kappa-block { margin-block-start: 1.8rem; }

/* Numbered chain. The counter sits before the tick so a reader can say "I am
   on step 9" without counting rows. */
.kappa-list { counter-reset: kappa-step; }
.kappa-list > .kappa-task { counter-increment: kappa-step; }
.kappa-list > .kappa-task::before {
  content: counter(kappa-step);
  flex: 0 0 1.4rem;
  font-size: 0.78rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  text-align: end;
}

/* Visually hidden but read aloud — used for headings that would be redundant
   on screen next to the figures they label. */
.sr-only {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Game-mode switcher. Only rendered when a player has linked more than one,
   so it never appears as a single dead tab. */
.mode-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-block: 0.9rem 0.6rem;
}
.mode-tab {
  padding: 0.35rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 0.85rem;
  color: var(--text-muted);
  text-decoration: none;
  background: var(--bg-panel);
}
.mode-tab:hover { border-color: var(--line-strong); color: var(--text); }
.mode-tab.is-on {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-weight: 600;
}
/* Collapsed by default: adding a second mode is a rare, deliberate act, and an
   always-open token form on the page you visit to read your progress is noise. */
.add-mode { margin-block: 1.2rem; }
.add-mode > summary { cursor: pointer; font-weight: 600; }
.add-mode[open] > summary { margin-block-end: 0.6rem; }

/* --------------------------------------------------------------------------
   Chat mini-games. Built to be legible on a stream overlay at a distance, not
   at reading distance: big clock, big item name, high contrast on the winner.
   -------------------------------------------------------------------------- */
.mg-settings .mg-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.8rem;
  margin-block: 0.6rem;
}
.row-inline { display: flex; align-items: center; gap: 0.5rem; }

.mg-game-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
.mg-game {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.9rem 1rem;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg-panel);
}
.mg-game-body { flex: 1 1 auto; min-inline-size: 0; }
.mg-game-body h3 { margin: 0 0 0.2rem; font-size: 1rem; }
.mg-game-body p { margin: 0; }
/* Dimmed rather than hidden: these are the same engine with a different
   question list, and listing them is how the next one gets asked for. */
.mg-game.is-soon { opacity: 0.6; }

/* --- the board ----------------------------------------------------------- */
.mg-head {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: flex-start;
  justify-content: space-between;
  margin-block-end: 1rem;
}
.mg-head h1 { margin: 0 0 0.25rem; }
.mg-controls { display: flex; gap: 0.5rem; flex-wrap: wrap; }

.mg-status { margin: 0; font-size: 0.9rem; color: var(--text-muted); }
.mg-status.mg-ok { color: var(--ok); }
.mg-status.mg-warn { color: var(--warn); }
.mg-status.mg-error { color: var(--danger); }

.mg-stage {
  display: flex;
  gap: 1.5rem;
  align-items: center;
  padding: 1.5rem;
  border: 1px solid var(--line-strong);
  border-radius: 14px;
  background: var(--bg-elevated);
}
.mg-question { flex: 1 1 auto; min-inline-size: 0; text-align: center; }
.mg-round { margin: 0 0 0.5rem; font-size: 0.8rem; color: var(--text-muted);
            text-transform: uppercase; letter-spacing: 0.08em; }
.mg-icon { max-inline-size: 8rem; max-block-size: 8rem; object-fit: contain; }
.mg-item { margin: 0.5rem 0 0; font-size: 1.5rem; line-height: 1.2; }

/* Both empty until the round ends, so they must not reserve space and leave a
   gap in the middle of the board while the clock is running. */
.mg-answer:empty, .mg-winner:empty { display: none; }
.mg-answer {
  margin: 0.6rem 0 0;
  font-size: 1.6rem;
  font-weight: 700;
  color: var(--accent-strong);
  font-variant-numeric: tabular-nums;
}
.mg-winner { margin: 0.4rem 0 0; font-size: 1.1rem; }
.mg-winner-name { color: var(--ok); }
.mg-winner-guess { color: var(--text-muted); }
.mg-nobody { color: var(--text-muted); font-style: italic; }

.mg-clockbox {
  flex: 0 0 auto;
  text-align: center;
  min-inline-size: 6rem;
}
.mg-clock {
  display: block;
  font-size: 3.5rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--accent-strong);
}
/* Answers are in; the clock stops shouting for them. */
.is-revealed .mg-clock { color: var(--text-muted); }
.mg-clock-label { font-size: 0.75rem; color: var(--text-muted);
                  text-transform: uppercase; letter-spacing: 0.08em; }

.mg-panels {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1rem;
  margin-block: 1.2rem;
}
.mg-panel h3 { font-size: 0.85rem; text-transform: uppercase;
               letter-spacing: 0.06em; color: var(--text-muted);
               margin: 0 0 0.5rem; }
.mg-list { list-style: none; margin: 0; padding: 0; }
.mg-list li {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.35rem 0.6rem;
  border-block-start: 1px solid var(--line);
  background: var(--bg-panel);
}
.mg-list > :first-child { border-block-start: 0; }
/* Chat display names are arbitrary and can be long; the number must not be
   pushed off the row by one of them. */
.mg-who { flex: 1 1 auto; min-inline-size: 0; overflow: hidden;
          text-overflow: ellipsis; white-space: nowrap; }
.mg-val { flex: 0 0 auto; font-variant-numeric: tabular-nums;
          color: var(--accent-strong); font-weight: 600; }
.mg-rank { flex: 0 0 1.2rem; color: var(--text-muted);
           font-variant-numeric: tabular-nums; }

@media (max-width: 40rem) {
  .mg-stage { flex-direction: column; }
  .mg-clock { font-size: 2.5rem; }
}

/* Mini-game leaderboard. A table because it is one: seven columns of numbers
   that a streamer scans down, not cards. */
.table-scroll { overflow-x: auto; }
.lb-table { inline-size: 100%; border-collapse: collapse; font-size: 0.92rem; }
.lb-table th, .lb-table td {
  padding: 0.5rem 0.7rem;
  border-block-end: 1px solid var(--line);
  text-align: start;
  white-space: nowrap;
}
.lb-table th {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}
/* Numbers align on their last digit, which is the only way a column of them
   can be compared at a glance. */
.lb-table .num { text-align: end; font-variant-numeric: tabular-nums; }
.lb-points { color: var(--accent-strong); font-weight: 700; }
.lb-table tbody tr:hover { background: var(--bg-hover); }
.lb-top td { background: var(--accent-soft); }
.lb-rules { margin: 0.6rem 0; padding-inline-start: 1.1rem; }
.lb-rules li { margin-block-end: 0.3rem; }
.lb-reset {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-block-start: 1.5rem;
}

/* --------------------------------------------------------------------------
   Document spawn map.
   -------------------------------------------------------------------------- */
.dm-maps { list-style: none; margin: 1rem 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: 0.6rem; }
.dm-map-card {
  display: flex; flex-direction: column; gap: 0.2rem;
  padding: 0.8rem 0.9rem; border: 1px solid var(--line); border-radius: 10px;
  background: var(--bg-panel); text-decoration: none; color: inherit;
}
.dm-map-card:hover { border-color: var(--accent); background: var(--accent-soft); }
.dm-map-name { font-weight: 600; }
.dm-doclist {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 0.25rem 1.5rem;
  margin: 0.5rem 0 0;
  padding-inline-start: 1.1rem;
}
.dm-doclist li { margin-block-end: 0.25rem; break-inside: avoid; }

.dm-head { display: flex; flex-wrap: wrap; gap: 1rem;
  align-items: flex-start; justify-content: space-between; }
.dm-switch { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.dm-switch-item {
  padding: 0.3rem 0.65rem; border: 1px solid var(--line); border-radius: 999px;
  font-size: 0.8rem; color: var(--text-muted); text-decoration: none;
  background: var(--bg-panel);
}
.dm-switch-item:hover { border-color: var(--line-strong); color: var(--text); }
.dm-switch-item.is-on {
  border-color: var(--accent); background: var(--accent-soft);
  color: var(--accent-strong); font-weight: 600;
}

.dm-toolbar { display: flex; flex-wrap: wrap; gap: 1rem;
  align-items: flex-start; justify-content: space-between; margin-block: 1rem 0.6rem; }
.dm-filters { display: flex; flex-wrap: wrap; gap: 0.4rem 0.9rem; }
.dm-filter { display: flex; align-items: center; gap: 0.35rem;
  font-size: 0.82rem; color: var(--text-muted); cursor: pointer; }
.dm-zoom { display: flex; gap: 0.3rem; }

/* The frame the map is looked at through. overflow:hidden is what makes the
   transform read as panning rather than as a very large image. */
.dm-stage {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: var(--bg-inset);
  block-size: min(70vh, 40rem);
  cursor: grab;
  touch-action: none;      /* the script owns the gesture, not the browser */
  /* Centres the canvas, which is only as big as the map itself. */
  display: flex;
  align-items: center;
  justify-content: center;
}
.dm-stage.is-dragging { cursor: grabbing; }
.dm-stage.is-placing { cursor: crosshair; }

/* Shrink-wrapped to the image, NOT stretched to the frame.
   This is the whole reason pins line up. With `inset: 0` the canvas filled the
   stage, `object-fit: contain` letterboxed the image inside it, and a pin at
   85% of the *canvas* landed well right of 85% of the *image* — off the map
   entirely on a tall map like Ground Zero. Wide maps nearly fill the frame, so
   they hid it. */
.dm-canvas {
  position: relative;
  line-height: 0;                /* no descender gap under the image */
  /* aspect-ratio comes from the map's own viewBox, set inline per map. These
     SVGs declare no width or height, so a box that sizes to its content
     collapses to nothing — the width has to be definite and the height
     derived from it. max-block-size then pulls both back for a tall map. */
  /* Sized in pixels by the script, which is also how zoom works: growing the
     box makes the browser re-render the SVG at the new size instead of
     magnifying an already-rasterised bitmap.
     No max-inline-size here — it would clamp the width the script sets and
     zooming would stretch the map taller without widening it. */
  block-size: 100%;
  inline-size: auto;
  transform-origin: center center;
  will-change: transform;
}
.dm-img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  user-select: none;
  -webkit-user-drag: none;
}
/* Now exactly the image box, so one percentage means one place. */
.dm-pins { position: absolute; inset: 0; pointer-events: none; }

/* The dot alone is what sits on the coordinate. The label is taken out of
   flow beside it, because an invisible label still takes layout width — and
   with translate(-50%) that width dragged every dot well off its own
   position, by nearly twenty percent of the map on Ground Zero. */
.dm-pin {
  position: absolute;
  /* A definite box exactly the size of the dot. Without one, translate(-50%)
     has nothing dependable to halve and the marker drifts off its coordinate. */
  inline-size: 0.85rem;
  block-size: 0.85rem;
  transform: translate(-50%, -50%);
  pointer-events: auto;
  text-decoration: none;
}
.dm-dot {
  display: block;
  inline-size: 100%; block-size: 100%; border-radius: 50%;
  background: var(--accent-strong); border: 2px solid var(--accent-ink);
  box-shadow: 0 0 0 1px var(--accent-strong);
  flex: 0 0 auto;
}
/* Named only on hover: ten labels at once is not a map, it is a word cloud. */
.dm-pin-label {
  /* A caption, not a target: only the dot takes the click. */
  pointer-events: none;
  position: absolute;
  inset-inline-start: 1.1rem;
  inset-block-start: 50%;
  transform: translateY(-50%);
  font-size: 0.7rem; white-space: nowrap;
  background: var(--bg-elevated); color: var(--text);
  padding: 0.1rem 0.35rem; border-radius: 4px;
  border: 1px solid var(--line);
  opacity: 0; transition: opacity 0.12s;
}
.dm-pin:hover .dm-pin-label, .dm-pin:focus .dm-pin-label { opacity: 1; }
.dm-ghost .dm-dot, .dm-ghost { pointer-events: none; }
.dm-ghost {
  border-radius: 50%;
  background: var(--warn); border: 2px solid var(--accent-ink);
}
.dm-hint { position: absolute; inset-block-end: 0.5rem; inset-inline-start: 0.75rem;
  margin: 0; pointer-events: none; }

.dm-add { margin-block-start: 1rem; }
.dm-list { margin-block-start: 1.5rem; }
.dm-pinlist { list-style: none; margin: 0; padding: 0; }
.dm-pinlist li { padding: 0.6rem 0; border-block-start: 1px solid var(--line); }
.dm-pinlist > :first-child { border-block-start: 0; }
.dm-pinlist img { max-inline-size: 14rem; border-radius: 6px; display: block;
  margin-block-start: 0.4rem; }
.dm-del { display: inline-block; margin-inline-start: 0.5rem; }

/* --------------------------------------------------------------------------
   In-place text editing (admins only). Deliberately loud: this is a mode where
   clicking changes what every visitor reads, so it should not look like part
   of the page.
   -------------------------------------------------------------------------- */
.te-button {
  position: absolute;
  z-index: 60;
  padding: 0.25rem 0.6rem;
  font-size: 0.78rem;
  border: 1px solid var(--accent);
  border-radius: 6px;
  background: var(--accent);
  color: var(--accent-ink);
  cursor: pointer;
  box-shadow: var(--shadow);
}
.te-panel {
  position: fixed;
  inset-block-end: 1rem;
  inset-inline-end: 1rem;
  z-index: 70;
  inline-size: min(30rem, calc(100vw - 2rem));
  max-block-size: min(70vh, 32rem);
  overflow-y: auto;
  padding: 0.9rem 1rem 1rem;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: var(--bg-elevated);
  box-shadow: var(--shadow);
}
.te-head { display: flex; align-items: center; justify-content: space-between;
  margin-block-end: 0.6rem; }
.te-close { background: none; border: 0; color: var(--text-muted);
  font-size: 1.3rem; line-height: 1; cursor: pointer; }
.te-status { color: var(--text-muted); font-size: 0.85rem; margin: 0.4rem 0; }
.te-row { padding-block: 0.7rem; border-block-start: 1px solid var(--line); }
.te-row:first-of-type { border-block-start: 0; }
/* The English source, shown when the page is in another language so the editor
   can tell which string they are looking at. */
.te-source { margin: 0 0 0.3rem; font-size: 0.75rem; color: var(--text-muted);
  font-style: italic; }
.te-input { inline-size: 100%; font: inherit; font-size: 0.85rem;
  padding: 0.4rem 0.5rem; border: 1px solid var(--line); border-radius: 6px;
  background: var(--bg-inset); color: var(--text); resize: vertical; }
.te-bar { display: flex; align-items: center; gap: 0.6rem;
  margin-block-start: 0.4rem; }
.te-note { font-size: 0.78rem; color: var(--text-muted); }
.te-note.te-ok { color: var(--ok); }
.te-note.te-error { color: var(--danger); }

/* A prose panel on a content page: the site's panel, plus the spacing that
   every page which stacks them needs. Kept as its own class rather than put on
   `.panel` itself, because a panel inside a grid supplies its own gap and would
   end up double-spaced. */
.page-panel { margin-block-start: 1.5rem; }
/* The heading is the first thing in the box; its own top margin would push it
   off the padding and leave the box looking lopsided. */
.page-panel > h2:first-child,
.page-panel > h3:first-child { margin-block-start: 0; }

/* Pin popup. Lives inside the pin layer so it pans and zooms with the map
   rather than drifting away from the pin it belongs to. */
.dm-popup-head { display: flex; align-items: center; gap: 0.45rem; }
.dm-popup-icon { inline-size: 2rem; block-size: 2rem; object-fit: contain;
  flex: 0 0 auto; }
.dm-popup-name { flex: 1 1 auto; font-size: 0.82rem; }
.dm-popup-close { flex: 0 0 auto; background: none; border: 0; cursor: pointer;
  color: var(--text-muted); font-size: 1.1rem; line-height: 1; padding: 0 0.2rem; }
.dm-popup-note { margin: 0.4rem 0 0; font-size: 0.8rem; color: var(--text-muted); }

/* The pin is a button now, so the browser's own button chrome has to go. */
.dm-pin {
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  appearance: none;
}

/* The pin-placing toggle, lit while the mode is on so an admin can tell at a
   glance whether their next click will drop a pin. */
.dm-zoom .is-on {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent-strong);
}

/* Document item renders, beside the name wherever a type is listed. */
.dm-doc-icon {
  inline-size: 1.6rem;
  block-size: 1.6rem;
  object-fit: contain;
  vertical-align: middle;
  flex: 0 0 auto;
}
.dm-doc-icon-sm { inline-size: 1.15rem; block-size: 1.15rem; }
/* The list items become rows so the icon and the name sit on one baseline
   rather than the text wrapping under the picture. */
.dm-doclist li { display: flex; align-items: center; gap: 0.45rem; }
.dm-pinlist li > .dm-doc-icon { margin-inline-end: 0.35rem; }

/* Per-pin admin controls: attach or swap a screenshot without re-placing the
   pin, and delete. Wraps on a narrow screen rather than pushing the row wide. */
.dm-pin-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-block-start: 0.5rem;
}
.dm-attach { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.dm-attach-label { font-size: 0.78rem; color: var(--text-muted); }
.dm-attach input[type="file"] { font-size: 0.78rem; max-inline-size: 13rem; }

/* Admin controls inside the pin popup, now that the list under the map is
   gone. Stacked, because the popup is narrow and a row of three would push it
   wider than the thing it is pointing at. */
.dm-popup-admin {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin-block-start: 0.6rem;
  padding-block-start: 0.6rem;
  border-block-start: 1px solid var(--line);
}
.dm-popup-attach { display: flex; flex-direction: column; gap: 0.3rem; }
.dm-popup-admin input[type="file"] { font-size: 0.72rem; max-inline-size: 100%; }
.dm-popup-admin .btn { inline-size: 100%; }



/* ---- Loot containers on the document map ------------------------------- */
/* A layer under the pins, the same box as the image, so a percentage means the
   same place in both. */
.dm-loot-layer { position: absolute; inset: 0; pointer-events: none; }
.dm-loot-group[hidden] { display: none; }
.dm-loot-mark, .dm-loot-cluster {
  position: absolute;
  transform: translate(-50%, -50%);
  pointer-events: auto;
  padding: 0; border: 0; appearance: none; cursor: pointer;
  border-radius: 50%;
  background: var(--bg-elevated) center / 74% no-repeat;
  box-shadow: 0 0 0 1.5px var(--line-strong), 0 1px 4px rgba(0, 0, 0, 0.45);
}
/* The size tarkov.dev draws them at. Smaller and the icons stop being
   readable; the clustering is what keeps this size from turning into a pile. */
.dm-loot-mark { inline-size: 1.5rem; block-size: 1.5rem; }
.dm-loot-mark:hover, .dm-loot-mark:focus-visible,
.dm-loot-cluster:hover, .dm-loot-cluster:focus-visible {
  z-index: 2; transform: translate(-50%, -50%) scale(1.25);
  box-shadow: 0 0 0 2px var(--accent-strong), 0 2px 6px rgba(0, 0, 0, 0.5);
}
/* Several containers that would overlap at this zoom, as one bubble. */
.dm-loot-cluster {
  inline-size: 1.9rem; block-size: 1.9rem;
  background-size: 62%;
  background-position: 40% 55%;
}
.dm-loot-count {
  position: absolute; inset-block-start: -0.35rem; inset-inline-end: -0.35rem;
  min-inline-size: 1.15rem; block-size: 1.15rem; padding-inline: 0.25rem;
  border-radius: 999px;
  background: var(--accent-strong); color: var(--accent-ink);
  font-size: 0.68rem; font-weight: 700; line-height: 1.15rem; text-align: center;
  box-shadow: 0 0 0 1.5px var(--bg-elevated);
}
/* Document pins sit over the loot icons and should not look smaller than them. */
.dm-pin { inline-size: 1.1rem; block-size: 1.1rem; }
.dm-loot { margin-block: 0.6rem 0.75rem; }
.dm-loot > summary {
  cursor: pointer; font-weight: 600;
  display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap;
}
.dm-loot-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
  gap: 0.2rem 0.9rem;
  margin-block: 0.4rem 0.6rem;
}
.dm-loot-icon {
  inline-size: 1.15rem; block-size: 1.15rem; object-fit: contain;
  flex: 0 0 auto;
}
.dm-loot-actions { align-items: center; flex-wrap: wrap; }


/* ---- Pin dialog --------------------------------------------------------- */
/* Over everything, page included: the point of opening a pin is the picture,
   and a bubble at the pin never had the room for one. */
.dm-modal {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center;
  padding: 1rem;
  background: rgba(0, 0, 0, 0.62);
  backdrop-filter: blur(2px);
}
.dm-modal-card {
  inline-size: 100%; max-inline-size: 30rem;
  max-block-size: 92vh; overflow: auto;
  padding: 1rem 1.1rem 1.1rem;
  border: 1px solid var(--line-strong); border-radius: 14px;
  background: var(--bg-elevated); box-shadow: var(--shadow);
  line-height: 1.45; text-align: start;
}
.dm-modal-card .dm-popup-icon { inline-size: 2.75rem; block-size: 2.75rem; }
.dm-modal-card .dm-popup-name { font-size: 1.05rem; }
.dm-modal-card .dm-popup-close { font-size: 1.5rem; }
.dm-modal-card .dm-popup-note { font-size: 0.92rem; margin-block-start: 0.55rem; }
.dm-modal-shot {
  display: block; inline-size: 100%; margin-block-start: 0.75rem;
  border-radius: 8px; max-block-size: 62vh; object-fit: contain;
  background: var(--bg-inset);
}
body.dm-locked { overflow: hidden; }

.dm-modal-credit { font-size: 0.8rem; }
.dm-modal-credit a { color: inherit; text-decoration: underline; }

/* The screenshot in a pin dialog is a button; clicking it fills the screen. */
.dm-modal-shot-btn {
  display: block; inline-size: 100%; padding: 0; border: 0; background: none;
  cursor: zoom-in; border-radius: 8px;
}
.dm-modal-shot-btn:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
.dm-lightbox {
  position: fixed; inset: 0; z-index: 1100;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.92); cursor: zoom-out;
}
.dm-lightbox-img {
  max-inline-size: 96vw; max-block-size: 96vh; object-fit: contain;
  border-radius: 6px; box-shadow: 0 8px 40px rgba(0, 0, 0, 0.7);
}
.dm-lightbox-close {
  position: fixed; inset-block-start: 0.75rem; inset-inline-end: 1rem;
  background: none; border: 0; color: #fff; font-size: 2.2rem; line-height: 1;
  cursor: pointer; padding: 0.25rem 0.5rem;
}

/* While a pin is being moved, the map has to get the click, not what sits on it. */
.dm-wrap.is-moving .dm-pin, .dm-wrap.is-moving .dm-loot-mark,
.dm-wrap.is-moving .dm-loot-cluster { pointer-events: none; }

/* Several spawns under one marker: one circle, the count inside it. The
   dot is hidden and the badge takes its place, so a group is never two
   overlapping discs. */
.dm-pin-group { inline-size: 1.45rem; block-size: 1.45rem; }
.dm-pin-group .dm-dot { display: none; }
.dm-pin-count {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--accent-strong); color: var(--accent-ink);
  border: 2px solid var(--accent-ink);
  box-shadow: 0 0 0 1px var(--accent-strong);
  font-size: 0.72rem; font-weight: 700; line-height: 1;
  pointer-events: none;
}
.dm-gallery-nav {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  margin-block-start: 0.6rem;
}
.dm-gallery-where { font-size: 0.85rem; color: var(--text-muted); }

/* ---- Kappa checklist ---------------------------------------------------- */
.kappa-mine { margin-block-end: 1.5rem; }
.kappa-mine .mode-tabs { margin-block-end: 0.75rem; }
.kappa-signin { margin-block-end: 1.5rem; }
.kappa-signin p { margin: 0 0 0.6rem; }
.kappa-signin .row-gap { margin: 0; }

/* A row is the step number, the tick, the name and the state on one line,
   with the how-to folded beneath and indented to the name. */
.kappa-task { flex-wrap: wrap; align-items: flex-start; row-gap: 0.15rem; }
.kappa-list > .kappa-task::before { padding-block-start: 0.2rem; }
.kappa-row { flex: 1 1 0; min-inline-size: 0; display: flex; align-items: center; gap: 0.6rem; }
.kappa-check { flex: 0 0 auto; display: flex; align-items: center; }
.kappa-check input { inline-size: 1.15rem; block-size: 1.15rem; accent-color: var(--accent-strong); cursor: pointer; }
.kappa-name { font-weight: 600; }
.kappa-task.is-done .kappa-name { color: var(--text-muted); text-decoration: line-through; }

.kappa-how { flex: 1 1 100%; margin-inline-start: 2rem; font-size: 0.88rem; }
.kappa-how summary { cursor: pointer; color: var(--text-muted); padding-block: 0.1rem; }
.kappa-how summary:hover { color: var(--accent-strong); }
.kappa-how p { margin: 0.35rem 0 0.2rem; }
.kappa-how a { color: var(--accent-strong); text-decoration: underline; }
.kappa-objectives { margin: 0.35rem 0 0.2rem; padding-inline-start: 1.1rem; }
.kappa-objectives li { margin-block-end: 0.15rem; }
.kappa-how-link .muted { font-size: 0.8rem; }

/* The other requirements are a list, not a numbered chain. */
.kappa-reqs > .kappa-task::before { content: none; }
.kappa-reqs .kappa-how { margin-inline-start: 0; }

.kappa-chain { margin-block-start: 1.2rem; }
.kappa-chain h3 { margin: 0 0 0.15rem; font-size: 1rem; }
.kappa-chain h3 .muted { font-weight: 400; font-size: 0.85rem; }
.kappa-chain > .small { margin: 0 0 0.5rem; }
.kappa-reset { margin-block-start: 0.6rem; }

/* ---- Tasks ------------------------------------------------------------- */
/* Logical properties throughout, so the Arabic page mirrors without a single
   [dir="rtl"] rule. Names carry dir="ltr" in the markup and stay put. */
.tk-mine { margin-block-end: 1.2rem; }

/* The trader picker, laid out like the game's trader screen: a portrait,
   a name plate, a count. Portraits are taller than wide, faces at the top. */
.tk-traders {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(7.4rem, 1fr));
  gap: 0.75rem;
  margin-block: 1.1rem 1.2rem;
}
.tk-trader {
  display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: 12px; overflow: hidden;
  background: var(--bg-panel); color: var(--text); text-decoration: none;
  transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.tk-trader:hover { border-color: var(--line-strong); transform: translateY(-2px); text-decoration: none; }
.tk-trader.is-on { border-color: var(--accent-strong); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-strong) 35%, transparent); }
.tk-portrait { display: block; aspect-ratio: 4 / 5; background: var(--bg-inset); position: relative; }
.tk-portrait img { inline-size: 100%; block-size: 100%; object-fit: cover; object-position: 50% 30%; display: block; }
.tk-portrait-letter { position: absolute; inset: 0; display: grid; place-items: center; font-size: 2.2rem; font-weight: 700; color: var(--text-muted); }
.tk-trader-name { padding: 0.4rem 0.4rem 0; font-weight: 600; font-size: 0.86rem; text-align: center; line-height: 1.2; }
.tk-trader-count { padding: 0.1rem 0.4rem 0.45rem; font-size: 0.78rem; color: var(--text-muted); text-align: center; font-variant-numeric: tabular-nums; }
.tk-trader.is-on .tk-trader-name { color: var(--accent-strong); }

.tk-search { display: flex; align-items: center; gap: 0.6rem; margin-block-end: 0.8rem; }
.tk-search input { flex: 1 1 auto; max-inline-size: 30rem; }
.tk-hint { margin: 0.4rem 0 1rem; }

/* One trader's tasks: a heading with the portrait, then a grid of cards. */
.tk-group { margin-block-start: 1.6rem; }
.tk-group-head { display: flex; align-items: center; gap: 0.6rem; font-size: 1.15rem; margin: 0 0 0.7rem; }
.tk-group-portrait { inline-size: 2.2rem; block-size: 2.2rem; border-radius: 50%; object-fit: cover; border: 1px solid var(--line); }
.tk-group-count { font-size: 0.85rem; font-weight: 400; }
.tk-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15.5rem, 1fr));
  gap: 0.8rem;
}
.tk-card {
  display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: 12px; overflow: hidden;
  background: var(--bg-panel);
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.tk-card:hover { border-color: var(--line-strong); transform: translateY(-2px); }
.tk-card.is-done { border-color: color-mix(in srgb, var(--ok) 55%, var(--line)); }
.tk-card.is-done .tk-card-media { opacity: 0.55; }
.tk-card-media { display: block; position: relative; aspect-ratio: 16 / 9; background: var(--bg-inset); }
.tk-card-media img { inline-size: 100%; block-size: 100%; object-fit: cover; object-position: center 20%; display: block; }
.tk-card-placeholder {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 0.85rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted);
  background: radial-gradient(circle at 30% 20%, color-mix(in srgb, var(--accent-strong) 18%, transparent), transparent 60%), var(--bg-inset);
}
.tk-card-flag {
  position: absolute; inset-block-start: 0.5rem; inset-inline-start: 0.5rem;
  padding: 0.15rem 0.5rem; border-radius: 999px; font-size: 0.72rem; font-weight: 700;
  background: var(--ok); color: #0b0f0e;
}
.tk-card-body { padding: 0.65rem 0.8rem 0.5rem; display: flex; flex-direction: column; gap: 0.3rem; flex: 1 1 auto; text-align: start; }
.tk-card-name { font-weight: 700; color: var(--text); text-decoration: none; line-height: 1.25; display: block; }
.tk-card-name:hover { color: var(--accent-strong); }
.tk-card.is-done .tk-card-name { color: var(--text-muted); text-decoration: line-through; }
/* The name keeps dir="ltr" so English reads correctly, but on the Arabic page
   it should still sit against the card's start edge like everything else. */
[dir="rtl"] .tk-card-name, [dir="rtl"] .tk-group-head > span[dir="ltr"] { text-align: right; }
.tk-card-meta, .tk-card-badges { margin: 0; display: flex; flex-wrap: wrap; gap: 0.3rem 0.7rem; font-size: 0.8rem; color: var(--text-muted); }
.tk-card-badges { gap: 0.3rem; }
.tk-card-badges:empty { display: none; }
.tk-card-tick {
  display: flex; align-items: center; gap: 0.5rem; cursor: pointer;
  padding: 0.5rem 0.8rem; border-block-start: 1px solid var(--line);
  font-size: 0.85rem; font-weight: 600; color: var(--text-muted);
}
.tk-card-tick input { inline-size: 1.1rem; block-size: 1.1rem; accent-color: var(--ok); }
.tk-card.is-done .tk-card-tick { color: var(--ok); }
.tk-empty { margin-block-start: 1rem; }

/* Detail page */
.tk-head { display: flex; gap: 1.2rem; align-items: flex-start; flex-wrap: wrap; }
.tk-banner { inline-size: 18rem; max-inline-size: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: 10px; border: 1px solid var(--line); }
.tk-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.6rem; font-size: 0.8rem; color: var(--text-muted); }
.tk-meta-head { font-size: 0.9rem; margin: 0.3rem 0 0.6rem; }
.tk-tick-big { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.8rem; border: 1px solid var(--line); border-radius: 999px; cursor: pointer; font-weight: 600; }
.tk-tick-big input { inline-size: 1.15rem; block-size: 1.15rem; accent-color: var(--accent-strong); }
.tk-tick-big.is-done { border-color: var(--ok); color: var(--ok); }
.tk-columns { display: grid; grid-template-columns: minmax(0, 2fr) minmax(16rem, 1fr); gap: 1.5rem; align-items: start; }
@media (max-width: 860px) { .tk-columns { grid-template-columns: 1fr; } }
.tk-objectives { padding-inline-start: 1.3rem; }
.tk-objectives li { margin-block-end: 0.45rem; }
.tk-objective-maps { display: inline-flex; gap: 0.3rem; margin-inline-start: 0.4rem; vertical-align: middle; }
.tk-english { display: block; }
.tk-map { margin: 0 0 1.2rem; }
.tk-map-bar { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; margin-block-end: 0.4rem; }
.tk-map-zoom { margin-inline-start: auto; }
/* The stage clips; the canvas inside is resized on zoom and translated on
   pan, exactly like the document map, so the SVG stays sharp. */
.tk-map-stage {
  position: relative; block-size: min(70vh, 34rem); overflow: hidden;
  border: 1px solid var(--line); border-radius: 10px; background: var(--bg-inset);
  display: grid; place-items: center; cursor: grab; touch-action: none; user-select: none;
}
.tk-map-stage.is-dragging { cursor: grabbing; }
.tk-map-canvas { position: relative; max-inline-size: 100%; max-block-size: 100%; }
.tk-map-canvas img { inline-size: 100%; block-size: 100%; display: block; pointer-events: none; }
/* Deep blue on a map that is mostly greens and greys, with a white ring and a
   soft halo so it reads at any zoom; a pulsing outer ring draws the eye on
   a busy map without hiding what is under it. */
.tk-pin {
  position: absolute; inline-size: 18px; block-size: 18px; margin: -9px 0 0 -9px; border-radius: 50%;
  background: #1d4ed8; border: 3px solid #fff;
  box-shadow: 0 0 0 3px #1d4ed8, 0 2px 8px rgba(0, 0, 0, 0.55);
  z-index: 2;
}
.tk-pin::after {
  content: ""; position: absolute; inset: -12px; border-radius: 50%;
  border: 2px solid rgba(29, 78, 216, 0.75);
  animation: tk-pulse 1.8s ease-out infinite;
}
@keyframes tk-pulse {
  0%   { transform: scale(0.6); opacity: 0.9; }
  100% { transform: scale(1.4); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .tk-pin::after { animation: none; opacity: 0.6; } }
[dir="rtl"] .tk-pin { margin: -9px -9px 0 0; }

/* Admin editing: pins can be dragged, a small cross removes one, and the
   stage shows a crosshair while a new pin is being placed. */
.tk-map-stage.is-editing .tk-pin { cursor: move; }
.tk-map-stage.is-placing { cursor: crosshair; }
.tk-pin-del {
  position: absolute; inset-block-start: -14px; inset-inline-end: -14px;
  inline-size: 18px; block-size: 18px; border-radius: 50%; border: 0; padding: 0;
  background: #b91c1c; color: #fff; font-size: 12px; line-height: 18px; text-align: center;
  cursor: pointer; display: none; z-index: 3;
}
.tk-map-stage.is-editing .tk-pin:hover .tk-pin-del { display: block; }
.tk-pin.is-dragging::after { animation: none; }
.tk-map-edit { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin-block: 0.4rem 0; }
.tk-map-edit .is-on { border-color: var(--accent-strong); color: var(--accent-strong); }
.tk-map-add { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin-block-start: 0.5rem; }

.tk-map figcaption { font-size: 0.82rem; color: var(--text-muted); margin-block-start: 0.3rem; }
.tk-credit { margin-block-start: 0.8rem; }
.tk-panel { padding: 0.9rem 1rem; margin-block-end: 1rem; }
.tk-panel h3 { margin: 0 0 0.5rem; font-size: 0.95rem; }
.tk-reqs { margin: 0; padding-inline-start: 1.1rem; }
.tk-reqs li { margin-block-end: 0.3rem; }
.tk-wiki-block img { max-inline-size: 100%; }
.tk-wiki-block ul { padding-inline-start: 1.1rem; }

/* Admin: trader pictures panel */
.tk-portrait-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: 0.8rem; }
.tk-portrait-cell { display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.85rem; }
.tk-portrait-cell img { inline-size: 100%; aspect-ratio: 4 / 5; object-fit: cover; border-radius: 8px; border: 1px solid var(--line); }
.tk-portrait-cell .tk-portrait-none { aspect-ratio: 4 / 5; border-radius: 8px; border: 1px dashed var(--line); display: grid; place-items: center; color: var(--text-muted); }

/* ---- Tournaments ------------------------------------------------------- */
.tn-card .event-title a { color: inherit; text-decoration: none; }
.tn-card .event-title a:hover { color: var(--accent-strong); }
.tn-facts { display: flex; flex-wrap: wrap; gap: 0.35rem 0.9rem; margin: 0.4rem 0 0.6rem; font-size: 0.9rem; color: var(--text-muted); }
.tn-prizes { margin-block: 0.6rem; padding: 0.6rem 0.8rem; border: 1px solid color-mix(in srgb, #f5c542 40%, var(--line)); border-radius: 10px;
             background: color-mix(in srgb, #f5c542 6%, transparent); }
.tn-prizes strong { display: block; margin-block-end: 0.2rem; color: #f5c542; }
.tn-prizes .prose ul { margin: 0; padding-inline-start: 1.1rem; }
.tn-actions { margin-block: 0.6rem 0.3rem; }
.tn-disclaimer { margin: 0.4rem 0 0; }
.tn-hero { inline-size: 100%; max-block-size: 18rem; object-fit: cover; border-radius: 12px; border: 1px solid var(--line); margin-block-end: 0.8rem; }
.tn-detail .tk-columns { margin-block-start: 1rem; }
