/*
  Greybear's Rest — shared component patterns (site-build-spec.md Section 5/8).
  Nav, glass/solid panel, card, crest badge, buttons, footer — written against
  tokens.css only. Requires tokens.css to be loaded first.
*/

/* ---------- Base ---------- */

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  /* Bakes in the ~125% browser-zoom look this site was designed at, site-wide,
     without touching any px/rem values individually. `zoom` (not `transform:
     scale`) is used deliberately — it reflows layout the same way the browser's
     own zoom does, rather than visually scaling a fixed-size box. */
  zoom: 1.25;
}

body {
  margin: 0;
  /* Ambient top-of-page wash — same two colors and positions as the Puzzle
     Ring Problem chronicle's own body gradient (blood red, then ancient
     gold), ported site-wide instead of being one article's own look.
     Sized in plain vh (not the source page's %, and not max(%, vh) —
     that mix of percentage and viewport units inside a gradient's size
     was silently invalid and dropped the whole background-image
     declaration in testing) so the reach is a large, fixed multiple of
     the screen height on every page regardless of how long it is.
     world-header.css layers an additional, concentrated accent-colored
     glow on top of this for .world-hero / .hub-nav pages. */
  background-color: var(--ink);
  background-image:
    radial-gradient(ellipse 80% 180vh at 50% -10%, color-mix(in srgb, var(--blood) 16%, transparent), transparent),
    radial-gradient(ellipse 60% 140vh at 90% 10%, color-mix(in srgb, var(--ancient) 5%, transparent), transparent);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.15;
  margin: 0 0 var(--space-4);
  color: var(--text);
}

p {
  margin: 0 0 var(--space-4);
}

a {
  color: var(--accent, var(--brass));
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

img {
  max-width: 100%;
  display: block;
}

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

.skip-link {
  position: absolute;
  left: var(--space-4);
  top: -48px;
  background: var(--panel-solid);
  color: var(--text);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--hairline-soft);
  z-index: 100;
  transition: top 0.15s ease;
}

.skip-link:focus {
  top: var(--space-4);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-mute);
}

/* Three-beam signature graphic (legendary/ancient/blood), from the Puzzle
   Ring Problem chronicle's own hero — ported here so every hero-style
   section site-wide (except the homepage) can drop it in the same way. */
.beam-signature {
  width: 100%;
  max-width: 420px;
  height: 90px;
  margin: 0 auto 36px;
  display: block;
}

/* ---------- Nav ---------- */

.site-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3) var(--space-5);
  padding: var(--space-4) clamp(var(--space-4), 4vw, var(--space-8));
  background: var(--ink);
  border-bottom: 2px solid color-mix(in srgb, var(--accent, var(--brass)) 45%, transparent);
}

.site-nav__brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--text);
}

.site-nav__brand:hover {
  text-decoration: none;
}

.site-nav__crest {
  width: 32px;
  height: 32px;
  color: var(--accent, var(--brass));
  flex-shrink: 0;
}

.site-nav__wordmark {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: 0.01em;
}

.site-nav__links {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  list-style: none;
  margin: 0;
  padding: 0;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.site-nav__links a {
  color: var(--text);
}

.site-nav__links a:hover,
.site-nav__links a[aria-current="page"] {
  color: var(--accent, var(--brass));
  text-decoration: none;
}

/* ---------- Panels ---------- */

.panel {
  border-radius: var(--radius-sm);
  border: 1px solid var(--hairline-soft);
  padding: var(--space-5);
}

.panel--glass {
  background: var(--panel);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.panel--solid {
  background: var(--panel-solid);
}

/* ---------- Card ---------- */

.card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  background: var(--panel-solid);
  border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
  padding: var(--space-5);
  color: inherit;
  transition: border-color 0.15s ease, transform 0.15s ease;
}

.card:hover,
.card:focus-within {
  border-color: var(--accent, var(--brass));
}

@media (prefers-reduced-motion: no-preference) {
  .card:hover,
  .card:focus-within {
    transform: translateY(-2px);
  }
}

.card__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.3rem;
  margin: 0;
  color: var(--text);
}

.card__meta {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  color: var(--text-mute);
}

.card__link {
  position: absolute;
  inset: 0;
}

.card {
  position: relative;
}

/* ---------- Hub nav (persistent on all pages within a hub — Section 4.2) ---------- */

.hub-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3) var(--space-5);
  padding: var(--space-3) clamp(var(--space-4), 4vw, var(--space-8));
  background: var(--panel-solid);
  border-bottom: 1px solid var(--hairline-soft);
}

.hub-nav__name {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--accent, var(--brass));
}

.hub-nav__crest {
  width: 24px;
  height: 24px;
  color: var(--accent, var(--brass));
  flex-shrink: 0;
}

.hub-nav__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  list-style: none;
  margin: 0;
  padding: 0;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.hub-nav__links a {
  color: var(--text);
}

.hub-nav__links a:hover,
.hub-nav__links a[aria-current="page"] {
  color: var(--accent, var(--brass));
  text-decoration: none;
}

/* ---------- World overview (Section 4.3) ---------- */

.world-hero {
  text-align: center;
  padding: clamp(var(--space-7), 10vw, 100px) clamp(var(--space-4), 4vw, var(--space-8));
  background:
    radial-gradient(ellipse 80% 60% at 50% -10%, color-mix(in srgb, var(--accent, var(--brass)) 24%, transparent), transparent),
    var(--ink);
  border-bottom: 1px solid var(--hairline-soft);
}

.world-hero h1 {
  font-size: clamp(2.2rem, 5vw, 3.2rem);
  margin: var(--space-2) 0 var(--space-4);
}

.world-hero__tagline {
  max-width: 620px;
  margin: 0 auto;
  color: var(--text-dim);
  font-size: clamp(1rem, 2vw, 1.1rem);
}

/* An emptied-out tagline (no static copy, just the lore-count line that
   JS inserts right after it) collapses to nothing instead of leaving a
   gap, so the lore-count reads as sitting directly under the H1. */
.world-hero__tagline:empty {
  display: none;
}

.world-hero__lore-count {
  max-width: 620px;
  margin: var(--space-3) auto 0;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.02em;
  color: var(--accent, var(--brass));
}

.world-section {
  max-width: 900px;
  margin: 0 auto;
  padding: var(--space-7) clamp(var(--space-4), 4vw, var(--space-8));
}

/* Catalog pages (maps/chronicles/hall of heroes) pull their search bar and
   results up closer to the tagline instead of the default wide hero gap. */
.world-hero--tight {
  padding-bottom: var(--space-4);
}

.world-hero--tight + .world-section {
  padding-top: var(--space-4);
}

.world-section h2 {
  font-size: 1.5rem;
  margin-bottom: var(--space-4);
}

.region-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-4);
}

.region-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  color: inherit;
  padding: var(--space-3);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  transition: border-color 0.15s ease, transform 0.15s ease;
}

.region-card:hover,
.region-card:focus-visible {
  border-color: var(--accent, var(--brass));
  text-decoration: none;
}

@media (prefers-reduced-motion: no-preference) {
  .region-card:hover,
  .region-card:focus-visible {
    transform: translateY(-2px);
  }
}

.region-card__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
  margin: 0;
  color: var(--text);
}

.region-card__desc {
  margin: 0;
  font-size: 0.9rem;
  color: var(--text-dim);
}

/* Same fill-plus-border treatment as .map-card__thumb--map, for a region
   card's real screenshot once one exists (falls back to .placeholder-slot,
   which is already sized the same way, until then). */
.region-card__thumb {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
}

.world-maps-cta {
  text-align: center;
  padding: var(--space-3) 0 var(--space-8);
}

/* ---------- Placeholder slot (screenshot/art stand-in until real assets exist) ---------- */

.placeholder-slot {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  aspect-ratio: 16 / 9;
  border: 1px dashed var(--hairline-soft);
  border-radius: var(--radius-sm);
  color: var(--text-mute);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: var(--space-4);
}

.map-card__thumb {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-radius: var(--radius-sm);
}

/* Map thumbnails (as opposed to character portraits above) are a plain
   rectangle, not a portrait crop — but like the catalog's other thumbs, the
   preview should still fill the box (cover, zooming/cropping as needed).
   The full detail view (.map-detail__image below) is where the whole map is
   shown uncropped. Most source scans have a white background, so the border
   is what keeps the thumb from bleeding into the page. */
.map-card__thumb--map {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
}

/* Same frame treatment for a map's full detail-page image. */
.map-detail__image {
  max-width: 100%;
  display: block;
  margin: 0 auto;
  background: var(--panel-solid);
  border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
}

/* ---------- Vcard gear/stats slots ---------- */

/* Same box-fill-plus-border idea as .map-card__thumb--map: a fixed-size
   frame so both slots in a gear-grid row match, regardless of the source
   screenshot's actual dimensions. Clicking (or Enter/Space, for keyboard
   users) opens the shared .lightbox below with a full-size, uncropped
   version — gear/character-sheet screenshots are usually too detailed to
   read at thumbnail size. Wired up via assets/scripts/lightbox.js. */
.gear-slot {
  position: relative;
}

.gear-slot__thumb {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
  cursor: zoom-in;
}

/* A confined popup box, not a full-bleed zoom — lives once per page as a
   direct child of <body> (outside the narrow vcard-split column) so it's
   never constrained by that column's width or any ancestor's
   backdrop-filter/transform containing-block quirks. The box itself is a
   fixed size regardless of the source image's aspect ratio, so every
   popup reads as the same consistent size — the image fits inside via
   object-fit: contain rather than dictating the box. Sized well under the
   viewport (not 100%) so the page behind stays visible as a backdrop —
   this is a flyout, not a fullscreen takeover. Also capped in px: the
   site's site-wide `zoom: 1.25` (see components.css base rules) inflates
   vw/vh-sized boxes on rendering, so a naive 85vh/85vw here visually
   overflowed the real viewport. */
.lightbox {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.8);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.15s ease, visibility 0.15s;
  z-index: 100;
  cursor: zoom-out;
}

.lightbox.is-visible {
  opacity: 1;
  visibility: visible;
}

.lightbox__box {
  width: min(58vw, 760px);
  height: min(58vh, 560px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  background: var(--panel-solid);
  border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
  padding: var(--space-4);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
  cursor: default;
}

.lightbox__image {
  flex: 1;
  min-height: 0;
  max-width: 100%;
  object-fit: contain;
}

/* A real labeled button above the image, not a small corner icon —
   deliberately hard to miss. */
.lightbox__close {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
  background: var(--panel-solid);
  border: 1px solid var(--accent, var(--brass));
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-5);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.lightbox__close:hover,
.lightbox__close:focus-visible {
  background: var(--accent, var(--brass));
  color: var(--ink);
}

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

/* ---------- Crest badge ---------- */

.crest-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  color: var(--accent, var(--brass));
  flex-shrink: 0;
}

.crest-badge svg {
  width: 100%;
  height: 100%;
}

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

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent, var(--brass));
  color: var(--accent, var(--brass));
  background: transparent;
  transition: background 0.15s ease, color 0.15s ease;
}

.btn:hover {
  background: var(--accent, var(--brass));
  color: var(--ink);
  text-decoration: none;
}

.btn--sm {
  font-size: 0.72rem;
  padding: var(--space-2) var(--space-4);
}

/* ---------- Maps catalog (Section 4.8) ---------- */

.maps-catalog__controls {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}

.maps-catalog__search {
  width: 100%;
  max-width: 420px;
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--text);
  background: var(--panel-solid);
  border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
}

.maps-catalog__search::placeholder {
  color: var(--text-mute);
}

.maps-catalog__select {
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--text);
  background: var(--panel-solid);
  border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
}

.filter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.filter-chip {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-dim);
  background: var(--panel-solid);
  border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  transition: border-color 0.15s ease, color 0.15s ease;
}

.filter-chip:hover {
  color: var(--text);
}

.filter-chip.is-active {
  color: var(--accent, var(--brass));
  border-color: var(--accent, var(--brass));
}

.maps-catalog__count {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  color: var(--text-mute);
  margin: 0 0 var(--space-4);
  min-height: 1.2em;
}

.maps-catalog__empty {
  color: var(--text-mute);
  padding: var(--space-6) 0;
}

.map-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: var(--space-5);
}

/* ---------- Pagination ---------- */

.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--hairline-soft);
}

.pagination__size {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-mute);
}

.pagination__size-select {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--text);
  background: var(--panel-solid);
  border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
}

.pagination__pages {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.pagination__nav,
.pagination__page {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  min-width: 2.2em;
  text-align: center;
  color: var(--text-dim);
  background: var(--panel-solid);
  border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  transition: border-color 0.15s ease, color 0.15s ease;
}

.pagination__nav:hover,
.pagination__page:hover {
  color: var(--text);
}

.pagination__nav:disabled {
  opacity: 0.35;
  cursor: default;
}

.pagination__page.is-active {
  color: var(--accent, var(--brass));
  border-color: var(--accent, var(--brass));
}

.pagination__ellipsis {
  color: var(--text-mute);
  padding: 0 var(--space-1);
}

.map-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  color: inherit;
  padding: var(--space-3);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  transition: border-color 0.15s ease, transform 0.15s ease;
}

.map-card:hover,
.map-card:focus-visible {
  border-color: var(--accent, var(--brass));
  text-decoration: none;
}

@media (prefers-reduced-motion: no-preference) {
  .map-card:hover,
  .map-card:focus-visible {
    transform: translateY(-2px);
  }
}

.map-card__badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* Pulls the badges row up closer to the class line right above it on
   character cards (roster + Hall of Heroes) — the default <p> bottom
   margin otherwise leaves too much air between them. */
.map-card__body > .card__meta {
  margin-bottom: var(--space-2);
}

.badge {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.2em 0.55em;
  border-radius: var(--radius-sm);
  border: 1px solid var(--hairline-soft);
  color: var(--text-mute);
}

.badge--world {
  color: var(--accent, var(--brass));
  /* color-mix rather than var(--accent-dim, ...) — only D3 defines --accent-dim,
     so the fallback silently mismatched the border against the text on every
     other hub (blue/red/violet text, brass border). This derives the dim
     border tone directly from whatever --accent actually resolves to. */
  border-color: color-mix(in srgb, var(--accent, var(--brass)) 55%, transparent);
}

.badge--fallen {
  color: #e0574a;
  border-color: color-mix(in srgb, #e0574a 55%, transparent);
}

.badge--retired {
  color: var(--text-mute);
  border-color: color-mix(in srgb, var(--text-mute) 55%, transparent);
}

.badge--cross-link {
  background: var(--accent, var(--brass));
  color: var(--ink);
  border-color: transparent;
  /* 15% larger than the base .badge size (padding scales with it via em units) */
  font-size: calc(0.7rem * 1.15);
}

.map-card__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
  margin: 0;
  color: var(--text);
}

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

.site-footer {
  background: var(--ink);
  padding: calc(var(--space-8) * 0.75) clamp(var(--space-4), 4vw, var(--space-8)) calc(var(--space-6) * 0.75);
  border-top: 1px solid var(--hairline-soft);
  text-align: center;
}

.site-footer__crest {
  width: 30px;
  height: 30px;
  margin: 0 auto var(--space-4);
  color: var(--accent, var(--brass-dim));
}

.site-footer__fine-print {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--text-mute);
}

.site-footer__fine-print a {
  color: var(--text-dim);
}

.site-footer__fine-print a:hover {
  color: var(--brass);
}

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  * {
    transition-duration: 0.001ms !important;
    animation-duration: 0.001ms !important;
  }
}
