/* ============================================================
   MUNINN — "The Ledger"
   bizat.co/muninn · hand-written static HTML, no build step.
   Load after the two font links below. Everything is tokens +
   plain classes; nothing here needs a framework.

   <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
   <link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300;9..144,400;9..144,500&family=Source+Serif+4:opsz,wght@8..60,300;8..60,400;8..60,600&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
   ============================================================ */

:root {
  /* ---- ground & ink (light is default) ---- */
  --ground:      #faf8f4;  /* page */
  --raise:       #f3efe6;  /* masthead, stat strip, record card */
  --sink:        #ece7dd;  /* rare: inset wells */
  --ink:         #1c1a17;  /* headings, primary prose */
  --ink-2:       #4a453e;  /* body prose */
  --ink-3:       #736c63;  /* secondary prose — 4.88:1 on --ground, 4.51:1 on --raise */
  --ink-4:       #6f6960;  /* labels, metadata — 5.12:1 on --ground, 4.73:1 on --raise */
  --rule:        #e2dcd1;  /* hairline, inside a block */
  --rule-strong: #c9c0b1;  /* block edge */
  --rule-ink:    #1c1a17;  /* 2px section rule */
  --accent:      #f5a623;  /* Biz-Automation orange — marks, never text */
  --accent-ink:  #9c5a09;  /* orange for links & numerals — 5.10:1 on --ground, 4.72:1 on --raise */
  --accent-wash: #fdf2dd;  /* the "ours" column in a comparison */

  /* ---- type ---- */
  --display: Fraunces, "Iowan Old Style", Georgia, serif;
  --text:    "Source Serif 4", Georgia, serif;
  --mono:    "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;

  /* ---- type scale (1.22, prose-first) ---- */
  --t-micro: 10px;  /* rail labels, stat captions */
  --t-meta:  11px;  /* mono metadata */
  --t-mono:  13px;  /* code, buttons */
  --t-small: 15px;
  --t-body:  17px;  /* default prose */
  --t-lead:  18px;
  --t-intro: 21px;  /* deck under an h1 */
  --t-quote: 30px;
  --t-h2:    36px;
  --t-h1:    62px;  /* /muninn only; essays cap at 44px */

  --lh-tight: 1.06;
  --lh-head:  1.2;
  --lh-prose: 1.62;   /* long-form: 1.65 */
  --measure:  62ch;   /* never wider */

  /* ---- spacing (4px base) ---- */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px; --s10: 128px;

  /* ---- page frame ---- */
  --page:  1180px;
  --gutter: 48px;
  --rail:   168px;   /* left label column */
}

[data-theme="dark"] {
  --ground: #14130f; --raise: #1c1b16; --sink: #0f0e0b;
  --ink: #f2ede3; --ink-2: #cfc8ba; --ink-3: #9c958a; --ink-4: #958e82;
  --rule: #2e2c25; --rule-strong: #3d3a31; --rule-ink: #6b6559;
  --accent: #f5a623; --accent-ink: #f7b64d; --accent-wash: #241d10;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground: #14130f; --raise: #1c1b16; --sink: #0f0e0b;
    --ink: #f2ede3; --ink-2: #cfc8ba; --ink-3: #9c958a; --ink-4: #958e82;
    --rule: #2e2c25; --rule-strong: #3d3a31; --rule-ink: #6b6559;
    --accent-ink: #f7b64d; --accent-wash: #241d10;
  }
}

/* ---------- base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--ground); color: var(--ink);
  font-family: var(--text); font-size: var(--t-body); line-height: var(--lh-prose);
  font-synthesis-weight: none; text-rendering: optimizeLegibility;
}
h1, h2, h3 { font-family: var(--display); font-weight: 400; letter-spacing: -0.015em; margin: 0; }
h1 { font-size: var(--t-h1); line-height: var(--lh-tight); letter-spacing: -0.02em; text-wrap: balance; }
h2 { font-size: var(--t-h2); }
h3 { font-size: 21px; }
p  { margin: 0 0 var(--s5); max-width: var(--measure); text-wrap: pretty; color: var(--ink-2); }
a  { color: var(--accent-ink); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--ink); }
::selection { background: var(--accent); color: #14130f; }
:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

/* ---------- page frame ---------- */
.wrap { max-width: var(--page); margin: 0 auto; padding: 0 var(--gutter); }
/* Every band is a two-column grid: label rail, then measure. */
.band { display: grid; grid-template-columns: var(--rail) minmax(0, 1fr); gap: var(--s7); }
/* A grid or flex child defaults to min-width:auto, which means it refuses to
   shrink below its content. One <pre> wider than the column is therefore enough
   to push the whole page wider than the viewport — the page then scrolls
   sideways and the gutters look uneven, wider on one side depending on where the
   scroll sits. min-width:0 lets the column shrink and hands the scrolling back to
   the element that should own it (the pre, which already sets overflow-x:auto). */
.band > * { min-width: 0; }
.band > .rail { font-family: var(--mono); font-size: var(--t-meta);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-4); }
.prose { max-width: var(--measure); }
.prose p { font-size: var(--t-body); line-height: 1.65; }
.prose > * + h2 { margin-top: var(--s9); }
.prose > * + h3 { margin-top: var(--s6); }

/* ---------- section header ---------- */
.section { padding-top: var(--s9); }
.section-head { border-top: 2px solid var(--rule-ink); padding-top: var(--s3); }
.section-head .num { font-family: var(--mono); font-size: var(--t-meta);
  letter-spacing: 0.14em; color: var(--accent-ink); }
.section-head .deck { font-size: var(--t-lead); color: var(--ink-2); margin: var(--s2) 0 0; }
/* essay variant: no 2px rule, hairline only */
.section-head--quiet { border-top: 1px solid var(--rule); }

/* ---------- journey figure (.jfig) ----------
   A marginal figure, NOT a full-measure plate.

   NAMED .jfig AND NOT .plate ON PURPOSE. /muninn/features/encryption/ has
   carried its own page-local `.plate` since long before this, for the key
   schematic, and that rule declares no width. So a global `.plate { width:
   340px }` won the cascade there and shrank the diagram's frame to 340px around
   a 718px image, with `.plate figcaption { display: none }` pointing the other
   way and hiding this figure's caption. Two pages, one class name, both wrong.
   Keep this prefix unique to the journey figures.

   The first pass ran these at the .rec measure (780px), centred in the column,
   with a rule above and a caption below. At 4:3 that is ~580px of height each:
   the page stopped dead at every one and read like an encyclopedia plate.
   Nothing else on this site does that. The hero sets its record card BESIDE the
   argument in .hero-split; the homepage shelves images 150px tall; bg-raven and
   bg-field sit BEHIND the type at 0.4-0.72 opacity. Images here go beside,
   small, or behind — never across the reading column.

   So the figure is 340px and hugs the right edge of the measure, leaving the
   text ragged against it. Asymmetry rather than a centred block, and about a
   third of the vertical cost.

   It is deliberately NOT a .bg. Those washes are dark-on-transparent and the
   dark theme inverts them with `filter: invert(1) hue-rotate(180deg)`; a plate
   carries a baked warm-paper ground, so that same filter renders its charcoal
   raven WHITE. That was rendered and looked at on 2026-09-08 before this
   treatment was chosen, and it is why panel 06 is a figure here rather than a
   replacement for bg-field. Never give .jfig the .bg class. */
.jfig { width: 340px; max-width: 100%; margin: var(--s6) 0 var(--s7) auto; }
/* Right-aligning alone left a hole: the figure hugged the margin with nothing
   beside it. It has to be an actual second column of the section head, which is
   the same device the hero already uses for its record card. The 2px section
   rule moves up to the wrapper so it spans BOTH columns — left on .section-head
   it would have stopped short at the text column and read as a broken rule. */
.headrow { border-top: 2px solid var(--rule-ink); padding-bottom: var(--s6); }
.headrow > * { min-width: 0; }
.headrow > .section-head { border-top: 0; }
/* In the row the section rule already sits directly above the figure, so the
   figure's own hairline would be a second rule a few pixels under the first. */
.headrow .jfig { width: auto; margin: var(--s6) 0 0; }
.headrow .jfig .jfig-cap { border-top: 0; padding-top: 0; }

/* Two columns, and the caption crosses back into the left one.

   Putting the figure in the right column fixed the height but left the left
   column empty: a heading, two lines of deck, then 250px of nothing down to the
   bottom of the picture. So the caption is placed at the BOTTOM of the left
   column, right-aligned, so it sits against the picture's left edge and the
   column has weight at both ends. It is the treatment an art book uses for a
   margin note, and it costs no extra height — the caption lives in space the
   picture had already claimed.

   `display: contents` is what allows it: the figure dissolves so its label,
   image and caption become items of this grid and can be placed independently,
   while figure/figcaption stay associated for assistive tech. It is also why
   the figures no longer carry `reveal` — a contents box cannot be faded or
   translated. */
@media (min-width: 1080px) {
  .headrow { display: grid; gap: 0 var(--s7);
    grid-template-columns: minmax(0, 1fr) 340px; align-items: start; }
  .headrow .jfig { display: contents; }
  .headrow > .section-head { grid-column: 1; grid-row: 1; }
  .headrow .jfig .jfig-cap { grid-column: 2; grid-row: 1; align-self: end; }
  .headrow .jfig img { grid-column: 2; grid-row: 2; }
  .headrow .jfig figcaption { grid-column: 1; grid-row: 2;
    align-self: end; justify-self: end; text-align: right;
    max-width: 36ch; margin: 0; }
}
.jfig .jfig-cap { font-family: var(--mono); font-size: var(--t-meta);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-4);
  border-top: 1px solid var(--rule-strong); padding-top: var(--s2);
  margin-bottom: var(--s3); }
.jfig img { display: block; width: 100%; height: auto;
  border: 1px solid var(--rule); }
.jfig figcaption { margin-top: var(--s3); font-size: var(--t-small);
  line-height: 1.5; color: var(--ink-3); }
/* Two paragraphs, not one line. A single line left the left column still mostly
   empty between the deck and the picture's bottom edge; at two short paragraphs
   the caption reads as a margin note that belongs there rather than as a label
   that drifted down the page. */
.jfig figcaption p { margin: 0; }
.jfig figcaption p + p { margin-top: var(--s3); }
/* The figure brings its own paper with it, so on the dark ground it arrives as
   a lit rectangle. Take the edge off it; do not invert it. */
:root[data-theme="dark"] .jfig img { filter: brightness(0.86); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .jfig img { filter: brightness(0.86); }
}
/* Below the .band collapse there is no margin left to hug, so it takes the full
   column rather than sitting as a narrow slab on a narrow screen. */
@media (max-width: 900px) {
  .jfig { width: 100%; margin-left: 0; }
}

/* ---------- the film ----------
   A band the reader can choose, not a background they are given. The video
   carries `preload="none"`, so the only bytes this section costs an arriving
   visitor are the poster — which is /assets/og-muninn.jpg, already this page's
   og:image and so already fetched by anything that unfurled the link.

   The hairline and the 780px stop are .rec's, so the film, the record card and
   a journey figure all measure the same. No rounding and no shadow, for the
   same reason nothing else here has them. */
.film { padding-top: var(--s8); }
/* The film drops the label RAIL and takes the whole measure, so it plays at
   1084px instead of the 780px a .band column allows — the one place on this
   page where a picture is the argument rather than a note beside it. The rail
   label moves above it and becomes the same mono eyebrow a .jfig carries. */
.filmband > .rail { font-family: var(--mono); font-size: var(--t-meta);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-4);
  border-top: 1px solid var(--rule-strong); padding-top: var(--s2);
  margin-bottom: var(--s3); }
.reel { margin: 0; max-width: 100%; }
.reel video { display: block; width: 100%; height: auto;
  border: 1px solid var(--rule); background: var(--raise); }
.reel figcaption { margin-top: var(--s3); font-size: var(--t-small);
  line-height: 1.5; color: var(--ink-3); }

/* ---------- feature cluster of three ---------- */
.triad { display: grid; grid-template-columns: repeat(3, 1fr);
  border-top: 1px solid var(--rule-strong); max-width: 900px; }
.triad > * { padding: var(--s5) var(--s5) var(--s5) 0; border-right: 1px solid var(--rule); }
.triad > * + * { padding-left: var(--s5); }
.triad > *:last-child { border-right: 0; padding-right: 0; }
.triad .ord { font-family: var(--mono); font-size: var(--t-meta);
  letter-spacing: 0.1em; color: var(--accent-ink); margin-bottom: var(--s3); }
.triad p { font-size: 16px; line-height: 1.58; color: var(--ink-3); margin: var(--s2) 0 0; }
@media (max-width: 860px) {
  .triad { grid-template-columns: 1fr; }
  .triad > * { border-right: 0; border-bottom: 1px solid var(--rule); padding: var(--s5) 0; }
}

/* ---------- pull quote ---------- */
.pull { margin: var(--s8) 0; border-left: 2px solid var(--accent);
  padding-left: var(--s6); max-width: 760px; }
.pull p { font-family: var(--display); font-weight: 300; font-size: var(--t-quote);
  line-height: 1.36; letter-spacing: -0.01em; color: var(--ink); margin: 0; max-width: none; }
.pull cite { display: block; font-family: var(--mono); font-style: normal;
  font-size: var(--t-meta); letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-4); margin-top: var(--s4); }

/* ---------- comparison ---------- */
.compare { width: 100%; border-collapse: collapse; font-size: 16px; max-width: 900px; }
.compare th, .compare td { padding: var(--s3) var(--s4); border-top: 1px solid var(--rule);
  vertical-align: top; text-align: left; }
.compare tr:first-child th, .compare tr:first-child td { border-top: 1px solid var(--rule-strong); }
.compare tr:last-child th, .compare tr:last-child td { border-bottom: 1px solid var(--rule-strong); }
.compare .axis { font-family: var(--mono); font-size: var(--t-meta); letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-4); padding-left: 0; width: 34%; }
.compare .them { border-left: 1px solid var(--rule); color: var(--ink-3); }
.compare .ours { border-left: 1px solid var(--rule); color: var(--ink);
  background: var(--accent-wash); }

/* ---------- stat strip (live numbers) ---------- */
.stats { display: grid; grid-template-columns: repeat(4, 1fr);
  border: 1px solid var(--rule-strong); background: var(--raise); }
.stats > * { padding: var(--s5); border-right: 1px solid var(--rule); }
.stats > *:last-child { border-right: 0; }
.stats .n { font-family: var(--mono); font-size: 30px; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums; color: var(--ink); }
.stats .n--accent { color: var(--accent-ink); }
.stats .cap { font-family: var(--mono); font-size: var(--t-micro); letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-4); margin-top: var(--s1); }
/* live numbers must not reflow while updating */
.stats .n, .rec pre, .compare td { font-variant-numeric: tabular-nums; }
@media (max-width: 860px) { .stats { grid-template-columns: repeat(2, 1fr); } }

/* ---------- record sample / inline code ---------- */
.rec { border: 1px solid var(--rule-strong); background: var(--raise); max-width: min(780px, 100%); }
.rec header, .rec footer { display: flex; gap: var(--s4); padding: var(--s3) var(--s4);
  font-family: var(--mono); font-size: var(--t-meta); letter-spacing: 0.08em; color: var(--ink-3); }
.rec header { border-bottom: 1px solid var(--rule); }
.rec footer { border-top: 1px solid var(--rule); color: var(--ink-4); }
.rec .seal { margin-left: auto; color: var(--accent-ink); }
.rec pre { margin: 0; padding: var(--s4); font-family: var(--mono); font-size: var(--t-mono);
  line-height: 1.75; color: var(--ink-2); overflow-x: auto; }
code { font-family: var(--mono); font-size: 0.92em; color: var(--ink);
  background: var(--raise); border: 1px solid var(--rule); padding: 1px 5px; }

/* ---------- call to action ---------- */
.cta { display: flex; gap: var(--s3); align-items: center; flex-wrap: wrap; }
.btn { font-family: var(--mono); font-size: var(--t-mono); letter-spacing: 0.06em;
  text-decoration: none; padding: 13px 22px; border: 1px solid var(--ink);
  background: var(--ink); color: var(--ground); transition: background 100ms linear; }
.btn:hover { background: var(--accent-ink); border-color: var(--accent-ink); color: var(--ground); }
.btn--quiet { background: transparent; color: var(--ink-2); border-color: var(--rule-strong); }
.btn--quiet:hover { background: transparent; color: var(--ink); border-color: var(--ink-3); }
.command { display: flex; align-items: center; gap: var(--s3); max-width: 560px;
  border: 1px solid var(--rule-strong); background: var(--raise);
  padding: 14px var(--s4); font-family: var(--mono); font-size: 14px; }
.command .sigil { color: var(--accent-ink); }
.command .copy { margin-left: auto; font-size: var(--t-meta); letter-spacing: 0.1em; color: var(--ink-4); }

/* ---------- masthead / colophon ---------- */
.masthead, .colophon { background: var(--raise); }
.masthead { border-bottom: 1px solid var(--rule-strong); }
.colophon { border-top: 1px solid var(--rule-strong); }
.masthead .inner { display: flex; align-items: center; gap: var(--s6); height: 62px; }
.masthead .parent { font-family: var(--mono); font-size: 12px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-3); text-decoration: none;
  display: flex; align-items: center; gap: var(--s2); }
.masthead .parent::before { content: ""; width: 9px; height: 9px; background: var(--accent); }
.masthead .product { font-family: var(--display); font-size: 19px; }
.masthead nav { margin-left: auto; display: flex; gap: 22px;
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em; }
.masthead nav a { color: var(--ink-2); text-decoration: none; }
.masthead nav a:hover, .masthead nav a[aria-current] { color: var(--accent-ink); }

/* ---------- responsive: the rail folds above the measure ---------- */
@media (max-width: 900px) {
  :root { --gutter: 24px; --t-h1: 40px; --t-h2: 28px; --t-quote: 24px; }
  .band { grid-template-columns: 1fr; gap: var(--s4); }
  .band > .rail { margin-bottom: 0; }
}

/* ---------- masthead on small screens ----------
   The handoff specifies the masthead as one 62px row and stops there, which
   holds down to about 760px and then breaks: four nav links, the parent mark,
   the product name and the theme button do not fit, and the row silently wraps
   into the page. The old site had a hamburger; this restores one in the ledger's
   own idiom — a hairline glyph, no rounding, no shadow, colour transition only.
   The panel is plain markup, so it degrades to a visible list with no JS. */
.masthead .nav-btn { display: none; background: transparent; border: 1px solid var(--rule-strong);
  color: var(--ink-3); width: 34px; height: 30px; padding: 0; cursor: pointer;
  align-items: center; justify-content: center; transition: border-color 100ms linear, color 100ms linear; }
.masthead .nav-btn svg { width: 16px; height: 16px; display: block; fill: none;
  stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; }
.masthead .nav-btn:hover { color: var(--ink); border-color: var(--ink-3); }

/* Theme toggle. Same square, same hairline, same ink as the menu button, so the
   two controls read as a pair rather than a button and a word. The glyph shows
   the state you would MOVE TO, which is the convention people already have from
   every other toggle: a moon while you are in light, a sun while you are in dark. */
.masthead .theme-btn { background: transparent; border: 1px solid var(--rule-strong);
  color: var(--ink-3); width: 34px; height: 30px; padding: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: border-color 100ms linear, color 100ms linear; }
.masthead .theme-btn:hover { color: var(--ink); border-color: var(--ink-3); }
.masthead .theme-btn svg { width: 15px; height: 15px; display: block; fill: none;
  stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.masthead .theme-btn .sun { display: none; }
.masthead .theme-btn[data-mode="dark"] .sun { display: block; }
.masthead .theme-btn[data-mode="dark"] .moon { display: none; }

/* 1024, not 820. 820 is where the row genuinely stops fitting, but a phone with
   "Request Desktop Website" turned on reports about 980px and would then get the
   desktop bar on a 6-inch screen. Small laptops sit just above 1024 and keep the
   text links. */
@media (max-width: 1024px) {
  .masthead .inner { gap: var(--s4); height: 56px; }
  .masthead .product { display: none; }        /* the parent mark already says whose site this is */
  .masthead .nav-btn { display: flex; margin-left: auto; }
  .masthead nav {
    position: absolute; left: 0; right: 0; top: 100%;
    margin-left: 0; display: none; flex-direction: column; gap: 0;
    background: var(--raise); border-bottom: 1px solid var(--rule-strong);
    padding: var(--s2) var(--gutter) var(--s4); z-index: 20;
  }
  .masthead[data-open] nav { display: flex; }
  .masthead nav a { padding: var(--s3) 0; border-top: 1px solid var(--rule); font-size: 13px; }
  .masthead { position: relative; }
  /* the theme toggle stays in the bar, before the button */
  .masthead .inner > .btn { order: 2; }
  .masthead .nav-btn { order: 3; }
}

/* ---------- comparison tables on small screens ----------
   Three columns at 34/33/33 are unreadable under ~700px, and a horizontally
   scrolling argument is worse than a stacked one. Each row becomes a block: the
   axis label, then their answer, then ours, with the accent wash still marking
   which is which. */
@media (max-width: 700px) {
  .compare, .compare tbody, .compare tr, .compare td, .compare th { display: block; width: auto; }
  .compare thead { display: none; }
  .compare tr { border-top: 1px solid var(--rule-strong); padding: var(--s4) 0; }
  .compare tr:first-child { border-top: 1px solid var(--rule-strong); }
  .compare td, .compare th { border: 0 !important; padding: 0 0 var(--s2); }
  .compare .axis { width: auto; padding-bottom: var(--s2); }
  .compare .them, .compare .ours { padding: var(--s3) var(--s4); }
  .compare .them { border-left: 2px solid var(--rule) !important; }
  .compare .ours { border-left: 2px solid var(--accent) !important; background: var(--accent-wash); }
}

/* ---------- print (the essay pages) ---------- */
@media print {
  :root { --ground: #fff; --raise: #fff; --ink: #000; --ink-2: #222; }
  .masthead nav, .cta, .btn { display: none; }
  .band { grid-template-columns: 1fr; }
  /* The reveal rule lives under `prefers-reduced-motion: no-preference`, which
     matches print as well as screen — without this reset, every band the reader
     had not scrolled into view before printing comes out as blank paper. The
     !important is doing the work: the reveal block is later in the file. */
  .reveal, .js-reveal .reveal { opacity: 1 !important; transform: none !important; }
}

/* ---------- response: hover state and one reveal ----------
   The page had no state changes at all — nothing lifted under the pointer and
   nothing arrived on scroll, which is most of why a disciplined page still read
   as flat. This block is the WHOLE motion budget for the site: a 1px lift under
   the pointer, a rule that appears over a hovered card, and one 12px fade-up as
   a band enters the viewport. No parallax, no sticky, no scroll-jacking.

   The lift is a hard 2px shadow, not a blur: the system has no soft edges
   anywhere else and a blurred drop shadow would be the one thing on the page
   that isn't printed.

   The reveal hides nothing until JS says it can — `.js-reveal` is set on <html>
   by the observer script itself, so with JS off (or the script failing) every
   element is simply visible, which is also what a crawler sees. */
.btn { transition: background 100ms linear, transform 120ms ease-out, box-shadow 120ms ease-out; }
.triad > * { transition: box-shadow 140ms ease-out; }
.triad > *:hover { box-shadow: inset 0 2px 0 var(--rule-ink); }

@media (prefers-reduced-motion: no-preference) {
  .btn:hover { transform: translateY(-1px); box-shadow: 0 2px 0 var(--rule-strong); }
  .btn:active { transform: none; box-shadow: none; }

  .js-reveal .reveal { opacity: 0; transform: translateY(12px); }
  .js-reveal .reveal.is-in { opacity: 1; transform: none;
    transition-property: opacity, transform;
    transition-duration: 240ms;
    transition-timing-function: ease-out; }
}
