/* ==========================================================================
   frostvale.css — the page gets colder the further north you read.

   The FrostVale guide opens at the hearth and ends on the ice. That arc is
   carried by three separate mechanisms, each doing the one thing it is best
   at, rather than one clever mechanism doing all of it:

     THE RAMP    one document-tall gradient on <body>.  No JS. Cannot fail.
     THE STOPS   [data-temp="1".."5"] on sections.      No JS. Cannot fail.
     THE LIGHT   a fixed hearth and aurora, opacity from --t.  JS only.

   With the script blocked the reader still descends from firelight into rime;
   only the two glows are missing, and they are display:none by default so they
   go missing cleanly rather than as an ember stuck at the footer.

   THE RULE THAT MAKES THIS SAFE: no token that text sits on, and no token that
   text is made of, is ever computed from scroll position. --bone, --text,
   --text-dim and --text-faint are the same on every screen of this page, and
   every ground the page passes through sits between 12.9:1 and 14.5:1 against
   them. Legibility is a property of the construction here, not something that
   has to be re-checked every time a hex is nudged.

   Loaded only by gaming/valheim.html, on top of dehaven.css.
   Deployed to /assets/frostvale.css. Site-only — the WordPress side never
   renders this page.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. THE RAMP

   dehaven.css gives every page three fixed radial gradients — a brass sun at
   top right, dusk bleeding in from the left, leather rising from the floor.
   This replaces all three, deliberately, because the whole point of this page
   is that the light changes as you go and a fixed backdrop cannot do that.
   background-attachment has to be reset with it: the base rule pins the sun to
   the viewport, and a pinned ramp is just a flat colour.

   Not fixed, then, but document-tall, so each screen shows a real slice of the
   ramp — the bottom of your screen slightly colder than the top of it — and
   descending walks you through the slices. A fixed ramp would make every
   screen internally uniform and the change would only register as a memory.

   Every stop here is 13.8:1 to 14.5:1 against --bone. Because the colours in
   between are channel-wise mixes of the stops, and relative luminance is
   monotone in each channel, every point between them is inside that band too.
   There is no scroll position on this page where the body copy is harder to
   read than it is at the top.
   -------------------------------------------------------------------------- */

body.frostvale {
  background-image: linear-gradient(180deg,
    #1C1512   0%,   /* firelight on the wall behind you */
    #171310  15%,   /* --ink: home, the ground the rest of the site stands on */
    #161519  46%,   /* the light going */
    #141A21  74%,   /* out past the treeline */
    #111A22 100%);  /* the north */
  background-attachment: scroll;
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

/* --------------------------------------------------------------------------
   2. THE LIGHT

   Two sources: the hearth, burning low and to the left of where you came in,
   and the aurora, which only ever shows at the far end. These are fixed,
   because a light source stays where it is relative to the eye — a fire that
   scrolled away with the page would read as wallpaper.

   dehaven.css puts every direct child of <body> at z-index 1, so .shell is
   already a stacking context; the two pseudo-elements sit at -1 inside it,
   which paints them under every word on the page and over the ramp.

   They are display:none until frost.js sets data-frost on <html>. Without the
   script --t never moves, so a viewport-fixed light would burn at identical
   strength on every screen — an ember still going strong at the footer, which
   is precisely the thing this page exists not to do. The ramp and the stops
   carry the temperature perfectly well on their own.
   -------------------------------------------------------------------------- */

.shell { position: relative; }

.shell::before,
.shell::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  display: none;
}

html[data-frost] .shell::before,
html[data-frost] .shell::after { display: block; }

/* The hearth. Below you and to the left, the way a longhouse fire sits, not
   overhead. Burned out by the end of the first third. */
.shell::before {
  background:
    radial-gradient(78% 52% at 16% 102%, rgba(180, 85, 58, .40), transparent 64%),
    radial-gradient(54% 36% at 11%  96%, rgba(200, 150, 62, .30), transparent 60%);
  opacity: clamp(0, calc(1 - var(--t, 0) * 2.6), 1);
}

/* The aurora. Nothing at all until the last quarter, and then it is the only
   light left in the page. */
.shell::after {
  background:
    radial-gradient(72% 30% at 50% -8%, rgba(95, 169, 140, .26), transparent 70%),
    radial-gradient(42% 26% at 80%  0%, rgba(198, 214, 228, .20), transparent 66%);
  opacity: clamp(0, calc(var(--t, 0) * 3.4 - 2.5), 1);
}

/* --------------------------------------------------------------------------
   3. THE STOPS — five temperatures for the metal

   The ramp carries the ground; these carry everything made of metal. Sections
   are marked data-temp="1".."5" from the top of the document down, and each
   stop redeclares the ornamental tokens. This is the move dehaven.css already
   makes for [data-surface="parchment"], for the same reason: this codebase
   changes its appearance by redeclaring tokens on a container, never by
   writing a second set of components. Every card, callout, rule, hairline and
   ornament inside a band cools without one component rule knowing this page
   exists.

   Five hand-picked stops rather than interpolating the tokens from --t, for
   two reasons:

     1. No JavaScript. The progression is in the markup, so a reader with the
        script blocked still descends from brass into rime.
     2. Five colours can each be measured against the text once. An
        interpolation has to be trusted at every value simultaneously, and the
        place it would be wrong is the accent — the token carrying links.

   No stop touches --ink, --bone, --text, --text-dim or --text-faint.

   The route from rust to glacier runs through twilight on purpose. The short
   way round the wheel from #B4553A to #8FC3D8 is olive and mustard; the long
   way is magenta. Neither is Norse.

   THE CONSTRAINT, if you tune these: contrast per column must never decrease
   going down the table. Measured, bone / brass / clay against --ink-raised:

     stop 1   13.67   6.53   5.26
     stop 2   13.45   6.99   5.69
     stop 3   13.20   7.61   6.67
     stop 4   13.00   8.15   7.81
     stop 5   12.91   8.69   8.57

   The page is strictly more legible at the bottom than at the top.
   -------------------------------------------------------------------------- */

/* Stop 1 is the site palette with one correction. --clay on --ink is 3.78:1,
   which is under AA for body-size text, and the links on this page are long
   and load-bearing. Fired a little hotter it clears at 5.26:1 and reads as
   firelight rather than rust — which is the story at the top of the page
   anyway. The site-wide version of that fix is its own conversation. */
[data-temp="1"] {
  --ink-raised:  #1F1915;  --ink-high: #2A211B;
  --brass:       #C8963E;  --clay:     #CE7658;  --turquoise: #4E8C8A;
  --hair:        rgba(200, 150, 62, .28);
  --hair-soft:   rgba(200, 150, 62, .14);
  --hair-strong: rgba(200, 150, 62, .55);
}

[data-temp="2"] {
  --ink-raised:  #1F1B18;  --ink-high: #292322;
  --brass:       #C6A05B;  --clay:     #C9846A;  --turquoise: #5D9793;
  --hair:        rgba(198, 160, 91, .28);
  --hair-soft:   rgba(198, 160, 91, .14);
  --hair-strong: rgba(198, 160, 91, .55);
}

[data-temp="3"] {                        /* the light going */
  --ink-raised:  #1D1D22;  --ink-high: #272730;
  --brass:       #BCAE84;  --clay:     #BE9AA4;  --turquoise: #6EA5A0;
  --hair:        rgba(188, 174, 132, .28);
  --hair-soft:   rgba(188, 174, 132, .14);
  --hair-strong: rgba(188, 174, 132, .55);
}

[data-temp="4"] {
  --ink-raised:  #1B1F26;  --ink-high: #252C34;
  --brass:       #B0B9A8;  --clay:     #A9B2CB;  --turquoise: #7FB3AC;
  --hair:        rgba(176, 185, 168, .29);
  --hair-soft:   rgba(176, 185, 168, .15);
  --hair-strong: rgba(176, 185, 168, .56);
}

[data-temp="5"] {                        /* rime on iron */
  --ink-raised:  #1A2027;  --ink-high: #243039;
  --brass:       #A9C0CE;  --clay:     #8FC3D8;  --turquoise: #8FBFB8;
  --hair:        rgba(169, 192, 206, .30);
  --hair-soft:   rgba(169, 192, 206, .15);
  --hair-strong: rgba(169, 192, 206, .58);
}

/* The stop callout keeps the fire's colour at every temperature. It is the one
   thing on this page that must not blend into its surroundings, and the entire
   point of the cold end is that everything else has. */
[data-temp] .callout--stop        { border-left-color: #B4553A; background: rgba(180, 85, 58, .13); }
[data-temp] .callout--stop > .orn { color: #C4623F; }

/* .status is semantic, not decorative — "Running" has to stay the same green
   on every page of this site, and a paused server has to stay unmistakably
   not-green. Neither follows the temperature. */
[data-temp] .status       { color: var(--sage); }
[data-temp] .status--warn { color: #C8963E; }
[data-temp] .status--off  { color: var(--text-faint); }

/* --------------------------------------------------------------------------
   4. PLATES

   Everything readable sits on one of these: a plate that darkens the ramp
   without recolouring it. Black at low alpha rather than a translucent --ink,
   because an --ink plate is a warm rectangle sitting on cold ground by the
   bottom of the page — it would fight the very ramp it is meant to be showing
   through. Black takes whatever temperature is behind it and only lowers it,
   which means a plate is always strictly more legible than the ramp, never
   less.

   No backdrop-filter, unlike landing.css. That page blurs a photograph; this
   one has nothing behind it but smooth gradients, so a blur would cost a
   full-screen filter pass per plate on a phone and change nothing on screen.
   A long guide has three times as many plates as the landing page has.
   -------------------------------------------------------------------------- */

.plate {
  position: relative;
  padding: clamp(1.4rem, 1rem + 2.2vw, 2.6rem);
  border: 1px solid var(--hair-soft);
  border-radius: var(--radius);
  background: rgba(0, 0, 0, .34);
  box-shadow: var(--shadow);
}

/* Bands are the temperature unit. Generous spacing between them is what gives
   the ramp room to actually change between one band and the next. */
.band { padding-top: clamp(2.5rem, 1.8rem + 3vw, 4.5rem); }

/* --------------------------------------------------------------------------
   5. THE CONNECTION PANEL

   Lifted from gaming/project-zomboid.html so the two server guides read as
   siblings — same panel, same copy buttons, same behaviour. It lives here
   rather than inline because a second page now needs it.

   NOTE: this is currently a second copy; the PZ page still carries its own in
   an inline <style>. Hoisting both into dehaven.css and slimming the two pages
   is the right follow-up, and was deliberately left out of this pass rather
   than risk a page that already works.
   -------------------------------------------------------------------------- */

.connect {
  margin-block: 2rem .5rem;
  border: 1px solid var(--hair);
  border-radius: var(--radius);
  background: linear-gradient(165deg, var(--ink-high), var(--ink-raised) 62%);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.connect__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .75rem;
  padding: .85rem 1.25rem;
  border-bottom: 1px solid var(--hair-soft);
  font-family: var(--font-head);
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--text-faint);
}
.connect__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }

.field { padding: 1.1rem 1.25rem; border-right: 1px solid var(--hair-soft); }
.field:last-child { border-right: none; }
.field__label {
  font-family: var(--font-head);
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--text-faint);
}
.field__value {
  display: flex; align-items: center; gap: .65rem;
  margin-top: .35rem;
  font-family: var(--font-mono);
  font-size: 1.05rem;
  color: var(--bone);
  word-break: break-all;
}

/* The server is paused, so the panel says so rather than looking live. The
   details themselves stay perfectly readable — they are what you need in order
   to be ready when it comes back, and dimming them into decoration would be
   worse than useless. */
.connect--paused .connect__grid { opacity: .82; }

.copy {
  flex: none;
  border: 1px solid var(--hair);
  background: rgba(200, 150, 62, .07);
  color: var(--text-dim);
  border-radius: 6px;
  padding: .22rem .55rem;
  font-family: var(--font-head);
  font-size: .68rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color .18s var(--ease), border-color .18s var(--ease);
}
.copy:hover { color: var(--bone); border-color: var(--brass); }
.copy.is-done { color: var(--sage); border-color: var(--sage); }

/* --------------------------------------------------------------------------
   6. STEPS

   Also from the PZ page. The design system's .steps auto-numbers, which is
   right for a short list and wrong for a guide that needs a critical variant
   and a per-step done checkbox that survives a reload.
   -------------------------------------------------------------------------- */

.guide-steps { margin: 2.5rem 0 0; padding: 0; list-style: none; }
.step {
  margin-bottom: 1.15rem;
  padding: 1.4rem 1.5rem;
  border: 1px solid var(--hair-soft);
  border-radius: var(--radius);
  background: rgba(0, 0, 0, .30);
}
.step__head { display: flex; align-items: flex-start; gap: .9rem; }
.step__num {
  flex: none;
  display: grid; place-items: center;
  width: 2.3rem; height: 2.3rem;
  border: 1px solid var(--hair);
  border-radius: 50%;
  background: rgba(200, 150, 62, .07);
  font-family: var(--font-head);
  font-size: var(--step--1);
  color: var(--brass);
}
.step h3 { margin: .25rem 0 .5rem; font-size: var(--step-1); }
.step__body p { margin: 0 0 .8rem; }
.step__body p:last-child { margin-bottom: 0; }
.step__body ol, .step__body ul { margin: 0 0 .8rem; padding-left: 1.35rem; }
.step__body li { margin-bottom: .35rem; }
.step__body li::marker { color: var(--brass); }
.muted { color: var(--text-dim); }

.step--critical { border-color: var(--hair-strong); }
.step--critical .step__num { background: var(--brass); color: #201400; border-color: var(--brass); }

.tag {
  display: inline-block;
  margin-left: .6rem;
  vertical-align: .18em;
  padding: .18rem .55rem;
  border-radius: var(--radius-pill);
  border: 1px solid rgba(180, 85, 58, .5);
  background: rgba(180, 85, 58, .16);
  color: #E08A6E;
  font-family: var(--font-head);
  font-size: .64rem;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.done-row {
  margin-top: 1rem;
  padding-top: .85rem;
  border-top: 1px dashed var(--hair-soft);
  display: flex; align-items: center; gap: .6rem;
  font-family: var(--font-head);
  font-size: var(--step--1);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.done-row input { width: 17px; height: 17px; accent-color: var(--sage); cursor: pointer; }
.done-row label { cursor: pointer; user-select: none; }
.step.is-checked { border-color: rgba(124, 139, 107, .45); }
.step.is-checked .step__num { background: rgba(124, 139, 107, .15); border-color: rgba(124, 139, 107, .5); color: var(--sage); }

.size-note { font-size: var(--step--1); color: var(--text-faint); margin-left: .7rem; }

/* --------------------------------------------------------------------------
   7. THE MOD LIST

   Fifty dependencies. Printed flat they are a wall that nobody reads and that
   pushes the troubleshooting section off the bottom of the world; collapsed
   into groups they are a reference you can actually use. Native <details>, so
   it works with no script at all.
   -------------------------------------------------------------------------- */

.mod-group {
  border: 1px solid var(--hair-soft);
  border-radius: var(--radius);
  background: rgba(0, 0, 0, .30);
  margin-bottom: .8rem;
  overflow: hidden;
}
.mod-group[open] { border-color: var(--hair); }

.mod-group summary {
  display: flex;
  align-items: baseline;
  gap: .75rem;
  padding: 1rem 1.25rem;
  cursor: pointer;
  font-family: var(--font-head);
  font-size: var(--step-0);
  color: var(--bone);
  list-style: none;
}
.mod-group summary::-webkit-details-marker { display: none; }
.mod-group summary::before {
  content: "+";
  flex: none;
  width: 1.1rem;
  font-family: var(--font-mono);
  color: var(--brass);
}
.mod-group[open] summary::before { content: "\2212"; }
.mod-group summary:hover { color: var(--brass); }

.mod-group__count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--text-faint);
}

.mod-group__body { padding: 0 1.25rem 1.25rem 3.35rem; }
.mod-group__body > p {
  margin: 0 0 .9rem;
  color: var(--text-dim);
  font-size: var(--step--1);
}

.mod-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: .3rem .9rem;
  grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr));
}
.mod-list li {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--text-dim);
  word-break: break-all;
}

/* --------------------------------------------------------------------------
   8. SMALL PARTS
   -------------------------------------------------------------------------- */

/* The pack cover, framed the way the cards are framed. It is a square, and a
   square at full page width is a wall — capped and centred it reads as what it
   is, a package cover, rather than as a hero image the page does not have yet. */
.cover {
  border: 1px solid var(--hair);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  margin: 2rem auto;
  max-width: 24rem;
}
.cover img { width: 100%; }

/* A changelog entry: the version, then what it fixed. */
.release { margin-bottom: 1.6rem; }
.release h3 {
  display: flex;
  align-items: baseline;
  gap: .75rem;
  margin: 0 0 .4rem;
  font-size: var(--step-0);
}
.release h3 code { font-size: var(--step--1); color: var(--brass); }
.release p, .release ul { color: var(--text-dim); }
.release ul { margin: .4rem 0 0; padding-left: 1.35rem; }
.release li::marker { color: var(--brass); }

/* --------------------------------------------------------------------------
   9. RESPONSIVE
   -------------------------------------------------------------------------- */

@media (max-width: 560px) {
  .field { border-right: none; border-bottom: 1px solid var(--hair-soft); }
  .field:last-child { border-bottom: none; }
  .step { padding: 1.15rem 1rem; }
  .mod-group__body { padding-left: 1.25rem; }
}

/* --------------------------------------------------------------------------
   10. MOTION, TRANSPARENCY, PRINT

   There is no prefers-reduced-motion block here, and that is deliberate. The
   temperature is a colour bound to where the reader already is, not something
   moving on its own — the same call landing.css makes for the day cycle, for
   the same reason. Nothing on this page animates unless the reader pushed it,
   so there is nothing for reduced motion to switch off.
   -------------------------------------------------------------------------- */

/* A reader who has asked for less transparency wants a plate to be a plate.
   The ramp keeps its gradient, it just stops reaching up through the type.
   Chrome and Safari only so far; purely additive, so Firefox loses nothing. */
@media (prefers-reduced-transparency: reduce) {
  .plate, .step, .mod-group { background: var(--ink-raised); }
  .shell::before, .shell::after { display: none; }
}

/* A "how to join" guide is a page people print, and a document-tall dark
   gradient is the classic way to empty a cartridge. dehaven.css already forces
   white; this makes sure the ramp and the glows go with it. */
@media print {
  body.frostvale { background-image: none; }
  .shell::before, .shell::after { display: none; }
  .plate, .step, .mod-group { background: none; box-shadow: none; border-color: #999; }
  [data-temp] { --brass: #666; --clay: #000; --hair: #999; --hair-soft: #ccc; --hair-strong: #666; }
  .copy { display: none; }
}
