/* SPT Mod Archive — one stylesheet, no framework, no build step. */

:root {
  --bg: #14171c;
  --surface: #1b1f26;
  --surface-2: #222831;
  --line: #2e3641;
  --text: #e6e9ef;
  --muted: #9aa5b4;
  --faint: #6b7686;
  --accent: #d9a441;
  --accent-dim: #8a6a2b;
  --fika: #4da3ff;
  --ok: #5cb87a;
  --warn: #d97a4a;
  /* Distinct from --warn on purpose: orange qualifies a mod ("contains ads"),
     red rules it out ("Fika not supported"). Kept apart enough to survive the
     two sitting in the same badge row. */
  --bad: #e0625c;
  --radius: 8px;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #f5f6f8;
    --surface: #ffffff;
    --surface-2: #eef0f4;
    --line: #d8dde5;
    --text: #1b1f26;
    --muted: #5a6472;
    --faint: #858e9c;
    --accent: #9a6d12;
    --accent-dim: #c8a24a;
    --fika: #1667c4;
    --ok: #2f7d4a;
    --warn: #b4551f;
    --bad: #b3302b;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

.wrap { max-width: 1240px; margin: 0 auto; padding: 0 20px 64px; }

/* --- header ------------------------------------------------------------ */

.masthead {
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  margin-bottom: 24px;
}
.masthead .wrap { padding-top: 20px; padding-bottom: 20px; }
.masthead h1 { margin: 0 0 4px; font-size: 21px; letter-spacing: -0.01em; }
.masthead h1 a { color: var(--text); }
.masthead-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: flex-start;
  justify-content: space-between;
}
.archived { color: var(--muted); font-size: 13px; white-space: nowrap; }
.archived strong { color: var(--accent); font-size: 15px; }
.counts { display: flex; flex-direction: column; gap: 2px; }
#count { color: var(--muted); font-weight: 600; }
#count .num, .listcard .statnums b { color: var(--accent); }
.subcount { color: var(--faint); font-size: 12px; }
.tagline { margin: 0; color: var(--muted); font-size: 13.5px; }
.tagline strong { color: var(--text); font-weight: 600; }

/* --- controls ---------------------------------------------------------- */

.controls {
  display: grid;
  grid-template-columns: minmax(220px, 2fr) repeat(5, minmax(120px, 1fr));
  gap: 10px;
  margin-bottom: 14px;
}
/* The addon catalogue filters on three things rather than six, because an
   addon has no SPT version, category or Fika status to filter by. Same row,
   same search-box proportion, just fewer columns -- stretching three controls
   across six tracks would leave each one absurdly wide. */
.controls-narrow { grid-template-columns: minmax(220px, 2fr) repeat(2, minmax(120px, 1fr)); }
@media (max-width: 900px) { .controls { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .controls { grid-template-columns: 1fr; } }

input[type="search"], input[type="text"], select {
  width: 100%;
  padding: 9px 11px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
  font-size: 14px;
}
input:focus, select:focus {
  outline: 2px solid var(--accent-dim);
  outline-offset: -1px;
  border-color: var(--accent-dim);
}

.resultbar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
  color: var(--muted);
  font-size: 13px;
}
.linkbtn {
  background: none;
  border: 1px solid var(--line);
  color: var(--muted);
  padding: 5px 10px;
  border-radius: var(--radius);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.linkbtn:hover { color: var(--text); border-color: var(--faint); }

/* The switch between the two catalogues. Deliberately not a .linkbtn: those
   act on the list in front of you (reset it, copy from it) and are muted
   because they are secondary. This one leaves the page entirely, so it is
   filled and tinted -- next to two grey outlines it was invisible, which is
   the one thing a navigation control cannot be.
   A link rather than a button, so it stays middle-clickable and copyable;
   the first two declarations are what an <a> needs to sit level beside them. */
.switchbtn {
  display: inline-block;
  text-decoration: none;
  padding: 5px 12px;
  border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--accent);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
}
.switchbtn:hover {
  background: color-mix(in srgb, var(--accent) 26%, transparent);
  border-color: var(--accent);
}

/* Holds the counts and the switch on one line, so the switch reads as part of
   "Showing N mods" rather than as another action over on the right. */
.countgroup { display: flex; align-items: center; gap: 14px; }

.resultactions { display: flex; gap: 12px; align-items: center; }
.masthead-actions { display: flex; gap: 12px; align-items: center; }

/* --- SPT version filter -------------------------------------------------- */

.sptfilter { position: relative; }
.sptsummary {
  width: 100%;
  padding: 9px 11px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
  font-size: 14px;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sptsummary:hover { border-color: var(--faint); }

.sptpanel {
  position: absolute;
  z-index: 30;
  top: calc(100% + 4px);
  left: 0;
  min-width: 240px;
  max-height: 60vh;
  overflow-y: auto;
  padding: 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.4);
}
.sptactions { display: flex; gap: 6px; margin-bottom: 8px; }
.sptactions .linkbtn { flex: 1; font-size: 12px; padding: 4px 6px; }

.sptgroup { border-top: 1px solid var(--line); padding-top: 6px; margin-top: 6px; }
.sptgroup:first-of-type { border-top: 0; margin-top: 0; }
.sptgroup > summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 700;
  padding: 4px 2px;
  display: flex;
  gap: 8px;
  align-items: center;
}
.anymajor { display: flex; gap: 7px; align-items: center; cursor: pointer; }
.sptgroup > summary::-webkit-details-marker { display: none; }
.sptversions { display: flex; flex-direction: column; padding: 4px 2px 8px; }
.sptversions label {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 3px 4px;
  border-radius: 4px;
  font-size: 13px;
  cursor: pointer;
}
.sptversions label:hover { background: var(--surface-2); }
.sptversions .n { margin-left: auto; color: var(--faint); font-size: 11.5px; }

/* --- mod list ---------------------------------------------------------- */

.modlist { display: flex; flex-direction: column; gap: 8px; }

.mod {
  display: grid;
  grid-template-columns: 48px 1fr auto;
  gap: 14px;
  align-items: start;
  padding: 12px 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.mod:hover { border-color: var(--faint); }

/* The mod you came back from, so your place is visible and not just scrolled
   to. An inset rule rather than a real left border, because a border would be
   1px of layout and shunt the tile's contents sideways against its neighbours.
   Quiet on purpose: it marks where you were, it does not claim anything about
   the mod, so it stays below the badges in the tile's reading order. */
.mod.lastopened {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
  box-shadow: inset 3px 0 0 var(--accent-dim);
}
.mod.lastopened:hover { border-color: var(--accent-dim); }

.mod .thumb {
  width: 48px; height: 48px;
  border-radius: 6px;
  object-fit: cover;
  background: var(--surface-2);
}
/* Stands in for a missing image on a tile, as .headthumb-empty does on the
   page itself -- same idea, sized for 48px instead of 96px. */
.mod .thumb-empty {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  color: var(--faint);
  font-size: 20px;
  font-weight: 700;
  line-height: 1;
  user-select: none;
}
.mod .title { margin: 0 0 2px; font-size: 15.5px; font-weight: 600; }
.mod .teaser {
  margin: 3px 0 0;
  color: var(--muted);
  font-size: 13.5px;
  overflow: hidden;
  display: -webkit-box;
  /* Three lines fits the longest teaser the Forge allows (254 chars), so
     nothing is visually cut while tile heights stay bounded. */
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}
.mod .byline { color: var(--faint); font-size: 12.5px; }
.mod .byline a { color: var(--faint); }
/* Was a button that pre-filled the search; now a link to the author's page,
   so it needs its underline suppressed until hover to sit inside a byline. */
.authorlink {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--faint);
  cursor: pointer;
  text-decoration: none;
}
.authorlink:hover { color: var(--accent); text-decoration: underline; }

.stats {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  text-align: right;
  color: var(--muted);
  font-size: 12.5px;
  white-space: nowrap;
}
.statnums { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.dlcount { display: inline-flex; align-items: center; gap: 5px; }
.dlcount b { color: var(--text); font-size: 14px; font-weight: 600; }
/* The icon replaces the word "downloads", so it sits quieter than the number. */
.dlicon { color: var(--faint); flex: none; }
.release { color: var(--faint); font-size: 11.5px; white-space: nowrap; }

/* Stars sit at the bottom of the rail and link straight to the repository. */
.stars {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--muted);
  font-size: 12.5px;
  font-weight: 600;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 9px;
}
.stars:hover { color: var(--accent); border-color: var(--accent-dim); text-decoration: none; }
.repo-icon { flex: none; fill: currentColor; opacity: 0.85; }

@media (max-width: 560px) {
  .mod { grid-template-columns: 40px 1fr; }
  .mod .thumb { width: 40px; height: 40px; }
  .stats { grid-column: 2; align-items: flex-start; text-align: left; }
}

/* The list scrolls inside itself so search and filters stay on screen.
   On phones that costs more than it gives, so the page scrolls normally. */
.listscroll {
  overflow-y: auto;
  max-height: calc(100vh - 200px);
  padding-right: 4px;
  overscroll-behavior: contain;
}
@media (max-width: 560px) {
  .listscroll { overflow-y: visible; max-height: none; padding-right: 0; }
}

.to-top {
  position: fixed;
  left: 18px;
  bottom: 18px;
  z-index: 50;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--line);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.18s, visibility 0.18s;
}
.to-top.show { opacity: 1; visibility: visible; }
.to-top:hover { color: var(--accent); border-color: var(--accent-dim); }

.listcard {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 14px;
  align-items: center;
  padding: 12px 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.listcard:hover { border-color: var(--faint); }
.listcard .badges { margin-top: 6px; }
.linklist.listmods li { align-items: center; gap: 10px; }
.listthumb {
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 5px;
  object-fit: cover;
  background: var(--surface-2);
  display: inline-block;
}
.textlink {
  align-self: center;
  color: var(--accent);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
}
.textlink:hover { text-decoration: underline; }

/* --- badges ------------------------------------------------------------ */

.badges { display: flex; flex-wrap: wrap; gap: 5px; }

/* Tags and the star pill share the tile's bottom row, tags left, stars right. */
.modfoot {
  grid-column: 2 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-top: 2px;
}
/* Bottom row: stars, a divider, then tags, with the collection button pushed
   to the far corner. The divider marks the stars as a repository stat rather
   than another filter tag. */
.footdivider {
  flex: none;
  width: 1px;
  align-self: stretch;
  min-height: 18px;
  background: var(--line);
  margin: 0 2px;
}
.modfoot .badges { flex: 1; }
.modfoot .mark { margin-left: auto; }
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 7px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
/* Badge colour is a fixed language, not decoration, so a colour means the same
   thing wherever it appears: bronze is the SPT version and blue is Fika, the
   two facts that decide whether a mod is usable; red is an outright no, orange
   qualifies, green marks a community submission. Everything descriptive --
   category, dependency count
   -- stays neutral, which is what keeps the two that matter legible in a row
   of six. */
.badge.spt {
  font-family: var(--mono);
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
}
button.badge { font: inherit; font-size: 11.5px; font-weight: 600; cursor: pointer; }
button.badge:hover { border-color: var(--faint); filter: brightness(1.25); }
.stars.muted { color: var(--faint); opacity: 0.7; }
.badge.fika { color: var(--fika); border-color: color-mix(in srgb, var(--fika) 45%, transparent); }
.badge.warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 45%, transparent); }
.badge.bad { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 45%, transparent); }
.badge.community { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 45%, transparent); }

/* --- mod page ---------------------------------------------------------- */

.crumbs { margin: 0 0 16px; font-size: 13px; color: var(--faint); }

.modhead { display: flex; gap: 18px; align-items: flex-start; margin-bottom: 22px; }
.modhead img, .headthumb-empty {
  width: 96px;
  height: 96px;
  border-radius: 10px;
  object-fit: cover;
  background: var(--surface-2);
}
/* Same footprint as the image, so the 129 mods without a thumbnail start
   their title where every other mod's title starts. The initial makes an
   empty slot read as intentional rather than as a failed image. */
.headthumb-empty {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  color: var(--faint);
  font-size: 38px;
  font-weight: 700;
  line-height: 1;
  user-select: none;
}
.modhead h1 { margin: 0 0 4px; font-size: 26px; letter-spacing: -0.015em; }
.modhead .teaser { margin: 6px 0 0; color: var(--muted); }
@media (max-width: 560px) { .modhead { flex-direction: column; } }

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 18px;
  margin-bottom: 16px;
}
.panel > h2 {
  margin: 0 0 12px;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--faint);
  font-weight: 700;
}

.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 14px; }
.fact .k { color: var(--faint); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.05em; }
.fact .v { font-size: 15px; font-weight: 600; margin-top: 2px; word-break: break-word; }
/* A GUID is an identifier to be copied verbatim, not a number to be read, so
   it is monospaced -- and given the whole row, because the longest is 62
   characters and wraps to three lines in a half-panel column. */
.fact-guid { grid-column: 1 / -1; }
.fact-guid .v {
  font-family: var(--mono);
  font-size: 12.5px;
  font-weight: 500;
  user-select: all;
}

.linklist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.linklist li { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; }
.linklist .label {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 1px 6px;
}
.linklist a { word-break: break-all; }

.prose { overflow-wrap: break-word; }
.prose img { max-width: 100%; height: auto; border-radius: 6px; }
.prose pre {
  background: var(--surface-2);
  padding: 12px;
  border-radius: 6px;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 13px;
}
.prose code { font-family: var(--mono); font-size: 13px; }
.prose blockquote {
  margin: 12px 0;
  padding-left: 14px;
  border-left: 3px solid var(--line);
  color: var(--muted);
}
.prose table { border-collapse: collapse; width: 100%; display: block; overflow-x: auto; }
.prose th, .prose td { border: 1px solid var(--line); padding: 6px 9px; text-align: left; }
.prose h1, .prose h2, .prose h3 { font-size: 17px; margin: 18px 0 8px; }

/* Details and Source share a row: both are short, and stacking them was the
   single biggest contributor to mod-page height. */
.splitcols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  align-items: start;
}
@media (max-width: 720px) { .splitcols { grid-template-columns: 1fr; } }
/* The panels lose their own margin, so the row supplies the gap to whatever
   follows it -- without this the tabs below sat flush against them. */
.splitcols { margin-bottom: 16px; }

/* Tags sit at the end of the author line, where there is full page width for
   them -- inside a half-width panel a mod with several flags wrapped badly. */
.bylinerow {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  justify-content: space-between;
}
.bylinerow .badges { margin-left: auto; justify-content: flex-end; }
/* Pinned explicitly so Details and Source match and neither depends on
   browser defaults. Scoped to .splitcols: other panels keep the small
   uppercase heading style. */
.splitcols .panel > h2,
.factshead h2,
.sourcehead h2 {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  color: var(--text);
  text-transform: none;
  letter-spacing: normal;
}
.splitcols .panel > h2 { margin-bottom: 12px; }

/* The SPT version heads the Details panel, pinned to the far edge so it reads
   as the panel's headline fact rather than as another tag. Yellow because it
   is the one thing on the page that decides whether a mod is usable at all. */
.factshead {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
}
.factshead h2 { margin-bottom: 0; }
.factshead .badge { margin-left: auto; }

/* Heading and its download link on one row, the link sitting next to the word
   rather than pushed to the panel's far edge. */
.sourcehead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 12px;
}
/* The heading takes the free space so the star count and the download sit
   together at the right edge -- they describe the same repository, and
   space-between alone would have stranded the stars in the middle. */
.sourcehead h2 { margin-bottom: 0; margin-right: auto; }
.sourcedl { display: inline-flex; align-items: center; line-height: 1; color: var(--muted); }
.sourcedl:hover { color: var(--accent); }
.splitcols .panel { margin-bottom: 0; }
/* Two fixed columns rather than auto-fit: in a half-width panel auto-fit made
   three ~110px cells, which left License squeezed into the last one. Two
   columns pair it with Published on the same row at roughly double the width. */
.splitcols .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 18px; }

.sourcelist { list-style: none; margin: 0; padding: 0; }
.sourcerow { padding: 7px 0; border-top: 1px solid var(--line); }
.sourcerow:first-child { border-top: 0; padding-top: 0; }
.sourcetop {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  align-items: baseline;
}
.sourcetop a { font-weight: 600; overflow-wrap: anywhere; }
.host {
  font-size: 11px;
  color: var(--faint);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 7px;
}
.sourcemeta { margin-top: 3px; }
.sourcemeta .label { border: 0; padding: 0; color: var(--faint); font-size: 12px; }
.forgelink { margin: 12px 0 0; font-size: 13px; }

/* Dependency cards: enough to judge a mod without leaving the tab. */
.deplist { list-style: none; margin: 0; padding: 0; display: flex;
           flex-direction: column; gap: 10px; }
.depcard {
  display: grid;
  grid-template-columns: 40px 1fr auto;
  gap: 12px;
  align-items: center;
  padding: 10px 12px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.depcard:hover { border-color: var(--faint); }
/* An addon card carries a badge as well as its toggle, and .depcard is a
   three-column grid -- a fourth child would wrap onto a row of its own. They
   share the last column instead, so every card keeps the same skeleton. */
.depside { display: flex; align-items: center; gap: 10px; }
.depthumb {
  width: 40px; height: 40px;
  border-radius: 6px;
  object-fit: cover;
  background: var(--surface);
  display: inline-block;
}
.depmain { min-width: 0; }
.depname { font-weight: 600; font-size: 14.5px; }
.depcard .teaser {
  margin: 2px 0 0;
  color: var(--muted);
  font-size: 12.5px;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

/* --- versions ---------------------------------------------------------- */

/* Each version is a card rather than a rule-separated paragraph. With release
   notes from two sources under it, a hairline border was not enough to say
   where one version ended and the next began. */
.versions { display: flex; flex-direction: column; gap: 12px; }
.version {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}
.version .vhead {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  padding: 9px 12px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
}
.version .num {
  font-family: var(--mono);
  font-weight: 700;
  font-size: 15.5px;
  color: var(--text);
}
.version .when { color: var(--faint); font-size: 12.5px; margin-left: auto; }

/* The + that pins one release, first in the title bar so a column of versions
   offers its buttons in one line down the left edge. A size smaller than the
   same button on a card: it sits in a title bar rather than a tile's gutter,
   and next to 15.5px type a 28px control reads as the loudest thing there. */
.version .vhead .mark {
  width: 24px;
  min-width: 24px;
  height: 24px;
  font-size: 14px;
}
.version .notes { color: var(--muted); font-size: 13.5px; }

/* --- per-version source tabs -------------------------------------------- */

/* Radio inputs, not script: there is one of these per version and tabs.js
   owns a single strip, so the switch has to stand on its own. The inputs are
   hidden from sight but not from the keyboard, so tabbing still reaches them
   and the labels still toggle. */
.vsources .vin {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.vtabs {
  display: flex;
  gap: 2px;
  padding: 6px 12px 0;
  border-bottom: 1px solid var(--line);
}
.vtab, .vtab-static {
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 600;
  color: var(--faint);
  border: 1px solid transparent;
  border-bottom: 0;
  border-radius: 6px 6px 0 0;
  cursor: pointer;
}
.vtab-static { cursor: default; }
.vtab:hover { color: var(--text); }
.vsources.one .vtabs { padding-bottom: 0; }
.vpanel { display: none; padding: 10px 12px; }
/* Keyed by class rather than by id, so one rule serves every version on the
   page instead of one rule per version. */
.vin-forge:checked ~ .vpanels .p-forge,
.vin-repo:checked ~ .vpanels .p-repo { display: block; }
.vsources.one .vpanel { display: block; }
.vin-forge:checked ~ .vtabs .lab-forge,
.vin-repo:checked ~ .vtabs .lab-repo {
  color: var(--text);
  background: var(--surface);
  border-color: var(--line);
}
.vin:focus-visible ~ .vtabs .vtab { outline: 2px solid var(--accent-dim); outline-offset: 1px; }
/* Sits after .when, which takes the free space, so the glyph lands at the far
   edge of the row. Smaller than the Source panel's copy of the same icon:
   there is one per version, and at 22px a long history reads as a column of
   arrows rather than a version list. */
/* The one thing in a version header you can act on, so it carries the accent
   at rest rather than earning it on hover -- faint read as decoration and the
   button went unnoticed. Hover then nudges it down, the same acknowledgement
   the collection drawer's download link gives (.dl). */
.vdl {
  display: inline-flex;
  align-items: center;
  line-height: 1;
  color: var(--accent);
  transition: transform 0.15s ease, filter 0.15s ease;
}
.vdl svg { width: 16px; height: 16px; }
.vdl:hover { filter: brightness(1.15); transform: translateY(1px); }
/* The repository's account of the same release, set apart from the Forge's so
   the two are never read as one text. Its own rule rather than a border on
   .notes, because a version can carry both, either, or neither. */
.reponotes { margin-top: 8px; padding-left: 10px; border-left: 2px solid var(--line); }
.reponotes .label { display: inline-block; margin-bottom: 2px; }
.reponotes .notes { margin-top: 2px; }

/* --- collection: mark toggles ------------------------------------------- */

.mark {
  display: inline-flex;
  align-self: center;
  flex: none;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 28px;
  width: 28px;
  height: 28px;
  padding: 0 7px;
  background: var(--surface-2);
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 6px;
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}
.mark:hover { color: var(--text); border-color: var(--faint); }
.mark:focus-visible { outline: 2px solid var(--accent-dim); outline-offset: 1px; }

.mark.marked {
  background: color-mix(in srgb, var(--ok) 22%, transparent);
  border-color: color-mix(in srgb, var(--ok) 55%, transparent);
  color: var(--ok);
}
/* The glyph is the state: + to add, ✓ once it is in the collection. */
.mark.marked::before { content: "✓"; }
.mark.marked .mark-plus { display: none; }

/* Hovering a marked button previews what clicking will do: remove it. */
.mark.marked:hover {
  background: color-mix(in srgb, var(--warn) 20%, transparent);
  border-color: color-mix(in srgb, var(--warn) 55%, transparent);
  color: var(--warn);
}
.mark.marked:hover::before { content: "×"; }

/* Only a marked button reserves room for both labels, by stacking them in one
   grid cell: it is the one that swaps text on hover, and a button that resizes
   under the cursor shoves whatever sits beside it. "Remove" is the shorter of
   the two, so this costs nothing today -- it holds the width at whichever
   label is longer, whatever either is later reworded to. An unmarked button
   never shows the hover label and stays the width of "Add to collection". */
.mark .lbl-hover { display: none; }
.mark.marked .mark-label { display: inline-grid; grid-template-areas: "label"; }
.mark.marked .mark-label > span { grid-area: label; }
/* Hidden with visibility, not display, so it still reserves the width. */
.mark.marked .lbl-hover { display: block; visibility: hidden; }
.mark.marked:hover .lbl-state { visibility: hidden; }
.mark.marked:hover .lbl-hover { visibility: visible; }
.mark:not(.marked).mark-wide::before { content: "+"; }
.mark.mark-wide {
  width: auto;
  /* Fixed rather than auto: as a flex item, "auto" lets the button stretch to
     whatever its container is, which made it balloon when the label changed. */
  height: 28px;
  padding: 4px 10px;
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
}
.mark-label { font-size: inherit; }

.modhead-main { flex: 1; min-width: 0; }
.modhead-title {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  justify-content: space-between;
}
.modhead-title h1 { min-width: 0; word-break: break-word; }
.modhead-title .mark { flex: none; }

/* --- collection: drawer -------------------------------------------------- */

/* The opener lives in the header, so nothing floats over the page while you
   browse. The header sticks so it stays reachable. */
.masthead { position: sticky; top: 0; z-index: 35; }
.masthead-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 7px;
}

.collection-open {
  display: inline-flex;
  align-items: center;
  /* Explicit height and line-height: the button holds a text label and a
     pill badge with different natural line boxes, and letting it size to
     content made it grow to fit both stacked. */
  height: 32px;
  line-height: 1;
  gap: 7px;
  padding: 0 13px;
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.collection-open:hover { border-color: var(--accent-dim); color: var(--accent); }
.collection-count {
  display: inline-flex;
  align-items: center;
  line-height: 1;
  background: var(--accent);
  color: #14171c;
  border-radius: 999px;
  padding: 1px 7px;
  font-size: 11.5px;
  font-weight: 700;
}
.collection-open.is-empty .collection-count { background: var(--faint); }

.drawer-backdrop {
  position: fixed;
  inset: 0;
  z-index: 44;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 0.2s ease;
}
.drawer-backdrop.open { opacity: 1; }

.collection-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 45;
  width: min(380px, 92vw);
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-left: 1px solid var(--line);
  box-shadow: -18px 0 50px rgba(0, 0, 0, 0.4);
  transform: translateX(100%);
  transition: transform 0.2s ease;
}
.collection-drawer.open { transform: none; }

.drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 16px 18px 12px;
}
.drawer-head h2 {
  margin: 0;
  font-size: 16px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.drawer-close {
  background: none;
  border: 0;
  color: var(--muted);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
}
.drawer-close:hover { color: var(--text); }

.collection-head {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0 18px 14px;
  border-bottom: 1px solid var(--line);
}
.collection-head .linkbtn { font-size: 12px; padding: 4px 8px; }
.linkbtn.danger:hover { color: var(--warn); border-color: var(--warn); }

.collection-list {
  list-style: none;
  margin: 0;
  padding: 10px 18px 18px;
  overflow-y: auto;
  flex: 1;
  font-size: 13.5px;
}
.collection-list li {
  display: flex;
  gap: 8px;
  align-items: baseline;
  padding: 5px 0;
  border-bottom: 1px solid var(--line);
}
.collection-list li:last-child { border-bottom: 0; }

/* Dependencies are indented under the mod that required them. */
.collection-list li.dependency {
  padding-left: 14px;
  border-bottom: 0;
  font-size: 12.5px;
  color: var(--muted);
}
.collection-list li.dependency a { color: var(--muted); }
.collection-list li.dependency + li:not(.dependency) {
  border-top: 1px solid var(--line);
  padding-top: 8px;
}
.dep-mark { flex: none; color: var(--faint); font-size: 11px; }

/* Download shortcut, left of the name. A disabled placeholder keeps names
   aligned when a mod has no repository we can build a releases URL for. */
.dl {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  line-height: 1;
  text-decoration: none;
  color: #fff;
  opacity: 0.7;
  transition: opacity 0.15s ease, transform 0.15s ease;
}
a.dl:hover { opacity: 1; transform: translateY(1px); text-decoration: none; }
.dl.empty { opacity: 0.18; }

/* White reads as an icon on the dark theme; on light it would vanish. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .dl { color: var(--text); }
}

@media (prefers-reduced-motion: reduce) { .dl { transition: none; } }

.collection-list li > a:not(.dl) { flex: 1; min-width: 0; overflow-wrap: anywhere; }

/* The release a row means. Monospace because it is a version number, and set
   apart from the name because it is doing a job: it says which build the
   download beside it is.

   Accented when it was pinned, faint when the row is simply following the
   latest. Same shape, because both answer "which version"; different weight,
   because only one of them is a decision -- a pinned 1.4.2 stays 1.4.2, while
   a floating one is today's answer and will change without asking. Rows in a
   long collection are scanned, not read, so the colour is what carries that. */
.collection-list .pin {
  flex: none;
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--accent);
}
.collection-list .pin.floating { color: var(--faint); }
.collection-remove {
  flex: none;
  background: none;
  border: 0;
  color: var(--faint);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 0 2px;
}
.collection-remove:hover { color: var(--warn); }
.collection-list li.collection-empty {
  display: block;
  color: var(--faint);
  border: 0;
  padding: 14px 0;
  line-height: 1.5;
}
.collection-list li.collection-empty strong { color: var(--muted); }

@media (prefers-reduced-motion: reduce) {
  .collection-drawer, .drawer-backdrop { transition: none; }
}

/* --- collection: toasts --------------------------------------------------- */

.toast-stack {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}

.toast {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--ok);
  border-left-width: 3px;
  border-radius: var(--radius);
  padding: 10px 14px;
  font-size: 13.5px;
  font-weight: 600;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
  animation: toast-in 0.18s ease-out;
}
.toast.out { opacity: 0; transform: translateY(-6px); transition: 0.5s ease; }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
  .toast.out { transition: opacity 0.5s linear; transform: none; }
}

/* --- collection: shared-link modal --------------------------------------- */

/* A bar at the top of the page was too easy to scroll past for something that
   can replace the reader's whole collection. This blocks until answered. */
.import-modal {
  max-width: 460px;
  padding: 22px 24px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.45);
}
.import-modal::backdrop { background: rgba(0, 0, 0, 0.6); }
.import-modal h2 { margin: 0 0 10px; font-size: 18px; }
.import-modal p { margin: 0 0 10px; color: var(--muted); font-size: 14px; }
.import-modal .note { color: var(--warn); font-size: 13px; }

.modal-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 18px;
}
.linkbtn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #14171c;
  font-weight: 700;
}
.linkbtn.primary:hover { color: #14171c; filter: brightness(1.08); }

/* --- tabs --------------------------------------------------------------- */

.tabs {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-bottom: 16px;
  overflow: hidden;
}

.tablist {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 0 8px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
}

/* Inside the shell the panels are plain content: the shell already draws the
   border, so a panel of its own would double it. */
.tabs .tabpanel {
  background: transparent;
  border: 0;
  border-radius: 0;
  margin: 0;
}

.tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 9px 15px;
  color: var(--muted);
  font-size: 14px;
  font-weight: 600;
  border: 1px solid transparent;
  border-bottom: 0;
  border-radius: var(--radius) var(--radius) 0 0;
  margin-bottom: -1px;
  cursor: pointer;
  text-decoration: none;
}
.tab:hover { color: var(--text); text-decoration: none; }

/* Selected state is driven by aria-selected, so the styling cannot drift out
   of step with what assistive technology is told. */
/* The selected tab shares the panel's background and hides the divider
   beneath it, so it reads as continuous with the content. */
.tab[aria-selected="true"] {
  color: var(--text);
  background: var(--surface);
  border-color: var(--line);
  border-bottom: 1px solid var(--surface);
}
.tab:focus-visible { outline: 2px solid var(--accent-dim); outline-offset: -2px; }

.tabcount {
  font-size: 11.5px;
  font-weight: 700;
  color: var(--faint);
  background: var(--surface-2);
  border-radius: 999px;
  padding: 1px 7px;
}
.tab[aria-selected="true"] .tabcount { color: var(--accent); }

.tabpanel:focus { outline: none; }

/* Until tabs.js promotes them, the panels are ordinary stacked sections and
   each needs its own heading. Once it has, the tab *is* the heading. */
#mod-tabs[data-enhanced] .panel-heading { display: none; }

.panel-note { margin: 0 0 12px; color: var(--muted); font-size: 13px; }

/* --- comments ---------------------------------------------------------- */

details.comments { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); }
details.comments > summary {
  padding: 14px 18px;
  cursor: pointer;
  font-weight: 600;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
}
details.comments > summary::-webkit-details-marker { display: none; }
details.comments > summary::before {
  content: "▸";
  color: var(--faint);
  transition: transform 0.15s;
}
details.comments[open] > summary::before { transform: rotate(90deg); }
details.comments > summary .count { color: var(--faint); font-weight: 400; }
.comments-body { padding: 0 18px 18px; }

.comment-controls {
  display: grid;
  grid-template-columns: 1fr 180px;
  gap: 10px;
  margin-bottom: 14px;
}
@media (max-width: 560px) { .comment-controls { grid-template-columns: 1fr; } }

.thread { display: flex; flex-direction: column; gap: 12px; }
.comment {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 11px 13px;
}
.comment .chead { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; font-size: 13px; }
.comment .who { font-weight: 700; }
.comment .when { color: var(--faint); font-size: 12px; }
.comment .likes { color: var(--faint); font-size: 12px; margin-left: auto; }
.comment .cbody { margin-top: 6px; font-size: 14px; }
.comment .cbody p:first-child { margin-top: 0; }
.comment .cbody p:last-child { margin-bottom: 0; }
.replies { margin-top: 10px; padding-left: 14px; border-left: 2px solid var(--line); display: flex; flex-direction: column; gap: 10px; }
.replies .comment { background: var(--surface); }

mark { background: color-mix(in srgb, var(--accent) 35%, transparent); color: inherit; border-radius: 2px; }

/* Comment search hides threads with [hidden]. Flex and grid children ignore
   the UA stylesheet's display:none, so state it here where it cannot lose. */
[hidden] { display: none !important; }

.empty { color: var(--faint); padding: 20px 0; text-align: center; }

footer.site {
  margin-top: 40px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  color: var(--faint);
  font-size: 12.5px;
}
