/* ============================================================
   Darkside — the room
   ------------------------------------------------------------
   Sheet three of the same drawing set, printed on the app's own
   stock: true black, no colour, no cards, no product shots. A
   screenshot of Darkside is a screenshot of your own desktop, so
   the only honest picture of it is the thing itself, running.

   The one rule that cannot be taken literally: black on black is
   not minimal, it is invisible. What makes the real bezel legible
   is the lit display behind it, so the page carries a faint light
   down its right side for the darkness to be dark against, and
   the shape is drawn with a rim rather than as a flat mass.
   ============================================================ */

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

:root {
    --ink-bg: #000;
    --paper:  #050506;

    /* Measured against #050506. The page wants to whisper, but a whisper
       under 4.5:1 is just text nobody can read: 0.52 is the floor where
       white-on-black clears AA, so nothing on this page goes below it. */
    --text:   #f2f2f4;                      /* 18.9:1 */
    --dim:    rgba(240, 241, 246, 0.66);    /*  7.3:1 — body */
    --faint:  rgba(240, 241, 246, 0.52);    /*  5.0:1 — smallest allowed */
    --hair:   rgba(255, 255, 255, 0.1);
    --hair-2: rgba(255, 255, 255, 0.055);

    --rail:   50px;
    --gutter: clamp(20px, 4.6vw, 76px);
    --sheet:  1200px;

    --sans: 'Archivo', ui-sans-serif, system-ui, -apple-system, sans-serif;
    --mono: 'Fragment Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
    --ease: cubic-bezier(0.16, 1, 0.3, 1);

    /* The press. The one duration on this page short enough to read as
       contact rather than as motion. */
    --press: 90ms;
}

/* One sheet at a time, and never a resting place between two of them.
   Every sheet is exactly one viewport tall, so each snap position is a whole
   screen and there is nowhere to end up half in and half out. The scroll
   container has to be the same box the snapping is declared on, so the
   horizontal clamp stays here rather than on <body>. */
html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    overflow-x: hidden;
    scroll-snap-type: y mandatory;
}

/* Set by the script if a sheet ever outgrows the window. At that point
   mandatory has no snap position inside the oversized sheet and would drag
   you off the part you were reading, so it loosens rather than fights. */
html.snap-loose { scroll-snap-type: y proximity; }

body {
    font-family: var(--sans);
    background: var(--paper);
    color: var(--text);
    /* 17px at the browser's default, stated relatively so a reader who has
       raised their base text size is honoured rather than overridden. */
    font-size: 1.0625rem;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

a { color: inherit; text-decoration: none; }
img, svg { display: block; max-width: 100%; }
:focus-visible { outline: 2px solid #8fa8e8; outline-offset: 4px; border-radius: 2px; }
::selection { background: rgba(150, 176, 238, 0.32); }

/* The lit panel the darkness is cut out of. Faint on purpose: you
   should not catch it looking at the page head-on, only notice that
   the right side has depth and something is standing in front of it. */
.glow {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
        radial-gradient(76% 58% at 103% 50%, rgba(122, 146, 205, 0.15), transparent 63%),
        radial-gradient(40% 32% at 100% 18%, rgba(148, 126, 205, 0.08), transparent 72%),
        radial-gradient(44% 36% at 100% 84%, rgba(86, 148, 190, 0.08), transparent 72%);
}

/* The live bezel. Above the page, because the app is another layer
   on your screen — so it is another layer on the page too. */
#bezel {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    z-index: 40;
    pointer-events: none;
}

/* ── Rail ──────────────────────────────────────────────────── */

.rail {
    position: fixed;
    inset: 0 0 auto 0;
    z-index: 60;
    height: var(--rail);
    display: flex;
    align-items: center;
    gap: 18px;
    padding: 0 var(--gutter);
    background: rgba(5, 5, 6, 0.72);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
}

/* A scroll edge rather than a rule — and on this page especially, since a
   hairline is the one mark a black page cannot afford to spend on nothing.
   It appears only once a sheet has travelled under the rail. */
.rail::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    height: 14px;
    pointer-events: none;
    opacity: 0;
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0.55), rgba(0, 0, 0, 0));
    transition: opacity 0.28s var(--ease);
}

.scrolled .rail::after { opacity: 1; }

.rail-mark {
    font-variation-settings: 'wdth' 78;
    font-weight: 700;
    font-size: 1.06rem;
    letter-spacing: 0.02em;
    padding-right: 18px;
    border-right: 1px solid var(--hair);
    align-self: stretch;
    display: flex;
    align-items: center;
    gap: 0.5em;
    color: var(--dim);
    transition: color 0.2s;
}
.rail-mark:hover { color: var(--text); }

/* The mark is this page's own geometry, borrowed back: the same profile
   function js/bezel.js draws the edge with. It takes --dim like the wordmark
   beside it. See board.css for why it is set larger than the cap height. */
.rail-glyph {
    flex: none;
    height: 0.90em;
    width: auto;
    fill: currentColor;
}

.rail-here {
    font-family: var(--mono);
    font-size: 0.688rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text);
    margin-right: auto;
    display: flex;
    align-items: center;
    gap: 9px;
}
.rail-here::before { content: ''; width: 6px; height: 6px; background: var(--text); }

/* Deliberately quiet: a shortcut for people who already know what
   they came for, not a call to action competing with the page. */
.rail-get {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 30px;
    padding: 5px 13px;
    font-size: 0.781rem;
    font-weight: 500;
    color: var(--dim);
    border: 1px solid var(--hair);
    border-radius: 100px;
    transition: color 0.28s, border-color 0.28s, background 0.28s;
}
.rail-get:hover { color: var(--text); border-color: rgba(255, 255, 255, 0.34); background: rgba(255, 255, 255, 0.04); }
.rail-get svg { width: 13px; height: 13px; opacity: 0.6; }

/* ── Stage ─────────────────────────────────────────────────── */

/* Pushed left, but only far enough to leave the bezel and its light
   a lane of their own. */
.stage { position: relative; z-index: 10; }

.sheet {
    position: relative;
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: clamp(84px, 11vh, 132px) 0 clamp(64px, 9vh, 104px);
    scroll-snap-align: start;
    /* The little effort to move on: the container may not pass over a snap
       position, so even a hard flick advances exactly one screen. */
    scroll-snap-stop: always;
}

/* The closing sheet sizes to its contents rather than to the window, because
   the title block below it is part of the same screen and needs the room.
   Every other sheet still owns a whole viewport. */
#install { min-height: 0; padding-bottom: clamp(34px, 5vh, 60px); }

/* No `width: 100%` — a block already fills its parent, and combining the
   two with a left margin makes the box wider than the page. The left margin
   is wide enough to hold the sheet mark beside the heading. */
.sheet-inner {
    position: relative;
    max-width: 760px;
    margin-left: clamp(20px, 8vw, 150px);
    padding-right: clamp(80px, 14vw, 220px);
}

h1 {
    font-size: clamp(2.6rem, 6.4vw, 5.4rem);
    font-variation-settings: 'wdth' 78;
    font-weight: 500;
    line-height: 0.97;
    letter-spacing: -0.042em;
}

h2 {
    font-size: clamp(1.9rem, 4.2vw, 3.5rem);
    font-variation-settings: 'wdth' 82;
    font-weight: 500;
    line-height: 1.02;
    letter-spacing: -0.036em;
}

.body {
    margin-top: 26px;
    max-width: 50ch;
    font-size: 1.03rem;
    line-height: 1.66;
    color: var(--dim);
    font-weight: 400;
}

/* The sheet's own mark, set vertically in the margin *beside* the heading.
   It used to sit at the top of the sheet while the copy was centred, which
   opened a hundred-odd pixels of nothing between a three-word label and the
   thing it labels. Beside the heading there is no gap to open, and a margin
   running bottom-to-top is what a drawing does with a sheet reference. */
.sheet-mark {
    position: absolute;
    top: 0.3em;
    left: -34px;
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font-family: var(--mono);
    font-size: 0.688rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--faint);
    white-space: nowrap;
}

.rule { width: 36px; height: 1px; background: var(--hair); margin: 32px 0; }

.aside { margin-top: 18px; max-width: 50ch; font-size: 0.938rem; line-height: 1.6; color: var(--faint); }

/* ── Install ───────────────────────────────────────────────── */

/* Block-level and sized to its content. An inline box sits on a text
   baseline, so the line reserves room for descenders underneath it —
   phantom pixels under the pill that no margin accounts for. */
.get {
    display: inline-flex;
    align-items: center;
    /* Taller. At 18px the pill read as a wide strip rather than a primary
       action; the extra vertical air is what makes it a place to press. */
    margin-top: 30px;
    padding: 21px 30px;
    border: 1px solid rgba(255, 255, 255, 0.16);
    border-radius: 100px;
    font-size: 0.969rem;
    font-weight: 500;
    transition: border-color 0.35s, background 0.35s, transform 0.35s var(--ease);
}
.get:hover { border-color: rgba(255, 255, 255, 0.42); background: rgba(255, 255, 255, 0.045); transform: translateX(6px); }

/* The press.
   ------------------------------------------------------------------
   The download pill is the only thing on this page anyone has to click,
   and until now it answered a pointer and ignored a finger completely.
   Pressed, it stops travelling and takes the weight: the border comes
   up, the ground fills, and the pill settles a hair into the page. */
.get:active {
    transform: translateX(6px) scale(0.985);
    border-color: rgba(255, 255, 255, 0.6);
    background: rgba(255, 255, 255, 0.09);
    transition-duration: var(--press);
}

.rail-get:active {
    color: var(--text);
    border-color: rgba(255, 255, 255, 0.5);
    background: rgba(255, 255, 255, 0.09);
    transition-duration: var(--press);
}

.rail-mark:active,
.block-cell dd a:active,
.back-live:active { color: var(--text); transition-duration: var(--press); }

/* Every technical detail on one line, under the button that needs them.
   Two elements, because release.sh owns the first one. */
.build-line {
    display: flex;
    flex-wrap: wrap;
    gap: 0 8px;
    margin-top: 20px;
    font-family: var(--mono);
    font-size: 0.781rem;
    color: var(--faint);
    font-variant-numeric: tabular-nums;
}

.build-req::before { content: '\00B7'; margin-right: 8px; opacity: 0.5; }

.steps { margin-top: 34px; max-width: 54ch; }
.step { display: flex; gap: 18px; padding: 15px 0; border-top: 1px solid var(--hair-2); }
.step:last-child { border-bottom: 1px solid var(--hair-2); }
.step .n { font-family: var(--mono); font-size: 0.75rem; color: var(--faint); padding-top: 3px; flex: none; }
.step .t { font-size: 0.938rem; line-height: 1.6; color: var(--dim); }
.step .t strong { color: var(--text); font-weight: 500; }

.note { margin-top: 28px; max-width: 54ch; font-size: 0.875rem; line-height: 1.62; color: var(--faint); }
.note kbd { font-family: var(--mono); font-size: 0.813rem; color: var(--dim); }

/* ── Title block ───────────────────────────────────────────── */

/* The title block closes the last sheet; it is not a sheet of its own.
   ------------------------------------------------------------------
   It used to carry `scroll-snap-align: end`, which made it a snap
   destination — so leaving Install landed you on a whole screen holding
   nothing but a footer. On a drawing the title block sits at the foot of
   the last sheet, not on a sheet after it, so it shares Install's snap
   position and the two are read as one closing screen.

   The pairing is why `#install` gives up `min-height: 100svh` above: the
   two have to fit one window between them. The script measures exactly
   that and loosens to proximity if they ever don't. */
.block {
    position: relative;
    z-index: 10;
    border-top: 1px solid var(--hair);
    background: #030304;
    scroll-snap-align: none;
}
.block-inner { max-width: var(--sheet); margin: 0 auto; padding: clamp(40px, 5vw, 64px) var(--gutter) 0; }
.block-grid { display: grid; grid-template-columns: 1.5fr 1fr 1fr; border-left: 1px solid var(--hair-2); border-top: 1px solid var(--hair-2); }
.block-cell { padding: 22px 20px 26px; border-right: 1px solid var(--hair-2); border-bottom: 1px solid var(--hair-2); min-height: 128px; }
.block-cell dt { font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--faint); margin-bottom: 12px; }
.block-cell dd { font-size: 0.938rem; line-height: 1.7; color: var(--dim); }
/* Inline-block with real vertical padding: a 16px link box is under the
   minimum target size however comfortable the line-height around it looks. */
.block-cell dd a { display: inline-block; padding: 4px 0; transition: color 0.2s; }
.block-cell dd a:hover { color: var(--text); }
.block-title { font-variation-settings: 'wdth' 78; font-weight: 700; font-size: clamp(1.7rem, 3vw, 2.3rem); letter-spacing: -0.02em; line-height: 1; margin-bottom: 10px; color: var(--text); }
.block-foot { display: flex; flex-wrap: wrap; gap: 8px 22px; justify-content: space-between; padding: 16px 0 26px; }
.t-dim { font-family: var(--mono); font-size: 0.75rem; color: var(--faint); }

/* ── Reveal ────────────────────────────────────────────────── */

/* Promoted only while the rise is still owed — a will-change carried for
   the life of the page is a memory bill, not an optimisation. */
.js .up { opacity: 0; transform: translateY(14px); transition: opacity 0.9s var(--ease), transform 0.9s var(--ease); will-change: opacity, transform; }
.up.seen { opacity: 1; transform: none; will-change: auto; }
.up.d1 { transition-delay: 0.1s; }
.up.d2 { transition-delay: 0.2s; }
.up.d3 { transition-delay: 0.3s; }

/* ── Short windows ─────────────────────────────────────────── */

/* Install carries more than any other sheet — pill, version line,
   three steps, the Gatekeeper note. Tighten rather than let it spill. */
@media (max-height: 760px) {
    .get { margin-top: 22px; padding: 16px 26px; }
    .build-line { margin-top: 14px; }
    .steps { margin-top: 24px; }
    .step { padding: 11px 0; }
    .note { margin-top: 20px; }
}

/* ── Small screens ─────────────────────────────────────────── */

@media (max-width: 860px) {
    /* Nothing to catch on here — the sheets are short and several share the
       screen, so snapping would fight the scroll instead of shaping it. */
    html { scroll-snap-type: none; }
    .sheet {
        min-height: 0;
        padding: clamp(72px, 11vh, 110px) 0;
        scroll-snap-align: none;
        scroll-snap-stop: normal;
    }
    .sheet:first-of-type { min-height: 82svh; padding-top: calc(var(--rail) + 64px); }
    .sheet-inner { margin-left: 20px; padding-right: 20px; max-width: none; }
    /* Static, it sat directly above the heading — a kicker, which is the one
       thing this page must not grow. Ordered last it becomes what it is on
       the wide layout: the mark that identifies the plate, not a label for
       the words. */
    .sheet-mark {
        position: static;
        order: 2;
        margin: 26px 20px 0;
        writing-mode: horizontal-tb;
        transform: none;
    }
    /* The light is aimed at a lane that does not exist on a phone. */
    .glow { background: radial-gradient(120% 40% at 100% 50%, rgba(122, 146, 205, 0.11), transparent 70%); }
    /* The bezel is the whole point, but a phone has no room beside it,
       so it gets out of the way rather than sitting on the text. */
    #bezel { opacity: 0.5; }
    .block-grid { grid-template-columns: 1fr; }
    .block-cell { min-height: 0; }
}

@media (max-width: 620px) {
    body { font-size: 1rem; }
    .rail-here { display: none; }
    .rail-mark { margin-right: auto; padding-right: 0; border-right: none; }
}

/* Reduced motion is a gentler equivalent, not silence. Movement goes; the
   colour and opacity changes that tell a reader something answered them
   stay, because those are comprehension rather than decoration. */
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-property: opacity, color, background-color, border-color, box-shadow, outline-color !important;
        transition-duration: 0.18s !important;
    }
    .js .up { opacity: 1; transform: none; }
    /* Neither the hover nor the press travels; both still answer. */
    .get:hover, .get:active { transform: none; }
}

/* Frostier, not transparent. The rail is the page's one translucent
   surface — asked for less transparency it becomes the page it floats
   over. The bezel is left alone: it is the product, drawn, and its whole
   subject is a black shape read against a lit display. */
@media (prefers-reduced-transparency: reduce) {
    .rail {
        background: #050506;
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
    }
}

/* This page runs deliberately quiet — --faint sits at the 5.0:1 floor and
   the hairlines at 0.055 alpha. Asked for more contrast it stops
   whispering. */
@media (prefers-contrast: more) {
    :root {
        --dim:    rgba(240, 241, 246, 0.94);
        --faint:  rgba(240, 241, 246, 0.82);
        --hair:   rgba(255, 255, 255, 0.44);
        --hair-2: rgba(255, 255, 255, 0.3);
    }
    .rail {
        background: #000;
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
        border-bottom: 1px solid var(--hair);
    }
    .get { border-width: 2px; border-color: rgba(255, 255, 255, 0.6); }
    .rail-get { border-color: rgba(255, 255, 255, 0.5); }
    :focus-visible { outline-width: 3px; }
}

/* The way back out of the preview. */
.back-live { display: inline-flex; align-items: center; min-height: 44px; color: var(--dim); transition: color 0.2s; }
.back-live:hover { color: var(--text); }
