/* =============================================================================
   Legends of Tolria — hub. Tokens, typography, and the shared deco primitives.

   Direction: art deco geometry (symmetry, brass hairlines, fan rays, lozenges,
   letterspaced caps) built on a deep-space base, with violet→magenta rift energy
   as the only other accent. Boldness is spent on the rift; everything else stays
   quiet.
   ========================================================================== */

:root {
  /* --- palette ---------------------------------------------------------- */
  --void: #05060f; /* page base — near-black, blue bias */
  --abyss: #090b1a; /* alternating band */
  --panel: #10132a; /* raised surfaces */
  --panel-hi: #171b38; /* hover / active surfaces */

  --brass: #cba75b; /* deco metal — rules, labels, markers */
  --brass-bright: #ebd4a0; /* focus rings, hover metal */
  --brass-deep: #6b5730; /* hairlines at rest */

  --violet: #6f3bee; /* rift core */
  --magenta: #f04fd4; /* rift crest */

  --parchment: #e9e4da; /* body text, warm to sit with brass */
  --muted: #969cbe; /* secondary text, cool */
  /* Tertiary — captions and micro-labels. Light enough to clear 4.5:1 on every
     surface it lands on, including the cartouche, which is the palest. */
  --faint: #8087ab;

  /* --- type ------------------------------------------------------------- */
  --display: 'Poiret One', 'Didot', 'Bodoni MT', serif;
  --body: 'Jost', 'Futura', 'Century Gothic', system-ui, sans-serif;
  --data: 'DM Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;

  --fs-eyebrow: 0.7rem;
  --fs-micro: 0.75rem;
  --fs-sm: 0.875rem;
  --fs-body: 1rem;
  --fs-lead: clamp(1.05rem, 1.6vw, 1.2rem);
  --fs-h3: clamp(1.05rem, 1.5vw, 1.25rem);
  --fs-h2: clamp(1.55rem, 3.2vw, 2.3rem);
  --fs-h1: clamp(2.1rem, 6.4vw, 4.4rem);

  --track-wide: 0.3em; /* eyebrows and micro-labels */
  --track-display: 0.16em; /* deco display caps */

  /* The four-point star, defined once, and the only marker shape the hub uses:
     waypoints, nav gems, milestones, checkbox ticks, rule ornaments. The inner
     vertices sit at 37/63 rather than tighter — any thinner and it stops
     reading as a star at 12–16px and starts reading as a plus sign. */
  --star: polygon(50% 0%, 63% 37%, 100% 50%, 63% 63%, 50% 100%, 37% 63%, 0% 50%, 37% 37%);

  /* The shine — one light source for every star that can catch it. The waypoint
     on the loop set these values; everything else now reads them from here, so
     a nav gem, a rule ornament and a roadmap marker all light the same way.
     `--star-flare` is the pair of rays: the gradient runs the length of each
     one, so it tapers to nothing at the tips and reads as a glint rather than a
     crosshair. */
  --star-lit: #d8bcff;
  --star-lit-hot: #ffe4fa;
  --star-bloom: drop-shadow(0 0 7px rgba(178, 120, 255, 0.95)) drop-shadow(0 0 16px rgba(240, 79, 212, 0.55));
  --star-bloom-hot: drop-shadow(0 0 8px rgba(240, 79, 212, 1)) drop-shadow(0 0 20px rgba(240, 79, 212, 0.7));
  --star-grow: 1.3;
  --star-flare: linear-gradient(90deg, transparent, rgba(232, 205, 255, 0.85) 50%, transparent) center /
      100% 1.5px no-repeat,
    linear-gradient(180deg, transparent, rgba(232, 205, 255, 0.85) 50%, transparent) center / 1.5px 100%
      no-repeat;

  /* --- space ------------------------------------------------------------ */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.25rem;
  --sp-6: 1.5rem;
  --sp-8: 2rem;
  --sp-10: 2.5rem;
  --sp-12: 3rem;
  --sp-16: 4rem;
  --sp-20: 5rem;
  --sp-24: 6rem;

  --measure: 62ch;
  --shell: 1180px;
  --gutter: clamp(1.25rem, 4vw, 3rem);

  /* The wordmark art ships rose-copper. This swings it to the violet end and
     keeps the metal reading as metal — luminance is preserved, so the bevels
     and highlights survive the shift. */
  --wordmark-tint: hue-rotate(232deg) saturate(1.75) brightness(0.94) contrast(1.1);

  /* --- motion ----------------------------------------------------------- */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-deco: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast: 160ms;
  --dur: 320ms;
  --dur-slow: 620ms;
  --flow: 9s; /* one full circuit of rift energy */
}

/* ------------------------------------------------------------------ shine -- */

/* The ray flare, in one block for every star on the site. It lives here rather
   than beside each star because the whole point is that there is one of it:
   the loop's waypoints, the console bar's gems, the rule ornament and the
   roadmap's markers all throw the same glint.

   The host is the box the star sits in, never the star itself — a `clip-path`
   clips an element's pseudo-elements too, so a flare hung off a clipped star
   would be cut to the star's own silhouette. Each site sets `--flare` to about
   three times its star's width. */
.node__mark::before,
.navlink__gem::before,
.divider::before,
.lane__flare {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--flare, 38px);
  height: var(--flare, 38px);
  pointer-events: none;
  background: var(--star-flare);
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.45);
  transition:
    opacity var(--dur) var(--ease),
    transform var(--dur-slow) var(--ease-deco);
}

/* Lit. The star brightens and blooms; the rays fade up and open out. */
.node:hover .node__mark::after,
.node:focus-visible .node__mark::after,
.navlink:hover .navlink__gem::after,
.navlink:focus-visible .navlink__gem::after,
.divider:hover::after,
.lane__mark:hover::before,
.lane__mark:focus-visible::before {
  background: var(--star-lit);
  filter: var(--star-bloom);
  transform: translate(-50%, -50%) scale(var(--star-grow));
}

.node:hover .node__mark::before,
.node:focus-visible .node__mark::before,
.navlink:hover .navlink__gem::before,
.navlink:focus-visible .navlink__gem::before,
.divider:hover::before,
.lane__mark:hover .lane__flare,
.lane__mark:focus-visible .lane__flare {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

/* ------------------------------------------------------------------ reset -- */

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

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Keeps anchored and scrolled-to content clear of the sticky console bar. */
  scroll-padding-top: 84px;
}

body {
  min-height: 100vh;
  background-color: var(--void);
  color: var(--parchment);
  font-family: var(--body);
  font-size: var(--fs-body);
  font-weight: 300;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

a {
  color: inherit;
  text-decoration: none;
}

ul,
ol {
  list-style: none;
  padding: 0;
}

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

::selection {
  background: color-mix(in srgb, var(--magenta) 40%, transparent);
  color: #fff;
}

/* ------------------------------------------------------------- the comet -- */

/* Above the page, below the intro overlay, and never in the way of a click. */
.comet {
  position: fixed;
  inset: 0;
  z-index: 90;
  pointer-events: none;
  mix-blend-mode: screen;
}

/* ------------------------------------------------------------ the console -- */

/* The bar is the only navigation, so it stays put on every view. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  background: linear-gradient(180deg, rgba(5, 6, 15, 0.94), rgba(5, 6, 15, 0.72));
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid rgba(107, 87, 48, 0.4);
}

/* The brass underline is doubled with a violet thread — the same two-accent
   pairing the rift runs on, at hairline scale. */
.topbar::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(111, 59, 238, 0.55) 30%, rgba(240, 79, 212, 0.5) 50%, rgba(111, 59, 238, 0.55) 70%, transparent);
}

/* Three columns with equal outer tracks, so the nav is centred on the page
   rather than centred in whatever is left over after the wordmark and the
   outbound link — those two are never the same width. */
.topbar__inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--sp-6);
  min-height: 62px;
  padding-block: var(--sp-2);
}

.topbar__brand {
  flex: none;
  display: block;
  line-height: 0;
}

.topbar__brand img {
  width: 132px;
  height: auto;
  filter: var(--wordmark-tint) drop-shadow(0 0 10px rgba(150, 90, 255, 0.4));
  transition: filter var(--dur) var(--ease);
}

.topbar__brand:hover img,
.topbar__brand:focus-visible img {
  filter: var(--wordmark-tint) drop-shadow(0 0 16px rgba(200, 130, 255, 0.65));
}

.topbar__nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-1);
}

.navlink {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0.7em 0.95em;
  font-family: var(--data);
  font-size: var(--fs-micro);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid transparent;
  transition:
    color var(--dur) var(--ease),
    border-color var(--dur) var(--ease),
    background var(--dur) var(--ease);
}

/* Same star as the waypoints, at menu scale — and the same shine, which is why
   the gem is an empty host and the star is its `::after`. Put the clip-path on
   the gem itself and it would cut the flare to the star's own outline. */
.navlink__gem {
  position: relative;
  width: 11px;
  height: 11px;
  flex: none;
  --flare: 30px;
}

.navlink__gem::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 11px;
  height: 11px;
  transform: translate(-50%, -50%);
  background: var(--brass-deep);
  clip-path: var(--star);
  transition:
    background var(--dur) var(--ease),
    filter var(--dur) var(--ease),
    transform var(--dur) var(--ease-deco);
}

.navlink:hover,
.navlink:focus-visible {
  color: var(--brass-bright);
  border-color: rgba(107, 87, 48, 0.55);
}

.navlink[aria-current='page'] {
  color: var(--brass-bright);
  border-color: var(--brass-deep);
  background: rgba(23, 27, 56, 0.6);
}

/* Hover is the shared shine. The current page holds it, one notch hotter. */
.navlink[aria-current='page'] .navlink__gem::after {
  background: var(--star-lit-hot);
  filter: var(--star-bloom-hot);
}

.navlink[aria-current='page'] .navlink__gem::before {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

.topbar__out {
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--data);
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--brass);
  transition: color var(--dur) var(--ease);
}

.topbar__out:hover,
.topbar__out:focus-visible {
  color: var(--brass-bright);
}

@media (max-width: 760px) {
  /* Two rows: the wordmark and the outbound link, then the nav across the full
     width beneath them. Three labels and a wordmark do not share a line. */
  .topbar__inner {
    grid-template-columns: auto 1fr;
    gap: var(--sp-3);
  }

  .topbar__brand img {
    width: 108px;
  }

  .topbar__nav {
    grid-column: 1 / -1;
    grid-row: 2;
    flex-wrap: wrap;
    border-top: 1px solid rgba(107, 87, 48, 0.25);
    padding-top: var(--sp-2);
  }

  .navlink {
    letter-spacing: 0.12em;
    padding: 0.6em 0.7em;
  }

  .topbar__out {
    grid-column: 2;
    grid-row: 1;
  }
}

/* Small phones: tighten enough that the three labels stay on one line. */
@media (max-width: 400px) {
  .navlink {
    font-size: 0.66rem;
    letter-spacing: 0.04em;
    padding: 0.55em 0.4em;
    gap: var(--sp-1);
  }

  .navlink__gem {
    width: 9px;
    height: 9px;
  }
}

/* ------------------------------------------------------------- the intro -- */

/* The overlay is decided in a blocking head script, so a returning visitor gets
   `skip` before anything paints and the video is never fetched. */
.intro {
  display: none;
}

html[data-intro='play'] .intro {
  display: grid;
  place-items: center;
  position: fixed;
  inset: 0;
  z-index: 200;
  background: radial-gradient(ellipse at center, #0a0e12 0%, #000 78%);
  transition: opacity 700ms var(--ease);
}

html[data-intro='play'] body {
  overflow: hidden;
}

.intro.is-leaving {
  opacity: 0;
  pointer-events: none;
}

/* `contain` rather than `cover`: the wordmark sits mid-frame and a portrait
   phone would crop straight through it. */
.intro__video {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.intro__skip {
  position: absolute;
  right: clamp(1rem, 4vw, 2.5rem);
  bottom: clamp(1rem, 4vw, 2.5rem);
  padding: 0.7em 1.3em;
  border: 1px solid rgba(203, 167, 91, 0.55);
  color: var(--brass-bright);
  background: rgba(5, 6, 15, 0.6);
  font-family: var(--data);
  font-size: var(--fs-micro);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  transition:
    border-color var(--dur) var(--ease),
    background var(--dur) var(--ease);
}

.intro__skip:hover,
.intro__skip:focus-visible {
  border-color: var(--brass-bright);
  background: rgba(23, 27, 56, 0.85);
}

/* -------------------------------------------------------------- the sky --- */

/* Two star layers over a violet rift-glow, all fixed so the page scrolls
   through the sky rather than dragging it along. */
.sky {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 70% 42% at 50% 22%, rgba(111, 59, 238, 0.2), transparent 68%),
    radial-gradient(ellipse 46% 30% at 50% 20%, rgba(240, 79, 212, 0.09), transparent 70%),
    linear-gradient(180deg, var(--void) 0%, #070818 46%, var(--void) 100%);
}

/* Each layer travels exactly one tile width, so the pattern lands back on
   itself and the loop is invisible. The box carries that much slack on its
   right so the layer never slides clear of the viewport and opens a bare band —
   `right: auto` is what lets the width win over `inset`. */
.sky::before,
.sky::after {
  content: '';
  position: absolute;
  top: -8%;
  bottom: -8%;
  left: -8%;
  right: auto;
  width: calc(116% + 1900px);
  background-image: url('../assets/img/starfield.svg');
  background-size: 1100px 825px;
  background-repeat: repeat;
}

.sky::before {
  opacity: 0.55;
  animation: drift-near 240s linear infinite;
}

/* Second, larger-scaled copy gives depth without a second request. */
.sky::after {
  opacity: 0.3;
  background-size: 1900px 1425px;
  animation: drift-far 420s linear infinite reverse;
}

@keyframes drift-near {
  from {
    transform: translate3d(0, 0, 0);
  }
  to {
    transform: translate3d(-1100px, 0, 0);
  }
}

@keyframes drift-far {
  from {
    transform: translate3d(0, 0, 0);
  }
  to {
    transform: translate3d(-1900px, 0, 0);
  }
}

/* ------------------------------------------------------------- structure -- */

.shell {
  width: min(100% - var(--gutter) * 2, var(--shell));
  margin-inline: auto;
}

.band {
  padding-block: clamp(var(--sp-16), 9vw, var(--sp-24));
  position: relative;
}

.band--alt {
  background: linear-gradient(180deg, transparent, rgba(9, 11, 26, 0.72) 12%, rgba(9, 11, 26, 0.72) 88%, transparent);
}

.skip-link {
  position: absolute;
  left: var(--sp-4);
  top: var(--sp-4);
  z-index: 100;
  padding: var(--sp-2) var(--sp-4);
  background: var(--panel);
  border: 1px solid var(--brass);
  color: var(--brass-bright);
  font-family: var(--data);
  font-size: var(--fs-micro);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  transform: translateY(-160%);
  transition: transform var(--dur) var(--ease);
}

.skip-link:focus-visible {
  transform: translateY(0);
}

/* ------------------------------------------------------------ typography -- */

.eyebrow {
  font-family: var(--data);
  font-size: var(--fs-eyebrow);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: var(--track-wide);
  color: var(--brass);
}

/* Deco section head: an eyebrow, a display line, and a deck. */
.section-head {
  display: grid;
  gap: var(--sp-3);
  margin-bottom: var(--sp-12);
}

.section-head h2 {
  font-family: var(--display);
  font-weight: 400;
  font-size: var(--fs-h2);
  text-transform: uppercase;
  letter-spacing: var(--track-display);
  line-height: 1.1;
}

.section-head p {
  color: var(--muted);
  max-width: var(--measure);
  font-size: var(--fs-sm);
}

/* --------------------------------------------------------- deco fittings -- */

/* Stepped corner brackets, applied to a panel via ::before/::after. */
.bracketed {
  position: relative;
}

.bracketed::before,
.bracketed::after {
  content: '';
  position: absolute;
  width: 16px;
  height: 16px;
  border-color: var(--brass-deep);
  border-style: solid;
  pointer-events: none;
  transition: border-color var(--dur) var(--ease);
}

.bracketed::before {
  top: -1px;
  left: -1px;
  border-width: 1px 0 0 1px;
}

.bracketed::after {
  bottom: -1px;
  right: -1px;
  border-width: 0 1px 1px 0;
}

/* ------------------------------------------------------------- utilities -- */

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

/* --------------------------------------------------------- reduced motion -- */

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

  .sky::before,
  .sky::after {
    animation: none;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
