/* ==========================================================================
   MuNewSouls — Season 2 · Remaster
   Pre-launch stylesheet
   --------------------------------------------------------------------------
   Layers:
     1. fonts        self-hosted, no external requests
     2. tokens       design tokens (colour, metal, type, space, motion)
     3. reset        minimal, opinionated
     4. primitives   container, section, eyebrow, edition, rule, button, badge
     5. barra do portal   brasão, navegação, placa de estado, menu de celular
     6. portal           abertura reconstruída sobre o Template Oficial
     7. sections     nova-era / remaster / capitulo-1 / classes / guardioes /
                     estado / pre-lancamento
     8. footer
     9. motion       reveal + reduced-motion governance

   Governance (project rule §14): nothing here runs a continuous animation.
   Every transition is finite and event-driven; the only loop on the page is
   the ember canvas, hard-capped at 90 FPS in main.js. The portal parallax is
   scroll-driven — it does no work while the page is still.
   ========================================================================== */

/* ---------- 1. fonts ---------------------------------------------------- */

@font-face {
  font-family: 'Cinzel';
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url('../fonts/cinzel-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Cinzel';
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url('../fonts/cinzel-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url('../fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url('../fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---------- 2. tokens --------------------------------------------------- */

:root {
  /* ink — deep navy-black ground */
  --ink-900: #03050c;
  --ink-800: #06090f;
  --ink-700: #090e1c;
  --ink-600: #0d1428;
  --ink-500: #131c38;

  /* navy — structural blues */
  --navy-600: #101a3a;
  --navy-500: #16224a;
  --navy-400: #1e2f63;

  /* gold — the brand metal */
  --gold-100: #f7e8c4;
  --gold-200: #f0d69f;
  --gold-300: #e2be7c;
  --gold-400: #d0a75d;
  --gold-500: #bd9147;
  --gold-700: #8a6730;
  --gold-900: #4a3717;

  /* arcane — the blue of the crest sigil */
  --arcane-200: #bfdcff;
  --arcane-300: #8ec3ff;
  --arcane-400: #5aa3fb;
  --arcane-500: #3782ea;
  --arcane-700: #17408c;

  /* text */
  --text: #eae8e2;
  --text-soft: #c3c8d4;
  --text-muted: #8e98af;
  /* Lifted from #5f6a83: that scored 3.8:1 on the ink and only 3.1:1 where the
     navy glow brightens the ground. This clears 4.5:1 in both cases. */
  --text-dim: #8892a8;

  /* surfaces & lines */
  --surface: rgb(10 16 32 / 62%);
  --surface-solid: #080d1a;
  --line: rgb(189 145 71 / 22%);
  --line-strong: rgb(210 168 94 / 42%);
  --line-cool: rgb(90 163 251 / 18%);

  /* Engraved metal. Used with background-clip:text plus a drop-shadow, never a
     text-shadow: with `color: transparent` a text-shadow paints straight
     through the glyph and reads as a smear. drop-shadow respects the alpha of
     the clipped gradient, which is what makes the letter look cut into metal
     rather than printed on it. */
  --metal-gold: linear-gradient(180deg, #fffaf0 0%, #f2dcab 22%, #cf9f52 52%, #8a6730 76%, #f2ddb2 100%);
  --metal-title: linear-gradient(180deg, var(--gold-100) 4%, var(--gold-400) 52%, var(--gold-700) 100%);
  --metal-era: linear-gradient(180deg, #fff 0%, var(--gold-200) 46%, var(--gold-500) 100%);
  --engrave: drop-shadow(0 1px 0 rgb(0 0 0 / 82%)) drop-shadow(0 0 16px rgb(226 190 124 / 26%));

  /* per-class accents, taken from each character's own palette */
  --accent: var(--gold-300);
  --accent-dk: #e05a45;
  --accent-elf: #4fd39b;
  --accent-sm: #6fb2ff;
  --accent-dl: #8f92ec;
  --accent-mg: #b07df0;

  /* type */
  --font-display: 'Cinzel', 'Trajan Pro', 'Palatino Linotype', Georgia, serif;
  --font-body: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

  /* Raised again in Block 04. The Block 03 pass lifted the floors; this one
     lifts the CEILINGS, which is where the problem actually was — body copy
     topped out at 17.4px on a 1920 screen and card text at 15.0px, and both
     read as small in a wide, dark column. Ceilings now reach 19.4px and 16.2px.
     Floors move a little too, so nothing on a 390px phone sits under 12.4px. */
  --step--2: clamp(0.775rem, 0.74rem + 0.15vw, 0.845rem);
  --step--1: clamp(0.875rem, 0.82rem + 0.24vw, 1.015rem);
  --step-0: clamp(1.015rem, 0.955rem + 0.3vw, 1.215rem);
  --step-1: clamp(1.18rem, 1.08rem + 0.46vw, 1.46rem);
  --step-2: clamp(1.35rem, 1.2rem + 0.72vw, 1.95rem);
  --step-3: clamp(1.6rem, 1.34rem + 1.3vw, 2.75rem);
  /* Títulos de seção: 3rem a 1920 desaparecia numa página desta largura. */
  --step-4: clamp(1.95rem, 1.5rem + 2.3vw, 4.1rem);
  --step-5: clamp(2.15rem, 1.6rem + 3.2vw, 5rem);

  /* space */
  --space-2xs: 0.375rem;
  --space-xs: 0.625rem;
  --space-s: 1rem;
  --space-m: 1.6rem;
  --space-l: 2.6rem;
  --space-xl: 4rem;
  --space-2xl: clamp(4rem, 2.5rem + 7vw, 8rem);

  /* Bloco 05 — sistema de escala.
     78rem numa tela de 1920 deixa a coluna ocupando 65% da largura e o resto
     vazio; a 2560 e 3840 vira uma ilha no meio do nada. As medidas passam a
     crescer com a viewport e só param quando a linha de texto ficaria longa
     demais para ser lida com conforto. */
  --container: clamp(20rem, 88vw, 92rem);
  --container-wide: clamp(20rem, 94vw, 132rem);
  /* medida de leitura: prosa nunca passa disto, mesmo em container largo */
  --measure: 46rem;

  /* motion — finite, event-driven transitions only */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 160ms;
  --dur: 320ms;
  --dur-slow: 700ms;

  --header-h: 4.5rem;

  /* Written by main.js on scroll; 0 while the page is still. Used by the
     portal layers so the parallax is one custom-property write per frame
     instead of a style write per element. */
  --portal-shift: 0px;
}

@media (min-width: 62em) {
  :root { --header-h: 5.5rem; }
}

/* ---------- 3. reset ---------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1rem);
}

body {
  margin: 0;
  min-height: 100vh;
  background-color: var(--ink-900);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 400;
  line-height: 1.68;
  letter-spacing: 0.008em;
  overflow-x: hidden;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

/* Global atmosphere: a fixed, non-animated field behind every section. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(120% 78% at 50% -12%, rgb(30 47 99 / 55%) 0%, transparent 62%),
    radial-gradient(70% 50% at 12% 32%, rgb(23 64 140 / 26%) 0%, transparent 70%),
    radial-gradient(64% 48% at 88% 68%, rgb(138 103 48 / 18%) 0%, transparent 72%),
    linear-gradient(180deg, #05080f 0%, #03050c 46%, #04060e 100%);
}

/* Fine grain keeps the large dark gradients from banding. */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* No blend mode: a full-viewport blended layer forces the whole page to
     re-composite on every paint, which is ruinous with the ember canvas live. */
  opacity: 0.28;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.34'/%3E%3C/svg%3E");
}

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

img { height: auto; }

a { color: var(--gold-300); text-decoration: none; }

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: 0.02em;
}

p { margin: 0; }

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

blockquote { margin: 0; }

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

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

::selection {
  background: rgb(189 145 71 / 35%);
  color: var(--gold-100);
}

/* ---------- 4. primitives ----------------------------------------------- */

.container {
  width: min(100% - clamp(1.5rem, 5vw, 5rem), var(--container));
  margin-inline: auto;
}

.container--wide { width: min(100% - clamp(1.25rem, 4vw, 4rem), var(--container-wide)); }

.section {
  position: relative;
  padding-block: var(--space-2xl);
}

/* -- section grounds ------------------------------------------------------ */

/* The body read as one long dark document because every section sat on the
   same ground with the same box language. These two modifiers give the page a
   deliberate light/dark pulse — raised, deep, neutral, raised, neutral, deep —
   so no two consecutive sections share a depth. Both fade in and out at the
   edges rather than butting against the next section with a hard seam, which
   is what makes it read as depth instead of as stripes. */
.section--raised::before,
.section--deep::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}

.section--raised::before {
  background:
    linear-gradient(180deg, transparent, rgb(20 32 70 / 30%) 16%, rgb(20 32 70 / 26%) 84%, transparent),
    radial-gradient(78% 46% at 50% 0%, rgb(30 47 99 / 22%), transparent 70%);
}

.section--deep::before {
  background:
    linear-gradient(180deg, transparent, rgb(1 2 6 / 78%) 14%, rgb(1 2 6 / 82%) 86%, transparent);
}

/* A hairline at the seam of a raised band, so the change of ground is read as
   intentional rather than as a rendering artefact. */
.section--raised::after {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  z-index: -1;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--line), transparent);
}

.section__head {
  max-width: var(--measure);
  margin-inline: auto;
  text-align: center;
  margin-bottom: var(--space-xl);
  display: grid;
  justify-items: center;
  gap: var(--space-s);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.65em;
  font-family: var(--font-body);
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.28em;
  text-indent: 0.28em;
  text-transform: uppercase;
  color: var(--gold-400);
}

.eyebrow::before,
.eyebrow::after {
  content: '';
  width: clamp(1.25rem, 4vw, 2.75rem);
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--gold-700));
}

.eyebrow::after { background: linear-gradient(90deg, var(--gold-700), transparent); }

.section__title {
  font-size: var(--step-4);
  text-transform: uppercase;
  letter-spacing: 0.015em;
  background: var(--metal-title);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-wrap: balance;
}

.section__lede {
  font-size: var(--step-1);
  color: var(--text-soft);
  text-wrap: pretty;
}

/* -- edition: the SEASON 2 · REMASTER name plate -------------------------- */

/* This is the product's edition signature, not a caption. In the portal it is
   carried by the crest artwork itself, which has SEASON 2 · REMASTER engraved
   into it; as type it repeats — deliberately — at the Remaster section, the
   closing band and the footer, at three different weights. Repetition at
   descending scale is how an edition name behaves on a real product; saying it
   once and small is how a footnote behaves. */
.edition {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: clamp(0.55rem, 1.7vw, 1.05rem);
  padding: 0.7em clamp(1.1rem, 3.6vw, 2.1rem);
  font-family: var(--font-display);
  font-size: var(--step--1);
  text-transform: uppercase;
  white-space: nowrap;
  background:
    linear-gradient(180deg, rgb(40 30 12 / 42%) 0%, rgb(6 9 18 / 72%) 62%),
    radial-gradient(120% 220% at 50% -60%, rgb(210 168 94 / 14%), transparent 62%);
  border-block: 1px solid var(--line-strong);
}

/* Four corner ticks, two elements: each draws an L in two of its own borders
   and is mirrored to the opposite corner. Same motif as the panels, so the
   plate reads as part of the system rather than a one-off ornament. */
.edition::before,
.edition::after {
  content: '';
  position: absolute;
  width: 0.62rem;
  height: 0.62rem;
  border: 1px solid var(--gold-500);
  opacity: 0.85;
  pointer-events: none;
}

.edition::before {
  top: -1px;
  left: -1px;
  border-right-color: transparent;
  border-bottom-color: transparent;
}

.edition::after {
  right: -1px;
  bottom: -1px;
  border-left-color: transparent;
  border-top-color: transparent;
}

.edition__rule {
  width: clamp(0.9rem, 3.2vw, 2.4rem);
  height: 1px;
  flex: none;
  background: linear-gradient(90deg, transparent, var(--gold-700));
}

.edition__rule:last-child { background: linear-gradient(90deg, var(--gold-700), transparent); }

.edition__season {
  font-size: 0.94em;
  font-weight: 600;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  color: var(--text-soft);
}

.edition__gem {
  width: 0.36em;
  height: 0.36em;
  flex: none;
  rotate: 45deg;
  background: var(--gold-300);
  box-shadow: 0 0 0 3px rgb(210 168 94 / 14%), 0 0 10px rgb(226 190 124 / 45%);
}

/* The word the whole plate exists to carry. */
.edition__key {
  font-size: 1.34em;
  font-weight: 700;
  letter-spacing: 0.32em;
  text-indent: 0.32em;
  background: var(--metal-gold);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: var(--engrave);
}

/* Below 40em the full plate measures ~400px against a 335px column: the right
   rule and the last letters of REMASTER are clipped by the page's overflow
   guard, which hides the damage without fixing it. The compact plate drops the
   flanking rules and tightens the tracking rather than shrinking the word — the
   edition signature has to stay legible at the smallest size the site ships. */
@media (max-width: 39.999em) {
  .edition {
    gap: 0.5rem;
    padding-inline: 1rem;
  }

  .edition--closing { font-size: var(--step--1); }

  .edition__rule { display: none; }

  .edition__key {
    letter-spacing: 0.24em;
    text-indent: 0.24em;
  }

  .edition__season {
    letter-spacing: 0.22em;
    text-indent: 0.22em;
  }
}

.edition--section { margin-block: 0.2rem; }

.edition--closing { font-size: var(--step-0); }

.edition--foot {
  font-size: var(--step--2);
  border-block-color: var(--line);
}

.edition--foot::before,
.edition--foot::after { opacity: 0.5; }

/* Divider between major bands. Was a bare hairline; now the hairline carries a
   small gold lozenge at its centre, which is the same ornament used by the
   edition plate and the class cards. A transition between sections is a place
   the eye stops, and it should look like the brand when it does. */
.rule {
  position: relative;
  height: 1px;
  border: 0;
  margin: 0;
  overflow: visible;
  background: linear-gradient(90deg, transparent, var(--line-strong) 22%, var(--line-strong) 78%, transparent);
}

.rule::before,
.rule::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  pointer-events: none;
}

/* the lozenge */
.rule::before {
  width: 0.42rem;
  height: 0.42rem;
  translate: -50% -50%;
  rotate: 45deg;
  background: var(--gold-400);
  box-shadow: 0 0 0 0.34rem var(--ink-900), 0 0 14px rgb(210 168 94 / 50%);
}

/* a short brighter run of line under it, so the ornament sits ON something */
.rule::after {
  width: 7rem;
  height: 1px;
  translate: -50% -50%;
  background: linear-gradient(90deg, transparent, var(--line-strong), transparent);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.7em;
  padding: 0.95em 2.2em;
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-indent: 0.18em;
  text-transform: uppercase;
  color: var(--gold-100);
  background: linear-gradient(180deg, rgb(30 47 99 / 50%), rgb(9 14 28 / 80%));
  border: 1px solid var(--line-strong);
  border-radius: 2px;
  cursor: pointer;
  position: relative;
  isolation: isolate;
  transition:
    color var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out),
    transform var(--dur) var(--ease-out);
}

.btn::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0;
  background: linear-gradient(180deg, rgb(189 145 71 / 26%), rgb(23 64 140 / 24%));
  transition: opacity var(--dur) var(--ease-out);
}

.btn:hover,
.btn:focus-visible {
  color: #fff;
  border-color: var(--gold-300);
  box-shadow: 0 0 0 1px rgb(226 190 124 / 22%), 0 10px 38px -12px rgb(189 145 71 / 55%);
  transform: translateY(-1px);
}

.btn:hover::before,
.btn:focus-visible::before { opacity: 1; }

.btn__chevron { transition: transform var(--dur) var(--ease-out); }
.btn:hover .btn__chevron { transform: translateY(2px); }

.btn__chevron--up { rotate: 180deg; }
.btn:hover .btn__chevron--up { transform: translateY(-2px); }

/* The ghost button was reading as DISABLED, not as secondary: --text-soft on a
   transparent ground inside a 22%-gold hairline is roughly the contrast a
   greyed-out control has. A secondary action still has to look pressable. It
   now gets a real ground, a visible border and gold-tinted text — quieter than
   the primary, never inert. */
.btn--ghost {
  color: var(--gold-200);
  background: linear-gradient(180deg, rgb(20 15 6 / 34%), rgb(6 9 18 / 62%));
  border-color: var(--line-strong);
}

.btn--ghost:hover,
.btn--ghost:focus-visible {
  color: var(--gold-100);
  border-color: var(--gold-300);
}

/* Pressed feedback. Without it the buttons transition on hover and then do
   nothing at all on the click itself, which reads as an unresponsive control
   on touch, where there is no hover state to begin with. */
.btn:active {
  transform: translateY(1px);
  box-shadow: 0 0 0 1px rgb(226 190 124 / 30%), inset 0 2px 10px rgb(0 0 0 / 45%);
}

.skip-link {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translate(-50%, -120%);
  z-index: 100;
  padding: 0.75em 1.5em;
  background: var(--surface-solid);
  border: 1px solid var(--line-strong);
  color: var(--gold-100);
  font-size: var(--step--1);
  transition: transform var(--dur) var(--ease-out);
}

.skip-link:focus { transform: translate(-50%, 0); }

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

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  padding: 0.5em 1.1em;
  border: 1px solid var(--line-strong);
  border-radius: 2px;
  background: rgb(4 6 13 / 70%);
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.22em;
  text-indent: 0.22em;
  text-transform: uppercase;
  color: var(--gold-200);
}

.badge--cool {
  border-color: var(--line-cool);
  color: var(--arcane-200);
}

.badge__dot {
  width: 0.45em;
  height: 0.45em;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 0 3px rgb(255 255 255 / 8%);
}

/* ---------- 5. barra do portal ------------------------------------------ */

/* Bloco 14. O cabeçalho deixou de ser a barra fina de uma landing page e
   passou a ser a BARRA DE PORTAL do template: brasão à esquerda, trilho de
   navegação centrado em capitulares douradas, e uma placa emoldurada ancorada
   à direita.

   No template aquela placa são dois botões de conta — ENTRAR e CADASTRE-SE.
   Nenhum dos dois serviço existe, então o slot carrega o estado do projeto.
   A forma é a do template; o conteúdo é verdadeiro. */
.header {
  position: fixed;
  inset: 0 0 auto;
  z-index: 50;
  min-height: var(--header-h);
  display: flex;
  align-items: center;
  /* A barra do template nunca é invisível: ela tem chão desde o primeiro
     pixel. Um véu leve no topo já dá esse chão sem tapar a cena. */
  background: linear-gradient(180deg, rgb(3 5 12 / 78%) 0%, rgb(3 5 12 / 34%) 62%, transparent 100%);
  border-bottom: 1px solid transparent;
  transition:
    background-color var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out);
}

/* O fio dourado sob a barra — a primeira aresta de metal da página. */
.header::after {
  content: '';
  position: absolute;
  inset: auto 0 0;
  height: 1px;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, rgb(210 168 94 / 20%) 34%, rgb(210 168 94 / 20%) 66%, transparent);
  transition: background var(--dur) var(--ease-out);
}

.header.is-stuck::after,
.header.is-open::after {
  background: linear-gradient(90deg, transparent, var(--line-strong) 18%, var(--line-strong) 82%, transparent);
}

.header.is-stuck,
.header.is-open {
  background: rgb(4 6 13 / 93%);
  border-bottom-color: transparent;
  backdrop-filter: blur(14px) saturate(120%);
}

.header__inner {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-s);
  width: min(100% - 2.5rem, var(--container-wide));
  margin-inline: auto;
  padding-block: 0.55rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  flex-shrink: 0;
}

/* O brasão é um lockup alado LARGO, então é dimensionado pela largura. */
.brand__mark {
  width: 3.75rem;
  height: auto;
  filter: drop-shadow(0 0 14px rgb(90 163 251 / 34%));
  transition: filter var(--dur) var(--ease-out);
}

.brand:hover .brand__mark { filter: drop-shadow(0 0 20px rgb(142 195 255 / 52%)); }

.brand__text { display: none; }

.brand__name {
  display: block;
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  line-height: 1;
  background: var(--metal-gold);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 1px 0 rgb(0 0 0 / 70%));
}

.brand__era {
  display: block;
  margin-top: 0.45em;
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-500);
}

/* O trilho ocupa a coluna do meio e é CENTRADO, como no template — não
   empurrado contra a direita. */
.nav { display: flex; justify-content: center; }

.nav__list {
  display: none;
  align-items: center;
  gap: clamp(0.7rem, 1.5vw, 1.75rem);
}

.nav__link {
  position: relative;
  display: block;
  padding-block: 0.45rem;
  font-family: var(--font-display);
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--gold-200);
  opacity: 0.72;
  white-space: nowrap;
  transition: color var(--dur) var(--ease-out), opacity var(--dur) var(--ease-out);
}

.nav__link::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--gold-300), transparent);
  box-shadow: 0 0 8px rgb(226 190 124 / 55%);
  transform: scaleX(0);
  transition: transform var(--dur) var(--ease-out);
}

.nav__link:hover,
.nav__link:focus-visible { color: var(--gold-100); opacity: 1; }

.nav__link:hover::after,
.nav__link:focus-visible::after,
.nav__link.is-current::after { transform: scaleX(1); }

.nav__link.is-current { color: var(--gold-100); opacity: 1; }

/* A placa de estado: única coisa que a barra afirma, e a única que ela PODE
   afirmar hoje. Recebe a mesma cantoneira dos painéis do portal, para ler como
   peça do mesmo sistema. */
.chip {
  display: none;
  position: relative;
  align-items: center;
  gap: 0.6em;
  flex-shrink: 0;
  padding: 0.6em 1.15em;
  background: linear-gradient(180deg, rgb(30 47 99 / 42%), rgb(6 9 18 / 82%));
  border: 1px solid var(--line-strong);
  font-family: var(--font-display);
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold-200);
  white-space: nowrap;
}

.chip::before {
  content: '';
  position: absolute;
  inset: -3px;
  pointer-events: none;
  background:
    linear-gradient(var(--gold-500), var(--gold-500)) 0 0 / 9px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 0 0 / 1px 9px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 0 / 9px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 0 / 1px 9px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 0 100% / 9px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 0 100% / 1px 9px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 100% / 9px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 100% / 1px 9px no-repeat;
  opacity: 0.75;
}

.chip__dot {
  width: 0.45em;
  height: 0.45em;
  border-radius: 50%;
  background: var(--arcane-400);
  box-shadow: 0 0 0 3px rgb(90 163 251 / 18%), 0 0 10px rgb(90 163 251 / 70%);
}

/* -- menu de celular ------------------------------------------------------ */

.navtoggle {
  justify-self: end;
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  flex: none;
  border: 1px solid var(--line-strong);
  background: rgb(10 16 32 / 62%);
  color: var(--gold-200);
}

.navtoggle__bars {
  display: grid;
  gap: 0.28rem;
  width: 1.1rem;
}

.navtoggle__bars i {
  display: block;
  height: 1px;
  background: currentColor;
  transition: transform var(--dur) var(--ease-out), opacity var(--dur-fast) linear;
}

.navtoggle[aria-expanded='true'] .navtoggle__bars i:nth-child(1) { transform: translateY(0.38rem) rotate(45deg); }
.navtoggle[aria-expanded='true'] .navtoggle__bars i:nth-child(2) { opacity: 0; }
.navtoggle[aria-expanded='true'] .navtoggle__bars i:nth-child(3) { transform: translateY(-0.38rem) rotate(-45deg); }

/* O painel é montado sob a barra e revelado por transição de altura, então
   fechado ele é genuinamente inerte: sem tab stop, sem leitura de tela. */
@media (max-width: 61.999em) {
  .header__inner { grid-template-columns: auto 1fr; }

  .nav {
    position: absolute;
    inset: 100% 0 auto;
    display: grid;
    margin: 0;
    background: rgb(4 6 13 / 97%);
    border-bottom: 1px solid var(--line);
    backdrop-filter: blur(14px) saturate(120%);
    grid-template-rows: 0fr;
    visibility: hidden;
    transition: grid-template-rows var(--dur) var(--ease-out), visibility 0s linear var(--dur);
  }

  .nav__list {
    display: grid;
    overflow: hidden;
    min-height: 0;
    align-items: stretch;
    gap: 0;
  }

  .nav__link {
    padding: 0.95rem clamp(1.25rem, 5vw, 2.5rem);
    font-size: var(--step--1);
    border-top: 1px solid rgb(189 145 71 / 12%);
    opacity: 1;
  }

  .nav__list > li:first-child .nav__link { border-top: 0; }

  .nav__link::after { display: none; }

  .header.is-open .nav {
    grid-template-rows: 1fr;
    visibility: visible;
    transition: grid-template-rows var(--dur) var(--ease-out), visibility 0s linear;
  }
}

@media (min-width: 26em) {
  .brand__text { display: block; }
}

@media (min-width: 62em) {
  .nav__list { display: flex; }
  .navtoggle { display: none; }
  .brand__mark { width: 5.25rem; }
  .header__inner { gap: var(--space-m); }
}

/* A placa só aparece quando brasão + seis itens + placa realmente cabem. */
@media (min-width: 86em) {
  .chip { display: inline-flex; }
}

/* ---------- 6. portal (abertura) ---------------------------------------- */

/* Bloco 14 — a abertura reconstruída sobre `Template Ofical MunewSouls.png`.
   Este é o único bloco de estilo da abertura, e nada aqui é usado abaixo dela:
   toda a nomenclatura é própria (`.portal`, `.stage`, `.cast`, `.pframe`,
   `.pstrip`, `.pmod`, `.prail`, `.crest`, `.sentinel`, `.community`), justamente
   para que mexer no topo do site não possa vazar para o corpo já aprovado.

   A única primitiva compartilhada é `.reveal`, que já governa as entradas do
   resto da página.

   MOVIMENTO: nada aqui anima em laço. Toda a atmosfera é gradiente estático ou
   SVG embutido, rasterizado uma vez na decodificação. O único laço contínuo da
   página continua sendo o canvas de brasas, com teto de 90 FPS em main.js. */

/* Sem `min-height: 100svh`, e isso é uma correção, não uma omissão.
   A abertura mede 1.350 a 1.600px em todo desktop e mais de 2.500px no celular:
   ela já passa da altura da janela sozinha, em toda tela medida. A altura
   mínima só passava a valer em janelas MUITO altas — e ali, combinada com
   `align-content: space-between`, ela espalhava as quatro faixas com centenas
   de pixels de vazio entre elas. Medido a 1920x2600: buracos de ~250px entre a
   cena e a faixa e de ~230px entre a faixa e os módulos, com a composição
   desmontada. O portal agora tem a altura do que ele é. */
.portal {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  padding-block: calc(var(--header-h) + clamp(0.5rem, 1.6vh, 1.25rem)) clamp(1.75rem, 4vh, 3.25rem);
  overflow: hidden;

  /* O campo do portal. Herda a linguagem que os Blocos 07 e 13 construíram
     para o herói antigo — abismo central, corrupção violeta nas quinas, chão
     azul — reancorada para uma seção que agora tem painéis nas laterais e
     módulos embaixo, e não mais uma figura solitária no meio. */
  background:
    radial-gradient(86% 40% at 50% 20%, rgb(26 56 118 / 40%), transparent 74%),
    radial-gradient(46% 26% at 50% 26%, rgb(38 78 158 / 22%), transparent 76%),
    radial-gradient(62% 40% at 3% 0%, rgb(56 36 112 / 30%), transparent 70%),
    radial-gradient(62% 40% at 97% 0%, rgb(56 36 112 / 28%), transparent 70%),
    radial-gradient(120% 52% at 50% 100%, rgb(9 17 40 / 78%), transparent 72%),
    linear-gradient(180deg, #04060e 0%, #03050c 52%, #04060f 100%);
}

/* Brasas: a mesma camada viva do herói antigo, agora concentrada na metade de
   cima, que é onde a cena está. */
.portal__embers {
  position: absolute;
  inset: 0 0 34%;
  z-index: 1;
  width: 100%;
  height: 66%;
  pointer-events: none;
  opacity: 0.5;
}

/* Pintura de luz sobre a cena: núcleo quente atrás do elmo, halo frio nas
   asas. `screen` porque o que se quer é ACRESCENTAR luz, não clarear tinta. */
.portal__aura {
  position: absolute;
  inset: 0 0 auto;
  z-index: 2;
  height: 62%;
  pointer-events: none;
  background:
    radial-gradient(22% 30% at 50% 30%, rgb(214 156 62 / 26%), transparent 72%),
    radial-gradient(62% 52% at 50% 34%, rgb(38 94 196 / 18%), transparent 78%);
  mix-blend-mode: screen;
}

/* Véu: apaga as QUATRO bordas da seção para que a cena dissolva no campo em vez
   de bater numa aresta. Tinta cheia sobre chapa opaca, não `mask-image` — uma
   máscara obrigaria o compositor a reavaliar recorte a cada repintura, debaixo
   de um canvas de brasas vivo, e sobre um fundo que já é `--ink-900` o
   resultado pintado é idêntico por uma fração do custo. */
.portal__veil {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background-repeat: no-repeat;
  background-image:
    linear-gradient(180deg, var(--ink-900) 0%, rgb(3 5 12 / 55%) 40%, transparent 100%),
    linear-gradient(90deg, rgb(3 5 12 / 92%) 0%, rgb(3 5 12 / 30%) 1.6%, transparent 7%, transparent 93%, rgb(3 5 12 / 30%) 98.4%, rgb(3 5 12 / 92%) 100%);
  background-size:
    100% calc(var(--header-h) + 4rem),
    100% 100%;
  background-position: top center, center;
}

/* A costura com o corpo aprovado. O portal termina em tinta cheia e "A Nova
   Era" começa nela — a transição é uma mudança de assunto, não uma emenda. */
.portal__hem {
  position: absolute;
  inset: auto 0 0;
  z-index: 3;
  height: 9rem;
  pointer-events: none;
  background: linear-gradient(180deg, transparent, var(--ink-900) 92%);
}

.portal__frame {
  position: relative;
  z-index: 4;
  flex: 1;
  display: grid;
  align-content: start;
  gap: clamp(0.9rem, 1.7vh, 1.75rem);
  width: min(100% - clamp(1.25rem, 4vw, 4rem), var(--container-wide));
  margin-inline: auto;
}

/* -- A · cena ------------------------------------------------------------- */

.portal__scene {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(0.9rem, 1.6vw, 2rem);
  align-items: stretch;
}

/* -- trilho de brasões ---------------------------------------------------- */

.prail {
  display: grid;
  align-content: start;
  justify-items: center;
  gap: clamp(0.6rem, 1.2vh, 1.1rem);
  text-align: center;
}

.prail__head { display: grid; justify-items: center; gap: 0.2rem; }

@media (min-width: 62em) {
  .prail { position: relative; }

  .prail::after {
    content: '';
    position: absolute;
    inset: 6% auto 14% 100%;
    width: 1px;
    translate: clamp(0.45rem, 0.8vw, 1rem) 0;
    pointer-events: none;
    background: linear-gradient(180deg, transparent, var(--line) 22%, var(--line) 74%, transparent);
  }
}

.prail__title {
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.26em;
  text-indent: 0.26em;
  text-transform: uppercase;
  background: var(--metal-gold);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 1px 0 rgb(0 0 0 / 75%));
}

.prail__sub {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.crests {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.5rem;
  width: 100%;
}

/* Empilhados a partir do momento em que o trilho vira COLUNA. */
@media (min-width: 62em) {
  .crests {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.15rem;
  }

  .crest__link {
    grid-template-columns: auto minmax(0, 1fr);
    justify-items: start;
    align-items: center;
    gap: 0 0.55rem;
    padding: 0.28rem 0.1rem;
    --crest-sigil: 3.1rem;
  }

  .crest__art { grid-row: 1 / span 2; }
  .crest__name { align-self: end; }
  .crest__role { align-self: start; }
}

.crest__link {
  display: grid;
  justify-items: center;
  gap: 0.15rem;
  padding: 0.35rem 0.15rem 0.5rem;
  color: inherit;
}

.crest__art {
  display: block;
  width: var(--crest-sigil, 4.6rem);
  filter: drop-shadow(0 0 12px rgb(90 163 251 / 26%));
  transition: filter var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}

.crest__link:hover .crest__art,
.crest__link:focus-visible .crest__art {
  transform: translateY(-2px);
  filter: drop-shadow(0 0 18px rgb(142 195 255 / 55%));
}

.crest--guide .crest__art { filter: drop-shadow(0 0 12px rgb(226 190 124 / 30%)); }
.crest--guide .crest__link:hover .crest__art { filter: drop-shadow(0 0 18px rgb(240 214 159 / 58%)); }
.crest--merchant .crest__art { filter: drop-shadow(0 0 12px rgb(79 211 155 / 26%)); }
.crest--merchant .crest__link:hover .crest__art { filter: drop-shadow(0 0 18px rgb(79 211 155 / 52%)); }

.crest__name,
.crest__role {
  display: block;
  font-family: var(--font-display);
  line-height: 1.15;
  text-transform: uppercase;
}

.crest__name {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-indent: 0.18em;
  color: var(--gold-300);
}

.crest__role {
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-indent: 0.13em;
  color: var(--text-dim);
}

/* No template há aqui uma coluna de ícones de rede social. Nenhum canal
   oficial foi publicado, e um ícone de marca — mesmo marcado "em breve" —
   continua afirmando uma associação que não existe. Fica o ritmo vertical e o
   único texto verdadeiro. */
.prail__channels {
  display: grid;
  justify-items: center;
  gap: 0.28rem;
  width: 100%;
  padding: 0.7rem 0.4rem;
  border-top: 1px solid var(--line);
  color: var(--text-dim);
}

.prail__glyph { width: 1.05rem; height: 1.05rem; color: var(--gold-500); }

.prail__ctitle {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-400);
}

.prail__cstate {
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* -- palco ---------------------------------------------------------------- */

.stage {
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  align-items: end;

  /* As medidas que governam a composição inteira. `--core-w: 100%` é o valor de
     base porque abaixo de 48em a cena é uma coluna só: o núcleo ocupa o palco
     inteiro e o deslocamento de eixo do chefe tem de zerar. */
  --core-w: 100%;
  --boss-w: min(122vw, 34rem);
  --cast-w: 0px;
}

/* Feixes de luz atrás do chefe. O borrão mora DENTRO do SVG (`feGaussianBlur`),
   rasterizado uma vez na decodificação: sem ele os feixes leem como um leque
   geométrico — um diagrama em vez de clima. Estático: não anima, não conta para
   o teto de 90 FPS. Embutido como `data:`, então nenhum arquivo novo e nenhuma
   requisição nova.

   BLOCO 23 — aqui existia um segundo grupo, com TRÊS caminhos traçados em
   zigue-zague saindo do mesmo centro (rotacionados -44°, -12° e +30°). Eram
   relâmpagos DESENHADOS, e no Bloco 13 funcionavam: a cena era parada e eles
   liam como clima congelado num quadro.

   O Bloco 22 deu vida ao portal em volta deles, e aí a conta virou: três raios
   imóveis no meio de um portal que gira e pulsa não leem mais como atmosfera,
   leem como desenho travado. Foram removidos junto com o gradiente `#c` e o
   filtro `#thin`, que só eles usavam. O que restou — as cinco cunhas suaves —
   é luz difusa, não traço: não tem contorno para congelar. */
.stage::before {
  content: '';
  position: absolute;
  inset: -14% 0 16%;
  z-index: 0;
  pointer-events: none;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 800' preserveAspectRatio='none'%3E%3Cdefs%3E%3ClinearGradient id='b' gradientUnits='userSpaceOnUse' x1='600' y1='360' x2='600' y2='-60'%3E%3Cstop offset='0' stop-color='%238fb8ff' stop-opacity='.40'/%3E%3Cstop offset='.45' stop-color='%237fa8f5' stop-opacity='.15'/%3E%3Cstop offset='1' stop-color='%236f98e8' stop-opacity='0'/%3E%3C/linearGradient%3E%3Cfilter id='soft' filterUnits='userSpaceOnUse' x='-300' y='-300' width='1800' height='1400'%3E%3CfeGaussianBlur stdDeviation='26'/%3E%3C/filter%3E%3C/defs%3E%3Cg fill='url(%23b)' filter='url(%23soft)'%3E%3Cpath d='M600 360 L470 -50 L604 -50 Z' transform='rotate(-57 600 360)'/%3E%3Cpath d='M600 360 L556 -20 L648 -20 Z' transform='rotate(-24 600 360)'/%3E%3Cpath d='M600 360 L534 -60 L676 -60 Z' transform='rotate(-3 600 360)'/%3E%3Cpath d='M600 360 L566 10 L640 10 Z' transform='rotate(21 600 360)'/%3E%3Cpath d='M600 360 L500 -40 L628 -40 Z' transform='rotate(49 600 360)'/%3E%3C/g%3E%3C/svg%3E"),

    /* contraluz apertada logo atrás do elmo: recorta a coroa contra o campo */
    radial-gradient(14% 13% at 50% 24%, rgb(132 176 255 / 32%), transparent 74%),

    /* halo de portal, com o miolo transparente — quem preenche é o Kundun */
    radial-gradient(44% 34% at 50% 28%, transparent 56%, rgb(76 128 232 / 16%) 76%, rgb(104 84 216 / 11%) 89%, transparent 100%),

    /* núcleo abissal atrás do tronco: as asas e o dourado destacam por
       CONTRASTE, que é o oposto de bloom e o que impede o resultado lavado */
    radial-gradient(40% 34% at 50% 44%, rgb(2 4 12 / 44%), transparent 78%);
  background-repeat: no-repeat;
  background-size: 118% 74%, auto, auto, auto;
  background-position: 50% 2%, center, center, center;
}

/* A dissolução da cena. Nasce depois do elenco no mesmo contexto de
   empilhamento e pinta por cima dele: as bordas da arte somem no campo e o
   pé vira névoa de chão, de onde o núcleo da marca emerge. */
.stage::after {
  content: '';
  position: absolute;
  inset: 0 0 -1px;
  z-index: 3;
  pointer-events: none;
  background:
    radial-gradient(64% 46% at 50% 104%, rgb(3 5 12 / 86%), rgb(3 5 12 / 30%) 60%, transparent 78%),
    radial-gradient(110% 34% at 50% 88%, rgb(34 74 152 / 22%), transparent 72%),
    linear-gradient(180deg, transparent 52%, rgb(3 5 12 / 20%) 72%, rgb(3 5 12 / 62%) 90%, rgb(3 5 12 / 86%) 100%);
}

/* -- portal arcano vivo (Bloco 22) ----------------------------------------

   A elipse abaixo é a MESMA do halo de portal de `.stage::before` — a dedução
   está no comentário do HTML. Aqui só se repete o resultado: centro em
   50% x 13,44% do palco, raios 44% x 33,32%.

   Nada aqui afeta layout: absoluto, sem interação, dentro de um palco que já
   recorta o que passa. */
.stage__portal {
  position: absolute;
  left: 50%;
  top: 13.44%;
  z-index: 0;
  width: 88%;
  height: 66.64%;
  translate: -50% -50%;
  pointer-events: none;

  /* O topo do portal MORRE antes da navegação.

     A elipse nasce acima do palco (a 1920 o topo dela cai em -28px), então o
     arco de cima passaria por trás da barra de navegação. Ela é transparente:
     o texto não seria coberto — mas estrutura girando atrás de um menu é
     distração, e o briefing pede confinamento, não só legibilidade.

     Medido nos nove viewports: a base da navegação cai entre 25,2% e 26,7% da
     altura desta caixa — uma faixa notavelmente estável, porque cabeçalho e
     palco crescem juntos. Daí o desvanecimento terminar em 30%: cobre o pior
     caso com folga e não depende de tabela por breakpoint. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, rgb(0 0 0 / 30%) 19%, #000 30%, #000 100%);
          mask-image: linear-gradient(180deg, transparent 0%, rgb(0 0 0 / 30%) 19%, #000 30%, #000 100%);
}

/* O campo quadrado: aqui mora o achatamento. Os anéis são filhos, giram no
   espaço quadrado (circular) e só então são esmagados para a elipse — que é a
   ordem física certa para um disco visto em ângulo. O contrário (girar a
   elipse) produz um anel bambo, e é o erro clássico deste efeito.

   Os anéis medem 640px FIXOS e são escalados até o tamanho da cena. A textura
   no compositor fica constante: a 3840x2160 a caixa do portal tem 1866px de
   largura, e sem isto seriam duas camadas de ~14MB cada só para girar. */
.pfield {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 640px;
  height: 640px;
  translate: -50% -50%;
  scale: var(--pk, 1) var(--pky, .42);
}

.pring,
.pbreath,
.pcharge {
  position: absolute;
  inset: 0;
  border-radius: 50%;
}

/* O anel é um gradiente cônico recortado numa coroa. Os trechos de luz têm
   larguras e intensidades DIFERENTES de propósito: um anel de brilho uniforme
   girando lê como ícone de carregamento, que é exatamente o que não pode
   acontecer. A irregularidade é o que faz parecer energia. */
.pring {
  will-change: transform;
}

.pring--outer {
  background: conic-gradient(from 0deg,
    rgb(126 176 255 / 0%) 0deg,
    rgb(186 220 255 / 26%) 7deg,
    rgb(126 176 255 / 0%) 19deg,
    rgb(126 176 255 / 0%) 34deg,
    rgb(214 234 255 / 34%) 41deg,
    rgb(150 195 255 / 12%) 52deg,
    rgb(126 176 255 / 0%) 66deg,
    rgb(168 206 255 / 20%) 79deg,
    rgb(126 176 255 / 0%) 92deg,
    rgb(126 176 255 / 0%) 116deg,
    rgb(146 138 240 / 19%) 127deg,
    rgb(126 176 255 / 0%) 141deg,
    rgb(196 226 255 / 29%) 158deg,
    rgb(126 176 255 / 0%) 172deg,
    rgb(126 176 255 / 0%) 196deg,
    rgb(176 214 255 / 24%) 206deg,
    rgb(126 176 255 / 6%) 218deg,
    rgb(126 176 255 / 0%) 233deg,
    rgb(206 230 255 / 32%) 246deg,
    rgb(126 176 255 / 0%) 259deg,
    rgb(126 176 255 / 0%) 281deg,
    rgb(158 200 255 / 22%) 292deg,
    rgb(126 176 255 / 0%) 306deg,
    rgb(138 132 236 / 16%) 322deg,
    rgb(126 176 255 / 0%) 335deg,
    rgb(184 218 255 / 25%) 348deg,
    rgb(126 176 255 / 0%) 360deg);
  /* Coroa ESTREITA. Com a faixa larga que isto tinha antes, cada trecho do
     cônico virava uma mancha de centenas de pixels e o portal lia como
     nuvem — medido numa captura: duas manchas pálidas ao lado do Kundun, sem
     forma de anel nenhuma. Anel se lê por CONTRASTE de borda, não por brilho. */
  -webkit-mask-image: radial-gradient(closest-side, transparent 81%, #000 87%, #000 93%, transparent 99%);
          mask-image: radial-gradient(closest-side, transparent 81%, #000 87%, #000 93%, transparent 99%);
  animation: pspin 104s linear infinite;
}

/* O anel de dentro é mais apertado, mais fraco e gira ao contrário. A diferença
   de sentido e de período é o que dá profundidade — sem ela o portal lê como um
   sprite chapado só girando. */
.pring--inner {
  background: conic-gradient(from 210deg,
    rgb(150 198 255 / 0%) 0deg,
    rgb(198 226 255 / 34%) 11deg,
    rgb(150 198 255 / 0%) 26deg,
    rgb(150 198 255 / 0%) 52deg,
    rgb(176 212 255 / 26%) 63deg,
    rgb(150 198 255 / 0%) 78deg,
    rgb(150 198 255 / 0%) 104deg,
    rgb(206 230 255 / 30%) 116deg,
    rgb(150 198 255 / 0%) 131deg,
    rgb(136 130 234 / 20%) 168deg,
    rgb(150 198 255 / 0%) 184deg,
    rgb(150 198 255 / 0%) 212deg,
    rgb(188 220 255 / 28%) 224deg,
    rgb(150 198 255 / 0%) 240deg,
    rgb(150 198 255 / 0%) 274deg,
    rgb(170 208 255 / 24%) 286deg,
    rgb(150 198 255 / 0%) 302deg,
    rgb(150 198 255 / 0%) 334deg,
    rgb(196 224 255 / 26%) 344deg,
    rgb(150 198 255 / 0%) 360deg);
  -webkit-mask-image: radial-gradient(closest-side, transparent 48%, #000 56%, #000 63%, transparent 71%);
          mask-image: radial-gradient(closest-side, transparent 48%, #000 56%, #000 63%, transparent 71%);
  opacity: .66;
  animation: pspin 76s linear infinite reverse;
}

@keyframes pspin { to { rotate: 360deg; } }

/* A respiração. Duas camadas com períodos PRIMOS ENTRE SI (19s e 26,5s): a
   soma das duas só se repete a cada ~8,4 minutos, então o olho nunca pega o
   laço. Uma camada só, por mais lenta que fosse, seria um pulso reconhecível em
   poucos segundos — e o briefing chama isso de artificial com razão. */
/* A respiração acompanha a COROA, não o disco inteiro. Espalhada pelo miolo ela
   virava neblina — foi a maior responsável pelo aspecto de nuvem na primeira
   tentativa. Aqui ela só engrossa e afina a luz em volta do próprio anel. */
.pbreath {
  background: radial-gradient(closest-side,
    transparent 66%,
    rgb(86 140 240 / 12%) 84%,
    rgb(116 146 248 / 5%) 94%,
    transparent 100%);
  animation: pbreathe 19s ease-in-out infinite;
}

.pbreath--slow {
  opacity: .8;
  animation-duration: 26.5s;
  animation-direction: alternate-reverse;
}

@keyframes pbreathe {
  0%, 100% { opacity: .5; }
  50%      { opacity: 1; }
}

/* Carga e descarga. Fica em 0 e só o main.js o acende, durante o evento.
   `screen` porque é luz somada. O teto de brilho está nos alfas do próprio
   gradiente, não na opacidade: mesmo com opacidade 1 ele soma pouco — e, por
   estar em z-index 0, soma ATRÁS do Kundun, onde não há armadura para lavar. */
.pcharge {
  background: radial-gradient(closest-side,
    rgb(198 228 255 / 26%) 0%,
    rgb(126 184 255 / 19%) 33%,
    rgb(72 132 240 / 12%) 61%,
    transparent 87%);
  opacity: 0;
  mix-blend-mode: screen;
}

/* Aba oculta, abertura fora da tela: as animações de CSS param de verdade.
   Sem isto o compositor continuaria girando dois anéis para ninguém. */
.stage__portal.is-still .pring,
.stage__portal.is-still .pbreath {
  animation-play-state: paused;
}

/* -- condução arcana (Bloco 23) -------------------------------------------

   Quando um raio acerta o cajado, parte da energia às vezes percorre as
   inscrições da abertura. A implementação é deliberadamente SEM linhas:

   Desenhar um traço de um rótulo até outro exigiria atravessar o espaço entre
   eles — e esse espaço é onde estão o Kundun e as cinco classes. Um traço assim
   passaria por cima da armadura, que é justamente o que o briefing proíbe.
   Aqui não há o que atravessar: cada elemento acende a SI MESMO, e a sensação
   de percurso vem da ORDEM e do atraso entre um e outro. É impossível, por
   construção, a condução tocar um personagem.

   Também é por isso que a legibilidade não sofre: o brilho nasce das próprias
   letras, não de uma linha por cima delas.

   Custo: uma classe e uma animação de CSS por passo. Nenhum quadro de JS,
   nenhum laço — o `main.js` só põe e tira a classe. */
.is-conducting {
  animation: mnsConduct 560ms cubic-bezier(.2, .7, .3, 1) both;
}

/* Só o quadro do meio é declarado. As pontas ficam implícitas e o navegador as
   preenche com o valor BASE de cada elemento — é assim que uma regra só serve
   rótulos de cores diferentes sem apagar a cor de nenhum.

   O halo é curto de propósito. A primeira versão usava 13px e 28px de raio, e
   numa versalete de ~11px isso é muito mais largo que a própria letra: o brilho
   preenchia o espaço ENTRE os caracteres e lia como uma caixa cinza de seleção
   de texto, não como energia. Quem carrega a leitura aqui é a MUDANÇA DE COR;
   o halo só assenta a borda. */
@keyframes mnsConduct {
  16% {
    color: rgb(216 238 255);
    text-shadow: 0 0 2px rgb(170 214 255 / 90%), 0 0 6px rgb(80 150 255 / 50%);
  }
}

/* Os quatro casos especiais. Cada um repete a sua base porque `text-shadow` e
   `filter` são propriedades ÚNICAS: animar sem repetir a base apagaria a
   sombra de profundidade que o elemento já tem, e o texto perderia contraste
   contra a cena por meio segundo. */
.stage__locator.is-conducting { animation-name: mnsConductLoc; }

@keyframes mnsConductLoc {
  0%, 100% { text-shadow: 0 1px 10px rgb(3 5 12 / 92%); }
  16% {
    color: rgb(216 238 255);
    text-shadow: 0 1px 10px rgb(3 5 12 / 92%), 0 0 2px rgb(170 214 255 / 90%), 0 0 7px rgb(80 150 255 / 45%);
  }
}

/* Texto com gradiente recortado: o brilho tem de vir de `drop-shadow`, que
   trabalha sobre o alfa do que foi realmente pintado. `text-shadow` aqui
   apareceria ATRAVÉS da letra transparente e sujaria o metal. */
.prail__title.is-conducting { animation-name: mnsConductRail; }

@keyframes mnsConductRail {
  0%, 100% { filter: drop-shadow(0 1px 0 rgb(0 0 0 / 75%)); }
  16%      { filter: drop-shadow(0 1px 0 rgb(0 0 0 / 75%)) drop-shadow(0 0 6px rgb(160 205 255 / 78%)) drop-shadow(0 0 16px rgb(70 145 255 / 42%)); }
}

/* O letreiro principal leva a dose mais fraca das três: é a maior tipografia da
   página e um brilho forte aqui leria como texto piscando, não como energia. */
.stage__era.is-conducting { animation-name: mnsConductEra; }

@keyframes mnsConductEra {
  0%, 100% { filter: drop-shadow(0 3px 22px rgb(3 5 12 / 92%)); }
  14%      { filter: drop-shadow(0 3px 22px rgb(3 5 12 / 92%)) drop-shadow(0 0 7px rgb(160 205 255 / 52%)) drop-shadow(0 0 20px rgb(70 145 255 / 26%)); }
}

/* Os filetes da chamada: ornamento puro, sem texto para prejudicar. */
.playplate__wing.is-conducting { animation-name: mnsConductWing; }

@keyframes mnsConductWing {
  0%, 100% { box-shadow: none; filter: none; }
  16%      { box-shadow: 0 0 5px rgb(170 210 255 / 85%), 0 0 14px rgb(70 145 255 / 55%); filter: brightness(1.5); }
}

/* -- tempestade arcana (Bloco 21) -----------------------------------------

   Duas peças, e nenhuma delas é um elemento novo por relâmpago: uma tela que
   só é pintada durante o evento, e um clarão que mora no cajado.

   A TELA nasce como primeira filha do palco e leva o MESMO z-index do Kundun.
   Mesmo z-index, quem vem antes no documento pinta antes — então ela fica atrás
   da figura, que é o que faz o raio ler como céu e não como adesivo. Ela também
   está confinada ao palco, a coluna do meio: nenhum raio tem como cruzar o
   trilho, o painel do Sentinel ou a navegação.

   O topo estende um pouco acima do palco porque o chefe sangra para cima em
   várias faixas, e o cajado dele vai junto: sem essa folga o raio terminaria
   cortado na borda da tela em vez de encostar no alvo. Só o eixo vertical
   estende — na horizontal ela é exatamente o palco, e é isso que garante o
   confinamento. O `overflow: hidden` do portal apara o que passar do topo. */
.stage__storm {
  position: absolute;
  top: -22%;
  left: 0;
  z-index: 1;
  /* ALTURA EXPLÍCITA, e isso não é preferência.
     `canvas` é elemento SUBSTITUÍDO: com `top` e `bottom` declarados e altura
     automática, ele NÃO estica para preencher o vão — usa a proporção
     intrínseca dos atributos width/height, que sem eles é 300x150. Medido: a
     tela ficava com 183px de altura num palco de 490. Com a altura declarada em
     porcentagem do palco, ela cobre o que deve. */
  width: 100%;
  height: 122%;
  pointer-events: none;
}

/* O clarão do impacto, e o alvo do raio, na mesma peça.
   Posicionado em porcentagem da CHAPA do Kundun: acompanha a figura em toda
   tela sem coordenada fixa. `screen` porque é luz somada, não tinta. */
.stage__zap {
  position: absolute;
  left: 14%;
  top: 21%;
  width: 30%;
  aspect-ratio: 1;
  translate: -50% -50%;
  pointer-events: none;
  opacity: 0;
  mix-blend-mode: screen;
  background:
    radial-gradient(circle,
      rgb(228 244 255 / 92%) 0%,
      rgb(150 205 255 / 62%) 16%,
      rgb(70 145 255 / 26%) 38%,
      rgb(40 90 220 / 10%) 60%,
      transparent 74%);
}

/* Kundun. Recorte de personagem, não cena: entra CONTIDO, proporção nativa
   (1109x768) preservada em toda largura de tela — encolhe, nunca deforma,
   nunca perde asa nem cajado. As asas podem sangrar além do quadro; quem
   contém é o `overflow: hidden` da `.portal`. */
/* O eixo do chefe é o eixo da MARCA, não o do palco.
   As colunas do elenco são 1,38fr e 1fr, porque as silhuetas da esquerda somam
   1,38 vez as da direita. Isso desloca o centro da pista do núcleo para a
   direita do centro do palco em exatamente 0,08 x (palco − núcleo) — medido a
   1920: 65px. Centrado no palco, o Kundun ficava 65px à esquerda do brasão, do
   letreiro e da placa, e o conjunto lia como desalinhado.

   A conta está escrita na própria regra: `100%` aqui é a largura do palco (é o
   bloco de contenção deste absoluto) e `--core-w` é a pista. Abaixo de 48em a
   pista é `100%`, a diferença zera e o deslocamento some sozinho. */
.stage__boss {
  position: absolute;
  left: calc(50% + var(--axis, 0) * (100% - var(--core-w)));
  bottom: var(--boss-drop, 26%);
  z-index: 1;
  translate: -50% 0;
  pointer-events: none;
  /* Acompanha a parallax da seção — ver `--portal-shift` em main.js. */
  transform: translateY(calc(var(--portal-shift, 0px) * 0.32));
}

.stage__boss-img {
  width: var(--boss-w);
  max-width: none;
  height: auto;
}

/* Elenco. Decorativo aqui: cada classe tem nome, arte grande e legenda própria
   na seção "As Classes", logo abaixo.

   Dimensionado por ALTURA, não por largura. Numa fila de figuras em pé o que
   precisa combinar é a estatura; a largura é consequência da silhueta de cada
   classe, e elas variam muito (a Elfa tem proporção 0,49 e o Soul Master 0,68).
   Os derivados `cast-*-h*` já vêm recortados na caixa de alfa, então o pé da
   imagem É o pé da figura e `align-items: flex-end` põe as cinco no mesmo chão.

   O grupo é ancorado pela borda EXTERNA e cresce para dentro. Ao contrário:
   quando o elenco não preenche a coluna, a folga tem de cair na borda EXTERNA
   e não ao lado do brasão: um vão junto ao ponto focal lê como erro, um vão
   junto ao trilho lê como respiro. E quando ele passa da coluna, o excedente
   entra por baixo do núcleo, que está acima na pilha e o esconde. */
.stage__cast {
  display: none;
  align-items: flex-end;
  align-self: end;
  min-width: 0;
  margin-bottom: var(--cast-lift, 0px);
  z-index: 2;
  pointer-events: none;
  transform: translateY(calc(var(--portal-shift, 0px) * 0.14));
}

.stage__cast--left { justify-content: flex-end; }
.stage__cast--right { justify-content: flex-start; }

.cast {
  display: block;
  flex: none;
  /* Sobreposição: no template as figuras quase se tocam. É a sobreposição que
     faz cinco recortes lerem como um GRUPO e não como cinco adesivos. */
  margin-inline: calc(var(--cast-overlap, 0px) * -1);
  filter: drop-shadow(0 22px 32px rgb(0 0 0 / 62%));
}

/* A margem negativa serve para as figuras se sobreporem UMAS ÀS OUTRAS. Nas
   pontas do grupo ela não tem vizinho para morder e vira sangria pura: a figura
   externa avançava ~12px para fora da coluna. Sob o trilho de brasões isso
   passava despercebido, mas no tablet — onde a borda da coluna É a borda da
   tela — ela aparecia cortada. */
.stage__cast--left .cast:first-child { margin-inline-start: 0; }
.stage__cast--right .cast:last-child { margin-inline-end: 0; }

.cast img {
  width: auto;
  max-width: none;
  height: var(--cast-h, var(--cast-base));
}

/* Estatura por classe. Duas coisas ao mesmo tempo: a variação natural de um
   grupo real (a Elfa não tem a altura do Dark Lord) e a profundidade em leque —
   quem está na ponta é o mais distante, então é o mais baixo e o mais apagado. */
.cast--dk { --cast-h: calc(var(--cast-base) * 0.88); opacity: 0.92; }
.cast--sm { --cast-h: calc(var(--cast-base) * 0.95); opacity: 0.93; }
.cast--elf { --cast-h: calc(var(--cast-base) * 0.97); }
.cast--dl { --cast-h: var(--cast-base); }
.cast--mg { --cast-h: calc(var(--cast-base) * 0.9); opacity: 0.93; }

/* Núcleo da marca. */
.stage__core {
  position: relative;
  z-index: 4;
  display: grid;
  justify-items: center;
  text-align: center;
  gap: clamp(0.35rem, 0.9vh, 0.8rem);
  width: 100%;
  max-width: 34rem;
}

.stage__title {
  display: grid;
  justify-items: center;
  gap: clamp(0.15rem, 0.5vh, 0.5rem);
  margin: 0;
}

.stage__locator {
  display: inline-flex;
  align-items: center;
  gap: 0.7em;
  margin-top: 0.2rem;
  font-family: var(--font-body);
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  text-transform: uppercase;
  color: var(--gold-400);
  text-shadow: 0 1px 10px rgb(3 5 12 / 92%);
}

.stage__locator::before,
.stage__locator::after {
  content: '';
  width: clamp(1rem, 3vw, 2.5rem);
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--gold-700));
}

.stage__locator::after { background: linear-gradient(90deg, var(--gold-700), transparent); }

.stage__locator i { font-style: normal; margin-inline: 0.1em; color: var(--gold-700); }

.stage__era {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 0.85rem + 2.5vw, 3.35rem);
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: clamp(0.02em, 0.3vw, 0.06em);
  text-transform: uppercase;
  background: var(--metal-era);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-wrap: balance;
  filter: drop-shadow(0 3px 22px rgb(3 5 12 / 92%));
}

/* -- a placa de ação ------------------------------------------------------ */

/* O foco primário do template é JOGAR AGORA: uma placa larga entre duas asas
   de metal, o maior alvo da tela. Não há o que jogar — o servidor não abriu e
   não há cliente publicado — então a placa mantém a PRESENÇA e leva ao único
   destino real que a página tem hoje. */
.playplate {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: clamp(0.6rem, 1.6vw, 1.35rem);
  margin-top: clamp(0.15rem, 0.6vh, 0.55rem);
  padding: 0.85em clamp(1.4rem, 3.4vw, 2.8rem);
  background:
    linear-gradient(180deg, rgb(36 56 116 / 52%), rgb(7 11 24 / 88%) 68%),
    radial-gradient(120% 200% at 50% -70%, rgb(210 168 94 / 18%), transparent 62%);
  border-block: 1px solid var(--line-strong);
  font-family: var(--font-display);
  font-size: clamp(0.82rem, 0.7rem + 0.5vw, 1.25rem);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-indent: 0.2em;
  text-transform: uppercase;
  transition:
    border-color var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out),
    transform var(--dur) var(--ease-out);
}

/* As quatro cantoneiras, num só pseudo-elemento e sem DOM extra. */
.playplate::before {
  content: '';
  position: absolute;
  inset: -1px;
  pointer-events: none;
  background:
    linear-gradient(var(--gold-400), var(--gold-400)) 0 0 / 14px 1px no-repeat,
    linear-gradient(var(--gold-400), var(--gold-400)) 0 0 / 1px 14px no-repeat,
    linear-gradient(var(--gold-400), var(--gold-400)) 100% 0 / 14px 1px no-repeat,
    linear-gradient(var(--gold-400), var(--gold-400)) 100% 0 / 1px 14px no-repeat,
    linear-gradient(var(--gold-400), var(--gold-400)) 0 100% / 14px 1px no-repeat,
    linear-gradient(var(--gold-400), var(--gold-400)) 0 100% / 1px 14px no-repeat,
    linear-gradient(var(--gold-400), var(--gold-400)) 100% 100% / 14px 1px no-repeat,
    linear-gradient(var(--gold-400), var(--gold-400)) 100% 100% / 1px 14px no-repeat;
}

/* O brilho da placa, aceso na interação. Opacidade em transição finita — não
   há laço aqui. */
.playplate::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0;
  background: linear-gradient(180deg, rgb(189 145 71 / 26%), rgb(23 64 140 / 26%));
  transition: opacity var(--dur) var(--ease-out);
}

.playplate__label {
  background: var(--metal-gold);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: var(--engrave);
  white-space: nowrap;
}

/* As asas: um losango sobre um fio, o mesmo ornamento da placa de edição. */
.playplate__wing {
  position: relative;
  width: clamp(1.1rem, 3vw, 2.6rem);
  height: 1px;
  flex: none;
  background: linear-gradient(90deg, transparent, var(--gold-500));
}

.playplate__wing:last-of-type { background: linear-gradient(90deg, var(--gold-500), transparent); }

.playplate__wing::after {
  content: '';
  position: absolute;
  top: 50%;
  right: 0;
  width: 0.34rem;
  height: 0.34rem;
  translate: 50% -50%;
  rotate: 45deg;
  background: var(--gold-300);
  box-shadow: 0 0 9px rgb(226 190 124 / 65%);
}

.playplate__wing:last-of-type::after { right: auto; left: 0; translate: -50% -50%; }

.playplate:hover,
.playplate:focus-visible {
  border-block-color: var(--gold-300);
  box-shadow: 0 0 0 1px rgb(226 190 124 / 24%), 0 14px 44px -14px rgb(189 145 71 / 62%);
  transform: translateY(-1px);
}

.playplate:hover::after,
.playplate:focus-visible::after { opacity: 1; }

.playplate:active { transform: translateY(1px); }

.stage__note {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-size: var(--step--2);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-dim);
  text-wrap: balance;
  text-shadow: 0 1px 10px rgb(3 5 12 / 90%);
}

.stage__note i { font-style: normal; color: var(--gold-700); }

.stage__note-dot {
  width: 0.4em;
  height: 0.4em;
  flex: none;
  border-radius: 50%;
  background: var(--arcane-400);
  box-shadow: 0 0 0 3px rgb(90 163 251 / 16%);
}

/* -- painel: a moldura de UI do portal ------------------------------------ */

/* O painel emoldurado é a peça que faz o conjunto ler como INTERFACE DE JOGO e
   não como cartão de site. Chapa escura, fio dourado envelhecido, cantoneiras
   e um brilho interno de uma linha no topo — tudo em CSS, sem imagem. */
.pframe {
  position: relative;
  --pframe-padx: clamp(0.7rem, 1.1vw, 1.15rem);
  padding: var(--pframe-padx);
  background:
    linear-gradient(180deg, rgb(17 27 60 / 52%) 0%, rgb(5 8 18 / 90%) 62%),
    radial-gradient(130% 90% at 50% -22%, rgb(90 163 251 / 9%), transparent 62%);
  border: 1px solid var(--line);
  box-shadow:
    inset 0 1px 0 rgb(226 190 124 / 11%),
    0 20px 44px -30px rgb(0 0 0 / 92%);
}

.pframe::before {
  content: '';
  position: absolute;
  inset: -3px;
  pointer-events: none;
  opacity: 0.8;
  background:
    linear-gradient(var(--gold-500), var(--gold-500)) 0 0 / 11px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 0 0 / 1px 11px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 0 / 11px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 0 / 1px 11px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 0 100% / 11px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 0 100% / 1px 11px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 100% / 11px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 100% / 1px 11px no-repeat;
}

.pframe__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.15rem 0.7rem;
  padding-bottom: 0.5rem;
  margin-bottom: 0.65rem;
  border-bottom: 1px solid var(--line);
}

.pframe__title {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  background: var(--metal-gold);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 1px 0 rgb(0 0 0 / 70%));
}

/* O losango à esquerda do título, o mesmo do resto do sistema. */
.pframe__title::before {
  content: '';
  width: 0.34em;
  height: 0.34em;
  flex: none;
  rotate: 45deg;
  background: var(--gold-300);
  box-shadow: 0 0 8px rgb(226 190 124 / 60%);
}

.pframe__sub {
  margin-left: auto;
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-dim);
  white-space: nowrap;
}

.pframe__foot {
  margin-top: 0.55rem;
  padding-top: 0.45rem;
  border-top: 1px solid rgb(189 145 71 / 14%);
  font-size: var(--step--2);
  line-height: 1.55;
  color: var(--text-dim);
  text-wrap: pretty;
}

.pframe__link {
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold-300);
  transition: color var(--dur) var(--ease-out);
}

.pframe__link:hover,
.pframe__link:focus-visible { color: var(--gold-100); }

/* -- painel do Soul Sentinel ---------------------------------------------- */

.sentinel {
  display: grid;
  align-content: start;
  gap: 0.55rem;
  text-align: center;
}

.sentinel .pframe__head { justify-content: center; text-align: center; }
.sentinel .pframe__sub { margin-left: 0; width: 100%; text-align: center; }

/* A chapa de destaque do painel.

   Mesmo tratamento que "Os Três Guardiões" já dá a esta arte: sangra até as
   bordas internas, é cortada em `cover` — proporção nativa 1:1, nada esticado
   — e o pé dissolve no fundo do painel. Sem a dissolução, uma aresta reta e
   opaca bateria em cima de "EM IMPLANTAÇÃO" e a arte brigaria com o texto em
   vez de apresentá-lo. */
/* O emblema entra SOLTO — sem caixa, sem borda, sem máscara.

   A arte anterior era um quadrado OPACO e precisava de moldura, senão leria
   como adesivo colado dentro do painel. Esta traz anel, moldura e clarão
   próprios, e dissolve em transparência nas bordas: emoldurá-la seria pôr
   moldura em cima de moldura, e recortá-la num quadrado cortaria as pontas do
   raio que saem do anel.

   O que fica de CSS é a medida e uma sombra que assenta a peça. A largura é
   governada pela ALTURA disponível, não pela do painel — é o painel que dita a
   altura da linha inteira da cena, e uma peça grande demais aqui empurra o
   Kundun para baixo e a faixa de indicadores para fora da primeira tela.
   Proporção nativa 429x454 preservada: nada cortado, nada esticado. */
/* A medida do emblema (Bloco 18).

   Antes ela era só uma função da ALTURA da janela: `clamp(8rem, 17svh, 13rem)`.
   Isso protegia telas baixas, mas ignorava a largura do painel — a arte ficava
   em 60% da largura interna em todo desktop, com uma faixa vazia dos dois lados,
   e lia pequena. "Preencher o painel" é, antes de tudo, uma conta de LARGURA.

   Agora são três limites, e vence o menor:
     · uma fração da largura interna do painel — é o que faz a arte preencher;
     · um teto em rem — impede que ela vire um pôster em telas enormes;
     · um teto em svh — protege a janela baixa, onde o painel já é a peça mais
       alta da cena e não tem para onde crescer.

   O terceiro limite não é decorativo. Medido: a 1366x768 a faixa de indicadores
   termina a 3px do fim da primeira dobra. O painel dita a altura da linha
   inteira da grade em TODO desktop, então cada pixel que o emblema cresce é um
   pixel que empurra o Kundun para baixo e a faixa para fora da tela. */
.sentinel__art {
  /* Centrada mesmo quando é MAIS LARGA que o contêiner. `margin-inline: auto`
     não serve para isso: com largura maior que a caixa, o `auto` resolve para
     zero e a peça escorre para a direita. `left: 50%` empurra meia caixa,
     `translate: -50%` puxa meia peça — e o resultado centra nos dois casos. */
  position: relative;
  left: 50%;
  translate: -50%;
  width: min(88%, 20rem, 32svh);
  /* `figure` traz `margin: 1em 40px` da folha do navegador, e o reset deste
     projeto nunca zerou esse elemento. O recuo lateral some sozinho com o
     `auto`, mas o vertical não: eram ~26px de folga invisível empurrando o
     painel — e, com ele, a linha inteira da cena.

     O recuo negativo devolve parte do respiro que o `gap` do painel reserva
     acima e abaixo da arte. É pequeno de propósito: é ele que financia o
     crescimento na faixa apertada, mas passar disso encostaria o emblema no fio
     do cabeçalho e na placa "EM IMPLANTAÇÃO".

     ATALHO, NÃO `margin-block`. E isso é uma cicatriz, não estilo:
     `figure` chega com `margin: 1em 40px` da folha do NAVEGADOR, e o reset deste
     projeto nunca zerou esse elemento. O Bloco 16 matou os quatro lados com
     `margin: 0 auto`; o Bloco 17 trocou aquilo por `margin-block`, que mexe só
     no eixo VERTICAL — e devolveu, em silêncio, 40px de margem lateral.

     O efeito medido em produção: a arte 40px à direita do centro em TODA tela,
     enquanto a placa "EM IMPLANTAÇÃO" logo abaixo ficava em 0. A 1920 o emblema
     chegou a ultrapassar a borda direita do painel em 33px, com 47px sobrando à
     esquerda. Quanto mais a arte cresceu no Bloco 19, mais o desvio apareceu.

     O atalho de dois valores zera os quatro lados de uma vez. Não trocar por
     `margin-block` de novo. */
  margin: -0.25rem 0;
}

.sentinel__art img {
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 0 18px rgb(90 163 251 / 34%)) drop-shadow(0 6px 18px rgb(0 0 0 / 70%));
}

.sentinel__state {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55em;
  justify-self: center;
  padding: 0.4em 1em;
  border: 1px solid var(--line-cool);
  background: rgb(8 14 30 / 70%);
  font-family: var(--font-display);
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--arcane-200);
}

.sentinel__dot {
  width: 0.42em;
  height: 0.42em;
  border-radius: 50%;
  background: var(--arcane-400);
  box-shadow: 0 0 0 3px rgb(90 163 251 / 18%);
}

.sentinel__label {
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.22em;
  text-indent: 0.22em;
  text-transform: uppercase;
  color: var(--gold-400);
}

/* O template marca estes seis itens com um visto VERDE, que é a leitura
   universal de "ativo e aprovado". O Sentinel não está operando. O visto vira
   losango de escopo — a mesma densidade visual, sem a afirmação falsa. */
.sentinel__scope {
  display: grid;
  gap: 0.24rem 0.6rem;
  text-align: left;
}

/* 48em a 62em — tablet. O painel aqui é de largura CHEIA (722px a 768) e está
   empilhado abaixo da cena: não dita a altura de linha nenhuma. A regra base
   deixava o emblema em 320px, 46% de um painel enorme, e ele boiava no meio.
   Nesta faixa ele pode crescer sem custo de composição. */
@media (min-width: 48em) and (max-width: 61.999em) {
  .sentinel__art { width: min(58%, 24rem, 38svh); }
}

/* 62em a 88em — a lista "NO ESCOPO" ainda está em UMA coluna, o painel fica
   ~50px mais alto e a primeira dobra não tem sobra: medido a 1366x768, a faixa
   de indicadores termina a 3px do fim da tela. O emblema cresce só o que cabe.
   Numa janela baixa e estreita, emblema grande custa a faixa inteira. */
@media (min-width: 62em) and (max-width: 87.999em) {
  .sentinel__art { width: min(80%, 12rem, 18svh); }
}

@media (min-width: 88em) {
  .sentinel__scope { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Com o escopo em duas colunas o painel encurta ~50px, e é aqui que o
     emblema tem espaço de verdade. Ele avança sobre o recuo interno: a borda
     do painel continua a ~6px de distância, então não há colisão. */
  .sentinel__art {
    width: min(calc(100% + 1.2 * var(--pframe-padx)), 22rem, 34svh);
  }
}

/* Janela LARGA mas BAIXA — 1408x768, 1600x768 e afins. A lista de escopo já
   está em duas colunas, mas 768px de altura continuam sendo 768px: medido sem
   esta guarda, a 1408x768 a faixa de indicadores termina exatamente na borda da
   dobra, com zero de sobra. Aqui manda a altura, não a largura. */
@media (min-width: 88em) and (max-height: 52em) {
  .sentinel__art { width: min(80%, 12rem, 23svh); }
}

.sentinel__scope li {
  display: flex;
  align-items: center;
  gap: 0.6em;
  font-size: var(--step--2);
  letter-spacing: 0.06em;
  color: var(--text-soft);
}

.sentinel__mark {
  width: 0.42em;
  height: 0.42em;
  flex: none;
  rotate: 45deg;
  border: 1px solid var(--gold-400);
  background: rgb(226 190 124 / 22%);
}

/* -- B · faixa de indicadores --------------------------------------------- */

.pstrip {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  box-shadow: inset 0 1px 0 rgb(226 190 124 / 10%);
}

/* Rótulo EM CIMA do valor, não ao lado dele.
   Lado a lado, "SERVIDOR  PRÉ-LANÇAMENTO" precisa de ~230px numa célula de
   290px a 1920 — e a 1366 a mesma célula tem 190px. No primeiro render metade
   dos valores saiu cortada com reticências: "PRÉ-LANÇAMEN…", "ABRE NO LANÇA…",
   "EM IMPLA…". Empilhado, o valor tem a largura inteira da célula e nenhuma
   das seis leituras precisa ser abreviada para caber. */
.pstat {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 0 0.55rem;
  min-width: 0;
  padding: 0.5rem clamp(0.55rem, 1vw, 1rem);
  background: linear-gradient(180deg, rgb(14 22 48 / 82%), rgb(5 8 18 / 92%));
}

.pstat__icon {
  grid-row: 1 / span 2;
  width: 1.15rem;
  height: 1.15rem;
  flex: none;
  color: var(--gold-500);
}

.pstat__label {
  grid-column: 2;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-dim);
  white-space: nowrap;
}

.pstat__value {
  grid-column: 2;
  font-family: var(--font-display);
  font-size: var(--step--2);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-wrap: balance;
}

/* Os três estados verdadeiros que a faixa pode ter. Nenhum deles é "online". */
.pstat--wait .pstat__value { color: var(--gold-200); }
.pstat--soon .pstat__value { color: var(--arcane-200); }
.pstat--none .pstat__value { color: var(--text-dim); }
.pstat--none .pstat__icon { opacity: 0.55; }

/* -- C · módulos ---------------------------------------------------------- */

.pmods {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(0.7rem, 1.2vw, 1.15rem);
}

.pmod {
  display: grid;
  align-content: start;
  min-width: 0;
}

.news { display: grid; gap: 0.42rem; }

.news__item {
  display: grid;
  gap: 0.1rem;
  padding-left: 0.7rem;
  border-left: 1px solid var(--line);
}

.news__state {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.news__state--done { color: var(--gold-400); }
.news__state--wip { color: var(--arcane-300); }

.news__name {
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text);
}

.news__desc {
  font-size: var(--step--2);
  line-height: 1.5;
  color: var(--text-muted);
  text-wrap: pretty;
}

.rows { display: grid; gap: 1px; background: rgb(189 145 71 / 14%); }

.row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 0.15rem 0.6rem;
  padding: 0.38rem 0.1rem;
  background: transparent;
}

.row__key {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.row__val {
  grid-row: 2;
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--gold-200);
}

.row__val--none { color: var(--text-muted); }
.row__val--never { color: var(--arcane-200); }

.row__tag {
  grid-row: 1 / span 2;
  grid-column: 2;
  align-self: center;
  padding: 0.25em 0.6em;
  border: 1px solid var(--line);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--text-dim);
}

.row__tag--done { border-color: var(--line-strong); color: var(--gold-300); }
.row__tag--wip { border-color: var(--line-cool); color: var(--arcane-200); }
.row__tag--never { border-color: rgb(79 211 155 / 34%); color: #7fdcb4; }
.row__tag--none { opacity: 0.7; }

.row--rule { background: rgb(79 211 155 / 5%); }

/* O ranking do template vem povoado com cinco jogadores. Existem exatamente
   zero personagens de jogador. As cinco posições ficam DESENHADAS e vazias: a
   moldura diz "isto vai existir", o conteúdo diz "ainda não". */
.rank { display: grid; gap: 1px; background: rgb(189 145 71 / 14%); }

.rank__row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.34rem 0.15rem;
}

.rank__pos {
  width: 1.35rem;
  flex: none;
  font-family: var(--font-display);
  font-size: var(--step--2);
  font-weight: 700;
  text-align: center;
  color: var(--gold-500);
}

.rank__slot {
  flex: 1;
  min-width: 0;
  padding: 0.2rem 0.6rem;
  border: 1px dashed rgb(189 145 71 / 20%);
  background: rgb(6 10 22 / 55%);
  font-size: var(--step--2);
  letter-spacing: 0.3em;
  color: var(--text-dim);
  opacity: 0.75;
}

.pmod--sealed .pframe__sub { color: var(--gold-500); }

/* -- D · princípios + comunidade ------------------------------------------ */

.pfoot {
  display: grid;
  gap: clamp(0.7rem, 1.2vw, 1.1rem);
}

/* Os seis princípios têm, na seção seguinte, a própria banda com número, ícone
   e descrição. Repeti-los aqui como módulos seria dizer a mesma coisa duas
   vezes em dois tamanhos. Viram uma fita — o índice do que vem a seguir. */
.creedribbon {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(0.5rem, 1.6vw, 1.4rem);
  padding: 0.55rem clamp(0.75rem, 2vw, 1.5rem);
  background: linear-gradient(180deg, rgb(30 22 8 / 34%), rgb(5 8 18 / 74%));
  border-block: 1px solid var(--line);
}

.creedribbon__cap {
  width: clamp(0.9rem, 4vw, 3.5rem);
  height: 1px;
  flex: none;
  background: linear-gradient(90deg, transparent, var(--gold-700));
}

.creedribbon__cap:last-child { background: linear-gradient(90deg, var(--gold-700), transparent); }

.creedribbon__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.3em clamp(0.5rem, 1.4vw, 1.15rem);
  min-width: 0;
}

.creedribbon b {
  font-family: var(--font-display);
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-indent: 0.2em;
  text-transform: uppercase;
  color: var(--gold-200);
  white-space: nowrap;
}

.creedribbon i {
  font-style: normal;
  color: var(--gold-700);
}

.community {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
  gap: 0.75rem 1.25rem;
}

.community__body { display: grid; gap: 0.3rem; }

.community__title {
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  background: var(--metal-gold);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 1px 0 rgb(0 0 0 / 70%));
}

.community__text {
  font-size: var(--step--2);
  line-height: 1.6;
  color: var(--text-muted);
  text-wrap: pretty;
}

.community__state {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  justify-self: start;
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--arcane-200);
}

.community__dot {
  width: 0.42em;
  height: 0.42em;
  border-radius: 50%;
  background: var(--arcane-400);
  box-shadow: 0 0 0 3px rgb(90 163 251 / 16%);
}

.community__mark {
  width: 7.5rem;
  height: auto;
  justify-self: center;
  opacity: 0.9;
  filter: drop-shadow(0 0 16px rgb(90 163 251 / 26%));
}

/* -- responsivo: do celular ao portal completo ---------------------------- */

/* ≥ 40em — a faixa passa a caber em três colunas e os módulos em duas. */
@media (min-width: 40em) {
  .pstrip { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .pmods { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .community { grid-template-columns: minmax(0, 1fr) auto; }
  .community__mark { width: 8rem; }
}

/* ≥ 48em — o elenco entra. Abaixo disto a prioridade é brand + chefe + ação,
   e cinco recortes de 90px lado a lado seriam ruído, não elenco.

   A colocação nas três colunas é EXPLÍCITA. A ordem no DOM é chefe, elenco
   esquerdo, elenco direito, núcleo — que é a ordem de leitura e a ordem de
   pintura corretas — e não a ordem visual das colunas. Deixar a colocação
   automática poria o elenco direito no meio e o núcleo na terceira coluna. */
@media (min-width: 48em) {
  .stage {
    /* Quatro figuras nesta faixa, e as colunas são SIMÉTRICAS por causa disso.
       Somando as silhuetas: SM 0,649 + Elfa 0,471 = 1,120 à esquerda contra
       DL 0,656 + MG 0,554 = 1,210 à direita — praticamente o mesmo. A assimetria
       de 1,38 do desktop existe porque lá são três de um lado; aqui ela abriria
       um vão à direita.

       `--axis` acompanha: é o fator que desloca o Kundun do centro do palco
       para o centro da pista da marca, e com colunas simétricas os dois centros
       coincidem — o deslocamento tem de ser zero, não 0,08. */
    grid-template-columns: 1fr var(--core-w) 1fr;
    --axis: 0;
    align-items: end;
    --core-w: clamp(17rem, 52vw, 27rem);
    --cast-base: clamp(9rem, 23vw, 15rem);
    --cast-overlap: clamp(0.35rem, 1.6vw, 1.4rem);
    --cast-lift: 1.25rem;
    --boss-w: min(78vw, 40rem);
  }

  /* A figura da ponta esquerda sai. Cinco recortes numa faixa sem trilho e sem
     painel obrigavam cada um a 146px de altura para caberem — silhuetas
     pequenas demais para se lerem como personagens. Quatro cabem a 195px.

     Nada de verdade se perde: o elenco aqui é decorativo e as cinco classes têm
     nome, arte grande e legenda própria na seção "As Classes", logo abaixo. */
  .cast--dk { display: none; }

  .stage__cast { display: flex; }
  .stage__cast--left { grid-area: 1 / 1; }
  .stage__core { grid-area: 1 / 2; }
  .stage__cast--right { grid-area: 1 / 3; }

  /* O núcleo avança sobre as figuras de dentro, como o brasão do template
     avança sobre a Elfa e o Dark Lord. */
  .stage__core {
    width: auto;
    max-width: none;
    margin-inline: clamp(-2.75rem, -2.2vw, -0.75rem);
  }
}

/* ≥ 62em — o portal completo: trilho de brasões à esquerda, painel do Soul
   Sentinel à direita, cena no meio. */
@media (min-width: 62em) {
  .portal__scene {
    grid-template-columns: clamp(8rem, 10vw, 11.5rem) minmax(0, 1fr) clamp(13.5rem, 17.5vw, 20rem);
  }

  /* Volta o elenco de cinco e, com ele, a assimetria das colunas e o
     deslocamento de eixo que a compensa. */
  .cast--dk { display: block; }

  .stage {
    grid-template-columns: 1.38fr var(--core-w) 1fr;
    --axis: 0.08;
  }

  .prail { grid-area: 1 / 1; }
  .stage { grid-area: 1 / 2; }
  .sentinel { grid-area: 1 / 3; }


  .stage {
    /* Esta é a faixa que sobra quando a tela é larga o bastante para o portal
       completo mas ALTA demais para as compressões por altura — 1024x1366 é o
       caso típico. Ali o painel do Sentinel estica a linha da grade, o palco
       cresce com ela, e um pouso baixo joga a figura inteira para cima: medido,
       o rosto do Kundun 9px ATRÁS do brasão. O pouso alto compensa. */
    --boss-drop: 25%;
    --core-w: clamp(20rem, 26vw, 30rem);
    --cast-base: clamp(13rem, 20vw, 32rem);
    --cast-overlap: clamp(0.75rem, 1.9vw, 2.6rem);
    --cast-lift: clamp(2.5rem, 5vh, 5rem);
    --boss-w: min(52vw, 52rem);
    min-height: clamp(20rem, 42svh, 36rem);
  }

  .pstrip { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .pmods { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  /* A banda de fechamento é de LINHAS, não de colunas.
     Em duas colunas, a 2560 a fita ficava com 1.200px de largura e ~190px de
     altura para carregar seis palavras centradas — uma caixa vazia — e o painel
     da comunidade era espremido em 352px, com o título quebrando em duas linhas
     ao lado do brasão. Empilhados e de largura cheia, a fita vira o que ela é
     (um friso que atravessa o portal) e o painel ganha a linha inteira. */
  .creedribbon__list {
    flex: 1;
    justify-content: space-evenly;
  }

  .community__body { max-width: 62rem; }
}

/* ≥ 100em — telas largas. A cena pode abrir mais sem que os painéis laterais
   estiquem além do que a leitura aguenta. */
@media (min-width: 100em) {
  .stage {
    --boss-drop: 17%;
    --core-w: clamp(22rem, 24vw, 32rem);
    --cast-base: clamp(16rem, 20vw, 32rem);
    --cast-lift: clamp(3rem, 5.5vh, 6rem);
    --boss-w: min(50vw, 56rem);
  }
}

/* ≥ 160em (2560px e acima) — a abertura tem a própria medida.
   O corpo aprovado abaixo é governado por `--container-wide`, que trava em
   132rem: uma medida de LEITURA, escolhida para que a prosa não vire uma linha
   de 200 caracteres. A abertura não tem prosa; ela tem uma cena. Presa àquela
   trava, a 3840 ela ocupava 55% da tela e virava a "ilha no meio do nada" que o
   briefing proíbe.

   Só a moldura do portal abre. O corpo continua exatamente onde estava, e a
   costura entre os dois é o mesmo degradê de sempre. */
@media (min-width: 160em) {
  .portal__frame { width: min(100% - 4rem, 168rem); }

  .stage {
    --core-w: clamp(26rem, 22vw, 40rem);
    --cast-base: clamp(20rem, 22vw, 41rem);
    --boss-w: min(46vw, 66rem);
  }
}

/* -- a compressão por ALTURA ----------------------------------------------
   O template é uma composição 16:9 desenhada para caber numa tela. A largura
   não é o problema — a altura é: a 1366x768 e 1600x900 a mesma escala que
   funciona a 1440p empurra os módulos para fora do quadro.

   Estas duas faixas comprimem por altura, não por largura: brasão menor,
   figuras menores, respiro menor, título menor. É o mesmo desenho, não um
   desenho reduzido. */

/* ≤ 1080px de altura — a faixa mais comum de desktop. */
@media (min-width: 62em) and (max-height: 70em) {
  .stage {
    --boss-drop: 16%;
    --core-w: clamp(19rem, 24vw, 27rem);
    --cast-base: clamp(12rem, 21.5vw, 28rem);
    --cast-lift: clamp(2rem, 4.2vh, 3.5rem);
    --boss-w: min(48vw, 48rem);
    min-height: clamp(17rem, 37svh, 28rem);
  }

  .stage__era { font-size: clamp(1.4rem, 0.8rem + 2vw, 2.9rem); }
  .portal__frame { gap: clamp(0.6rem, 1.3vh, 1.15rem); }
  .pframe {
    --pframe-padx: clamp(0.6rem, 0.85vw, 0.95rem);
    padding: var(--pframe-padx);
  }
  .news { gap: 0.35rem; }
  .rank__row { padding-block: 0.3rem; }
  .row { padding-block: 0.34rem; }
}

/* ≤ 900px de altura — notebooks e 1366x768. */
@media (min-width: 62em) and (max-height: 58em) {
  .stage {
    --boss-drop: 15%;
    --core-w: clamp(17rem, 22vw, 23rem);
    --cast-base: clamp(10rem, 22vw, 24rem);
    --cast-lift: clamp(1.5rem, 3.4vh, 2.6rem);
    --boss-w: min(44vw, 40rem);
    min-height: clamp(14rem, 33svh, 23rem);
  }

  .stage__era { font-size: clamp(1.25rem, 0.75rem + 1.6vw, 2.3rem); }
  .news__desc { display: none; }
  .portal { padding-block-end: clamp(1.25rem, 2.5vh, 2rem); }
}

/* Telas muito altas (4K e afins): a chapa do Kundun tem 1109px de largura
   nativa e não existe ampliação honesta que preencha 2160px de altura. O
   conjunto ganha respiro em vez de esticar a arte. */
@media (min-height: 100em) {
  .stage { min-height: clamp(30rem, 46svh, 52rem); }
  .portal__frame { gap: clamp(1.5rem, 2.4vh, 3rem); }
}

/* Celular: o trilho de brasões vira uma faixa horizontal sob a cena e o painel
   do Sentinel vira um painel de largura cheia. Nada de UI de desktop
   encolhida — a mesma informação, remontada. */
@media (max-width: 61.999em) {
  .prail {
    grid-auto-flow: row;
    padding: 0.85rem 0.6rem;
    border: 1px solid var(--line);
    background: linear-gradient(180deg, rgb(14 22 48 / 46%), rgb(5 8 18 / 78%));
  }

  .crests { max-width: 26rem; }

  .prail__channels {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.3rem 0.7rem;
    max-width: 26rem;
    margin-top: 0.15rem;
  }
}

/* Tablet e telas médias: o elenco já entra, mas a cena ainda é uma coluna só —
   não há trilho nem painel disputando largura, então o palco é a página
   inteira e o chefe cabe grande sem esmagar o núcleo. A altura mínima existe
   para que a sangria do Kundun tenha para onde ir SEM passar por trás da barra:
   sem ela a altura do palco era a do núcleo, e a coroa subia para debaixo do
   cabeçalho (medido: os olhos a 30px da barra a 768x1024). */
/* A folga do rosto tem fórmula, e é ela que fixa os números destas faixas:

     folga = altura-do-palco x pouso + 0,611 x altura-da-figura − altura-do-núcleo

   (0,389 da altura da chapa é onde estão os olhos do Kundun, medido na arte;
   0,611 é o que sobra abaixo deles. O núcleo é ancorado no pé do palco, então
   o topo do brasão está a `altura-do-palco − altura-do-núcleo`.)

   Sem essa conta o resultado foi o de sempre: a 768x1024 o brasão começava
   93px ACIMA dos olhos do chefe e cobria o rosto inteiro. Sobravam as asas. */
@media (min-width: 48em) and (max-width: 61.999em) {
  .stage {
    min-height: clamp(26rem, 52svh, 38rem);
    --boss-drop: 24%;
    --boss-w: min(72vw, 34rem);
  }
}

@media (max-width: 47.999em) {
  .portal { padding-block-end: clamp(1.25rem, 3vh, 2rem); }

  /* No celular o chefe sangra de propósito nas LATERAIS: a chapa é DEITADA
     (1109x768), então numa tela alta e estreita quem manda é a largura.
     Perde-se ponta de asa, ganha-se o chefe do capítulo em escala de pôster.
     Quem corta é o `overflow: hidden` da `.portal` — e o teste de rolagem real
     do measure-portal confirma que não há rolagem horizontal.

     No topo, porém, ele NÃO pode sangrar: ali está a barra fixa, e uma coroa
     atrás dela é oclusão, não composição. A altura mínima do palco é o que dá
     à figura o espaço vertical que ela precisa para pousar inteira. */
  /* A conta que fixa estes quatro números.
     O núcleo é ancorado no pé do palco, então o topo do brasão fica a
     (altura do palco − altura do núcleo). O rosto do Kundun fica a
     (topo da figura + 0,389 da altura dela) — 0,389 é onde estão os olhos na
     chapa, medido. Na primeira versão o brasão começava 8px ABAIXO do topo da
     figura e cobria o rosto inteiro: sobravam só as asas, e o chefe do capítulo
     virava textura de fundo no celular.

     Com estes valores a 390x844: figura de 528x366 pousando a 20% do pé, olhos a
     252px da janela, topo do brasão a 281px. O rosto passa acima da marca com
     29px de folga, e a coroa fica 38px abaixo da barra fixa. */
  .stage {
    min-height: clamp(24rem, 58svh, 34rem);
    --boss-drop: 20%;
    --boss-w: min(165vw, 33rem);
  }

  .pstat__label { letter-spacing: 0.1em; }

  /* No celular a figura é menor e o clarão a 30% da chapa cobriria meio
     Kundun. A frequência e a complexidade dos raios também caem — em main.js,
     onde a decisão pode ser tomada uma vez e não a cada quadro. */
  .stage__zap { width: 24%; }

  /* Portal no celular: mais lento, mais fraco, e sem o anel de dentro. Numa
     tela de 390px a coroa interna tem poucos pixels de espessura — ela não
     acrescenta profundidade nenhuma ali, só custo de compositor. E a elipse
     vira quase um círculo (medido: 161x163), então a leitura de disco em
     perspectiva se perde de qualquer forma. */
  .pring--inner { display: none; }

  .pring--outer {
    opacity: .78;
    animation-duration: 150s;
  }

  .pbreath { animation-duration: 27s; }
  .pbreath--slow { animation-duration: 37s; }

  /* O painel do Sentinel media 457px empilhado. Com 367px de largura, seis
     itens em coluna única é desperdício de espaço horizontal, não densidade. */
  .sentinel__scope { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* -- densidade de celular ----------------------------------------------
     Medido no primeiro render: 3.375px de portal antes de "A Nova Era" — quatro
     telas de rolagem só para atravessar a abertura. A densidade do template é
     de desktop, e o briefing é explícito: não empurrar essa densidade para o
     celular, remontar.

     Nada é escondido que carregue verdade só dele. As descrições do módulo de
     atualizações são a versão curta do que a seção "O Remaster" diz por
     extenso, algumas telas abaixo, com o estado real de cada frente. */
  .portal__frame { gap: 0.75rem; }
  .pframe {
    --pframe-padx: 0.75rem;
    padding: 0.7rem var(--pframe-padx);
  }
  .news__desc { display: none; }
  .news { gap: 0.3rem; }
  .news__item { padding-left: 0.6rem; }
  .row { padding-block: 0.34rem; }
  .rank__row { padding-block: 0.3rem; }
  .pmods { gap: 0.6rem; }
  .pfoot { gap: 0.6rem; }

  .sentinel { gap: 0.4rem; }
  /* No celular o painel tem 343px de largura interna e não dita a altura de
     nada — a arte ocupava 42% dela. Pode crescer à vontade. */
  .sentinel__art { width: min(72%, 16rem, 30svh); }
  .sentinel .pframe__foot { font-size: 0.72rem; }

  .prail { padding: 0.7rem 0.5rem; gap: 0.5rem; }

  .community { gap: 0.6rem; }
  .community__mark { width: 6rem; }
}

/* ---------- 7. sections ------------------------------------------------- */

/* -- pillars (Nova Era) --------------------------------------------------- */

/* These were a hairline-gap grid on a shared background — which is the exact
   language of a data table, and read as one. They are now six separate
   artifacts: their own ground, their own corner brackets, a numbered rule and
   an iconed plate. Six commitments in a fantasy MMORPG, not six cells. */
.pillars {
  display: grid;
  gap: var(--space-s);
  grid-template-columns: 1fr;
}

@media (min-width: 34em) { .pillars { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64em) { .pillars { grid-template-columns: repeat(3, 1fr); } }

.pillar {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 0.6rem;
  padding: var(--space-m) var(--space-s) calc(var(--space-m) * 1.05);
  text-align: center;
  background:
    radial-gradient(120% 70% at 50% 0%, rgb(30 47 99 / 26%), transparent 68%),
    linear-gradient(180deg, rgb(10 16 32 / 88%), rgb(4 6 13 / 94%));
  border: 1px solid var(--line-cool);
  border-radius: 2px;
  transition:
    border-color var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out),
    transform var(--dur) var(--ease-out);
}

/* Corner brackets, the same motif as the edition plate and the guardian cards,
   so the six read as belonging to the same object family. */
.pillar::before,
.pillar::after {
  content: '';
  position: absolute;
  width: 0.8rem;
  height: 0.8rem;
  border: 1px solid var(--gold-500);
  opacity: 0.5;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease-out);
}

.pillar::before {
  top: -1px;
  left: -1px;
  border-right-color: transparent;
  border-bottom-color: transparent;
}

.pillar::after {
  right: -1px;
  bottom: -1px;
  border-left-color: transparent;
  border-top-color: transparent;
}

.pillar:hover {
  border-color: rgb(210 168 94 / 38%);
  box-shadow: 0 18px 44px -30px rgb(189 145 71 / 70%);
  transform: translateY(-3px);
}

.pillar:hover::before,
.pillar:hover::after { opacity: 1; }

/* The number is structure, not content: it sits above the icon on its own
   ruled line and stays quiet. */
.pillar__num {
  display: flex;
  align-items: center;
  gap: 0.7em;
  width: 100%;
  font-family: var(--font-display);
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.2em;
  color: var(--gold-700);
  transition: color var(--dur) var(--ease-out);
}

.pillar__num::before,
.pillar__num::after {
  content: '';
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgb(189 145 71 / 30%));
}

.pillar__num::after { background: linear-gradient(90deg, rgb(189 145 71 / 30%), transparent); }

.pillar:hover .pillar__num { color: var(--gold-400); }

/* The icon gets a plate of its own so it stops floating as a loose line
   drawing and starts reading as an emblem.
   The plate is a cut-corner square via clip-path, NOT a rotated box: rotating
   the element to make a diamond rotates the glyph inside it 45 degrees too,
   and a tilted shield or scale is just wrong. clip-path shapes the plate and
   leaves its contents upright. */
.pillar__icon {
  width: 1.75rem;
  height: 1.75rem;
  box-sizing: content-box;
  padding: 0.7rem;
  color: var(--gold-300);
  background:
    linear-gradient(180deg, rgb(30 47 99 / 62%), rgb(4 6 13 / 78%));
  clip-path: polygon(
    0.5rem 0, calc(100% - 0.5rem) 0, 100% 0.5rem,
    100% calc(100% - 0.5rem), calc(100% - 0.5rem) 100%,
    0.5rem 100%, 0 calc(100% - 0.5rem), 0 0.5rem);
  transition: color var(--dur) var(--ease-out), filter var(--dur) var(--ease-out);
}

.pillar:hover .pillar__icon {
  color: var(--gold-100);
  filter: drop-shadow(0 0 12px rgb(226 190 124 / 35%));
}

.pillar__name {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gold-100);
}

.pillar__desc {
  max-width: 18rem;
  font-size: var(--step--1);
  color: var(--text-soft);
  text-wrap: pretty;
}

/* -- remaster ------------------------------------------------------------- */

/* The defining-identity section. It gets the deepest ground on the page, the
   widest measure, and the only piece of display type large enough to be read
   as a graphic rather than as a heading. */
.section--remaster {
  position: relative;
  overflow: hidden;
  padding-block: calc(var(--space-2xl) * 1.15);
}

/* The word itself as the section's ground. Replaces an image watermark: this
   costs no request, scales perfectly, and says the thing the section is about.
   Clipped to a gradient so it fades out before it can fight the copy. */
.remaster__ghost {
  position: absolute;
  z-index: -1;
  top: 38%;
  left: 50%;
  translate: -50% -50%;
  margin: 0;
  width: max-content;
  max-width: none;
  font-family: var(--font-display);
  font-size: clamp(4.5rem, 17vw, 17rem);
  font-weight: 900;
  line-height: 1;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;
  user-select: none;
  background: linear-gradient(180deg, rgb(226 190 124 / 11%), rgb(226 190 124 / 2%) 58%, transparent);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.section__head--wide { max-width: 52rem; }

/* The one sentence that governs everything else in this section. It was an
   italic aside with a left border; it is now the largest body statement on the
   page, because it is the argument the section exists to make. */
.creed {
  max-width: 46rem;
  margin: var(--space-m) auto 0;
  text-align: center;
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 1.05rem + 1.35vw, 2.35rem);
  font-style: italic;
  font-weight: 700;
  line-height: 1.28;
  letter-spacing: 0.01em;
  background: var(--metal-era);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-wrap: balance;
  filter: drop-shadow(0 2px 14px rgb(3 5 12 / 80%));
}

.remaster__lede {
  max-width: 44rem;
  margin: var(--space-m) auto 0;
  text-align: center;
  font-size: var(--step-0);
  color: var(--text-soft);
  text-wrap: pretty;
}

/* -- the state key -------------------------------------------------------- */

/* Three states, named once, before the grid. Without it every card has to
   re-teach the colour code; with it the cards can be quieter. */
.legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem clamp(1rem, 3vw, 2rem);
  margin: var(--space-l) auto var(--space-m);
  padding-block: var(--space-s);
  border-block: 1px solid var(--line);
  width: min(100%, 44rem);
}

.legend__item {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--state-text);
}

.legend__mark {
  width: 0.46em;
  height: 0.46em;
  rotate: 45deg;
  background: var(--state);
  box-shadow: 0 0 10px color-mix(in srgb, var(--state) 60%, transparent);
}

.legend__item--done { --state: var(--gold-500); --state-text: var(--gold-300); }
.legend__item--wip { --state: var(--arcane-500); --state-text: var(--arcane-300); }
.legend__item--plan { --state: #6f7a96; --state-text: #9aa4bd; }

/* -- tracks (Remaster status ledger) -------------------------------------- */

.tracks {
  display: grid;
  gap: var(--space-s);
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}

.track {
  position: relative;
  display: grid;
  align-content: start;
  gap: 0.55rem;
  padding: var(--space-m);
  background:
    radial-gradient(120% 60% at 0% 0%, color-mix(in srgb, var(--state) 12%, transparent), transparent 62%),
    linear-gradient(180deg, rgb(10 16 32 / 82%), rgb(3 5 12 / 92%));
  border: 1px solid var(--state-line);
  border-radius: 2px;
  overflow: hidden;
  transition: border-color var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}

/* The state seal: a lozenge in the top-right corner, in the state's colour.
   The old design put a 2px stripe down the left edge of every card, which made
   eight cards read as eight rows of a board. A seal reads as a stamp on an
   artifact. */
.track::after {
  content: '';
  position: absolute;
  top: var(--space-m);
  right: var(--space-m);
  width: 0.5rem;
  height: 0.5rem;
  rotate: 45deg;
  background: var(--state);
  box-shadow: 0 0 12px color-mix(in srgb, var(--state) 55%, transparent);
}

.track:hover { transform: translateY(-3px); border-color: var(--state); }

.track--done { --state: var(--gold-500); --state-line: var(--line); --state-text: var(--gold-300); }
.track--wip { --state: var(--arcane-500); --state-line: var(--line-cool); --state-text: var(--arcane-300); }
/* Planned is deliberately the quietest of the three: it is the state with the
   least behind it, and it should not read as loudly as work already underway. */
.track--plan { --state: #6f7a96; --state-line: rgb(142 152 175 / 20%); --state-text: #9aa4bd; }

.track__state {
  display: inline-flex;
  align-items: center;
  padding-right: 1.4rem;
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--state-text);
}

/* the inline dot is redundant now that the corner seal carries the state */
.track__dot { display: none; }

.track__name {
  font-size: var(--step-1);
  letter-spacing: 0.04em;
  color: var(--gold-100);
}

.track__desc {
  font-size: var(--step--1);
  color: var(--text-soft);
  text-wrap: pretty;
}

/* -- chapter spotlight ---------------------------------------------------- */

.section--chapter { padding-block: calc(var(--space-2xl) * 1.05); }

.chapter {
  display: grid;
  gap: var(--space-l);
  align-items: center;
}

/* The glow is the element's own background, not a z-index:-1 pseudo-element.
   `.chapter__art` is not a stacking context, so a negative-z child lands behind
   the page's fixed grain layer and most of the light is eaten before it ever
   reaches the artwork — which matters here, because Kundun is a near-black
   subject that has nothing but this glow to sit against. */
.chapter__art {
  position: relative;
  justify-self: center;
  /* Bloco 05: a arte de corpo inteiro é a revelação do capítulo, não uma
     ilustração de apoio. Com o container largo ela tinha 34rem numa faixa de
     132rem — pequena demais para o papel que exerce. */
  width: min(100%, 46rem);
  background: radial-gradient(circle at 50% 46%, rgb(160 118 52 / 46%) 0%, rgb(23 64 140 / 18%) 42%, transparent 68%);
}

/* Este halo existe desde antes da moldura e sobrevive a ela: é a separação que
   um sujeito escuro precisa contra um fundo escuro — núcleo quente atrás da
   figura, borda fria em volta. Com o recorte do Bloco 08 ele volta a fazer o
   trabalho que fazia antes do Bloco 06, agora sem uma placa por cima. */
.chapter__art::before {
  content: '';
  position: absolute;
  inset: -12% -8% -6%;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(58% 44% at 50% 52%, rgb(196 142 58 / 26%), transparent 72%),
    radial-gradient(78% 62% at 50% 46%, rgb(30 60 130 / 22%), transparent 76%);
}

/* -- render do chefe (Bloco 08) -------------------------------------------
   Sem moldura, sem borda, sem fundo: a arte é um recorte com alfa e tem de
   assentar direto no fundo da seção. Qualquer caixa aqui recria exatamente o
   "retângulo colado na página" que este bloco veio desfazer.

   Sem `aspect-ratio` e sem `object-fit` também: a chapa entra na proporção
   nativa (1109x768) e escala por largura. É o que garante que nada é esticado,
   que a cabeça e a ponta do cajado não são cortadas, e que as asas chegam
   inteiras em qualquer largura. */
.bossart {
  position: relative;
  margin: 0;
  display: grid;
  justify-items: center;
}

.bossart__img {
  display: block;
  width: 100%;
  height: auto;
}

/* A sombra de chão. Elipse achatada logo abaixo do manto, para o recorte pousar
   em vez de flutuar. Gradiente estático — não é `filter: drop-shadow`, que
   obrigaria o navegador a refazer a sombra da silhueta inteira a cada repintura
   de uma imagem de 1109 px. */
.bossart__ground {
  position: absolute;
  z-index: -1;
  left: 50%;
  bottom: 2.6rem;
  width: 78%;
  height: 12%;
  translate: -50% 0;
  pointer-events: none;
  background: radial-gradient(50% 50% at 50% 50%, rgb(3 5 12 / 78%), transparent 72%);
}

.bossart__cap {
  display: grid;
  gap: 0.15rem;
  margin-top: var(--space-2xs);
  text-align: center;
}

.bossart__name {
  font-family: var(--font-display);
  font-size: var(--step-1);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-100);
}

.bossart__role {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.chapter__body {
  display: grid;
  gap: var(--space-s);
  justify-items: start;
}

.chapter__title {
  display: grid;
  gap: 0.3rem;
  margin-top: var(--space-2xs);
}

.chapter__num {
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.38em;
  text-indent: 0.38em;
  text-transform: uppercase;
  color: var(--gold-400);
}

.chapter__name {
  font-size: var(--step-4);
  text-transform: uppercase;
  letter-spacing: 0.015em;
  background: var(--metal-title);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-wrap: balance;
}

.chapter__text {
  max-width: var(--measure);
  color: var(--text-soft);
  text-wrap: pretty;
}

/* Was a bordered 2x2 grid on a hairline background — a plain information box,
   which is exactly what this section was asked to stop looking like. It is now
   a ruled list: no outer box, no cell fills, just hairlines between entries. */
.chapter__facts {
  display: grid;
  width: 100%;
  margin: var(--space-2xs) 0 0;
}

@media (min-width: 30em) {
  .chapter__facts { grid-template-columns: repeat(2, 1fr); column-gap: var(--space-m); }
}

.fact {
  display: grid;
  gap: 0.15rem;
  padding-block: 0.85rem;
  border-top: 1px solid var(--line);
}

.fact dd { margin: 0; }

.fact__label {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.fact__value {
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--gold-200);
  text-wrap: balance;
}

.fact__value--wait { color: var(--arcane-300); }

@media (min-width: 62em) {
  .chapter {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
    gap: clamp(2rem, 4vw, 5rem);
    align-items: center;
  }
}

/* -- arc: the chapter progression ----------------------------------------- */

/* Later chapters are shown as sealed rather than hidden. The concept of the
   season is public; its content is not, and an empty slot states that more
   honestly than silence does. */
.arc {
  margin-top: var(--space-2xl);
  padding: var(--space-m) var(--space-s) var(--space-l);
  border: 1px solid var(--line);
  border-radius: 2px;
  background: linear-gradient(180deg, rgb(8 13 26 / 70%), rgb(3 5 12 / 86%));
  text-align: center;
}

.arc__title {
  font-family: var(--font-body);
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.26em;
  text-indent: 0.26em;
  text-transform: uppercase;
  color: var(--gold-400);
}

.arc__list {
  display: grid;
  gap: var(--space-s);
  margin-top: var(--space-m);
}

@media (min-width: 46em) {
  .arc__list { grid-template-columns: repeat(3, 1fr); gap: 0; }
}

.arc__step {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 0.28rem;
  padding: var(--space-m) var(--space-s) var(--space-s);
}

/* The connecting thread. Drawn from each step to the next, never after the
   last, so the sequence reads as open-ended rather than closed. */
@media (min-width: 46em) {
  .arc__step::after {
    content: '';
    position: absolute;
    top: calc(var(--space-m) + 1.17rem);
    left: 50%;
    width: 100%;
    height: 1px;
    background: linear-gradient(90deg, var(--line-strong), rgb(142 152 175 / 18%));
  }

  .arc__step:last-child::after { display: none; }
}

/* The marks carry the whole message of this component: one chapter is open,
   the rest are shut. The open one is a lit gem; the others are a padlock in a
   ring. Empty circles read as "nothing here"; a lock reads as "not yet". */
.arc__mark {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 2.35rem;
  height: 2.35rem;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--ink-900);
  box-shadow: 0 0 0 5px var(--ink-900);
  color: #6f7a96;
}

.arc__mark svg { width: 1.1rem; height: 1.1rem; }

/* the sealed steps get a faint hatch inside the ring */
.arc__step--sealed .arc__mark {
  background:
    repeating-linear-gradient(45deg, rgb(142 152 175 / 10%) 0 3px, transparent 3px 6px),
    var(--ink-900);
}

/* the live chapter: a lit gem, no lock */
.arc__step--now .arc__mark {
  border-color: var(--gold-400);
  color: var(--gold-100);
  box-shadow: 0 0 0 5px var(--ink-900), 0 0 22px rgb(226 190 124 / 45%);
}

.arc__step--now .arc__mark i {
  width: 0.62rem;
  height: 0.62rem;
  rotate: 45deg;
  background: var(--gold-300);
  box-shadow: 0 0 12px rgb(226 190 124 / 75%);
}

.arc__step--sealed { opacity: 0.72; }

.arc__num {
  margin-top: 0.55rem;
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.arc__step--now .arc__num { color: var(--gold-100); }

.arc__name {
  font-size: var(--step--1);
  color: var(--text-dim);
  text-wrap: balance;
}

.arc__step--now .arc__name { color: var(--text-soft); }

.arc__state {
  margin-top: 0.35rem;
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #7d87a1;
}

.arc__step--now .arc__state { color: var(--arcane-300); }

.arc__foot {
  max-width: 44rem;
  margin: var(--space-m) auto 0;
  font-size: var(--step--1);
  color: var(--text-dim);
  text-wrap: pretty;
}

/* -- classes gallery ------------------------------------------------------ */

/* Five cards never divide evenly into an auto-fit grid, and grid leaves the
   short last row hanging on the left. Flex with an explicit per-breakpoint
   basis keeps every row centred, so 3+2 and 4+1 both read as deliberate. */
/* O gap e a base do flex TÊM de sair da mesma variável. Quando o gap virou
   clamp(1rem, 1.4vw, 1.75rem) e a base continuou em --space-s, cinco cards de
   348px com quatro vãos de 27px passaram a somar mais que o container e o
   quinto caía para a linha de baixo. */
.classes {
  --class-gap: var(--space-s);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--class-gap);
}

.class { flex: 0 1 calc((100% - var(--class-gap)) / 2); }

@media (min-width: 46em) { .class { flex-basis: calc((100% - 2 * var(--class-gap)) / 3); } }
@media (min-width: 72em) { .class { flex-basis: calc((100% - 4 * var(--class-gap)) / 5); } }

/* Bloco 05 — os cinco entram como heróis, não como produtos numa prateleira.
   O container largo dá a eles quase o dobro da largura que tinham, e um leve
   escalonamento vertical alternado tira a fileira do alinhamento de vitrine. */
@media (min-width: 72em) {
  .classes { --class-gap: clamp(1rem, 1.4vw, 1.75rem); align-items: end; }
  .class:nth-child(odd) { transform: translateY(1.5rem); }
  .class:nth-child(odd):hover { transform: translateY(calc(1.5rem - 3px)); }
  .class:nth-child(3) { transform: translateY(0); }
  .class:nth-child(3):hover { transform: translateY(-3px); }
}

@media (prefers-reduced-motion: reduce) {
  .class:nth-child(odd):hover,
  .class:nth-child(3):hover { transform: none; }
}

.class {
  position: relative;
  display: grid;
  grid-template-rows: 1fr auto;
  overflow: hidden;
  background:
    radial-gradient(120% 68% at 50% 6%, rgb(30 47 99 / 34%), transparent 62%),
    linear-gradient(180deg, rgb(7 11 22 / 92%), rgb(3 5 12 / 96%));
  border: 1px solid var(--line-cool);
  border-radius: 2px;
  transition:
    border-color var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out),
    transform var(--dur) var(--ease-out);
}

/* A single accent hairline at the top edge, lit on hover. Cheaper than a glow
   and it gives each card an identity even at rest. */
.class::after {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  z-index: 2;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  opacity: 0.35;
  transition: opacity var(--dur) var(--ease-out);
}

.class:hover::after { opacity: 1; }

.class__art {
  position: relative;
  display: grid;
  place-items: end center;
  padding-top: var(--space-s);
  overflow: hidden;
}

/* The plates are cut off their ground by luminance, which leaves a faint
   rectangular residue at the very edge. Feathering the art box hides it and
   makes each figure emerge from the card rather than sit in a box.
   ONE mask layer, no mask-composite: a two-layer composited mask on five
   600px plates dropped this section off the fast raster path and stalled the
   renderer outright. A single gradient does the same job on the GPU.
   The vertical edges are covered by .class__art::before instead. */
.class__art img {
  width: 100%;
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 11%, #000 89%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 11%, #000 89%, transparent 100%);
  transition: transform var(--dur-slow) var(--ease-out);
}

/* Side vignette — cheaper than a second mask layer and it also grounds the
   figure in the card instead of leaving a hard plate edge. */
.class__art::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(90deg, rgb(4 6 13 / 92%) 0%, transparent 11%, transparent 89%, rgb(4 6 13 / 92%) 100%);
}

/* The footer carries the class name and nothing else. The equipment line that
   used to sit under it was removed: these cards present the characters, not
   their gear. Two consequences handled here — the name has to hold the space on
   its own, and the per-class accent colour lost its only text carrier, so it
   moves into the ornament instead. */
.class__body {
  position: relative;
  display: grid;
  place-items: center;
  padding: var(--space-s) var(--space-s) calc(var(--space-s) * 1.25);
  text-align: center;
  background: linear-gradient(180deg, transparent, rgb(3 5 12 / 92%) 42%);
}

.class__body::before {
  content: '';
  position: absolute;
  inset: 0 auto auto 50%;
  width: 4.25rem;
  height: 1px;
  translate: -50% 0;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  opacity: 0.85;
}

/* A small accent lozenge seated on the rule. With the coloured equipment line
   gone this is what still tells the five cards apart at a glance, and it gives
   the single name something to hang from instead of floating in the panel. */
.class__body::after {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  width: 0.34rem;
  height: 0.34rem;
  translate: -50% -50%;
  rotate: 45deg;
  background: var(--accent);
  box-shadow:
    0 0 0 0.28rem rgb(4 6 13 / 92%),
    0 0 12px color-mix(in srgb, var(--accent) 65%, transparent);
}

/* Its own clamp rather than --step-1: with the second line gone the name can
   carry more weight, but --step-2 breaks "Magic Gladiator" onto two lines in
   the five-across layout and the row stops reading level. This tops out just
   under that. */
.class__name {
  display: grid;
  place-items: center;
  font-size: clamp(1.18rem, 1.02rem + 0.5vw, 1.46rem);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--gold-100);
  text-wrap: balance;
  filter: drop-shadow(0 1px 0 rgb(0 0 0 / 65%));
}

/* "Magic Gladiator" is the one name long enough to wrap, and where it wraps is
   not a question of column count: at 1366 the five-across layout is active but
   the cards are narrow enough to break it, so a breakpoint rule cannot cover
   the case. Reserving two lines of the NAME's own line box does — `lh` resolves
   against this element's font-size and line-height, so the reservation tracks
   the fluid type scale by itself at every width.
   The name is its own centring grid so a one-line card sits in the middle of
   that box rather than at the top of it, and the padding above was trimmed to
   keep the footer at roughly the height it had with two lines of content.
   Without `lh` support the declaration is simply dropped and the footer falls
   back to its natural height — the behaviour that shipped before this. */
@supports (min-height: 2lh) {
  .class__name { min-height: 2lh; }
}

.class--dk { --accent: var(--accent-dk); }
.class--elf { --accent: var(--accent-elf); }
.class--sm { --accent: var(--accent-sm); }
.class--dl { --accent: var(--accent-dl); }
.class--mg { --accent: var(--accent-mg); }

.class:hover {
  border-color: color-mix(in srgb, var(--accent) 44%, transparent);
  box-shadow: 0 18px 48px -26px color-mix(in srgb, var(--accent) 70%, transparent);
  transform: translateY(-3px);
}

.class:hover .class__art img { transform: scale(1.035); }

.classes__foot {
  max-width: 46rem;
  margin: var(--space-l) auto 0;
  text-align: center;
  font-size: var(--step--1);
  color: var(--text-dim);
  text-wrap: pretty;
}

/* -- souls (the three sigils) --------------------------------------------- */

/* The trio is wrapped and labelled so it reads as ONE system with three parts,
   which is the thing the section is meant to communicate first. Before this
   the three cards and the large Sentinel band below competed as four peers. */
.trio {
  position: relative;
  padding: var(--space-m) clamp(0.75rem, 2.5vw, var(--space-m)) var(--space-l);
  border: 1px solid var(--line);
  border-radius: 2px;
  background: linear-gradient(180deg, rgb(8 13 26 / 55%), rgb(3 5 12 / 30%));
}

.trio__label {
  margin-bottom: var(--space-m);
  text-align: center;
  font-family: var(--font-body);
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.26em;
  text-indent: 0.26em;
  text-transform: uppercase;
  color: var(--gold-400);
}

/* Três colunas ou uma — nunca duas.
   Com `auto-fit` o tablet caía em 2+1 e o terceiro guardião ficava sozinho numa
   linha, que é exatamente a leitura que esta seção não pode ter. E há um teto:
   sem ele, num container de 132rem cada card passava de 570 px e o retrato
   quadrado virava um pôster por guardião. */
.souls {
  display: grid;
  gap: var(--space-s);
  grid-template-columns: minmax(0, 30rem);
  justify-content: center;
  max-width: 84rem;
  margin-inline: auto;
}

@media (min-width: 56em) {
  .souls {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    justify-content: stretch;
  }
}

.soul {
  /* Um padding só, porque o retrato sangra para fora dele com margem negativa
     e precisa do mesmo valor nos dois lados para encostar nas bordas do card. */
  --soul-pad: clamp(0.85rem, 1.9vw, 1.2rem);
  position: relative;
  display: grid;
  /* Retrato no tamanho natural, ficha ocupando o resto: é o que permite ao selo
     de estado descer até a base e os três se alinharem numa linha só, mesmo com
     descrições de comprimentos diferentes. */
  grid-template-rows: auto 1fr;
  justify-items: center;
  align-content: start;
  gap: 0;
  padding: var(--soul-pad) var(--soul-pad) var(--space-l);
  text-align: center;
  background: linear-gradient(180deg, rgb(10 16 32 / 78%), rgb(4 6 13 / 90%));
  border: 1px solid var(--line-cool);
  border-radius: 2px;
  transition: border-color var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}

/* Same corner-bracket motif as the edition plate, kept local so .soul stays
   one element. */
.soul::before,
.soul::after {
  content: '';
  position: absolute;
  width: 0.9rem;
  height: 0.9rem;
  border: 1px solid var(--accent);
  opacity: 0.6;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease-out);
}

.soul::before {
  top: -1px;
  left: -1px;
  border-right-color: transparent;
  border-bottom-color: transparent;
}

.soul::after {
  right: -1px;
  bottom: -1px;
  border-left-color: transparent;
  border-top-color: transparent;
}

.soul--sentinel { --accent: var(--arcane-400); }
.soul--guide { --accent: var(--gold-400); }
.soul--merchant { --accent: #3fbf8f; }

.soul:hover {
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  transform: translateY(-3px);
}

.soul:hover::before,
.soul:hover::after { opacity: 1; }

/* -- retrato do guardião (Bloco 06) --------------------------------------- */

/* Quadrado para os três. É o único formato que serve às três pranchas: Guide e
   Merchant são figuras de corpo inteiro, e a do Sentinel é emblema com o
   letreiro gravado começando em y≈750 — em 3:4 o recorte entraria no texto. */
.soul__portrait {
  position: relative;
  width: calc(100% + 2 * var(--soul-pad));
  margin: calc(var(--soul-pad) * -1) calc(var(--soul-pad) * -1) 0;
  aspect-ratio: 1;
  overflow: hidden;
}

.soul__figure {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* O pé do retrato dissolve no card em vez de terminar numa linha dura. Máscara,
   não recorte: a arte continua inteira por baixo. */
.soul__portrait::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(180deg, transparent 52%, rgb(4 6 13 / 72%) 84%, var(--ink-900) 100%),
    radial-gradient(120% 80% at 50% 30%, transparent 58%, rgb(3 5 12 / 45%) 100%);
}

/* O sigilo vira medalhão sobre a costura entre retrato e ficha — é o que amarra
   os dois blocos do card e devolve uso à arte dos brasões. */
.soul__sigil {
  position: absolute;
  z-index: 1;
  left: 50%;
  bottom: -0.4rem;
  translate: -50% 0;
  width: 7rem;
  filter: drop-shadow(0 2px 10px rgb(3 5 12 / 90%));
}

.soul__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  width: 100%;
  padding-top: 2.6rem;
}

/* Sized so the longest of the three ("Soul Merchant") still sets on one line
   at the narrowest card width — otherwise that card's copy drops a line and
   the trio stops reading as a set. */
.soul__name {
  font-size: clamp(1.2rem, 1.02rem + 0.55vw, 1.5rem);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gold-100);
}

.soul__role {
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--accent);
}

/* A citação é do próprio material oficial de cada NPC. Os três têm uma; dar a
   um só era metade do desequilíbrio que esta seção tinha. */
.soul__quote {
  margin: 0.35rem 0 0;
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-style: italic;
  line-height: 1.5;
  color: color-mix(in srgb, var(--accent) 62%, var(--text));
  text-wrap: balance;
}

.soul__desc {
  max-width: 24rem;
  margin-top: 0.35rem;
  font-size: var(--step--1);
  color: var(--text-muted);
  text-wrap: pretty;
}

.soul__spec-label {
  margin-top: 0.7rem;
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.22em;
  text-indent: 0.22em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* A "ficha" que faltava ao Guide e ao Merchant: três atribuições, mesmo número
   e mesma forma nos três cards. */
.soul__spec {
  display: grid;
  gap: 0.3rem;
  width: 100%;
  max-width: 22rem;
  margin: 0.1rem 0 0;
  padding: 0.65rem 0 0;
  border-top: 1px solid var(--line-cool);
  list-style: none;
  font-size: var(--step--2);
  color: var(--text-soft);
}

.soul__spec li {
  position: relative;
  padding-left: 1.05rem;
  text-align: left;
  text-wrap: pretty;
}

.soul__spec li::before {
  content: '';
  position: absolute;
  top: 0.52em;
  left: 0.15rem;
  width: 0.34rem;
  height: 0.34rem;
  rotate: 45deg;
  background: var(--accent);
  opacity: 0.85;
}

.soul__state {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  /* auto: empurra o selo para a base do card, alinhando os três. */
  margin-top: auto;
  padding: 0.32rem 0.85rem;
  border: 1px solid color-mix(in srgb, var(--accent) 34%, transparent);
  border-radius: 999px;
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-soft);
}

.soul__dot {
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  background: var(--accent);
}

.trio__foot {
  max-width: 52rem;
  margin: var(--space-m) auto 0;
  text-align: center;
  font-size: var(--step--1);
  color: var(--text-dim);
  text-wrap: pretty;
}

/* A banda dedicada ao Soul Sentinel saiu no Bloco 06 junto com o seu CSS.
   Ela era a causa estrutural do desequilíbrio: os três cards eram pares, e
   logo abaixo um deles ganhava retrato grande, citação, texto e selo próprios.
   O conteúdo dela não se perdeu — citação e estado passaram a existir nos três
   cards, e a nota sobre o selo "IA online 24h" deixou de ser necessária quando
   o emblema com esse letreiro saiu da página. */

/* -- estado (truthful status board) --------------------------------------- */

.status {
  display: grid;
  gap: var(--space-s);
  /* Explicit counts, not auto-fit: eight items divide evenly by 2 and by 4, and
     auto-fit picked 1 column at 360px and 5 (so 5+3) at 900px. */
  grid-template-columns: repeat(2, 1fr);
}

@media (min-width: 46em) {
  .status { grid-template-columns: repeat(4, 1fr); }
}

.status__item {
  position: relative;
  display: grid;
  gap: 0.5rem;
  padding: var(--space-m) var(--space-s);
  background: linear-gradient(180deg, rgb(12 19 38 / 78%), rgb(3 5 12 / 88%));
  border: 1px solid var(--line-cool);
  border-radius: 2px;
  text-align: center;
  overflow: hidden;
}

.status__item::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--state), transparent);
  opacity: 0.7;
}

/* Three truthful states, three colours. "—" is not a value, it is the absence
   of one, and it is the only thing on this board allowed to look inert. */
.status__item { --state: var(--line-strong); --state-dot: var(--text-dim); }
.status__item--wait { --state: var(--arcane-500); --state-dot: var(--arcane-300); }
.status__item--soon { --state: var(--gold-500); --state-dot: var(--gold-300); }
.status__item--none { --state: rgb(142 152 175 / 30%); --state-dot: #5f6a83; }

.status__label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55em;
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.status__dot {
  width: 0.42em;
  height: 0.42em;
  flex: none;
  rotate: 45deg;
  background: var(--state-dot);
}

.status__value {
  display: block;
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gold-200);
  text-wrap: balance;
}

.status__value--none {
  font-size: var(--step-3);
  color: var(--text-dim);
  letter-spacing: 0;
}

.status__value--wait { color: var(--arcane-300); }

.status__foot {
  margin-top: var(--space-m);
  text-align: center;
  font-size: var(--step--1);
  color: var(--text-dim);
  text-wrap: pretty;
}

/* -- closing band (pre-launch) -------------------------------------------- */

/* The last brand moment. No form, no counter, no "notify me": none of those
   exist yet, and inventing one here would undo everything the Estado section
   is for. What closes the page is the mark, the promise and the plain truth
   about what is not open. */
.closing {
  position: relative;
  isolation: isolate;
  padding-block: var(--space-2xl);
  border-top: 1px solid var(--line);
  overflow: hidden;
}

.closing__glow {
  position: absolute;
  z-index: -1;
  top: 34%;
  left: 50%;
  width: min(160vw, 76rem);
  aspect-ratio: 1.6;
  translate: -50% -50%;
  pointer-events: none;
  background:
    radial-gradient(closest-side, rgb(30 47 99 / 42%), transparent 72%),
    radial-gradient(closest-side, rgb(189 145 71 / 14%), transparent 55%);
}

.closing__inner {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: var(--space-m);
}

/* The crest returns here at full size. In the portal it shares the frame with
   Kundun and the ensemble;
   for the single focal point; at the end of the page there is nothing left to
   compete with, and the emblem is what the visitor should leave holding. */
.closing__crest {
  width: clamp(13rem, 26vw, 17rem);
  height: auto;
  filter: drop-shadow(0 12px 40px rgb(23 64 140 / 50%));
}

.closing__title {
  max-width: 36rem;
  font-size: var(--step-3);
  line-height: 1.28;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  background: var(--metal-era);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-wrap: balance;
}

.closing__text {
  max-width: 40rem;
  color: var(--text-soft);
  text-wrap: pretty;
}

.closing__list {
  display: grid;
  gap: 1px;
  width: min(100%, 44rem);
  margin-top: var(--space-2xs);
  background: var(--line);
  border: 1px solid var(--line);
}

@media (min-width: 40em) { .closing__list { grid-template-columns: repeat(3, 1fr); } }

.closing__item {
  display: grid;
  gap: 0.3rem;
  padding: var(--space-s);
  background: rgb(4 6 13 / 88%);
}

.closing__what {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.closing__when {
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--gold-200);
}

.closing__when--none { color: var(--arcane-300); }

.closing__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-s);
  margin-top: var(--space-s);
}

/* The last call to action on the page carries more weight than the ones in the
   portal: bigger box, brighter edge, a standing glow rather than a hover-only
   one. It is the end of the journey, not another link. */
.closing__actions .btn {
  padding: 1.05em 2.6em;
  font-size: var(--step-0);
  box-shadow: 0 0 0 1px rgb(226 190 124 / 14%), 0 16px 44px -24px rgb(189 145 71 / 55%);
}

.closing__actions .btn:hover,
.closing__actions .btn:focus-visible {
  box-shadow: 0 0 0 1px rgb(226 190 124 / 34%), 0 18px 52px -18px rgb(189 145 71 / 70%);
}

/* ---------- 8. footer --------------------------------------------------- */

.footer {
  position: relative;
  padding-block: var(--space-xl) var(--space-l);
  border-top: 1px solid var(--line);
  background: linear-gradient(180deg, transparent, rgb(3 5 12 / 90%));
}

.footer__inner {
  display: grid;
  gap: var(--space-m);
  justify-items: center;
  text-align: center;
}

.footer__mark {
  width: 8rem;
  height: auto;
  opacity: 0.9;
}

.footer__domain {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  letter-spacing: 0.26em;
  text-indent: 0.26em;
  text-transform: uppercase;
  color: var(--gold-300);
}

.footer__meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6em 1.4em;
  font-size: var(--step--1);
  color: var(--text-muted);
}

.footer__legal {
  max-width: 46rem;
  font-size: var(--step--2);
  line-height: 1.7;
  color: var(--text-dim);
  text-wrap: pretty;
}

/* ---------- 9. motion --------------------------------------------------- */

/* Entrance reveals are one-shot CSS transitions driven by IntersectionObserver,
   never a continuous loop. See §14 of the project governance (90 FPS ceiling). */
.reveal {
  opacity: 0;
  transform: translateY(1.25rem);
  transition:
    opacity var(--dur-slow) var(--ease-out),
    transform var(--dur-slow) var(--ease-out);
}

.reveal.is-visible { opacity: 1; transform: none; }

.js-off .reveal { opacity: 1; transform: none; }

/* Staggering inside the closing band only — it is the one place where the
   items arrive as a sequence rather than as a block. */
.closing__title.reveal { transition-delay: 60ms; }
.closing .edition.reveal { transition-delay: 120ms; }
.closing__text.reveal { transition-delay: 180ms; }
.closing__list.reveal { transition-delay: 240ms; }
.closing__actions.reveal { transition-delay: 300ms; }

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

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0s !important;
  }

  .reveal { opacity: 1; transform: none; }
  .portal__embers { display: none; }
  /* Nenhum relâmpago sob movimento reduzido. main.js nem chega a agendar o
     primeiro evento; isto é a garantia de que nada pisca mesmo que chegasse. */
  .stage__storm,
  .stage__zap { display: none; }
  /* O portal NÃO some sob movimento reduzido — ele congela. A regra geral acima
     já zera a duração das animações, então os anéis param onde estão e a
     respiração assenta num valor fixo. Sobra o portal estático, que é o que o
     briefing pede: nada se mexe, mas a cena não perde a estrutura mágica.
     A carga fica em zero porque o main.js nem chega a agendar evento. */
  .pfield { animation: none; }
  .class:hover,
  .soul:hover,
  .track:hover { transform: none; }
}

@media (prefers-contrast: more) {
  :root {
    --text-muted: #c3c8d4;
    --text-dim: #9ba5bd;
    --line: rgb(226 190 124 / 45%);
    --line-cool: rgb(90 163 251 / 40%);
  }
  /* Com mais contraste pedido, a máscara do retrato não pode comer o rosto do
     guardião — ela recua e o card ganha uma borda legível. */
  .soul__portrait::after { background: linear-gradient(180deg, transparent 66%, var(--ink-900) 100%); }
}

@media print {
  .stage__storm,
  .stage__zap,
  .stage__portal,
  .portal__embers,
  .portal__aura,
  .portal__veil,
  .portal__hem,
  .stage__boss,
  .stage__cast,
  .closing__glow,
  .navtoggle { display: none; }
  body { background: #fff; color: #000; }
}

/* -- guardian atmosphere ---------------------------------------------------
   Bloco 05 punha aqui as figuras oficiais do Guide e do Merchant a 0,16 de
   opacidade, ladeando a seção. No Bloco 06 elas subiram para dentro dos cards,
   em tamanho real — manter cópias pálidas das mesmas duas figuras atrás delas
   seria repetir em fantasma o que já está em primeiro plano, e o Sentinel
   continuaria sendo o único sem presença no fundo.
   Ficou luz, que não tem dono: um halo frio alto e um calor baixo. */
.section--temple { position: relative; overflow: hidden; }

.section--temple::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(60% 40% at 50% 0%, rgb(30 60 130 / 22%), transparent 72%),
    radial-gradient(80% 50% at 50% 100%, rgb(120 88 40 / 12%), transparent 76%);
}


/* ==========================================================================
   14 · SOUL GUIDE — guia textual (Bloco 24)

   Sem avatar, sem retrato, sem mascote. A identidade vem de três coisas que a
   página já usa em todo lugar: os ticks de canto do `.pframe`, o losango
   dourado antes de um título e o metal escovado do texto de capitular. O Guia
   é montado no MESMO vocabulário do portal, para ler como parte dele e não
   como um widget colado por cima.

   Nada aqui anima sozinho. Não há laço, não há keyframes em execução
   permanente: só transições de abertura, com duração finita, que o
   `prefers-reduced-motion` zera.
   ========================================================================== */

/* Enquanto o soul-guide.js não reportar presença, o botão não existe. Um botão
   flutuante que não faz nada é pior que botão nenhum — e é exatamente o que
   sobraria se o script falhasse em carregar. */
.sg__launcher { display: none; }
[data-sg-ready] .sg__launcher { display: grid; }

.sg__launcher {
  position: fixed;
  z-index: 60;
  right: max(1.6rem, env(safe-area-inset-right, 0px));
  bottom: max(1.6rem, env(safe-area-inset-bottom, 0px));
  gap: 0.18rem;
  padding: 0.72rem 1.15rem;
  text-align: left;
  cursor: pointer;

  /* Chapa OPACA, e a ordem das camadas importa: o lavado arcano vai por CIMA
     de um fundo sólido, não no lugar dele.

     A primeira versão usava as mesmas alfas do `.pframe`. Funciona lá, porque
     o `.pframe` assenta sobre o fundo escuro da página. Aqui não: o controle
     flutua sobre a ARTE, e a 78% de opacidade a palavra MAGIC GLADIATOR
     atravessava o botão e lia como defeito de renderização, não como
     transparência intencional. É a mesma lição do Bloco 06 — chapa opaca
     ganha de recorte com alfa. */
  background:
    radial-gradient(130% 110% at 50% -30%, rgb(90 163 251 / 15%), transparent 66%),
    linear-gradient(180deg, #101a38 0%, #05080f 100%);
  border: 1px solid var(--line);
  box-shadow:
    inset 0 1px 0 rgb(226 190 124 / 12%),
    0 22px 46px -18px rgb(0 0 0 / 96%);

  /* Aparece e some com o portão do §9 do script. Opacidade + deslocamento,
     nunca `display`: trocar display cancelaria a transição e o botão pularia
     na tela em vez de assentar. */
  opacity: 0;
  translate: 0 12px;
  pointer-events: none;
  transition:
    opacity var(--dur) var(--ease-out),
    translate var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out);
}

.sg__launcher.is-live {
  opacity: 1;
  translate: 0 0;
  pointer-events: auto;
}

/* -- modo abertura (Bloco 24.1) --------------------------------------------

   O Bloco 24 fazia o botão CEDER na abertura: ele não existia enquanto
   `.portal` estivesse em cena. Como proteção funcionou; como descoberta
   falhou. Quem chega em MuNewSouls não fica sabendo que o Soul Guide existe,
   e o Fundador confirmou isso na produção real.

   A correção não apaga a proteção — dá à abertura um lugar PRÓPRIO. E o lugar
   foi MEDIDO, não escolhido. Uma varredura em grade da primeira tela
   (`tools/probe-hero-free.mjs`) mostrou que, em toda largura de desktop, o
   único vazio da abertura é a cauda do trilho de brasões: a faixa entre o fim
   de "Canais oficiais" e a faixa de estado, à esquerda da classe mais à
   esquerda. Kundun, as cinco classes, o núcleo do palco e o painel do Soul
   Sentinel ocupam todo o resto — inclusive os dois cantos de baixo, que é
   onde o botão estava.

   Dois números, e nenhum deles é chute:

   `left: 3vw` é EXATAMENTE a borda esquerda de `.portal__frame`. A moldura é
   `min(100% - gutter, var(--container-wide))`, `--container-wide` é `94vw`
   antes do teto de 132rem, e o gutter nunca passa de 6vw entre 320px e
   2247px. Logo a largura é 94vw e a margem é 3vw — a mesma conta do grid, não
   uma aproximação dela.

   A largura é o canal livre menos folga. A arte das classes NÃO tem margem
   transparente: `tools/probe-cast-ink.mjs` leu o alfa das cinco chapas e da
   chapa do Kundun e achou tinta de borda a borda (fração 0 0 1 1). Então a
   caixa de layout É o contorno do desenho, e a classe mais à esquerda começa
   em 12.2vw ou mais em toda largura ≥62em. Canal = 12.2vw − 3vw = 9.2vw; os
   0.9rem são o respiro que sobra.

   `top: 32rem` cai abaixo de "Canais oficiais" (que termina entre 448px e
   486px em TODA largura de desktop, porque o trilho é preso ao topo e tem
   tamanho fixo) e bem acima da faixa de estado (que começa em 673px ou mais).
   -------------------------------------------------------------------------- */

@media (min-width: 62em) {
  .sg__launcher--hero {
    right: auto;
    bottom: auto;
    left: 3vw;
    top: 32rem;
    width: min(11.5rem, 9.2vw - 0.9rem);
    padding: 0.72rem 0.45rem;
    justify-items: center;
    text-align: center;
  }

  /* No canal mais estreito sobram 77px. A legenda não cabe, e o nome precisa
     poder quebrar — então o nome vira pilha: losango em cima, palavra embaixo.
     É o mesmo vocabulário dos brasões que estão logo acima, o que faz a peça
     ler como parte do trilho e não como enfeite grudado por cima dele. */
  .sg__launcher--hero .sg__launcher-sub { display: none; }

  .sg__launcher--hero .sg__launcher-name {
    display: grid;
    justify-items: center;
    gap: 0.45em;
    line-height: 1.25;
    /* A tracking de 0.2em deixa um vão morto DEPOIS da última letra; sem o
       recuo, texto centralizado nasce meio caractere à esquerda. Mesmo ajuste
       que `.prail__title` já faz. */
    text-indent: 0.2em;
  }
}

/* -- modo abertura no celular (Bloco 24.1 REV B) ---------------------------

   Abaixo de 62em a abertura é UMA COLUNA que ocupa a largura toda, e não
   existe canal nenhum: qualquer controle flutuante fica por cima de conteúdo.
   Medido na primeira versão do Bloco 24.1, com a pastilha inteira de 149px:

     390x844   cobria 31% de "Canais oficiais"
     360x800   cobria 38%
     375x667   cobria 38% de um brasão — e brasão é LINK

   Cobrir mais de um terço de um alvo clicável não é aceitável, e o Fundador
   não aprovou isso. A saída que o briefing permite é encolher: na abertura do
   celular o botão vira uma placa quadrada com o SIGILO OFICIAL DO SOUL GUIDE
   — a mesma arte que o trilho de brasões mostra alguns pixels acima, com o
   rótulo "Soul / Guide" embaixo dela. Não é ícone novo nem identidade nova: é
   a marca que a página já usa para esta figura.

   O nome continua no DOM, só que invisível, então o nome acessível do botão
   segue sendo "Soul Guide" — quem usa leitor de tela não perde nada.

   Fora da abertura, a pastilha inteira volta: no corpo há espaço, e ali o
   nome escrito é melhor que qualquer sigilo. */

@media (max-width: 61.999em) {
  .sg__launcher--hero {
    width: 3.25rem;
    height: 3.25rem;
    padding: 0;
    place-items: center;
    /* O sigilo entra por `background-image` de propósito: assim o modo compacto
       não precisa de marcação nova, e continua existindo UM único botão no
       documento — não dois, um para cada tamanho. */
    background-image:
      url('../img/sigil-guide-v3-112.webp'),
      radial-gradient(130% 110% at 50% -30%, rgb(90 163 251 / 15%), transparent 66%),
      linear-gradient(180deg, #101a38 0%, #05080f 100%);
    background-repeat: no-repeat;
    background-position: center 52%, center, center;
    background-size: 1.75rem auto, cover, cover;
  }

  /* O texto continua no botão, e continua sendo o nome acessível dele. Some da
     tela, não da árvore de acessibilidade — é a mesma técnica do
     `.visually-hidden` que o resto da página usa. */
  .sg__launcher--hero .sg__launcher-name,
  .sg__launcher--hero .sg__launcher-sub {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}

/* A troca de lugar entre os dois modos não pode ser um salto. O botão apaga,
   troca de lugar apagado e acende de novo — é a mesma transição de opacidade
   que ele já usa para nascer, sem propriedade de layout animada e sem nada
   atravessando a tela. */
.sg__launcher.is-swapping {
  opacity: 0;
  translate: 0 6px;
  pointer-events: none;
}

/* Os mesmos oito filetes de canto do `.pframe`. É esse detalhe, mais que a
   cor, que faz o controle pertencer a esta página. */
.sg__launcher::before {
  content: '';
  position: absolute;
  inset: -3px;
  pointer-events: none;
  opacity: 0.78;
  background:
    linear-gradient(var(--gold-500), var(--gold-500)) 0 0 / 10px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 0 0 / 1px 10px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 0 / 10px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 0 / 1px 10px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 0 100% / 10px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 0 100% / 1px 10px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 100% / 10px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 100% / 1px 10px no-repeat;
  transition: opacity var(--dur) var(--ease-out);
}

.sg__launcher:hover,
.sg__launcher:focus-visible {
  border-color: var(--line-strong);
  box-shadow:
    inset 0 1px 0 rgb(226 190 124 / 18%),
    0 0 0 1px rgb(90 163 251 / 16%),
    0 22px 46px -22px rgb(0 0 0 / 94%),
    0 0 26px -6px rgb(90 163 251 / 34%);
}

.sg__launcher:hover::before,
.sg__launcher:focus-visible::before { opacity: 1; }

.sg__launcher-name {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-family: var(--font-display);
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  background: var(--metal-gold);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 1px 0 rgb(0 0 0 / 70%));
}

/* O losango do sistema, o mesmo do `.pframe__title`. */
.sg__launcher-name::before {
  content: '';
  width: 0.34em;
  height: 0.34em;
  flex: none;
  rotate: 45deg;
  background: var(--gold-300);
  box-shadow: 0 0 8px rgb(226 190 124 / 60%);
}

.sg__launcher-sub {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text-dim);
  white-space: nowrap;
}

/* -- o painel -------------------------------------------------------------- */

.sg {
  position: fixed;
  z-index: 70;
  right: max(1.6rem, env(safe-area-inset-right, 0px));
  bottom: max(1.6rem, env(safe-area-inset-bottom, 0px));
  width: min(25rem, calc(100vw - 3.2rem));
  max-height: min(34rem, calc(100dvh - 6rem));
  display: flex;
}

.sg[hidden] { display: none; }

.sg__frame {
  position: relative;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto auto;
  width: 100%;
  min-height: 0;
  padding: clamp(0.8rem, 1.1vw, 1.1rem);
  /* Opaca pela mesma razão do botão: o painel pode abrir sobre qualquer coisa
     da página, e texto de leitura precisa de chão próprio. */
  background:
    radial-gradient(120% 70% at 50% -18%, rgb(90 163 251 / 12%), transparent 64%),
    linear-gradient(180deg, #111c3c 0%, #05070f 62%);
  border: 1px solid var(--line);
  box-shadow:
    inset 0 1px 0 rgb(226 190 124 / 11%),
    0 30px 70px -22px rgb(0 0 0 / 96%);

  animation: sg-rise var(--dur) var(--ease-out) both;
}

@keyframes sg-rise {
  from { opacity: 0; translate: 0 14px; }
  to { opacity: 1; translate: 0 0; }
}

.sg__frame::before {
  content: '';
  position: absolute;
  inset: -3px;
  pointer-events: none;
  opacity: 0.8;
  background:
    linear-gradient(var(--gold-500), var(--gold-500)) 0 0 / 11px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 0 0 / 1px 11px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 0 / 11px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 0 / 1px 11px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 0 100% / 11px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 0 100% / 1px 11px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 100% / 11px 1px no-repeat,
    linear-gradient(var(--gold-500), var(--gold-500)) 100% 100% / 1px 11px no-repeat;
}

.sg__head {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 0 0.7rem;
  padding-bottom: 0.55rem;
  border-bottom: 1px solid var(--line);
}

.sg__title {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  background: var(--metal-gold);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 1px 0 rgb(0 0 0 / 70%));
}

.sg__title::before {
  content: '';
  width: 0.34em;
  height: 0.34em;
  flex: none;
  rotate: 45deg;
  background: var(--gold-300);
  box-shadow: 0 0 8px rgb(226 190 124 / 60%);
}

.sg__role {
  grid-column: 1;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.sg__close {
  grid-column: 2;
  grid-row: 1 / span 2;
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  cursor: pointer;
  color: var(--text-muted);
  border: 1px solid transparent;
  transition:
    color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}

.sg__close:hover,
.sg__close:focus-visible {
  color: var(--gold-200);
  border-color: var(--line);
}

/* -- o registro da conversa ------------------------------------------------ */

.sg__log {
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0.85rem 0.15rem 0.3rem;
  display: grid;
  align-content: start;
  gap: 0.9rem;
  scrollbar-width: thin;
  scrollbar-color: rgb(189 145 71 / 34%) transparent;
}

.sg__log::-webkit-scrollbar { width: 6px; }
.sg__log::-webkit-scrollbar-thumb { background: rgb(189 145 71 / 34%); }

.sg__turn { display: grid; gap: 0.3rem; }

.sg__who {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.sg__turn--you .sg__who { color: var(--arcane-300); }

.sg__say {
  font-size: var(--step--1);
  line-height: 1.62;
  color: var(--text-soft);
  text-wrap: pretty;
  /* Uma pergunta colada é uma palavra só e transbordaria a caixa. */
  overflow-wrap: anywhere;
}

.sg__turn--guide .sg__say { color: var(--text); }

/* A fala do visitante recua e ganha um fio frio à esquerda: separa os dois
   lados sem precisar de balão, avatar ou cor de fundo. */
.sg__turn--you .sg__say {
  padding-left: 0.7rem;
  border-left: 1px solid rgb(90 163 251 / 34%);
  color: var(--text-soft);
}

.sg__say + .sg__say { margin-top: 0.45rem; }


/* -- revelação digital da resposta (Bloco 24.1 REV B) ----------------------

   A resposta já existe inteira no DOM quando isto começa: o `soul-guide.js`
   quebra o parágrafo em palavras e cada palavra vira um `.sg__w`. Aqui só se
   acende opacidade.

   É por isso que a caixa não reflui: as palavras apagadas JÁ OCUPAM o lugar
   delas, com o tamanho final. Nada entra, nada cresce, nada empurra. É a
   diferença entre revelar um texto e escrevê-lo — e a segunda faria a última
   linha do registro pular a cada palavra.

   Sem `animation` em lugar nenhum desta seção: o teto de 90 FPS e a regra de
   "nenhuma animação CSS infinita" do `check-local.mjs` valem aqui também. O
   que existe é uma transição de opacidade com duração finita. */

.sg__w {
  opacity: 0;
  /* A transição é curta de propósito: quem manda no ritmo é o passo do
     script, não a duração daqui. Esta serve só para a palavra não estalar. */
  transition: opacity 190ms var(--ease-out);
}

.sg__w.is-on { opacity: 1; }

/* O cursor. Uma barra fina no fim da palavra que acabou de acender — e nada
   mais: não pisca, não brilha, não deixa rastro. Um cursor que pisca seria uma
   animação infinita, e um cursor grande viraria o gimmick de terminal que o
   briefing proíbe.

   `position: absolute` dentro de um `inline-block` de largura zero: assim ele
   não ocupa espaço nenhum na linha e não pode deslocar o texto ao aparecer ou
   ao sumir. */
.sg__w.is-tip { position: relative; }

.sg__w.is-tip::after {
  content: '';
  position: absolute;
  top: 0.12em;
  right: -0.06em;
  width: 2px;
  height: 1.02em;
  background: var(--arcane-300);
  box-shadow: 0 0 6px rgb(90 163 251 / 45%);
  pointer-events: none;
}

/* Uma palavra terminada em espaço carrega o espaço dentro do próprio span; o
   cursor no fim dela ficaria solto, depois do vão. Encostar no último glifo é
   o que faz ele ler como cursor, e não como enfeite. */
.sg__w.is-tip::after { right: 0.22em; }

/* O rótulo de estado, ao lado de "Soul Guide" enquanto a resposta aparece.
   "Consultando o conhecimento" é literal: houve uma busca, num corpus local,
   e ela terminou. Nenhuma palavra aqui sugere inteligência remota, porque não
   existe nenhuma. */
.sg__state {
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: none;
  color: var(--text-dim);
}

/* Movimento reduzido: a resposta simplesmente está lá. O script já nem chama a
   revelação nesse caso — esta regra é a segunda porta, para o caso de a
   preferência mudar com um turno já na tela. */
@media (prefers-reduced-motion: reduce) {
  .sg__w { opacity: 1; transition: none; }
  .sg__w.is-tip::after { display: none; }
}

.sg-still .sg__w { opacity: 1; transition: none; }
.sg-still .sg__w.is-tip::after { display: none; }

/* Impressão: papel não tem revelação. */
@media print {
  .sg__w { opacity: 1; }
  .sg__w.is-tip::after { display: none; }
}

/* -- procedência ----------------------------------------------------------- */

.sg__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.55rem;
  margin-top: 0.15rem;
}

.sg__tag {
  padding: 0.16em 0.55em;
  border: 1px solid var(--line);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--gold-200);
}

.sg__tag--mu {
  border-color: var(--line-cool);
  color: var(--arcane-200);
}

.sg__tag--late {
  border-color: rgb(143 146 236 / 34%);
  color: #b9bbf4;
}

/* A referência externa é discreta de propósito: o briefing pede que ela nunca
   apareça com mais força visual que o próprio MuNewSouls. */
.sg__ref {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  text-decoration: underline;
  text-decoration-color: rgb(136 146 168 / 40%);
  text-underline-offset: 3px;
  transition: color var(--dur-fast) var(--ease-out);
}

.sg__ref:hover,
.sg__ref:focus-visible { color: var(--text-muted); }

/* -- sugestões e atalhos --------------------------------------------------- */

.sg__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: 0.2rem;
}

.sg__chip,
.sg__topic {
  padding: 0.32em 0.7em;
  cursor: pointer;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  background: #0a1020;
  border: 1px solid var(--line-cool);
  transition:
    color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out);
}

.sg__chip:hover, .sg__chip:focus-visible,
.sg__topic:hover, .sg__topic:focus-visible {
  color: var(--gold-100);
  border-color: var(--line-strong);
  background: #111c3c;
}

.sg__topics {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  padding: 0.7rem 0.15rem;
  margin-top: 0.2rem;
  border-top: 1px solid rgb(189 145 71 / 14%);
}

/* -- o campo --------------------------------------------------------------- */

.sg__form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.4rem;
}

.sg__input {
  min-width: 0;
  padding: 0.62em 0.8em;
  font-family: var(--font-body);
  font-size: max(1rem, var(--step--1));  /* 16px mínimo: abaixo disso o iOS dá zoom */
  color: var(--text);
  background: #04060e;
  border: 1px solid var(--line-cool);
  transition:
    border-color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out);
}

.sg__input::placeholder { color: var(--text-dim); }

.sg__input:focus {
  outline: none;
  border-color: rgb(90 163 251 / 46%);
  box-shadow: 0 0 0 1px rgb(90 163 251 / 26%), 0 0 18px -8px rgb(90 163 251 / 60%);
}

.sg__send {
  display: grid;
  place-items: center;
  width: 2.7rem;
  cursor: pointer;
  color: var(--gold-200);
  background: #111c3c;
  border: 1px solid var(--line);
  transition:
    color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out);
}

.sg__send:hover,
.sg__send:focus-visible {
  color: var(--gold-100);
  border-color: var(--line-strong);
  background: #1a2a5a;
}

/* -- celular: folha ancorada no rodapé ------------------------------------- */

@media (max-width: 39.99em) {
  .sg__launcher {
    right: max(0.9rem, env(safe-area-inset-right, 0px));
    bottom: max(0.9rem, env(safe-area-inset-bottom, 0px));
    padding: 0.6rem 0.95rem;
  }

  .sg__launcher-sub { display: none; }

  /* Larga e baixa, encostada nas três bordas: num telefone, uma caixinha
     flutuante no canto deixaria o campo de texto do lado do polegar e o
     conteúdo espremido no meio. */
  .sg {
    right: 0;
    left: 0;
    bottom: 0;
    width: auto;
    max-height: min(82dvh, calc(100dvh - 3.5rem));
  }

  .sg__frame {
    padding-bottom: max(0.9rem, env(safe-area-inset-bottom, 0px));
    border-left: none;
    border-right: none;
    border-bottom: none;
  }

  /* Os ticks de canto de baixo sairiam da tela; ficam só os de cima. */
  .sg__frame::before {
    inset: -3px -3px 0;
    background:
      linear-gradient(var(--gold-500), var(--gold-500)) 0 0 / 11px 1px no-repeat,
      linear-gradient(var(--gold-500), var(--gold-500)) 0 0 / 1px 11px no-repeat,
      linear-gradient(var(--gold-500), var(--gold-500)) 100% 0 / 11px 1px no-repeat,
      linear-gradient(var(--gold-500), var(--gold-500)) 100% 0 / 1px 11px no-repeat;
  }
}

/* -- entrada por voz (Bloco 24 · extensão) ---------------------------------
   Ditado, não assistente falante. O Soul Guide continua respondendo só em
   texto — isto muda a ENTRADA e nada mais.

   O microfone é a terceira coluna do formulário, do mesmo tamanho do enviar e
   com o mesmo peso visual: nem escondido, nem dominante. Digitar continua
   sendo o caminho normal, e o campo continua ocupando toda a largura que
   sobra. */
.sg__form {
  grid-template-columns: minmax(0, 1fr) auto auto;
}

.sg__mic {
  display: grid;
  place-items: center;
  width: 2.7rem;
  cursor: pointer;
  color: var(--text-muted);
  background: #0a1020;
  border: 1px solid var(--line-cool);
  transition:
    color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out);
}

.sg__mic[hidden] { display: none; }

.sg__mic:hover,
.sg__mic:focus-visible {
  color: var(--gold-200);
  border-color: var(--line);
  background: #111c3c;
}

/* ESTADO DE ESCUTA.

   Sem animação infinita, de propósito. A governança do projeto proíbe laço
   permanente de CSS — `check-local.mjs` falha o build por isso — e um pulso
   eterno num botão de 43px não vale abrir exceção numa regra que existe para
   proteger o teto de 90 FPS.

   No lugar do pulso, contraste: a borda vira azul arcano cheio, o ícone
   acende e um anel frio assenta em volta. E o que REALMENTE comunica o estado
   não é a cor — é a linha de texto "Ouvindo…" logo abaixo, que existe também
   para quem não enxerga a cor e para quem usa leitor de tela. */
.sg__mic.is-listening {
  color: var(--arcane-200);
  border-color: rgb(90 163 251 / 62%);
  background: #10224a;
  box-shadow:
    inset 0 0 0 1px rgb(90 163 251 / 22%),
    0 0 18px -4px rgb(90 163 251 / 55%);
}

.sg__mic.is-listening .sg__mic-icon {
  filter: drop-shadow(0 0 5px rgb(143 195 255 / 70%));
}

/* A linha de estado ocupa a largura inteira, abaixo do campo. */
.sg__voice {
  grid-column: 1 / -1;
  margin-top: 0.1rem;
  font-size: 0.7rem;
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  text-wrap: pretty;
  overflow-wrap: anywhere;
}

.sg__voice[hidden] { display: none; }

/* Ouvindo: frio, vivo, discreto. */
.sg__voice[data-tone='live'] { color: var(--arcane-200); }

/* Aviso: dourado, NUNCA vermelho. Não conseguir usar o microfone não é um
   erro do visitante nem uma falha do site — digitar continua ali. Um alerta
   vermelho diria o contrário. */
.sg__voice[data-tone='warn'] { color: var(--gold-300); }

@media (max-width: 39.99em) {
  /* No celular os dois controles precisam continuar confortáveis para o
     polegar mesmo com o teclado virtual aberto e a folha encolhida. */
  .sg__mic,
  .sg__send { width: 2.9rem; }
}

@media print {
  .sg__mic,
  .sg__voice { display: none !important; }
}

/* -- movimento reduzido ---------------------------------------------------- */

/* Duas portas para a mesma regra: a consulta de mídia, que é a fonte da
   verdade, e a classe que o script escreve — presente porque o painel pode
   abrir no mesmo quadro em que a preferência muda. */
@media (prefers-reduced-motion: reduce) {
  .sg__launcher { transition: none; translate: none; }
  .sg__frame { animation: none; }
}

.sg-still .sg__launcher { transition: none; translate: none; }
.sg-still .sg__frame { animation: none; }

@media print {
  .sg__launcher,
  .sg { display: none !important; }
}
