/* ============================================================styles
   Catppuccin Latte — terminal + markdown personal site
   ============================================================ */

/* opt into the default cross-document cross-fade on same-origin navigation */
@view-transition {
  navigation: auto;
}

/* Design tokens (:root, @font-face) live in tokens.css, shared with the OG cards. */

@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scrollbar-gutter: stable; }

body {
  margin: 0; background: var(--bg); color: var(--fg);
  font-family: var(--mono); font-size: 15px; line-height: 1.7;

  /* blueprint grid — framed in the margins, faded out behind the reading column.
     Lower layer so the coffee stains sit ON TOP of it (like ink on paper). */
  &::before {
    content: ""; position: fixed; inset: 0; z-index: -2; pointer-events: none;
    background-image:
      linear-gradient(color-mix(in srgb, var(--sel) 22%, var(--bg)) 1px, transparent 1px),
      linear-gradient(90deg, color-mix(in srgb, var(--sel) 22%, var(--bg)) 1px, transparent 1px);
    background-size: 26px 26px;
    -webkit-mask-image: linear-gradient(90deg,
      #000 0, #000 calc(50% - 420px), transparent calc(50% - 300px),
      transparent calc(50% + 300px), #000 calc(50% + 420px), #000 100%);
            mask-image: linear-gradient(90deg,
      #000 0, #000 calc(50% - 420px), transparent calc(50% - 300px),
      transparent calc(50% + 300px), #000 calc(50% + 420px), #000 100%);

    /* the grid is masked to clear the centre column, so it only crowds text once
       the viewport is too narrow for real margins — hide it there. */
    @media (max-width: 900px) { display: none; }
  }

}

/* ---------- coffee stains ----------
   Soft watercolour SVGs with a feathered bleed, scattered in the side margins
   and sitting ABOVE the grid, like ink on paper.

   Which ones, how big and where all come from site/template's PageLayout,
   seeded from the page's slug and emitted as custom properties in the <style>
   block Head writes — so every page gets its own arrangement, and it is the
   same one its Open Graph card carries. This file only says how to paint them.

   Real elements rather than background layers on body::after, which is what
   this was: a background layer accepts a position and a size, but there is no
   way to rotate or flip one, and the arrangement turns and mirrors its assets.
*/
.stains {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;

  /* Multiply, as the card does, so a ring darkens the grid it crosses instead
     of sitting on top of it as a flat wash. The old fixed layer could get away
     with plain alpha because its four positions were chosen to miss the grid's
     strong areas; a seeded one goes wherever the slug puts it. */
  mix-blend-mode: multiply;

  /* Second line of defence only, and it should never do anything: placement
     already guarantees a stain's rotated bounding box stays out of the column
     (TestPageStainsNeverEnterTheReadingColumn), and widening --maxw without
     widening the column the arrangement cleared is a test failure
     (TestColumnWidthMatchesTheToken), not something to be caught here. What it
     buys is that the failure mode is a trimmed stain rather than ink under the
     words. */
  --clear-column: linear-gradient(90deg,
    #000 0, #000 calc(50% - var(--maxw) / 2 - 70px), transparent calc(50% - var(--maxw) / 2),
    transparent calc(50% + var(--maxw) / 2), #000 calc(50% + var(--maxw) / 2 + 70px), #000 100%);
  -webkit-mask-image: var(--clear-column);
          mask-image: var(--clear-column);

  /* Below this there are no real margins left, so there is no column to clear
     and the mask has nothing useful to do — worse, its stops go negative and
     clamp non-decreasing, which makes the whole gradient transparent and paints
     nothing at all. That is what used to happen: not faint stains, no stains.

     So it comes off, and the stains pass behind the words the way ink on paper
     does. --x parks them at the window edge on its own (see stainX), so only
     their inner reach crosses the text.

     Down in weight as it goes, though, and by degrees rather than at once. In
     a margin a stain is beside the reading and can be as present as it likes;
     behind it, the same ink is something the eye gets past on every line, and a
     phone is where the reading is hardest already.

     A staircase because a ramp is not available: opacity wants a number and
     there is no way to turn 100vw into one. Steps of 0.14 down to 0.30 and a
     last of 0.10 to 0.20 — smaller in the number, the same fraction of what is
     left, which is what the eye actually reads. Each is small enough to pass
     for a fade; one step of the whole distance reads as the stains being
     switched off, which is what a single drop to 0.18 at 1080px did. */
  @media (max-width: 1080px) {
    -webkit-mask-image: none;
            mask-image: none;
    opacity: 0.86;
  }
  @media (max-width: 900px)  { opacity: 0.72; }
  @media (max-width: 760px)  { opacity: 0.58; }
  @media (max-width: 620px)  { opacity: 0.44; }
  @media (max-width: 500px)  { opacity: 0.30; }
  @media (max-width: 420px)  { opacity: 0.20; }
}

/* --x is the wider of two offsets, not a percentage: one measured from the
   reading column's edge, which is what is actually fixed on a page, and one from
   the window's. On a wide window the column's wins and the stains hold their
   distance from the text while the window comes and goes behind them; as it
   narrows the two cross and the stains park at the glass instead of sliding off
   it. stainX writes the whole thing, min or max as the side needs. --y is a
   percentage, since the layer is the viewport. */
.stain {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w);
  height: var(--h);
  opacity: var(--o);
  transform: translate(-50%, -50%) rotate(var(--r)) scale(var(--sx), var(--sy));
}
.stain img { width: 100%; height: 100%; display: block; }

/* nav and main share the reading column; nav now sits above main */
nav, main { max-width: var(--maxw); margin-left: auto; margin-right: auto; }
main { display: block; padding: 0 var(--pad) 32px; }
a { color: inherit; text-decoration: none; }

/* skip-to-content link: hidden until focused (first tab stop) */
.skip-link {
  position: fixed; top: 8px; left: 8px; z-index: 100;
  transform: translateY(-150%); transition: transform 0.15s ease-out;
  background: var(--panel); color: var(--fg);
  border: 1px solid var(--accent); border-radius: 5px;
  padding: 8px 14px; font-size: 13px;

  &:focus { transform: translateY(0); outline: 2px solid var(--accent); outline-offset: 2px; }
}
#content:focus { outline: none; }   /* don't ring the whole header when jumped to */

/* ---------- markdown headings (# / ## / ### automatic) ---------- */
h1, h2, h3 {
  color: var(--fg); letter-spacing: -0.4px; font-weight: 700;

  &::before { color: var(--punct); font-weight: 400; }
}
h1 {
  font-size: 27px; margin: 0; line-height: 1.25;

  &::before { content: "# "; }
  &.name::before { content: none; }  /* home identity name isn't a doc title */
}
h2 {
  font-size: 17px; margin: 34px 0 12px;

  &::before { content: "## "; }
}
h3 {
  font-size: 15.5px; margin: 28px 0 10px;

  &::before { content: "### "; }
}
p { margin: 0 0 18px; }
strong { color: var(--fg); font-weight: 700; }

/* ---------- prose links ---------- */
/* neutral + underline at rest, accent on hover — same rule as every other
   interactive element. (Underline marks them as links mid-sentence.) */
:where(article, section, header, footer, .sub) a[href]:not(.cmd):not(ul.log a):not(.links a) {
  color: var(--fg); border-bottom: 1px solid var(--sel);
  transition: color var(--t-release), border-color var(--t-hover);

  &:hover {
    color: var(--accent); border-color: var(--accent); transition: color var(--t-hover), border-color var(--t-hover);
  }
}
/* quick color change while the wave is actively on the link */
:where(article, section, header, footer, .sub) a[href].pulse { transition: color var(--t-enter); }

/* ============================================================ chip */
.chip {
  position: fixed; top: 20px; right: 20px; z-index: 10;
  display: inline-flex; align-items: center; gap: 8px;
  font: inherit; font-size: 12px; color: var(--dim);
  background: var(--panel); border: 1px solid var(--sel);
  border-radius: 5px; padding: 6px 12px 6px 9px; cursor: pointer;
  transition: border-color var(--t-hover), transform 0.05s;

  &:hover { border-color: var(--accent); }
  &:active { transform: translateY(1px); }
  &:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

  .sw {
    width: 11px; height: 11px; border-radius: 3px; background: var(--accent);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 40%, transparent);
    transition: background var(--t-hover);
  }
  b { color: var(--accent); font-weight: 700; }
  .val::after {
    content: ""; display: inline-block; width: 6px; height: 12px;
    margin-left: 1px; vertical-align: -2px; background: var(--accent); opacity: 0;
  }
  &.typing .val::after,
  &.finishing .val::after { opacity: 1; animation: chipblink 0.7s steps(1) infinite; }

  /* On a narrow window it belongs to the page rather than to the screen.

     Fixed is the right answer on a desktop, where it sits out in the margin
     with nothing to collide with and the page scrolls past behind it. On a
     phone there is no margin to sit in, so a fixed chip is a thing floating
     over the words for the whole length of the document, permanently spending
     screen that is already short. Absolute puts it at the top of the page
     instead: it is there when you arrive, and it scrolls away like everything
     else once you start reading.

     Body has no position of its own, so this resolves against the initial
     containing block — the document's top-left, which is exactly what is
     wanted. The nav starts below it rather than beside it; see nav. */
  @media (max-width: 520px) { position: absolute; top: 14px; right: 14px; }
}
@keyframes chipblink { 50% { opacity: 0; } }

/* ============================================================ nav */
nav {
  display: flex; align-items: baseline; gap: 20px; font-size: 13px;
  padding: 40px var(--pad) 0; margin-bottom: 30px;

  /* The accent chip is pinned to the top-right of the window. On a wide one the
     nav sits in a centred column and never reaches it; on a narrow one the nav
     runs the full width and would pass straight under it. So the nav starts
     below the chip instead of beside it. */
  @media (max-width: 520px) { padding-top: 60px; }

  b {
    color: var(--accent); font-weight: 400; transition: color var(--t-release);

    &.pulse { transition: color var(--t-enter); }
  }
  a {
    color: var(--dim); border-bottom: 1px solid transparent; padding-bottom: 2px; transition: color var(--t-release);

    &:hover { color: var(--accent); transition: color var(--t-hover); }
    &[aria-current="page"] { color: var(--fg); border-color: var(--fg); }
  }
}

/* ============================================================ // comment box */
.comment {
  display: inline-block; color: var(--dim); background: var(--crust);
  font-size: 13px; padding: 2px 8px; border-radius: 3px; margin-bottom: 12px;

  &::before { content: "// "; color: var(--punct); }
  .sep { color: var(--punct); margin: 0 6px; }
}

/* ============================================================ header */
header {
  margin-bottom: 52px;

  .whoami {
    font-size: 13px; color: var(--dim); margin-bottom: 12px;

    b {
      color: var(--accent); font-weight: 400; transition: color var(--t-release);

      &.pulse { transition: color var(--t-enter); }
    }
  }
  .caret { color: var(--accent); animation: blink 1.1s steps(1) infinite; }
  .sub { color: var(--dim); font-size: 14px; margin-top: 6px; }
}
@keyframes blink { 50% { opacity: 0; } }

/* ============================================================ sections */
section {
  margin-bottom: 44px; scroll-margin-top: 24px;

  > header {
    display: flex; align-items: baseline; justify-content: space-between; margin: 0 0 16px;

    h2 { margin: 0; }
  }
}

/* cd command link */
.cmd {
  font-size: 12px; color: var(--dim); white-space: nowrap;

  .path { color: var(--fg); transition: color var(--t-release); }
  .arr { color: var(--punct); margin-left: 2px; transition: color var(--t-release), margin-left 0.12s; }

  &:hover {
    .path { color: var(--accent); transition: color var(--t-hover); }
    .arr { color: var(--accent); margin-left: 6px; transition: color var(--t-hover), margin-left 0.12s; }
  }
}

/* ============================================================ log list */
ul.log {
  list-style: none; padding: 0; margin: 0;

  a {
    display: flex; gap: 16px; padding: 6px 0; align-items: baseline; color: var(--fg);

    &.pulse .title { transition: color var(--t-enter); }
    &:hover .title {
      color: var(--accent); transition: color var(--t-hover);

      &::before { color: var(--accent); margin-left: 4px; transition: color var(--t-hover), margin-left 0.12s; }
    }
  }
  time { color: var(--dim); font-size: 13px; width: 54px; flex: none; }
  .title {
    flex: 1; transition: color var(--t-release);

    &::before { content: "→ "; color: var(--accent); transition: color var(--t-release), margin-left 0.12s; }
  }
  .tag { color: var(--punct); font-size: 12px; }
}
/* year divider */
.year {
  font-size: 16px; font-weight: 700; color: var(--fg); margin: 34px 0 12px; letter-spacing: -0.3px;

  &::before { content: "## "; color: var(--punct); font-weight: 400; }
}

/* ============================================================ cards */
.card {
  border: 1px solid var(--sel); border-left: 2px solid var(--accent);
  border-radius: 4px; padding: 16px 18px; margin-bottom: 12px; background: var(--panel);
  transition: border-left-color var(--t-release), background-color var(--t-release);

  &:hover { border-left-color: var(--accent); background: color-mix(in srgb, var(--accent) 6%, var(--panel)); transition: border-left-color var(--t-hover), background-color var(--t-hover); }
  &.compact { padding: 12px 14px; border-radius: 4px; }

  .name { font-weight: 700; font-size: 16px; color: var(--fg); }
  .desc { color: var(--dim); font-size: 14px; }
  > header { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0; }
  .status {
    font-size: 12px; color: var(--dim); flex: none;

    .live {
      color: var(--accent); transition: color var(--t-release);

      &.pulse { transition: color var(--t-enter); }
    }
  }
  p.desc { margin: 8px 0 12px; line-height: 1.65; }
  > footer { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 0; border: 0; padding: 0; color: inherit; font-size: inherit; }
}

/* tag + tool lists (pipe separated) */
.tags, .toollist {
  color: var(--fg);

  .sep { color: var(--punct); margin: 0 4px; }
}
.tags { font-size: 12px; }
.toollist { font-size: 13px; margin: 0 0 8px; }

/* icon links */
.links {
  margin-left: auto; display: flex; gap: 16px; font-size: 13px;

  a {
    color: var(--dim); display: inline-flex; align-items: center; gap: 5px; border-bottom: 1px solid var(--sel); padding-bottom: 1px; transition: color var(--t-hover), border-color var(--t-hover);

    &:hover { color: var(--accent); border-color: var(--accent); }
  }
  svg { width: 14px; height: 14px; stroke: currentColor; }
}

/* ============================================================ article prose */
article {
  font-size: 15.5px; line-height: 1.8;

  h2 { font-size: 18px; margin: 40px 0 14px; }

  ul:not(.log) {
    padding-left: 0; list-style: none; margin: 0 0 20px;

    li {
      padding-left: 22px; position: relative; margin-bottom: 8px;

      &::before { content: "-"; color: var(--accent); position: absolute; left: 6px; transition: color var(--t-release); }
      &.pulse::before { transition: color var(--t-enter); }
    }
  }
  /* ordered lists: accent numbers to match the bullet dashes */
  ol {
    padding-left: 0; list-style: none; counter-reset: item; margin: 0 0 20px;

    li {
      padding-left: 28px; position: relative; margin-bottom: 8px; counter-increment: item;

      &::before { content: counter(item) "."; color: var(--accent); position: absolute; left: 4px;
        font-variant-numeric: tabular-nums; transition: color var(--t-release); }
      &.pulse::before { transition: color var(--t-enter); }
    }
  }
  /* tables: terminal-flavoured, header underlined, monospace columns */
  table {
    width: 100%; border-collapse: collapse; margin: 0 0 22px; font-size: 14px;

    th { text-align: left; font-weight: 700; color: var(--fg); padding: 6px 14px 6px 0;
      border-bottom: 1px solid var(--sel); }
    td { padding: 6px 14px 6px 0; color: var(--dim); border-bottom: 1px dashed var(--sel); vertical-align: top; }
    th:last-child, td:last-child { padding-right: 0; }
    tr:last-child td { border-bottom: none; }
  }
}
/* emphasis */
em, i { font-style: italic; }
code { font-family: var(--mono); font-size: 0.88em; background: var(--crust); color: var(--lit); padding: 1px 5px; border-radius: 3px; }
blockquote {
  margin: 0 0 20px; padding: 4px 0 4px 16px; border-left: 2px solid var(--accent); color: var(--dim); transition: border-left-color var(--t-release);

  &.pulse { border-left-color: var(--accent); transition: border-left-color var(--t-enter); }
  p { margin: 0; }
}
pre {
  background: var(--panel); border: 1px solid var(--sel); border-radius: 6px; padding: 16px 18px; overflow-x: auto; margin: 0 0 24px;

  code { background: none; color: var(--fg); padding: 0; font-size: 13.5px; line-height: 1.75; }
}
.tok-com { color: var(--dim); background: var(--crust); border-radius: 2px; padding: 0 2px; }
.tok-str, .tok-num { color: var(--lit); }
.tok-fn { color: var(--decl); }
.tok-var { color: var(--vdef); }
.tok-type { color: var(--type); }
.tok-punc { color: var(--punct); }

/* figures — works for both proper <figure> and bare markdown <img> */
figure {
  margin: 28px 0;

  /* wide figures break out past the column, but the caption re-aligns to the
     reading column: same max-width, centred, with the page padding. */
  &.wide {
    width: min(96vw, 920px); margin-left: 50%; transform: translateX(-50%);

    figcaption {
      max-width: var(--maxw); margin: 8px auto 0; padding: 0 var(--pad); box-sizing: border-box;
    }
  }
}
/* any image inside article prose is constrained to the column and framed,
   whether it's wrapped in <figure> or emitted bare by the markdown renderer */
figure img,
article > img,
article > p > img { display: block; width: 100%; height: auto; max-width: 100%;
  border: 1px solid var(--sel); border-radius: 6px; background: var(--panel); }
figcaption {
  font-size: 12px; color: var(--dim); margin-top: 8px; text-align: left;

  &::before { content: "// "; color: var(--punct); }
}

/* pager nav (post prev/next) */
footer {
  nav {
    padding: 0; display: flex; justify-content: space-between; font-size: 13px; gap: 20px;

    a {
      color: var(--dim);

      &:hover { color: var(--accent); }
    }

    &:has(:empty) {
      display: none;
    }
  }
}

/* ============================================================ about user-card + now */
.usercard {
  display: flex; align-items: stretch; gap: 16px;

  .idblock {
    min-width: 0; flex: 1;

    .comment { margin-bottom: 8px; }
  }
  .avatar {
    flex: none; align-self: stretch; width: 72px; border: 1px solid var(--sel); border-radius: 6px; background: var(--crust); overflow: hidden; filter: grayscale(1) contrast(0.95);

    svg, img { width: 100%; height: 100%; display: block; object-fit: cover; }
  }
}
.now {
  border-left: 2px solid var(--accent); padding: 2px 0 2px 14px; margin: 0 0 18px; color: var(--dim); transition: border-left-color var(--t-release);

  &.pulse { transition: border-left-color var(--t-enter); }
  .k { color: var(--fg); }
}
.elsewhere {
  display: flex; flex-direction: column; gap: 6px; font-size: 14px;

  a {
    color: var(--fg); border-bottom: 1px solid var(--sel); width: fit-content; transition: color var(--t-release), border-color var(--t-hover);

    &:hover { color: var(--accent); border-color: var(--accent); transition: color var(--t-hover), border-color var(--t-hover); }
    &.pulse { transition: color var(--t-enter); }
    .k { color: var(--dim); margin-right: 10px; }
  }
}

/* ============================================================ footer */
main > footer { color: var(--dim); font-size: 12px; border-top: 1px dashed var(--sel); padding-top: 16px; margin-top: 44px; }
footer .status .dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); margin-right: 6px; box-shadow: 0 0 6px color-mix(in srgb, var(--accent) 50%, transparent); animation: pulse 2.4s ease-in-out infinite; transition: transform var(--t-release), background-color var(--t-release); }
.status .dot.pulse { transition: transform var(--t-enter), background-color var(--t-enter); }
@keyframes pulse { 50% { opacity: 0.35; } }

/* ============================================================ ACCENT WAVE
   base transition (above, per element) = slow release.
   .pulse = held active state; .pulse also speeds up entrance.
   Deliberately flat: these cross-cutting selectors reach into every component,
   so they stay in one layer instead of being scattered through the blocks. */
header .whoami b.pulse, .caret.pulse,
ul.log a.pulse .title, ul.log a.pulse .title::before,
.cmd.pulse .path, .cmd.pulse .arr,
nav b.pulse,
a[href].pulse:not(.cmd):not(ul.log a):not(.links a),
.elsewhere a.pulse { color: var(--accent); }
a[href].pulse:not(.cmd):not(ul.log a):not(.links a) { border-color: var(--accent); }
.card.pulse { border-left-color: var(--accent); background: color-mix(in srgb, var(--accent) 6%, var(--panel)); }
.status .dot.pulse { transform: scale(1.35); }
/* newly-waved accent elements hold their accent while pulsed */
blockquote.pulse, .now.pulse { border-left-color: var(--accent); }
article ul:not(.log) li.pulse::before, .card .status .live.pulse { color: var(--accent); }
/* quick entrance while pulsing */
.pulse, .cmd.pulse .path, .cmd.pulse .arr,
ul.log a.pulse .title, ul.log a.pulse .title::before { transition-duration: var(--t-enter); }

/* ============================================================
   Boot sequence — cinematic: wash -> mounts -> login -> final beat.
   .booting is set on <html> before paint to avoid any flash.
   ============================================================ */
html.booting {
  cursor: default;

  body > nav,
  body > main,
  .chip { opacity: 0; }

  .bootlog { display: block; }

  @media (prefers-reduced-motion: reduce) {
    body > nav, body > main, .chip { opacity: 1; }
    .bootlog { display: none; }
  }
}

/* the log fills the container; content starts at the TOP and grows down.
   Once it overflows, JS scrolls it to keep the newest line in view (real
   terminal behaviour). overflow hidden = the scroll is driven, not user. */
.bootlog {
  display: none;
  position: fixed; inset: 0; z-index: 50;
  margin: 0 auto; max-width: 660px; padding: 40px 24px;
  font-size: 14px; line-height: 1.9; color: var(--dim);
  white-space: pre-wrap; overflow: hidden;

  .stream { }
  .ok { color: var(--accent); }
  .cursor {
    display: inline-block; width: 8px; height: 15px; vertical-align: -2px;
    background: var(--accent); animation: blink 1.1s steps(1) infinite;
  }
  /* skip button — deliberate dismissal, auto-focused during the boot */
  .skipbtn {
    position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
    font: inherit; font-size: 12px; color: var(--dim);
    background: var(--panel); border: 1px solid var(--sel);
    border-radius: 5px; padding: 8px 16px; cursor: pointer;
    transition: border-color 0.15s, color 0.15s;

    &:hover { color: var(--accent); border-color: var(--accent); }
    &:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  }

  /* final beat: the log clears and shows one prominent message, vertically
     centred (left-aligned horizontally to match the boot text — centering only
     the vertical axis avoids the horizontal shift the scrollbar used to cause).
     The html.booting copy is there to win during .booting so there's no position snap. */
  &.final,
  html.booting &.final {
    display: flex; flex-direction: column;
    justify-content: center; align-items: flex-start;
  }
  .finalmsg {
    font-size: 20px; color: var(--fg); letter-spacing: -0.3px;
    opacity: 0; transition: opacity 0.5s ease-out;

    .ok { color: var(--accent); }
  }
  &.final .finalmsg { opacity: 1; }
  /* on the way out, the whole log fades as the page fades in (crossfade) */
  &.fadeout { opacity: 0; transition: opacity 0.7s ease-out; }
}

/* the reveal: nav + main + chip fade in (crossfading with the log fadeout),
   then blocks cascade up. Armed only while .boot-reveal is present. */
html.boot-reveal {
  body > nav,
  body > main { opacity: 1; transition: opacity 0.7s ease-out; }
  .chip { opacity: 1; transition: opacity 0.5s ease-out 0.4s; }

  body > nav,
  main > * {
    opacity: 0; transform: translateY(8px);
    animation: bootrise 0.5s ease-out forwards;
  }
  body > nav { animation-delay: 0.10s; }
  main > *:nth-child(1) { animation-delay: 0.25s; }
  main > *:nth-child(2) { animation-delay: 0.40s; }
  main > *:nth-child(3) { animation-delay: 0.55s; }
  main > *:nth-child(4) { animation-delay: 0.70s; }
  main > *:nth-child(5) { animation-delay: 0.85s; }
  main > *:nth-child(n+6) { animation-delay: 1.00s; }

  @media (prefers-reduced-motion: reduce) {
    body > nav, main > * { opacity: 1; transform: none; animation: none; }
  }
}
@keyframes bootrise { to { opacity: 1; transform: none; } }
