/*
 * FabricCraft homepage — design tokens and layout.
 *
 * Palette is taken from Minecraft materials: deepslate for every surface by
 * default, calcite for the one deliberately light section, verdigris
 * (oxidised copper) as the single interactive accent. Martian Mono is
 * reserved for figures and the wordmark; Schibsted Grotesk carries
 * everything else.
 *
 * Components colour themselves through the surface roles (--bg, --text,
 * --accent, --rule and so on), never through the raw palette, so they read
 * correctly on both surfaces. The roles are dark on :root; .band-light
 * switches a section to the light ones. The page does not follow
 * prefers-color-scheme: it is dark whatever the visitor's setting.
 */

:root {
    --deepslate: #1a1b20;
    --deepslate-2: #24262c;
    --calcite: #eceeed;
    --calcite-2: #e2e5e3;
    --ink: #24262c;
    --ink-soft: #4a4e55;
    --tuff: #646866;
    --verdigris: #266e63;
    --verdigris-bright: #7fd8c4;
    --on-dark: #eef0ef;
    --on-dark-soft: #a3a8ad;
    --rule-light: rgba(36, 38, 44, 0.14);
    --rule-dark: rgba(238, 240, 239, 0.14);

    /* Surface roles: dark by default. Contrast on --bg / --fill / --raised:
       text 15.0 / 13.2 / 11.5, text-soft 7.2 / 6.3 / 5.5, text-faint
       6.2 / 5.5 / 4.8, accent 10.3 / 9.0 / 7.9, danger 8.6 / 7.6 / 6.6;
       --on-accent on --accent 10.3, control borders 3.5 or more. */
    color-scheme: dark;
    --bg: var(--deepslate);
    --fill: var(--deepslate-2);
    --raised: #2e3037;
    --text: var(--on-dark);
    --text-soft: var(--on-dark-soft);
    --text-faint: #979ca2;
    --accent: var(--verdigris-bright);
    --accent-hover: #a4e6d6;
    --on-accent: var(--deepslate);
    --rule: var(--rule-dark);
    --control-border: #80858c;
    --danger: #ff9d92;
    --overlay: rgba(14, 15, 18, 0.75);

    --font-text: "Schibsted Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-figure: "Martian Mono", ui-monospace, "SF Mono", Menlo, monospace;

    /* Type scale: 1.25 ratio from a 17px base. */
    --step--1: 0.85rem;
    --step-0: 1.0625rem;
    --step-1: 1.33rem;
    --step-2: 1.66rem;
    --step-3: clamp(1.9rem, 3vw, 2.6rem);
    --step-4: clamp(2.4rem, min(6.4vw, 10.5vh), 5.6rem);

    --gutter: clamp(1rem, 4vw, 3rem);
    --masthead-height: 3.75rem;
    --max: 1320px;
    --band-pad: clamp(4.5rem, 10vw, 8.5rem);
    --ease-out: cubic-bezier(0.2, 0.7, 0.2, 1);
}

/* The one light section on a page (the homepage's "How the server works").
   Light is an accent, used on purpose, never a page default. */
.band-light {
    color-scheme: light;
    --bg: var(--calcite);
    --fill: var(--calcite-2);
    --raised: #f7f8f7;
    --text: var(--ink);
    --text-soft: var(--ink-soft);
    /* tuff darkened a little so faint text also passes on --fill (4.8). */
    --text-faint: #5f6361;
    --accent: var(--verdigris);
    --accent-hover: #1d5a51;
    --on-accent: #fff;
    --rule: var(--rule-light);
    --control-border: #7a7d7f;
    --danger: #a3372b;
    --overlay: rgba(26, 27, 32, 0.52);
    color: var(--text);
    background: var(--bg);
}

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

html {
    background: var(--bg);
    scroll-behavior: smooth;
    /* Anchors land below the fixed masthead. */
    scroll-padding-top: calc(var(--masthead-height) + 1.5rem);
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    color: var(--text);
    background: var(--bg);
    font-family: var(--font-text);
    font-size: var(--step-0);
    line-height: 1.55;
    text-rendering: optimizeLegibility;
}

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

a {
    color: inherit;
}

button {
    font: inherit;
    color: inherit;
}

h1,
h2,
h3,
p,
ul,
ol,
dl,
dd,
figure {
    margin: 0;
}

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

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

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

.skip-link {
    position: absolute;
    left: var(--gutter);
    top: -4rem;
    z-index: 50;
    padding: 0.6rem 0.9rem;
    color: var(--deepslate);
    background: var(--verdigris-bright);
    font-weight: 700;
    text-decoration: none;
}

.skip-link:focus {
    top: 0.75rem;
}

/* ---------- Masthead ---------- */

/* A full-width bar that stays on screen; its content keeps the page width. */
.masthead {
    position: fixed;
    inset: 0 0 auto;
    z-index: 30;
    display: flex;
    align-items: center;
    gap: clamp(1rem, 3vw, 2.5rem);
    height: var(--masthead-height);
    padding: 0 max(var(--gutter), calc((100vw - var(--max)) / 2 + var(--gutter)));
    border-bottom: 1px solid var(--rule-dark);
    color: var(--on-dark);
    /* Opaque enough that the nav links keep 4.5:1 while the bar passes over
       the light band (4.8 over pure calcite). */
    background: rgba(26, 27, 32, 0.86);
    backdrop-filter: saturate(140%) blur(10px);
}

.masthead :focus-visible,
.stage :focus-visible,
.colophon :focus-visible {
    outline-color: var(--verdigris-bright);
}

.wordmark {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    font-family: var(--font-figure);
    font-size: 0.95rem;
    font-weight: 600;
    font-stretch: 112.5%;
    letter-spacing: -0.01em;
    text-decoration: none;
}

.wordmark img {
    width: 28px;
    height: 28px;
    image-rendering: pixelated;
}

.masthead-nav {
    display: flex;
    gap: clamp(0.9rem, 2.2vw, 1.75rem);
    margin-left: auto;
}

.masthead-nav a {
    color: var(--on-dark-soft);
    font-size: 0.95rem;
    text-decoration: none;
    transition: color 0.15s;
}

.masthead-nav a:hover,
.masthead-nav a[aria-current="page"] {
    color: var(--on-dark);
}

.masthead-nav a[aria-current="page"] {
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.35em;
}

.ip-button {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.8rem;
    border: 1px solid var(--rule-dark);
    border-radius: 6px;
    background: rgba(238, 240, 239, 0.04);
    font-family: var(--font-figure);
    font-size: 0.8rem;
    cursor: pointer;
    transition: border-color 0.15s, background-color 0.15s;
}

.ip-button:hover {
    border-color: rgba(238, 240, 239, 0.32);
}

.ip-button-icon {
    width: 14px;
    height: 14px;
    opacity: 0.7;
}

.ip-button.copied {
    border-color: var(--verdigris-bright);
    color: var(--verdigris-bright);
}

/* Confirmation under the masthead's copy button (built by copy.js). It is
   right-aligned with the masthead's content edge, so it sits under the
   button, and its width is capped to the viewport minus both gutters. */
.copy-toast {
    position: fixed;
    top: calc(var(--masthead-height) + 0.5rem);
    right: max(var(--gutter), calc((100vw - var(--max)) / 2 + var(--gutter)));
    z-index: 31;
    max-width: calc(100vw - 2 * var(--gutter));
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--rule-dark);
    border-radius: 6px;
    background: var(--raised);
    box-shadow: 0 6px 18px rgba(14, 15, 18, 0.35);
    color: var(--on-dark);
    font-family: var(--font-text);
    font-size: var(--step--1);
    line-height: 1.35;
    overflow-wrap: anywhere;
    opacity: 0;
    transform: translateY(-4px);
    pointer-events: none;
    transition: opacity 0.2s var(--ease-out), transform 0.2s var(--ease-out);
}

.copy-toast:empty {
    padding: 0;
    border: 0;
}

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

.copy-toast.is-error {
    border-color: var(--danger);
}

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

/* ---------- Stage: the 3D hero ---------- */

/* One screen tall. The contents menu moves the camera; scrolling only
   scrolls the page, so vertical swipes on the scene must reach it. */
.stage {
    position: relative;
    display: grid;
    color: var(--on-dark);
    background: var(--deepslate);
    isolation: isolate;
    touch-action: pan-y pinch-zoom;
}

.stage > .stage-scene,
.stage > .stage-content {
    grid-area: 1 / 1;
}

/* The stage grows with its content, so the ledger never spills onto the
   light band below on short screens. */
.stage > .stage-content {
    min-height: max(100svh, 40rem);
}

.stage-scene {
    overflow: hidden;
    z-index: 0;
}

.stage-poster,
.stage-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* The poster (the opening view) is the first paint and the fallback; the
   live scene fades in over it once the model has loaded. */
.stage-canvas {
    opacity: 0;
    transition: opacity 0.6s ease;
}

.stage.is-live .stage-canvas {
    opacity: 1;
}

/* Scrim keeps the copy readable wherever the scene lands. */
.stage-scene::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
        linear-gradient(90deg, rgba(26, 27, 32, 0.9) 0%, rgba(26, 27, 32, 0.62) 36%, rgba(26, 27, 32, 0) 62%),
        linear-gradient(270deg, rgba(26, 27, 32, 0.55) 0%, rgba(26, 27, 32, 0) 18%),
        linear-gradient(0deg, rgba(26, 27, 32, 0.92) 0%, rgba(26, 27, 32, 0) 30%);
    pointer-events: none;
}

.stage-content {
    z-index: 1;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: 1fr auto;
    width: 100%;
    /* Full-bleed box with the content held to --max by its padding, so the
       tour stops can sit near the viewport edge instead of the column edge. */
    padding: 6.5rem max(var(--gutter), calc((100% - var(--max)) / 2 + var(--gutter))) clamp(1.25rem, 3vh, 2.25rem);
    pointer-events: none;
}

.stage-content > * {
    pointer-events: auto;
}

/* Every tour step shares one slot; only the active one is shown. */
.tour {
    grid-column: 1;
    grid-row: 1;
    align-self: center;
    display: grid;
    max-width: 34rem;
}

.tour-step {
    grid-area: 1 / 1;
    align-self: center;
    opacity: 0;
    visibility: hidden;
    transform: translateY(0.75rem);
    /* A 0.4 s crossfade, centred on the middle of the camera's flight
       (spawn-scene.js CAPTION_FADE_S). */
    transition: opacity 0.4s ease, transform 0.4s var(--ease-out), visibility 0s linear 0.4s;
}

.tour-step[data-active] {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: opacity 0.4s ease, transform 0.4s var(--ease-out), visibility 0s;
}

.stage h1 {
    font-size: var(--step-4);
    font-weight: 650;
    line-height: 0.98;
    letter-spacing: -0.035em;
}

.tour-step h2 {
    font-size: clamp(2rem, 4.4vw, 3.6rem);
    font-weight: 650;
    line-height: 1;
    letter-spacing: -0.03em;
}

.tour-step p,
.stage-lead {
    max-width: 30rem;
    margin-top: 1.25rem;
    color: var(--on-dark-soft);
    font-size: var(--step-1);
    line-height: 1.45;
}

.stage-lead {
    margin-top: 1.5rem;
}


.stage-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 2.25rem;
}

/* Desktop: pinned near the right edge of the viewport, outside the content
   column, so it covers as little of the scene as possible. Centred on the
   scene area above the ledger. */
.tour-nav {
    position: absolute;
    top: calc(50% - 2.5rem);
    right: max(1rem, calc(var(--gutter) / 2));
    transform: translateY(-50%);
    padding: 0.9rem 1rem 0.9rem 0.6rem;
    border-radius: 8px;
    background: rgba(26, 27, 32, 0.82);
    backdrop-filter: blur(6px);
}

.tour-nav[hidden] {
    display: none;
}

.tour-nav ol {
    display: grid;
    border-left: 1px solid var(--rule-dark);
}

.tour-nav button {
    display: block;
    width: 100%;
    margin-left: -1px;
    padding: 0.4rem 0 0.4rem 0.9rem;
    border: 0;
    border-left: 2px solid transparent;
    background: none;
    color: var(--on-dark-soft);
    font-size: 0.92rem;
    text-align: left;
    white-space: nowrap;
    text-shadow: 0 1px 10px rgba(26, 27, 32, 0.9);
    cursor: pointer;
    transition: color 0.15s, border-color 0.2s;
}

.tour-nav button:hover {
    color: var(--on-dark);
}

.tour-nav button[aria-current] {
    border-left-color: var(--verdigris-bright);
    color: var(--on-dark);
    font-weight: 600;
}

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

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 2.9rem;
    padding: 0 1.25rem;
    border: 1px solid transparent;
    border-radius: 6px;
    font-size: 1rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}

.button-primary {
    color: var(--deepslate);
    background: var(--verdigris-bright);
}

.button-primary:hover {
    background: #a4e6d6;
}

.button-primary.copied {
    background: var(--on-dark);
}

.button-quiet {
    color: var(--on-dark);
    border-color: var(--rule-dark);
}

.button-quiet:hover {
    border-color: rgba(238, 240, 239, 0.36);
}

.button-outline {
    border-color: var(--rule);
    background: transparent;
}

.button-outline:hover:not(:disabled) {
    border-color: var(--text);
}

.button:disabled {
    opacity: 0.4;
    cursor: default;
}

/* ---------- Ledger: the live figures ---------- */

.ledger {
    grid-column: 1 / -1;
    grid-row: 2;
    display: grid;
    grid-template-columns: 0.7fr 1.35fr 1fr 1.75fr;
    border-top: 1px solid var(--rule-dark);
}

.ledger-item {
    padding: 1.1rem 1.5rem 0 0;
    min-width: 0;
}

.ledger-item + .ledger-item {
    padding-left: 1.5rem;
    border-left: 1px solid var(--rule-dark);
}

.ledger dt {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--on-dark-soft);
    font-size: var(--step--1);
}

.ledger dd {
    position: relative;
    display: flex;
    align-items: flex-start;
    margin-top: 0.45rem;
    overflow: hidden;
    font-family: var(--font-figure);
    font-size: clamp(1.1rem, 2vw, 1.85rem);
    font-weight: 500;
    font-stretch: 75%;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
    line-height: 1.05;
    white-space: nowrap;
}

.live-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--verdigris-bright);
    box-shadow: 0 0 0 0 rgba(127, 216, 196, 0.6);
    animation: liveDot 2.4s ease-out infinite;
}

@keyframes liveDot {
    0% { box-shadow: 0 0 0 0 rgba(127, 216, 196, 0.55); }
    70%, 100% { box-shadow: 0 0 0 7px rgba(127, 216, 196, 0); }
}

/* Digit roll, carried over from the previous stats strip. */
.ledger .stat-number {
    display: block;
    font: inherit;
}

.ledger .stat-number-previous {
    position: absolute;
    inset: 0 auto auto 0;
    width: 100%;
}

.ledger .is-rolling .stat-number-current {
    animation: statRollIn var(--stat-roll-duration, 0.42s) var(--ease-out) both;
}

.ledger .is-rolling .stat-number-previous {
    animation: statRollOut var(--stat-roll-duration, 0.42s) var(--ease-out) both;
}

.ledger .chunk-count-parts,
.ledger .playtime-parts {
    display: inline-flex;
    align-items: baseline;
    font: inherit;
    line-height: inherit;
    white-space: nowrap;
}

.ledger .playtime-parts {
    gap: 0.3em;
}

.ledger .chunk-count-digit,
.ledger .playtime-value {
    position: relative;
    display: inline-block;
    overflow: hidden;
    font: inherit;
    line-height: inherit;
}

.ledger [data-playtime-part="hours"] .playtime-value,
.ledger [data-playtime-part="minutes"] .playtime-value {
    min-width: 2ch;
}

.ledger .playtime-part {
    display: inline-flex;
    align-items: baseline;
}

.ledger .playtime-unit {
    color: var(--on-dark-soft);
    font-size: 0.62em;
    font-weight: 400;
    margin-left: 0.08em;
}

@keyframes statRollIn {
    from { opacity: 0.35; transform: translateY(78%); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes statRollOut {
    from { opacity: 0.72; transform: translateY(0); }
    to { opacity: 0; transform: translateY(-78%); }
}

/* ---------- Screenshot gallery ---------- */

.gallery {
    /* clip, not hidden: the section must never become a scroll container. */
    overflow: clip;
    padding-block: clamp(3.5rem, 8vw, 6rem);
    color: var(--on-dark);
    background: var(--deepslate);
}

.gallery-head {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    justify-content: space-between;
    gap: 1rem 2rem;
    max-width: var(--max);
    margin: 0 auto clamp(2rem, 4vw, 3rem);
    padding-inline: var(--gutter);
}

.gallery-head h2 {
    font-size: var(--step-3);
    font-weight: 650;
    line-height: 1.08;
    letter-spacing: -0.025em;
}

.gallery-rows {
    display: grid;
    /* Pin the column to the viewport so each row can overflow and travel. */
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(0.5rem, 1vw, 0.85rem);
}

.gallery-track {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--on-dark-soft) transparent;
}

/* While the rows move on their own the scrollbar adds nothing; swiping,
   the arrows and keyboard focus still scroll them. */
.gallery.is-looping .gallery-track {
    scrollbar-width: none;
}

.gallery.is-looping .gallery-track::-webkit-scrollbar {
    display: none;
}

.gallery-body {
    position: relative;
}

/* Manual controls over the rows: a dark disc keeps the light glyph at 3:1
   or better over the brightest screenshot. */
.gallery-arrow {
    position: absolute;
    top: 50%;
    z-index: 2;
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: 1px solid rgba(238, 240, 239, 0.22);
    border-radius: 50%;
    color: var(--on-dark);
    background: rgba(26, 27, 32, 0.58);
    backdrop-filter: blur(6px);
    cursor: pointer;
    transform: translateY(-50%);
    transition: opacity 0.15s, background-color 0.15s;
}

/* The arrows need gallery.js; without it they stay hidden. */
.gallery-arrow[hidden] {
    display: none;
}

.gallery-arrow:hover,
.gallery-arrow:focus-visible {
    background: rgba(26, 27, 32, 0.8);
}

/* At a row end without the loop (reduced motion): still focusable. */
.gallery-arrow[aria-disabled="true"] {
    opacity: 0.35;
    cursor: default;
}

.gallery-arrow svg {
    width: 1.25rem;
    height: 1.25rem;
}

.gallery-arrow-prev {
    left: max(0.75rem, calc(var(--gutter) / 2));
}

.gallery-arrow-next {
    right: max(0.75rem, calc(var(--gutter) / 2));
}

/* Spacing is a margin on every item rather than a flex gap, so a row and its
   duplicate are exactly the same width and the drift wraps without a seam. */
.gallery-row {
    display: flex;
    width: max-content;
    padding-inline: var(--gutter);
}

.gallery.is-looping .gallery-row {
    padding-inline: 0;
}

.gallery-row li {
    margin-right: clamp(0.5rem, 1vw, 0.85rem);
}

.gallery-row button {
    display: block;
    padding: 0;
    border: 0;
    border-radius: 3px;
    overflow: hidden;
    background: var(--deepslate-2);
    cursor: zoom-in;
}

.gallery-row img {
    /* Clicks always land on the button, even while an image is swapped for
       a sharper copy (gallery.js). */
    pointer-events: none;
    height: clamp(9.5rem, 26vh, 19rem);
    width: auto;
    transition: transform 0.4s var(--ease-out), opacity 0.2s;
}

/* With the script running, an image that arrives late fades in over the
   dark placeholder instead of popping in. */
.gallery.is-fading .gallery-row img:not(.is-loaded) {
    opacity: 0;
}

.gallery-row button:hover img {
    transform: scale(1.03);
}

.gallery :focus-visible {
    outline-color: var(--verdigris-bright);
}

.lightbox {
    width: min(94vw, 1600px);
    max-width: none;
    max-height: 94vh;
    padding: 0;
    border: 0;
    color: var(--on-dark);
    background: transparent;
}

.lightbox::backdrop {
    background: rgba(14, 15, 18, 0.92);
}

.lightbox figure {
    display: grid;
    gap: 0.75rem;
}

.lightbox-frame {
    display: grid;
}

/* The cached row image shows at once; the sharp one fades in over it. */
.lightbox-frame img {
    grid-area: 1 / 1;
    width: 100%;
    max-height: calc(94vh - 4rem);
    object-fit: contain;
    border-radius: 3px;
}

.lightbox-full {
    opacity: 0;
    transition: opacity 0.2s ease;
}

.lightbox-full.is-ready {
    opacity: 1;
}

.lightbox-frame {
    position: relative;
    /* Horizontal swipes go to the script (next/previous), not to a pan
       gesture that would cancel the pointer stream. */
    touch-action: pan-y pinch-zoom;
}

.lightbox-count {
    margin-top: 0.6rem;
    color: var(--on-dark-soft);
    font-size: var(--step--1);
    font-variant-numeric: tabular-nums;
    text-align: center;
}

/* Round controls over the image: a dark disc keeps the light glyph at 3:1
   or better over the brightest screenshot; they darken on hover or focus. */
.lightbox-arrow,
.lightbox-close {
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: 1px solid var(--rule-dark);
    border-radius: 50%;
    color: var(--on-dark);
    background: rgba(26, 27, 32, 0.58);
    backdrop-filter: blur(6px);
    cursor: pointer;
    transition: background-color 0.15s;
}

.lightbox-arrow:hover,
.lightbox-close:hover,
.lightbox-arrow:focus-visible,
.lightbox-close:focus-visible {
    background: rgba(26, 27, 32, 0.8);
}

.lightbox-arrow svg,
.lightbox-close svg {
    width: 1.25rem;
    height: 1.25rem;
}

.lightbox-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
}

.lightbox-arrow-prev {
    left: 0.75rem;
}

.lightbox-arrow-next {
    right: 0.75rem;
}

.lightbox-close {
    position: fixed;
    top: max(0.75rem, env(safe-area-inset-top, 0px));
    right: 0.75rem;
}

.lightbox :focus-visible {
    outline-color: var(--verdigris-bright);
}

/* ---------- Quiet bands ---------- */

.band {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: clamp(1rem, 2.5vw, 2rem);
    max-width: var(--max);
    margin: 0 auto;
    padding: var(--band-pad) var(--gutter);
}

.band + .band {
    border-top: 1px solid var(--rule);
}

/* The gallery is not a .band, so draw the hairline where it meets one. */
.gallery + .band {
  border-top: 1px solid var(--rule);
}

/* A light band runs the full width of the window; its padding keeps the
   content on the same columns as every other band. The change of surface
   separates it, so the hairline goes. */
.band-light {
    max-width: none;
    padding-inline: max(var(--gutter), calc((100% - var(--max)) / 2 + var(--gutter)));
}

.band + .band-light,
.band-light + .band {
    border-top: 0;
}

.band-head {
    grid-column: 1 / span 4;
}

.band-body {
    grid-column: 5 / -1;
}

.band h2 {
    font-size: var(--step-3);
    font-weight: 650;
    line-height: 1.08;
    letter-spacing: -0.025em;
}

.band-note {
    margin-top: 0.75rem;
    color: var(--text-soft);
    font-size: var(--step--1);
}

.text-link {
    color: var(--accent);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.2em;
}

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

.intro-statement {
    max-width: 36ch;
    font-size: var(--step-2);
    font-weight: 500;
    line-height: 1.3;
    letter-spacing: -0.015em;
}

.facts {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 2.5rem clamp(1.5rem, 4vw, 3.5rem);
    margin-top: clamp(3rem, 6vw, 4.5rem);
}

.facts li {
    padding-top: 1rem;
    border-top: 1px solid var(--rule);
}

.facts h3 {
    font-size: var(--step-0);
    font-weight: 650;
}

.facts p {
    max-width: 38ch;
    margin-top: 0.4rem;
    color: var(--text-soft);
}

/* Join */

.join-steps {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    border-top: 1px solid var(--rule);
}

.join-steps button {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    width: 100%;
    padding: 0.9rem 0.75rem 0.9rem 0;
    border: 0;
    border-top: 2px solid transparent;
    margin-top: -1px;
    background: none;
    color: var(--text-soft);
    font-size: 0.95rem;
    text-align: left;
    cursor: pointer;
    transition: color 0.15s, border-color 0.2s;
}

.join-steps button:hover {
    color: var(--text);
}

.join-steps button[aria-selected="true"] {
    border-top-color: var(--accent);
    color: var(--text);
    font-weight: 600;
}

.join-index {
    font-family: var(--font-figure);
    font-size: 0.8rem;
    font-weight: 500;
    color: var(--text-faint);
}

.join-steps button[aria-selected="true"] .join-index {
    color: var(--accent);
}

.join-panel {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    gap: clamp(1.25rem, 3vw, 2.5rem);
    margin-top: 1.75rem;
}

.join-shot {
    overflow: hidden;
    border-radius: 4px;
    background: var(--fill);
    aspect-ratio: 16 / 9;
}

.join-shot img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    image-rendering: pixelated;
}

.join-copy {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 1.5rem;
}

.join-body {
    font-size: var(--step-1);
    line-height: 1.4;
}

.join-nav {
    display: flex;
    gap: 0.6rem;
}

/* Store and Discord */

.band-links {
    padding-top: 0;
    padding-bottom: 0;
}

.exit-link {
    grid-column: span 6;
    display: block;
    padding: clamp(3rem, 7vw, 5rem) 0;
    text-decoration: none;
}

.exit-link + .exit-link {
    padding-left: clamp(1rem, 3vw, 2rem);
    border-left: 1px solid var(--rule);
}

.exit-link h2 {
    display: inline;
    background: linear-gradient(currentColor, currentColor) 0 100% / 0 1px no-repeat;
    transition: background-size 0.3s var(--ease-out);
}

.exit-link:hover h2,
.exit-link:focus-visible h2 {
    background-size: 100% 1px;
}

.exit-link p {
    max-width: 32ch;
    margin-top: 0.75rem;
    color: var(--text-soft);
}

/* Rules */

.rules {
    max-width: 68ch;
}

.rules li {
    padding: 1rem 0;
    border-top: 1px solid var(--rule);
}

.rules li:last-child {
    border-bottom: 1px solid var(--rule);
}

/* ---------- Colophon ---------- */

.colophon {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 2rem;
    padding: 3rem max(var(--gutter), calc((100vw - var(--max)) / 2 + var(--gutter)));
    /* The page above is dark too, so a hairline marks where it ends. */
    border-top: 1px solid var(--rule-dark);
    color: var(--on-dark-soft);
    background: var(--deepslate);
    font-size: var(--step--1);
}

.colophon .wordmark {
    color: var(--on-dark);
    margin-bottom: 0.6rem;
}

.colophon .wordmark img {
    width: 24px;
    height: 24px;
}

.colophon-credit {
    max-width: 34rem;
    margin-top: 0.5rem;
    font-size: 0.78rem;
}

.colophon-credit a {
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.2em;
}

.colophon-nav {
    display: flex;
    flex-wrap: wrap;
    align-content: flex-start;
    gap: 0.5rem 1.5rem;
}

.colophon-nav a {
    text-decoration: none;
}

.colophon-nav a:hover {
    color: var(--on-dark);
}

/* ---------- Document pages (rules, privacy) ---------- */

.masthead-sticky,
.masthead-light {
    /* Document and store pages: the masthead scrolls in with the page
       rather than floating over a hero. masthead-light is the old name,
       from when these pages were light; it is the same dark bar now. */
    position: sticky;
    top: 0;
}

.doc {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: clamp(1rem, 2.5vw, 2rem);
    max-width: var(--max);
    margin: 0 auto;
    padding: clamp(3rem, 8vw, 6rem) var(--gutter) var(--band-pad);
}

.doc-head {
    grid-column: 1 / span 4;
}

.doc-head h1 {
    position: sticky;
    top: 1.5rem;
    font-size: var(--step-3);
    font-weight: 650;
    line-height: 1.08;
    letter-spacing: -0.025em;
}

.doc-meta {
    margin-top: 0.75rem;
    color: var(--text-soft);
    font-size: var(--step--1);
}

.doc-body {
    grid-column: 5 / -1;
    max-width: 68ch;
}

.doc-intro {
    font-size: var(--step-1);
    line-height: 1.45;
}

.prose h2 {
    margin-top: 3rem;
    padding-top: 1rem;
    border-top: 1px solid var(--rule);
    font-size: var(--step-0);
    font-weight: 650;
}

.prose p,
.prose ul {
    margin-top: 0.9rem;
}

.prose ul {
    padding-left: 1.1rem;
    list-style: square;
}

.prose li + li {
    margin-top: 0.5rem;
}

.prose li::marker {
    color: var(--text-faint);
}

.prose a {
    color: var(--accent);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.2em;
}

.prose a:hover {
    color: var(--text);
}

.prose-note {
    padding-left: 1rem;
    border-left: 2px solid var(--accent);
    color: var(--text-soft);
}

.page-doc .colophon {
    margin-top: 0;
}

/* A document page shorter than the screen (404) keeps the footer at the
   bottom of the viewport. */
.page-short {
    display: flex;
    flex-direction: column;
    min-height: 100svh;
}

.page-short > main {
    flex: 1 0 auto;
}

/* ---------- Responsive ---------- */

@media (max-width: 960px) {
    .masthead-nav {
        gap: 1.1rem;
    }

    .stage-content {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: 1fr auto auto;
        padding-top: 5.5rem;
    }

    .tour {
        align-self: start;
        margin-top: 2vh;
    }

    /* The caption runs down into the scene, where the scrim thins out. A
       backdrop behind the shown caption, as tall as its text and strongest
       at its last lines, keeps every line at 4.5:1 or more over the
       brightest terrain on phones and tablets. It fades in and out with the
       caption, and runs from the left edge of the screen to the right one
       (the stage clips the scrollbar's width), so it has no side edges. */
    .stage {
        overflow-x: clip;
    }

    .tour-step {
        position: relative;
        isolation: isolate;
        align-self: start;
    }

    .tour-step::before {
        content: "";
        position: absolute;
        z-index: -1;
        inset: -1rem calc(100% + var(--gutter) - 100vw) -2.5rem calc(-1 * var(--gutter));
        background: linear-gradient(180deg, rgba(26, 27, 32, 0) 0%, rgba(26, 27, 32, 0.6) 1rem, rgba(26, 27, 32, 0.7) calc(100% - 2.5rem), rgba(26, 27, 32, 0) 100%);
        pointer-events: none;
    }

    /* Stops become a scrollable row above the ledger. */
    .tour-nav {
        position: static;
        transform: none;
        grid-column: 1;
        grid-row: 2;
        margin-bottom: 0.75rem;
        /* A slim strip: the buttons carry the tap height themselves. */
        padding: 0.15rem 0.9rem;
        overflow-x: auto;
        scrollbar-width: none;
    }

    .tour-nav ol {
        display: flex;
        gap: 1.1rem;
        border-left: 0;
    }

    .tour-nav button {
        width: auto;
        margin-left: 0;
        padding: 0.5rem 0;
        border-left: 0;
        border-bottom: 2px solid transparent;
        white-space: nowrap;
    }

    .tour-nav button[aria-current] {
        border-bottom-color: var(--verdigris-bright);
    }

    .ledger {
        grid-row: 3;
    }

    .stage-scene::after {
        background:
            linear-gradient(180deg, rgba(26, 27, 32, 0.9) 0%, rgba(26, 27, 32, 0.35) 45%, rgba(26, 27, 32, 0) 62%),
            linear-gradient(0deg, rgba(26, 27, 32, 0.95) 0%, rgba(26, 27, 32, 0) 42%);
    }

    .ledger {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .ledger-item {
        padding: 0.85rem 1rem 0.85rem 0;
    }

    .ledger-item + .ledger-item {
        padding-left: 0;
        border-left: 0;
    }

    .ledger-item:nth-child(even) {
        padding-left: 1rem;
        border-left: 1px solid var(--rule-dark);
    }

    .ledger-item:nth-child(n + 3) {
        border-top: 1px solid var(--rule-dark);
    }

    .ledger dd {
        font-size: clamp(0.9rem, 4.3vw, 1.5rem);
    }

    .band-head,
    .band-body,
    .doc-head,
    .doc-body {
        grid-column: 1 / -1;
    }

    .band-body,
    .doc-body {
        margin-top: 2rem;
    }

    .doc-head h1 {
        position: static;
    }

    .join-panel {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (max-width: 600px) {
    .stage-lead,
    .tour-step p {
        font-size: var(--step-0);
    }

    .stage-actions .button {
        flex: 1 1 auto;
    }

    .facts {
        grid-template-columns: minmax(0, 1fr);
    }

    .join-steps {
        grid-template-columns: repeat(4, minmax(7.5rem, 1fr));
        overflow-x: auto;
        scrollbar-width: none;
    }

    .exit-link {
        grid-column: 1 / -1;
    }

    .exit-link + .exit-link {
        padding-left: 0;
        border-left: 0;
        border-top: 1px solid var(--rule);
    }

}

/* One compact masthead row: logo, links, and the copy button as an icon.
   Six links with the wordmark and the address text need about 795px (the
   store return pages are the widest), so the compact row starts below that. */
@media (max-width: 799px) {
    .masthead {
        gap: 0.9rem;
    }

    .masthead .wordmark span,
    .ip-button-text {
        display: none;
    }

    .masthead-nav {
        gap: 0.75rem;
    }

    .masthead-nav a {
        font-size: 0.9rem;
    }

    .masthead .wordmark,
    .ip-button {
        flex: none;
    }

    .ip-button {
        padding: 0.5rem;
    }
}

/* Phones: six links no longer fit beside the logo and the copy button, so
   Rules leaves the masthead below 424px and Status below 374px. Both stay in
   the colophon. */
@media (max-width: 423px) {
    .masthead-nav a[href="/rules/"] {
        display: none;
    }
}

@media (max-width: 373px) {
    .masthead-nav a[href="https://status.fabriccraft.net/"] {
        display: none;
    }
}

/* 320px: tighter gaps keep the copy button inside the page gutter. */
@media (max-width: 340px) {
    .masthead {
        gap: 0.6rem;
    }

    .masthead-nav {
        gap: 0.6rem;
    }
}

/* Tighten the hero on short screens so it stays about one screen tall. */
@media (max-width: 960px) and (max-height: 760px) {
    .stage-content {
        padding-top: 4.75rem;
        padding-bottom: 0.75rem;
    }

    .stage .stage-lead,
    .stage .tour-step p {
        margin-top: 0.75rem;
        font-size: 0.95rem;
    }

    .stage .stage-actions {
        margin-top: 1rem;
    }

    .stage .button {
        min-height: 2.6rem;
    }

    .stage .ledger-item {
        padding-top: 0.55rem;
        padding-bottom: 0.55rem;
    }
}

@media (max-height: 720px) and (min-width: 961px) {
    .stage-content {
        padding-top: 5rem;
    }

    .stage-lead {
        margin-top: 1rem;
        font-size: var(--step-0);
    }

    .stage-actions {
        margin-top: 1.5rem;
    }
}

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

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