/* ==================================================================
   ARIZODO / SITE v2
   release 1.9.1  b20260829a    file v2.0.0

   Layout only. Not one colour literal in this file -- a gate fails the
   build if one appears -- so all three skins restyle the whole site
   without touching it. Same rule the app holds core.css to.

   V1's stylesheet was the app's, widened. It had five max-width
   breakpoints and no min-width, no clamp() anywhere, and one 720px
   column at every viewport, which meant it had a phone design and
   permission to view it on a desk. This file is written phone-first
   and every breakpoint adds.
   ================================================================== */

/* ---- FONTS ------------------------------------------------------- */
/* Served from the app's folder. The site does not carry a second copy
   of four faces the app already ships. */
@font-face {
    font-family: "Rajdhani";
    src: url("/app/fonts/rajdhani-600.woff") format("woff");
    font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
    font-family: "JetBrains Mono";
    src: url("/app/fonts/jetbrainsmono-400.woff") format("woff");
    font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
    font-family: "JetBrains Mono";
    src: url("/app/fonts/jetbrainsmono-500.woff") format("woff");
    font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
    font-family: "Noto Sans JP";
    src: url("/app/fonts/notosansjp-400.woff") format("woff");
    font-weight: 400 500; font-style: normal; font-display: swap;
}

/* ---- TOKENS ------------------------------------------------------ */
/* TYPE HAS TWO JOBS. Mono is for instruments -- numbers, labels,
   stamps, the ladder, the score. Prose gets a face built for reading.
   The two never blur: if it is a measurement it is mono, if it is a
   sentence it is not. V1 set six thousand words in the instrument
   face because the app's stylesheet arrived whole.

   The reading face is a system stack pending a subset webfont, which
   is a separate decision from this layout. */
:root {
    --u: 4px;
    --gut: clamp(20px, 4.5vw, 64px);
    --shell: 1340px;
    --measure: 68ch;
    --doc: 74ch;

    /* THREE WIDTHS, AND THE DIFFERENCE IS WHAT THE THING IS.
     *
     *   --measure / --doc   lines of text. A line longer than this is hard
     *                       to read, so prose is clamped to it.
     *   --fig               an explanatory figure. Something drawn to make a
     *                       paragraph clearer -- the scoring diagram is the
     *                       first. It belongs to the prose it explains and
     *                       sits in the same column, so it takes the measure
     *                       rather than the full width of the page.
     *   (unconstrained)     an instrument carrying the app's own data. The
     *                       ladder, and anything after it that shows real
     *                       records rather than explaining a rule. These are
     *                       exempt from the measure -- gate L4b -- because a
     *                       table row is not a line of text and clamping it
     *                       puts the header and the rows on different grids.
     *
     * The scoring diagram was built as though it were the third kind and read
     * as one: full width, competing with the ladder, implying it carried data
     * when it carries an explanation. Named separately so the two categories
     * cannot be confused again as more of each are added. */
    --fig: var(--doc);

    /* EVERY SIZE IS FLUID. Phone value -> desk value. V1 had none of
       this: a 44px headline was 44px on a phone and on a 32in display. */
    --fs-micro:   clamp(0.6875rem, 0.66rem + 0.10vw, 0.75rem);
    --fs-sm:      clamp(0.75rem,   0.72rem + 0.14vw, 0.8125rem);
    --fs-base:    clamp(0.9375rem, 0.90rem + 0.22vw, 1.0625rem);
    --fs-md:      clamp(1.0rem,    0.95rem + 0.30vw, 1.1875rem);
    --fs-lg:      clamp(1.1875rem, 1.02rem + 0.85vw, 1.625rem);
    --fs-xl:      clamp(1.625rem,  1.20rem + 2.10vw, 2.75rem);
    --fs-display: clamp(2.125rem,  1.30rem + 4.10vw, 4.75rem);

    --lh-tight: 1.06;
    --lh-base: 1.5;
    --lh-read: 1.62;

    --tr-micro: 0.22em;
    --tr-label: 0.15em;
    --tr-display: 0.01em;

    --ff-display: "Rajdhani", system-ui, sans-serif;
    --ff-mono: "JetBrains Mono", ui-monospace, Menlo, Consolas, monospace;
    --ff-ja: "Noto Sans JP", var(--ff-mono);
    --ff-read: "Iowan Old Style", "Charter", Cambria, Georgia,
               "Times New Roman", serif;

    --hit: 44px;
    --rule: 1px;

    /* THE HEIGHT OF THE STICKY BAR, DECLARED RATHER THAN GUESSED.
       Two things need this number and neither can measure it: the
       scroll padding that keeps an anchored heading out from under the
       bar, and nothing else -- the phone menu derives its own position
       from the bar instead, which is better and is why it is not listed
       here twice.

       On a phone the tallest thing in the bar is the burger, so the
       height is its hit target plus the row's padding plus the rule
       beneath: 44 + 20 + 1. Above 48em the burger is gone and the bar
       is about six pixels shorter, and this number is deliberately NOT
       re-declared there. It is an upper bound, and the error runs the
       safe way: too large lands a heading a few pixels lower than it
       needed to be, which reads as intent, while too small hides it
       under the bar, which is the bug this replaces.

       If --hit, --rule or the .bar__in padding changes, change this.
       A gate checks it is still derived from those terms. */
    --bar-h: calc(var(--hit) + var(--u) * 5 + var(--rule));
}

/* Aliases onto the skin tokens, so skins.css is used unmodified. */
:root {
    --ground: var(--az-ground);
    --panel: var(--az-panel);
    --panel-2: var(--az-panel-2);
    --scrim: var(--az-scrim);
    --ink: var(--az-ink);
    --ink-2: var(--az-ink-2);
    --ink-3: var(--az-ink-3);
    --ink-inv: var(--az-ink-inv);
    --line: var(--az-line);
    --line-2: var(--az-line-2);
    --signal: var(--az-signal);
    --advance: var(--az-advance);
    --hold: var(--az-hold);

    /* WHERE COLOUR IS ALLOWED. Three jobs, no others. --accent is shu,
       the seal red, and is punctuation only: the tick before a label,
       the seal, the black-belt rung. --signal is the app's own
       interactive colour and takes Japanese text and links, both rules
       copied from core.css where .belt-label-ja is already --az-signal.
       Belt colour is carried entirely by the marks themselves. */
    --accent: var(--az-shu);
    --accent-dan: var(--az-arc-dan);
}

/* ---- RESET ------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* EVERY IN-PAGE LINK LANDS BELOW THE BAR, NOT BEHIND IT.
   The bar is sticky at top:0, so a fragment link scrolls its target to
   y=0 and the bar then covers the first 65 pixels of it. On /how/ that
   was the whole of every section heading: the rail said "Ladder", the
   page jumped, and the reader arrived at a paragraph with no title on
   it. Reported as "half of titles is under menu bar", and it was
   worse than half.

   scroll-padding-top on the scroll container is the whole fix. It
   applies to fragment navigation and to scrollIntoView alike, so the
   rail, the footer links and any deep link from outside all land the
   same way. One extra --u*4 of air below the bar so the heading is not
   flush against it. */
html { scroll-padding-top: calc(var(--bar-h) + var(--u) * 4); }

body {
    background: var(--ground);
    color: var(--ink);
    font-family: var(--ff-read);
    font-size: var(--fs-md);
    line-height: var(--lh-read);
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
}

:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }
button { font: inherit; color: inherit; background: none; border: 0;
         padding: 0; cursor: pointer; -webkit-tap-highlight-color: transparent; }
a { color: inherit; }
code { font-family: var(--ff-mono); font-size: 0.9em;
       background: var(--panel-2); padding: 2px 5px; }

.az-ja { font-family: var(--ff-ja); font-weight: 500; }

/* ---- TYPE -------------------------------------------------------- */
.label {
    font-family: var(--ff-mono); font-size: var(--fs-sm);
    letter-spacing: var(--tr-label); text-transform: uppercase;
    color: var(--ink-2); margin: 0 0 calc(var(--u) * 4);
    display: flex; align-items: center; gap: calc(var(--u) * 2.5);
    flex-wrap: wrap;
}
/* The tick. One 3px bar of seal red before every section label -- the
   smallest amount of colour that still reads as a deliberate mark. */
.label::before {
    content: ""; flex: 0 0 auto;
    width: 3px; height: 12px; background: var(--accent);
}
.label .az-ja { color: var(--signal); }

h1, h2, h3 {
    font-family: var(--ff-display); font-weight: 600;
    letter-spacing: var(--tr-display); line-height: var(--lh-tight);
    margin: 0; text-wrap: balance;
}
h1 { font-size: var(--fs-display); }
h2 { font-size: var(--fs-xl); margin-bottom: calc(var(--u) * 5); }
h3 { font-size: var(--fs-lg); margin-bottom: calc(var(--u) * 3); }

p { margin: 0 0 1em; max-width: var(--measure); }
.lede { font-size: var(--fs-lg); line-height: 1.42; color: var(--ink);
        max-width: 46ch; }
.sm { font-size: var(--fs-base); }
b, strong { font-weight: 600; }

/* ---- SHELL ------------------------------------------------------- */
.band__in {
    max-width: var(--shell); margin-inline: auto;
    padding-inline: max(var(--gut), env(safe-area-inset-left, 0px));
}

/* Home runs edge to edge in bands. The five reading pages keep a
   column, because a document is read and a landing page is scanned. */
main.flow > section { padding-block: clamp(56px, 9vw, 132px); }
main.flow > section:nth-of-type(even) { background: var(--panel); }
main.flow > section.zone--close { background: var(--panel-2); }

/* The page's own h1, above its sections. Four of V1's six pages had no
   h1 anywhere in the document -- the title lived in the tab and the first
   heading a reader met was an h2 with nothing over it. */
.doc__head { margin-bottom: clamp(40px, 6vw, 88px); }
.doc__head h1 { font-size: var(--fs-xl); }

main.doc {
    max-width: var(--shell); margin-inline: auto;
    padding-inline: max(var(--gut), env(safe-area-inset-left, 0px));
    padding-block: clamp(40px, 6vw, 88px) clamp(56px, 8vw, 112px);
}
main.doc > section { margin-bottom: clamp(48px, 7vw, 104px); }
main.doc > section:last-of-type { margin-bottom: 0; }

/* The reading measure applies to prose and to nothing else. It used to be
   `main.doc p, main.doc li`, which also caught the twenty ladder rungs and
   the note under them: each row was clamped to 74ch inside a 943px table,
   so the header row sat on one grid and the rows on another, and the
   difference showed as a slab of the list's background colour down the
   right-hand side. A measure is for lines of text; a table row is not one. */
main.doc p, main.doc li { max-width: var(--doc); }
main.doc .ladder p, main.doc .ladder li,
main.doc .score p, main.doc .onward p { max-width: none; }

/* ---- BAR --------------------------------------------------------- */
.bar {
    position: sticky; top: 0; z-index: 30;
    background: var(--scrim);
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
    border-bottom: var(--rule) solid var(--line);
}
/* The bar is a single row that must survive its narrowest case: on a 375px
 * phone it carries the mark, three skin buttons that now spell their names,
 * and the burger. Nothing here may wrap -- the height is declared once, from
 * the burger -- so the rule is that the flexible parts give way and the
 * controls never do. The burger especially: a bar that pushes its own menu
 * button past the edge leaves a phone with no navigation at all, which is
 * how the 60px off-screen menu happened in August. */
.bar__in {
    max-width: var(--shell); margin-inline: auto;
    padding: calc(var(--u) * 2.5) var(--gut);
    display: flex; align-items: center; gap: calc(var(--u) * 2);
}
.mark { display: flex; align-items: center; gap: calc(var(--u) * 2.5);
        text-decoration: none; flex: 0 1 auto; min-width: 0; }
.mark__svg { display: block; width: 30px; height: 30px; }
/* The wordmark is the app's, not a second one. core.css sets .az-title in
   the display face at 600 with --az-tr-display tracking, and the app spells
   it ARIZODO in capitals. The site was spelling it "Arizodo" in the mono
   face, so the name looked like two different products depending on whether
   you were looking at the page or at the app running inside it. */
.mark__wd { font-family: var(--ff-display); font-weight: 600;
            font-size: var(--fs-md); letter-spacing: var(--tr-display);
            /* Last in the queue for space and first to give it up. On a
               narrow phone carrying three two-part skin buttons this clips
               rather than pushing the burger past the edge -- the mark next
               to it still says whose site this is, and still links home. */
            overflow: hidden; white-space: nowrap; min-width: 0; }
.mark__ja { font-size: 0.8em; font-weight: 500; color: var(--signal);
            letter-spacing: 0; display: none; }
.bar__sp { flex: 1 1 auto; }

.nav a {
    font-family: var(--ff-mono); font-size: var(--fs-sm);
    letter-spacing: 0.06em; text-decoration: none; color: var(--ink-2);
    padding: calc(var(--u) * 2) 0; border-bottom: 2px solid transparent;
}
.nav a:hover { color: var(--ink); border-bottom-color: var(--signal); }
.nav a[aria-current="page"] { color: var(--ink);
                              border-bottom-color: var(--accent); }

.skins { display: flex; border: var(--rule) solid var(--line);
         flex: 0 1 auto; min-width: 0; }
.skins button {
    /* The pair is baseline-aligned to each other -- the character is 0.85em
       and would otherwise float against the word -- and the pair as a whole
       is centred in the button. Those are two different axes and the button
       used to only get the first one right.

       No min-height. It was 30px against a ~23px line, and because the
       children are baseline-aligned the whole 7px surplus was dealt out
       below the text: every button sat with its label pushed up and a band
       of empty space underneath, out of line with the wordmark beside it.
       The height now comes from the padding, symmetrically, so the label
       sits on the bar's centre line where ARIZODO does. The touch target is
       held by .skins, which carries the full --hit height for all three. */
    /* Inline, not flex. The word and the character are two pieces of text
       on one line, and inline layout is the only thing that puts text on a
       shared baseline for free. Flex gave the pair a baseline relative to
       each other but no vertical position in the button, so the surplus
       height fell underneath and the label rode high against the wordmark.

       line-height carries the full touch height instead, which centres the
       line in the button by construction -- no min-height, no leftover to
       distribute, and the label sits on the bar's centre line where ARIZODO
       sits. */
    display: block; text-align: center; white-space: nowrap; min-width: 0;
    padding: 0 calc(var(--u) * 1.5);
    font-family: var(--ff-mono); font-size: var(--fs-micro);
    line-height: var(--hit); color: var(--ink-3);
    border-right: var(--rule) solid var(--line);
}
.skins b + i, .skins i { margin-left: 3px; }
.skins button:last-child { border-right: 0; }
.skins button[aria-pressed="true"] { background: var(--ink); color: var(--ink-inv); }
/* Both, at every width. The word alone is a label; the character is what the
 * control has always looked like and what the app is written in. Neither
 * carries it on its own -- the kanji alone is unreadable to most visitors,
 * the word alone is anonymous. They travel together, and the bar is made to
 * fit them rather than the pair being trimmed to fit the bar. */
.skins i { font-style: normal; font-size: 0.85em; opacity: 0.7;
           font-family: var(--ff-ja); display: inline; }
/* The English name is the label, as it is in the app -- the Contract screen
 * offers "Tetsu -- dark instrument", not a glyph. The site showed the kanji
 * alone and hid the word, which made the one control a first-time visitor
 * meets unreadable to anyone who does not read Japanese. Kanji returns as an
 * accent beside the word once there is room for both. */
.skins b { display: inline; font-weight: 500; }

.burger { flex: 0 0 auto;
         display: flex; flex-direction: column; justify-content: center;
          gap: 4px; min-width: var(--hit); min-height: var(--hit);
          align-items: center; flex: 0 0 auto; }
.burger i { display: block; width: 20px; height: 2px; background: var(--ink); }

/* Collapsed with [hidden], so the links leave the tab order rather than
   merely going invisible.

   Above 48em the rule below re-displays the nav while the attribute is
   still on it, for the instant before site.js removes it. That is an
   author declaration beating the UA's [hidden]{display:none}, which the
   HTML spec asks authors not to do, and it is deliberate: the choice is
   between a spec nit that no user can perceive and shipping a phone menu
   whose links are focusable but invisible whenever script does not run.
   The burger is display:none at this width, so its aria-expanded is not
   exposed while the attribute is stale. */
/* THE PANEL HANGS FROM THE BAR. `top` IS NOT OPTIONAL.
   This rule set left and right and no top, which does not mean "where
   it would have been vertically" -- it means the static position, and
   the static position of an absolutely positioned child of a flex
   container is where it would sit as the sole flex item, under
   align-items. .bar__in centres its items, so the browser centred a
   183px panel on a 45px row: 60px of it hung above the top of the
   viewport, taking the first two links with it, and the next two lay
   across the logo. Reported as "half of menu is out of screen", and
   the measurement said 60 of 183.

   top is a percentage of the containing block, which is .bar -- the
   nearest positioned ancestor, sticky counting as positioned -- so
   100% is the bar's own height whatever the fonts do to it, and the
   rule beneath it clears the border. Nothing to keep in sync.

   Then a ceiling: a panel taller than the screen left of the bar has
   to scroll, or its last link is unreachable on a short phone in
   landscape. dvh, not vh, because a mobile URL bar makes vh a lie. */
.nav { display: none; }
.nav:not([hidden]) { display: block; position: absolute;
                     top: calc(100% + var(--rule)); left: 0; right: 0;
                     max-height: calc(100dvh - var(--bar-h));
                     overflow-y: auto; overscroll-behavior: contain;
                     background: var(--scrim); border-bottom: var(--rule) solid var(--line); }
.nav__in { display: flex; flex-direction: column;
           padding: calc(var(--u) * 2) var(--gut) calc(var(--u) * 5); }

/* ---- HERO -------------------------------------------------------- */
/* min-width:0 because a grid child's default min-width:auto refuses to
   shrink below its longest unbreakable word, which is how a headline
   pushes a two-column hero wider than the viewport. */
.hero__text { min-width: 0; }
.hero h1 span, .hero h2 span { display: block; color: var(--ink-2); }
.hero__foot { font-family: var(--ff-mono); font-size: var(--fs-sm);
              color: var(--ink-3); letter-spacing: 0.04em;
              margin-top: calc(var(--u) * 6); }

.actions { display: flex; flex-wrap: wrap; gap: calc(var(--u) * 3);
           margin-top: calc(var(--u) * 8); }
.btn {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: var(--hit); padding: 0 calc(var(--u) * 6);
    border: var(--rule) solid var(--line-2); color: var(--ink);
    text-decoration: none; font-family: var(--ff-mono);
    font-size: var(--fs-sm); letter-spacing: var(--tr-label);
    text-transform: uppercase;
}
.btn:hover { border-color: var(--ink); background: var(--panel-2); }
/* The primary action takes the app's own signal colour, so the one
   button that matters is the one thing on the page wearing it. */
.btn--solid { background: var(--signal); color: var(--ink-inv);
              border-color: var(--signal); }
.btn--solid:hover { filter: brightness(1.12); background: var(--signal); }

/* The seal. A hanko, at the end, against the closing statement. The
   kanji is aria-hidden and the same word is in English beside it. */
.seal {
    display: flex; align-items: center; justify-content: center;
    width: 46px; height: 46px; flex: 0 0 auto;
    background: var(--accent); color: var(--ground);
    font-family: var(--ff-ja); font-size: 24px; line-height: 1;
    margin: 0 0 calc(var(--u) * 6);
}

/* ---- THE DEVICE -------------------------------------------------- */
/* The app is 393px wide and must stay 393px wide. In V1 that left it
   alone in the middle of a 720px column. Here it is a column partner:
   true width kept, text takes what is left. */
.demo-fig { margin: calc(var(--u) * 8) 0 0; }
.device {
    width: 100%; max-width: 393px; margin: 0 auto;
    aspect-ratio: 393 / 800;
    border: var(--rule) solid var(--line-2);
    background: var(--panel-2); overflow: hidden;
}
.device iframe { width: 100%; height: 100%; border: 0; display: block; }
.demo-fig figcaption {
    font-family: var(--ff-mono); font-size: var(--fs-sm);
    color: var(--ink-3); text-align: center;
    margin: calc(var(--u) * 3) auto 0; max-width: 393px;
}
.demo-fig figcaption b { color: var(--ink-2); font-weight: 500; }

/* ---- SCORE TRIPTYCH ---------------------------------------------- */
.score { display: grid; gap: var(--rule); background: var(--line);
         border: var(--rule) solid var(--line);
         margin-top: calc(var(--u) * 8); max-width: none; }
.score > div { background: var(--panel); padding: calc(var(--u) * 6);
               border-top: 3px solid var(--line-2); }
.score b { display: block; font-family: var(--ff-mono);
           font-size: var(--fs-xl); line-height: 1;
           margin-bottom: calc(var(--u) * 3); }
.score em { display: block; font-style: normal; font-family: var(--ff-mono);
            font-size: var(--fs-sm); letter-spacing: var(--tr-label);
            text-transform: uppercase; margin-bottom: calc(var(--u) * 2); }
.score span { display: block; font-size: var(--fs-base);
              color: var(--ink-2); line-height: 1.5; }
.score .adv { border-top-color: var(--advance); }
.score .hold { border-top-color: var(--hold); }
.score .ret { border-top-color: var(--accent); }
.score .adv b { color: var(--advance); }
.score .hold b { color: var(--hold); }
.score .ret b { color: var(--accent); }

/* ---- BELT MARK --------------------------------------------------- */
/* Lifted from the app's core.css. Geometry is fixed in belt.js and the
   marks are drawn by belt.js at build time; every colour here arrives
   from the skin, exactly as it does in the app. Nothing is stroked on
   the rim, so the ink keeps its drawn weight at 26px and at 200px. */
.belt-mark { width: 100%; height: auto; display: block; }
.bm-band { fill: var(--bm-fill); }
.bm-ink  { fill: var(--bm-edge); }
.bm-lay { stroke: var(--panel); stroke-width: 1.2; stroke-linecap: butt; }
.bm-stripe { stroke: var(--accent-dan); stroke-width: 2.5; stroke-linecap: butt; }
.bm-year { stroke: var(--bm-year); stroke-width: 3.5; stroke-linecap: butt; }

.belt-hero { width: clamp(84px, 12vw, 132px); flex: 0 0 auto;
             margin-block: calc(var(--u) * -4); }
.belt-hero .bm-lay { stroke: var(--ground); }

/* The belt stands beside the claim it belongs to, rather than the claim
   standing alone and the belt living two pages away.

   The negative block margin above is not a fudge: the mark's viewBox is
   0 0 100 104 and the artwork occupies roughly y24 to y80, so about a fifth
   of the box is empty at each end. Left alone that empty fifth reads as a
   gap the layout did not ask for, and the heading floats away from its own
   paragraph. The margin takes back the whitespace the drawing carries. */
.ladder__head { display: flex; align-items: center; gap: calc(var(--u) * 5);
                margin-bottom: calc(var(--u) * 4); }
.ladder__head h2 { margin-bottom: 0; }

/* ---- LADDER ------------------------------------------------------ */
/* Generated by build.py from arizodo.ladder.js and arizodo.belt.js. It
   used to be nineteen rungs typed into the prose file, which is
   nineteen chances to drift from the thing they describe. */
.ladder { margin-top: calc(var(--u) * 8); }
/* The key and the rows must be the SAME grid, declared once, or the header
   drifts off the column it names. They were 1fr and minmax(0,1fr), on
   containers of different widths, and read as two unrelated tables. */
.ladder__key, .rung {
    display: grid;
    grid-template-columns: 30px minmax(0, 1fr) 5.5ch;
    gap: calc(var(--u) * 3);
    align-items: center;
}
.ladder__key {
    font-family: var(--ff-mono); font-size: var(--fs-micro);
    letter-spacing: var(--tr-micro); text-transform: uppercase;
    color: var(--ink-3); padding-bottom: calc(var(--u) * 2);
    border-bottom: var(--rule) solid var(--line);
}
.ladder__key span:last-child { text-align: right; }
/* The bar is not drawn below 48em, so neither is the cell that names it.
   Leaving it in wrapped "Days" onto its own row and put the label "Days
   to the next" above the column showing the total. */
.ladder__key .k-track { display: none; }

.rungs { list-style: none; margin: 0; padding: 0;
         display: grid; gap: var(--rule); background: var(--line);
         border-bottom: var(--rule) solid var(--line); }
/* Three tracks at base, four at 48em -- declared once, above, for the key
   and the rows together. The bar is display:none below 48em and a
   display:none child is not a grid item, so a fourth track at base left an
   empty 5.5ch column at the right edge. */
.rung { background: var(--ground); padding: calc(var(--u) * 2) 0;
        font-family: var(--ff-mono); font-size: var(--fs-sm);
        position: relative; }
.rung__belt { display: block; width: 30px; }
.rung__en { color: var(--ink); letter-spacing: 0.03em; }
.rung__ja { color: var(--signal); font-size: 0.95em; }
.rung__pt { color: var(--ink-2); font-variant-numeric: tabular-nums;
            text-align: right; }
.rung__track { display: none; }

/* Black belt is the number the whole ladder is derived from, so it is
   the one rung that is marked. Seal red, on the edge, once. */
.rung--black::before {
    content: ""; position: absolute; left: -8px; top: 0; bottom: 0;
    width: 3px; background: var(--accent);
}
.rung--black .rung__pt { color: var(--accent); }

/* The twentieth row is not a grade and must not pretend to be one. It
   is set apart the way the app sets years apart from dan: same red
   cloth, different marking, a rule above it. */
.rung--beyond { border-top: 2px solid var(--accent-dan); }
.rung--beyond .rung__en { color: var(--ink-2); font-style: italic; }
.rung--beyond .rung__pt { color: var(--ink-3); }

.ladder__note { font-family: var(--ff-mono); font-size: var(--fs-sm);
                color: var(--ink-3); line-height: 1.6;
                margin-top: calc(var(--u) * 4); max-width: none; }

/* The spine. Each grade sized by its share of the points to 10 Dan, so
   the shape of the ladder -- cheap early, punishing late -- is seen
   rather than described. Needs real width; hidden below 64em. */
.spine { display: none; }
.spine__seg { display: block; height: 100%; background: var(--bm-fill); }

/* ---- EDITORIAL PAIR ---------------------------------------------- */
/* Heading and prose side by side above the breakpoint. The composition
   V1 never had: two things beside each other. */
.pair { display: grid; gap: calc(var(--u) * 6); }
.pair__body p:last-child { margin-bottom: 0; }

.more { margin-top: calc(var(--u) * 6); }
.more a { font-family: var(--ff-mono); font-size: var(--fs-sm);
          letter-spacing: 0.05em; color: var(--ink-2);
          text-decoration: none; border-bottom: 2px solid var(--signal);
          padding-bottom: 3px; }
.more a:hover { color: var(--ink); }

/* ---- DOCUMENT COMPONENTS ----------------------------------------- */
ul.list { list-style: none; margin: calc(var(--u) * 6) 0 0; padding: 0;
          border-top: var(--rule) solid var(--line); }
ul.list li { padding: calc(var(--u) * 5) 0;
             border-bottom: var(--rule) solid var(--line); }
ul.list b { display: block; font-family: var(--ff-display); font-weight: 600;
            font-size: var(--fs-lg); letter-spacing: var(--tr-display);
            color: var(--ink); margin-bottom: calc(var(--u)); }
ul.list span { display: block; color: var(--ink-2); max-width: var(--doc); }

ol.steps { margin: calc(var(--u) * 5) 0 0; padding-left: calc(var(--u) * 7); }
ol.steps li { margin-bottom: calc(var(--u) * 3); max-width: var(--doc); }
ol.steps li::marker { font-family: var(--ff-mono); color: var(--accent); }

.note {
    background: var(--panel); border: var(--rule) solid var(--line);
    border-left: 3px solid var(--line-2);
    padding: calc(var(--u) * 5); font-size: var(--fs-base);
    margin-top: calc(var(--u) * 6); max-width: var(--doc);
}
.note b { font-family: var(--ff-display); font-weight: 600;
          letter-spacing: var(--tr-display); color: var(--ink); }
.note p:last-child { margin-bottom: 0; }

.anchor { border-left: 3px solid var(--line-2);
          padding: calc(var(--u) * 2) 0 calc(var(--u) * 2) calc(var(--u) * 5);
          margin: calc(var(--u) * 8) 0; }
.anchor--ceremony { border-left-color: var(--accent); }
.anchor b { font-family: var(--ff-display); font-weight: 600;
            letter-spacing: var(--tr-display); color: var(--ink); }
.anchor p:last-child { margin-bottom: 0; }

/* ---- THE RAIL ---------------------------------------------------- */
/* A contents list for the reading pages, built from their own headings.
   V1 gave /why/ two thousand words in one column with no way to see the
   shape of the page. Hidden below 64em, where the page is short enough
   to scroll and a second nav would only be in the way. */
.doc__rail { display: none; }
.doc__rail-in { display: flex; flex-direction: column;
                border-left: var(--rule) solid var(--line); }
.doc__rail a {
    font-family: var(--ff-mono); font-size: var(--fs-sm);
    line-height: 1.4; color: var(--ink-3); text-decoration: none;
    padding: calc(var(--u) * 2) 0 calc(var(--u) * 2) calc(var(--u) * 4);
    border-left: 2px solid transparent; margin-left: -1px;
}
.doc__rail a:hover { color: var(--ink); }
.doc__rail a[aria-current="true"] { color: var(--ink);
                                    border-left-color: var(--accent); }

/* ---- ONWARD ------------------------------------------------------ */
/* Two links, not a table of anything. It ran to the full column width like
 * the ladder does, which is the width reserved for instruments carrying the
 * app's own data -- so the last thing on every reading page was a pair of
 * navigation cards stretched wider than the prose they follow. Same rule as
 * the scoring figure: it belongs to the text above it, so it takes the text's
 * measure. See --fig at the top of this file. */
.onward { display: grid; gap: var(--rule); background: var(--line);
          border: var(--rule) solid var(--line); max-width: var(--fig);
          margin-top: clamp(48px, 7vw, 104px); margin-bottom: 0; }
.onward a { display: block; background: var(--panel);
            padding: calc(var(--u) * 6); text-decoration: none; }
.onward a:hover { background: var(--panel-2); }
.onward b { display: block; font-family: var(--ff-display); font-weight: 600;
            font-size: var(--fs-lg); letter-spacing: var(--tr-display);
            color: var(--ink); margin-bottom: calc(var(--u) * 1.5); }
.onward span { display: block; font-size: var(--fs-base); color: var(--ink-2); }

/* ---- CLOSE ------------------------------------------------------- */
.zone--close { text-align: center; }
.zone--close p { margin-inline: auto; }
.zone--close .label { justify-content: center; }
.zone--close .actions { justify-content: center; }
.zone--close .seal { margin-inline: auto; }

/* ---- FOOTER ------------------------------------------------------ */
.foot { border-top: var(--rule) solid var(--line);
        padding-block: clamp(40px, 5vw, 72px)
                       calc(clamp(40px, 5vw, 72px) + env(safe-area-inset-bottom, 0px)); }
.foot__in { max-width: var(--shell); margin-inline: auto;
            padding-inline: var(--gut); display: grid;
            gap: calc(var(--u) * 8); }
.foot p { font-size: var(--fs-base); color: var(--ink-2); max-width: none; }
.foot__lead { grid-column: 1 / -1; }
.foot__lead p:last-child { margin-bottom: 0; }

/* WRAPPING IS DECLARED, NOT LEFT TO CHANCE.
 *
 * The meta lines are lists of short facts separated by a middot -- the
 * rights notice, the build stamp, the hero's four promises, the install
 * page's identity strip. They used to put &nbsp; AROUND the separators,
 * which is backwards: it glued each bullet to its neighbours and left the
 * only legal break points inside the facts, so "no account" could split
 * across two lines while the bullet beside it could not.
 *
 * Now .nb holds a fact together and the separators are ordinary spaces, so
 * a narrow screen stacks whole facts. text-wrap: pretty is the net under
 * that for everything else, and balance keeps headings from dropping a
 * single word onto a line of its own. */
.nb { white-space: nowrap; }
.rights, .stamp, .hero__foot, .label, .sm, figcaption,
.dscale__note, .dscale__rng { text-wrap: pretty; }
h1, h2, h3, .lede, .onward b, .dscale__band em { text-wrap: balance; }
.foot .stamp { font-family: var(--ff-mono); font-size: var(--fs-sm);
               color: var(--ink-3); letter-spacing: 0.05em; }
.foot .rights { font-family: var(--ff-mono); font-size: var(--fs-sm);
                color: var(--ink-3); }
.foot__links { display: flex; flex-wrap: wrap; gap: calc(var(--u) * 5);
               font-family: var(--ff-mono); font-size: var(--fs-sm); }
.foot__links a { color: var(--ink-2); text-decoration: none;
                 border-bottom: var(--rule) solid var(--line);
                 padding-bottom: 2px; }
.foot__links a:hover { color: var(--ink); border-bottom-color: var(--signal); }

/* ---- MOTION ------------------------------------------------------ */
.rise { opacity: 0; transform: translateY(10px); }
.rise.in { opacity: 1; transform: none;
           transition: opacity .45s ease, transform .45s ease; }
@media (prefers-reduced-motion: reduce) {
    .rise, .rise.in { opacity: 1; transform: none; transition: none; }
    * { animation: none !important; }
}

/* ==================================================================
   BREAKPOINTS -- every one min-width, every one ADDING structure the
   phone layout does not have.

       48em / 768px    nav unfolds, score becomes a triptych, pairs split
       64em / 1024px   hero splits, ladder gains its spine and two columns
       90em / 1440px   shell reaches full width
   ================================================================== */

@media (min-width: 48em) {
    .burger { display: none; }
    /* Five links from 30 August, when About joined the bar. The row is
       allowed to shrink so the skin switch and the burger never have
       to; the gap is the tightest here, where the bar is narrowest,
       and opens up at 64em where there is room for it. */
    .nav { display: block; flex: 0 1 auto; min-width: 0; }
    /* Back to a row in the flow. The panel's ceiling and its scrollbar
       have to be unset with it -- max-height and overflow still apply
       to a static box, and a nav that is 30px tall carrying a 100dvh
       ceiling and overflow:auto is a scroll container waiting to
       clip a focus ring. */
    .nav:not([hidden]) { position: static; background: none; border-bottom: 0;
                         max-height: none; overflow: visible; }
    .nav__in { flex-direction: row; gap: calc(var(--u) * 4); padding: 0;
               flex-wrap: nowrap; }
    .mark__ja { display: inline; }
    .skins button { padding: 0 calc(var(--u) * 2.5); }
    .bar__in { gap: calc(var(--u) * 4); }

    .score { grid-template-columns: repeat(3, 1fr); }
    .skins button { font-size: var(--fs-sm); }
    /* The grid does not change here -- it is three columns everywhere.
       The room a wider screen gains goes to padding and type size. */
    .dscale__band { padding: calc(var(--u) * 5) calc(var(--u) * 4); }
    .dscale__band b { font-size: var(--fs-lg); }
    .dscale__band em,
    .dscale__rng,
    .dscale__note { font-size: var(--fs-sm); }

    .pair { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
            gap: calc(var(--u) * 12); align-items: start; }
    /* .pair--flip was the alternating direction for a second pair block on
       the landing page. HOME NAME was the only section using it and left the
       home page on 30 August when the blueprint's block order took over; the
       rule went with it rather than being kept alive by a made-up use. The
       section is still in _sections.txt, unplaced -- if it returns, so does
       this. */

    .onward { grid-template-columns: repeat(2, 1fr); }
    .foot__in { grid-template-columns: 1fr auto; align-items: end; }

    /* The days-to-next bar returns once there is width to carry it. One
       declaration for both grids, so they cannot drift apart. */
    .ladder__key, .rung {
        grid-template-columns: 30px minmax(0, 1fr) minmax(0, 1fr) 5.5ch;
    }
    .ladder__key .k-track { display: block; }
    .rung__track { display: block; height: 8px; background: var(--panel-2); }
    .rung__track i { display: block; height: 100%; background: var(--bm-fill); }
}

@media (min-width: 64em) {
    /* The bar CTA appears with the horizontal nav, not before it. */
    .bar__cta { display: inline-flex; }

    /* The nav row can breathe again once the bar is wide enough for it. */
    .nav__in { gap: calc(var(--u) * 6); }

    /* THE hero change: the app stands beside the argument for it, at
       its true width, instead of below it and alone. */
    /* THE hero change: the app stands beside the argument for it, at its
       true width, instead of below it and alone.

       The phone is 393x800. Centring a 480px text block against an 800px
       phone pushed the headline about 160px down the page, and with the
       band's own padding on top of that the first words of the site sat
       nearly 300px below the header, under a screen of nothing. So the
       text starts at the top, and the frame is allowed to be shorter than
       a real phone on a laptop screen -- the app scrolls inside it either
       way, and a hero that does not fit the viewport is not a hero. */
    .hero__in { display: grid; grid-template-columns: minmax(0, 1fr) 393px;
                gap: clamp(40px, 5vw, 96px); align-items: start; }
    .hero__in .demo-fig { margin: 0; }
    .hero__in .device { margin: 0; aspect-ratio: auto;
                        height: min(74vh, 800px); min-height: 520px; }
    .hero__in figcaption { margin-inline: 0; text-align: left; }

    .spine { display: flex; height: 10px; margin-top: calc(var(--u) * 8);
             border: var(--rule) solid var(--line); }

    /* ROW GAP ZERO, AND IT MATTERS.
       The rail below is placed `grid-row: 1 / 99` so that it spans the
       whole page and its sticky position has somewhere to travel. That
       forces the grid to have ninety-nine row tracks. Empty tracks are
       zero-high, but the GAPS BETWEEN THEM ARE NOT: a 5vw row gap became
       ninety-eight gaps of dead space -- about six thousand pixels of
       nothing after the last section on every reading page, which is
       exactly what it looked like.
       The vertical rhythm is section margin, not grid gap, so the row gap
       was never doing any work. Only the column gap is real. */
    main.doc { display: grid;
               grid-template-columns: minmax(0, 24ch) minmax(0, 1fr);
               column-gap: clamp(40px, 5vw, 96px); row-gap: 0;
               align-items: start; }
    /* Both explicit. The page header was landing in the right column by
       auto-placement rather than by instruction, which happens to be
       correct and would stop being correct the moment anything else was
       added to main. */
    main.doc > .doc__head { grid-column: 2; }
    main.doc > section { grid-column: 2; }
    /* The onward pair lives in the reading column, not beside it.
       It used to sit outside <main> entirely -- no shell, no gutter,
       no rail offset -- so it began at the viewport edge while every
       paragraph above it began 24ch further in. Inside the grid it
       takes the same left edge as the prose by construction rather
       than by a matching number written twice. */
    main.doc > .onward { grid-column: 2; }
    main.doc > .doc__rail { display: block; grid-column: 1; grid-row: 1 / 99;
                            position: sticky; top: calc(var(--u) * 22); }
}

@media (min-width: 90em) {
    :root { --shell: 1400px; }
    .score > div { padding: calc(var(--u) * 8); }
}

/* ---------------------------------------------------------------- the day
 *
 * The scoring diagram. Every number in it is read out of arizodo.engine.js
 * at build time -- the band widths are advanceAt and holdAbove themselves,
 * expressed as flex ratios, so the picture cannot disagree with the app.
 * Colour is the same three tokens the .score grid uses, for the same three
 * results, because a reader who has learnt them once should not have to
 * learn them twice.
 */
.dflow { margin: calc(var(--u) * 7) 0 0; max-width: var(--fig); }
.dflow__row { display: flex; flex-wrap: wrap; align-items: baseline;
    gap: calc(var(--u) * 2) calc(var(--u) * 4); padding: calc(var(--u) * 3) 0;
    border-bottom: var(--rule) solid var(--line); }
.dflow__k { font-family: var(--ff-mono); font-size: var(--fs-sm);
    letter-spacing: .1em; text-transform: uppercase; color: var(--ink-2);
    flex: 0 0 auto; min-width: 9em; }
.dflow__pillar { font-family: var(--ff-display); font-weight: 600; }
.dflow__eq { color: var(--ink-2); }

/* Three columns at every width, phone included. The three results are one
 * comparison, and stacking them turns it into three announcements -- while
 * the .score grid further down the same page already reads as three columns,
 * so a reader who has met that shape once should not meet it again as a
 * list. The cells are narrow on a phone; the type steps down to suit rather
 * than the grid breaking. */
.dscale { display: grid; grid-template-columns: repeat(3, 1fr);
    gap: var(--rule); background: var(--line);
    border: var(--rule) solid var(--line); margin-top: calc(var(--u) * 7); }
.dscale__band { display: block; background: var(--panel); min-width: 0;
    padding: calc(var(--u) * 3) calc(var(--u) * 2);
    overflow-wrap: break-word; }
.dscale__band b { display: block; font-family: var(--ff-mono);
    font-size: var(--fs-md); line-height: 1; }
.dscale__band em { display: block; font-style: normal;
    font-family: var(--ff-mono); font-size: var(--fs-micro);
    letter-spacing: .04em; text-transform: uppercase; color: var(--ink-2);
    margin-top: calc(var(--u) * 2); }
.dscale__rng { display: block; font-family: var(--ff-mono);
    font-size: var(--fs-micro); margin-top: calc(var(--u) * 3); }
.dscale__note { display: block; font-size: var(--fs-micro);
    color: var(--ink-2); margin-top: calc(var(--u) * 1); }
.dscale__band--adv { border-top: 3px solid var(--advance); }
.dscale__band--hold { border-top: 3px solid var(--hold); }
.dscale__band--ret { border-top: 3px solid var(--accent); }
.dscale__band--adv b { color: var(--advance); }
.dscale__band--hold b { color: var(--hold); }
.dscale__band--ret b { color: var(--accent); }
.dflow figcaption { margin-top: calc(var(--u) * 9); color: var(--ink-2);
    font-size: var(--fs-base); max-width: var(--doc); }
.dflow figcaption b { color: var(--ink); font-family: var(--ff-mono); }

/* The bar's single primary action. The reference blueprint gives the header
 * one CTA and caps the menu; this is that CTA. It hides below the phone
 * breakpoint, where the same action is the first thing in the hero and a
 * second copy would crowd the burger off the bar. */
.bar__cta { display: none; flex: 0 0 auto; }

/* Written by build.py. The runtime check and check.html read this. */
:root { --build: "5b344b69c0158acd"; }
