/* ============================================================================
   jemo — the printed zone file

   A DNS root is a written record before it is a service. So the page is that
   record printed: warm bone paper, near-black ink, hairline rules that divide
   topics, and exactly one accent — a deep brick red — reserved for the
   sovereign apex and nothing else. A second green means "this resolved" and is
   used no other way.

   Depth is printed depth, not glass: a raised sheet is a lighter plane with a
   soft neutral shadow, never a glow and never a gradient on text. The previous
   identity was a dark terminal — near-black navy ground, luminous oxide accent,
   a gradient band across the top of the body. All of that is gone; what
   replaces it is the same content as a document.
   ============================================================================ */

:root {
  color-scheme: light;

  /* Ground: warm bone, not white. Each step up is a lifted sheet. */
  --bg:        #f4f1e9;
  --bg-2:      #ebe5d8;
  --surface:   #ffffff;
  --surface-2: #fbfaf6;
  --paper:     var(--bg);

  /* Ink: near-black, stepped down twice for hierarchy.
     `--faint` is #5f636b, the darkest value that clears 4.5:1 on every ground
     including `--bg-2` (4.74:1) while still reading a step below `--dim`
     (1.36:1). A lighter grey fails the floor on the two darker planes. */
  --ink:   #10131a;
  --dim:   #434956;
  --faint: #5f636b;

  /* Hairlines on paper: the primary structural device. Warm, low-contrast —
     a rule says "new topic", a box says "feature". */
  --rule:        #e2dccd;
  --rule-strong: #c2b9a4;

  /* Brand indigo — the page's voice: panels, primary actions, the illustration.
     This is the colour the previous two identities both lacked, and its absence is
     most of why they read as unfinished rather than restrained. */
  --brand:       #4f39e8;
  --brand-ink:   #3d29c9;
  --brand-soft:  #7c6cf0;
  --brand-wash:  rgba(79, 57, 232, 0.08);
  --brand-grad:  linear-gradient(135deg, #4f39e8 0%, #7c3aed 48%, #c026d3 100%);

  /* Vermilion — the sovereign apex, spent once. */
  /* #bd3a1a, not a brighter vermillion: `--seal` is not only a fill. `.sig`,
     `.dig-answer.bad`, `.apex-name`, `.logo-dot`, `.pill.err` and `.faq-q::before` all
     set *text* to this token, so it has to clear AA as foreground (4.92:1 on `--bg`,
     5.55:1 on `--surface`), not merely 3:1 as a graphic. The #d2451e it was set to
     measures 4.04:1 on the ground and fails. */
  --seal:      #bd3a1a;
  --seal-ink:  #b3391a;
  --seal-wash: rgba(189, 58, 26, 0.09);

  /* Verdigris — "an answer came back".
     #16795a, not a brighter green: this token is a *foreground* — it carries the
     resolved answer line, the featured card's foot, and the open-FAQ marker, all as
     text on the ground. The brighter #0f9d76 it was briefly set to scores 3.05:1 on
     `--bg` and 3.44:1 on `--surface`, so it fails AA at every size it is used at. This
     one is 4.75:1 on the ground and 5.36:1 on white, which is the floor. */
  --verdigris:      #16795a;
  --verdigris-wash: rgba(22, 121, 90, 0.10);

  /* Depth: two real elevation steps. The previous identity had one, at 6% opacity,
     which is too faint to read as a lifted sheet on a warm ground. */
  --shadow:    0 1px 2px rgba(16, 19, 26, 0.05), 0 2px 6px -1px rgba(16, 19, 26, 0.05);
  --shadow-md: 0 4px 12px -2px rgba(16, 19, 26, 0.08), 0 12px 28px -12px rgba(16, 19, 26, 0.12);
  --shadow-lg: 0 8px 24px -6px rgba(16, 19, 26, 0.10), 0 28px 60px -24px rgba(16, 19, 26, 0.22);

  --font-body: "Noto Sans Arabic", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --font-display: "Noto Kufi Arabic", "Noto Sans Arabic", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --font-mono: "Caskaydia Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --leading: 1.9;
  --step-0: 0.95rem;
  --step-1: 1.05rem;
  --step-2: 1.3rem;
  --step-3: 1.65rem;
  --step-4: 2.2rem;
  --step-5: 2.9rem;
  --gutter: clamp(1.25rem, 4vw, 2.5rem);
  --measure: 38rem;
  --radius: 14px;
  --radius-sm: 9px;
}

* { box-sizing: border-box; }

/* The one accent moment in text selection. Defaults to the browser blue, which on a dark
   ground is a colour the palette does not contain. */
::selection { background: var(--seal-wash); color: var(--ink); }

/* `hidden` is a promise the stylesheet can break. The UA rule is `[hidden] {
   display: none }` at the lowest specificity there is, so ANY author rule that sets
   `display` silently beats it — an inline `style="display:flex"` on `#dash-actions`, or
   the mobile `table { display: block }` block at the end of this file. Reading
   `el.hidden` says true while the pixels disagree, which no static check can see. */
[hidden] { display: none !important; }

/* -------------------------------------------------------------------- base */

/* The page has one full-bleed element (`.top`, sticky), and nothing should ever scroll
   sideways. `clip` rather than `hidden`: `hidden` on the root turns it into a scroll
   container, which silently breaks `position: sticky` on the header. `clip` contains
   overflow without creating a scroll container. */
html { overflow-x: clip; }

body {
  /* Flat paper. The previous identity washed the top of every page with a band of
     accent light, which is the single most recognisable thing about the dark look
     and has no place in a printed one — a printed page has a ground, not weather.
     `background-color` alone also removes the `background-image`/`-repeat`/`-size`
     trio that existed only to paint it. */
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step-1);
  line-height: var(--leading);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}

main {
  max-width: 72rem;
  margin: 0 auto;
  padding: 0 var(--gutter) 5rem;
}

/* Sections divide by a rule and a heading, never by a background block or a card. A rule
   says "a new topic"; a rounded box says "a feature". */
main > section { padding-block: 4rem; border-top: 1px solid var(--rule); }

/* The paper (and a few long pages) split a topic across TWO sibling sections — a
   `section-head` carrying the h2, then a section carrying the body. Both are direct
   children of `main`, so each one collected the full 4rem block-padding and the rule, and
   the heading floated 8rem above its own text with a hairline between them. A heading
   belongs to the text under it, so a `section-head` directly followed by a section gives
   its bottom padding to the top of that section and drops its own rule: one topic, one
   block, one rule — which is what the rest of the page already does. */
main > section:has(> .section-head) + main > section { padding-top: 0; border-top: 0; }
main > section.section-head { padding-bottom: 0.75rem; }
main > section.section-head:not(:first-of-type) { padding-top: 3rem; }
main > section:first-child { border-top: 0; padding-top: 2.5rem; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.4;
  margin: 0 0 0.5em;
  /* Arabic headings must not be tracked, and must not be set in all caps. */
}
h1 { font-size: var(--step-5); line-height: 1.25; }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); }
h4 { font-size: var(--step-1); }

p { margin: 0 0 1em; max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

a { color: var(--seal-ink); text-underline-offset: 0.22em; }
a:hover { color: var(--ink); }

/* One place decides what "emphasis" looks like. Not a second one for headings. */
.lead { font-size: var(--step-2); color: var(--dim); max-width: var(--measure); }
.muted { color: var(--dim); }
.faint { color: var(--faint); font-size: var(--step-0); }
/* `<small>` was falling back to the UA's 0.833em, which resolved to 12.7px against a
   0.95rem parent — below the floor every other piece of text on the site is held to.
   Small print is still small; it is just legible. */
small { font-size: 0.85rem; }
.mono { font-family: var(--font-mono); }
.center { text-align: center; }
.center .lead, .center p { margin-inline: auto; }
/* A section heading and its lead are one unit: the lead sits close enough under the
   heading to read as its sentence rather than as the start of the next block. */
.section-head { max-width: var(--measure); margin-inline: auto; }
.section-head h2 { margin-bottom: 0.35em; }
.section-head .lead { margin-bottom: 0; }

/* A literal — a hostname, a key, a command. Mono, LTR, and isolated so an RTL
   paragraph does not reorder the characters inside it. */
.host {
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  font-size: 0.92em;
  color: var(--seal-ink);
  background: var(--seal-wash);
  padding: 0.12em 0.4em;
  border-radius: 6px;
}
a.host { text-decoration: none; }

/* The sovereignty marker: the one place the accent is spent. */
.sov-apex-text, .sov .host { color: var(--seal-ink); }

/* ------------------------------------------------------------------- chrome */

.skip {
  position: absolute;
  inset-inline-start: -9999px;
}
.skip:focus {
  position: fixed;
  inset-inline-start: 1rem;
  top: 1rem;
  z-index: 20;
  background: var(--ink);
  color: var(--paper);
  padding: 0.7rem 1.1rem;
  border-radius: var(--radius-sm);
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}

/* The header is the one piece of chrome that must persist while a 3000px page scrolls,
   so it is sticky and translucent: the ground is a dark wash with a blur behind it, and a
   hairline separates it from whatever is passing underneath. Translucent, not opaque, so
   the content scrolling past reads as *under* the bar rather than being cut by it. */
.top {
  position: sticky;
  top: 0;
  z-index: 20;
  border-bottom: 1px solid var(--rule);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(14px) saturate(1.3);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
}
.top-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 0.9rem var(--gutter);
  display: flex;
  align-items: center;
  gap: 1.2rem;
  flex-wrap: wrap;
}

.logo {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  text-decoration: none;
  color: var(--ink);
  font-family: var(--font-display);
  font-weight: 600;
}
.logo-box {
  display: grid;
  place-items: center;
  width: 2.1rem; height: 2.1rem;
  border: 1px solid var(--seal);
  border-radius: var(--radius-sm);
  font-size: 1.05rem;
  color: var(--seal-ink);
  background: var(--seal-wash);
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.logo:hover .logo-box { background: color-mix(in srgb, var(--seal) 22%, transparent); }
.logo-dot { color: var(--seal); }

.top-nav .btn.primary {
  /* Only the fill differs from `.btn.small`, and the nav's button carries `.small`, so
     this rule states the skin and nothing else — a second padding and min-height here
     is what made the two drift apart. */
  background: transparent;
  color: var(--dim);
  border-color: var(--rule);
  font-weight: 400;
}
.top-nav .btn.primary:hover { background: transparent; color: var(--ink); border-color: var(--ink); }

.top-nav { margin-inline-start: auto; display: flex; align-items: center; gap: clamp(0.65rem, 1.5vw, 1.1rem); flex-wrap: wrap; }
/* The underline is drawn as a growing rule under the word rather than a text decoration,
   so the hit area and the affordance are the same shape, and `aria-current` marks the page
   you are on — the two states are visually identical, which is what makes "current" legible
   rather than merely asserted. */
.nav-link {
  position: relative;
  color: var(--dim);
  text-decoration: none;
  font-size: var(--step-0);
  padding-block: 0.2rem;
  transition: color 0.15s ease;
}
.nav-link::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: -2px;
  height: 2px;
  border-radius: 2px;
  background: var(--seal);
  transform: scaleX(0);
  transform-origin: inline-end;
  transition: transform 0.18s ease;
}
.nav-link:hover { color: var(--ink); }
.nav-link:hover::after, .nav-link[aria-current="page"]::after { transform: scaleX(1); }
.nav-link[aria-current="page"] { color: var(--ink); }

/* Status is a word and a dot, not a coloured pill with a glow. */
.status-badge {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font-size: var(--step-0); color: var(--dim);
  text-decoration: none;
  padding: 0.35rem 0.6rem;
  /* The badge states a fact ("الخدمة تعمل · السجل 5") and must not break mid-phrase:
     a wrapped line pushes the nav row taller and clips against the next item. */
  white-space: nowrap;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
}
.status-badge:hover { border-color: var(--rule-strong); color: var(--ink); }
.pulse-dot {
  width: 0.5rem; height: 0.5rem; border-radius: 50%;
  background: var(--verdigris);
  flex: none;
}
.status-badge.unknown .pulse-dot { background: var(--rule-strong); }

/* --------------------------------------------------------------------- icons */

.ic {
  width: 1em; height: 1em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -0.14em;
}

/* ---------------------------------------------------------------------- hero */

.hero {
  padding-block: 3.25rem 3.5rem;
  position: relative;
  /* Two bands, stacked: the sentence, then the record with its picture. The grid lives
     on `.hero-body`, where the two columns that actually have to agree on a height are. */
  display: grid;
  gap: clamp(1.75rem, 3.5vw, 3rem);
}
.hero-head { min-width: 0; }
.hero-body {
  display: grid;
  grid-template-columns: minmax(0, 1.06fr) minmax(0, 0.94fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  /* `stretch`, not `center`: the copy side runs down through the apex chooser and the
     panel is shorter than that. Stretched, the panel becomes the full-height colour
     block the composition needs instead of a card floating in the middle of one. */
  align-items: stretch;
}

/* Start-aligned, not centred. A centred hero reads as a form; a column with a
   picture beside it reads as a page. Both columns start on the same edge because the
   grid does, and the reading order in RTL puts the words first. */
.hero-copy { min-width: 0; }

/* The one orchestrated atmosphere: a broad wash of brand light falling from the top of
   the page, painted on a pseudo-element so it tints the ground without touching the body
   rule (which §13 keeps free of gradients) and without a radial "aurora".

   It spans the hero's own box rather than reaching `100vw`. `inset-inline: -50vw` is what
   a full-bleed wash wants, and it is exactly what pushes the document's scrollWidth past
   the viewport: `100vw` counts the scrollbar gutter that `clientWidth` excludes, so the
   page scrolls sideways by the width of the scrollbar for a decoration nobody can see the
   edge of. The two horizontal fades do the job the negative inset was for — the wash dies
   out before it reaches its own boundary, so there is no vertical edge to reveal. */
.hero::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: -14rem;
  height: 34rem;
  background: linear-gradient(180deg, rgba(79, 57, 232, 0.16) 0%, rgba(79, 57, 232, 0.05) 46%, transparent 100%);
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 14%, #000 86%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0%, #000 14%, #000 86%, transparent 100%);
  pointer-events: none;
  z-index: -1;
}

.hero h1 {
  /* `balance`, not a wider `ch` cap. At 20ch the rag put two words alone on the
     second line and left a preposition hanging at the end of the first, which is the
     one wrap an Arabic headline must not make. The cap only has to stop the heading
     running the full 72rem column; where the break lands is the browser's job. */
  text-wrap: balance;
  /* 32ch, not 17ch: at desktop this headline fits on one line, and the shorter measure
     held it to two and left the top-left quadrant of the hero empty. It still wraps to
     two lines below ~1180px, where one line would not fit anyway. */
  max-width: 32ch;
  margin-inline: 0;
  /* Two sizes up from `--step-5`. The headline was the same visual weight as the
     section headings below it, so nothing on the page was a headline at all. */
  font-size: clamp(2.1rem, 5.4vw, 3.9rem);
  line-height: 1.34;
}
/* Word, not letter, spacing. Arabic is cursive — `letter-spacing` severs the joins and
   a tracked Arabic headline looks broken, which is why the compliance gate bans it at
   every size. `word-spacing` has no such cost and is the only tracking knob Arabic can
   actually take. */
.hero h1 { word-spacing: 0.1em; }
.hero .lead { font-size: var(--step-2); color: var(--dim); max-width: 46ch; }

/* -------------------------------------------------------- hero illustration */

/* Decorative by construction: nothing here repeats content the copy does not already
   state, so it is `aria-hidden` in the markup and this column may be dropped entirely
   at any width without losing a sentence. */
.hero-art { min-width: 0; display: flex; }
.art-panel {
  position: relative;
  /* Fills the column. Height comes from the copy beside it, so the block is as tall as
     the headline and record combined rather than as tall as its three contents. */
  flex: 1;
  border-radius: 22px;
  padding: clamp(1.1rem, 2.4vw, 1.75rem);
  background: var(--brand-grad);
  box-shadow: var(--shadow-lg);
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto auto;
  /* The browser takes every spare pixel; the chain and the signature sit at a fixed
     size underneath it. `space-between` spread three small objects over a near-viewport
     panel instead, which reads as content that fell out of the middle of the page. */
  align-content: stretch;
  gap: clamp(0.75rem, 1.6vw, 1.25rem);
  overflow: hidden;
}
/* One highlight, not a glow: a soft white sheen that reads as the panel being lit
   from above. A coloured, zero-offset blur is what §13 forbids — this is a gradient on
   the panel's own surface, so it has an edge and a direction. */
.art-panel::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(150deg, rgba(255, 255, 255, 0.24) 0%, rgba(255, 255, 255, 0) 52%);
  pointer-events: none;
}

/* The browser the name opens in — the "any browser" half of the headline, drawn. */
.art-browser {
  position: relative;
  min-height: 0;
  background: var(--surface);
  border-radius: 13px;
  overflow: hidden;
  box-shadow: var(--shadow-md);
  /* Column flex so the page body can claim the space the panel leaves over. */
  display: flex;
  flex-direction: column;
}
.art-chrome {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.6rem 0.7rem;
  background: var(--bg-2);
  border-bottom: 1px solid var(--rule);
}
.art-chrome > span {
  width: 0.5rem; height: 0.5rem;
  border-radius: 50%;
  background: var(--rule-strong);
}
.art-chrome > span:nth-child(1) { background: var(--seal); }
.art-url {
  flex: 1;
  min-width: 0;
  margin-inline-start: 0.5rem;
  font-family: var(--font-mono);
  /* 0.85rem, not 0.8: 12.8px is under the 13.1px floor the sweep holds every leaf of
     text to, and the URL is the one line in this drawing that is meant to be read. */
  font-size: 0.85rem;
  color: var(--dim);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.2rem 0.7rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.art-url b { color: var(--ink); font-weight: 600; }
.art-page {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* Evenly spread, from a fixed set of blocks. The panel is as tall as the copy beside
     it, so the window has to absorb whatever that turns out to be: `space-between`
     lays six blocks over it, and `space-evenly`-style gaps are what a page with real
     breathing room looks like. One block at `flex:1` instead produced a 540px empty
     rectangle, which reads as a layout that failed rather than as a site. */
  justify-content: space-between;
  gap: 0.55rem;
  padding: 1rem 1rem 1.15rem;
}
/* The page *inside* the browser — a banner, a heading, a row of three, a call to action.
   Six blocks, all fixed size, so nothing stretches into a void. */
.art-page-hero {
  flex: none;
  height: clamp(5rem, 30%, 9rem);
  border-radius: 10px;
  background: linear-gradient(135deg, rgba(79, 57, 232, 0.16) 0%, rgba(22, 121, 90, 0.16) 100%);
  border: 1px solid var(--rule);
}
.art-page-cards {
  flex: none;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.5rem;
}
.art-page-cards i {
  height: clamp(3rem, 18%, 5rem);
  border-radius: 8px;
  background: var(--bg-2);
  border: 1px solid var(--rule);
}
.art-page-bar {
  height: 0.6rem;
  flex: none;
  border-radius: 999px;
  background: var(--bg-2);
}
.art-page-bar.b1 { width: 78%; background: var(--brand); opacity: 0.8; }
.art-page-bar.b2 { width: 56%; }
.art-page-bar.b3 { width: 44%; background: var(--verdigris); opacity: 0.45; }
/* The button on the page in the window: the thing the whole hero is asking the visitor
   to press, shown being pressed. */
.art-page-cta {
  width: clamp(4.5rem, 30%, 7rem);
  height: 1.7rem;
  flex: none;
  border-radius: 999px;
  background: var(--brand);
  opacity: 0.9;
}

/* The delegation chain: name → apex → root, the three literals the record below states
   in words. Stretched edge to edge so it reads as a path, not as three chips. */
.art-chain {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.art-node {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 600;
  padding: 0.42rem 0.7rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.94);
  color: var(--ink);
  white-space: nowrap;
}
/* The apex is the one node that carries meaning the other two do not: it is where the
   signed zone ends. Vermilion, and nowhere else on this panel. */
.art-node.node-apex { color: var(--seal-ink); background: #fff1ea; }
.art-node.node-key { color: var(--brand-ink); background: rgba(255, 255, 255, 0.8); }
.art-link {
  flex: 1;
  min-width: 0.75rem;
  height: 2px;
  background: rgba(255, 255, 255, 0.55);
  border-radius: 2px;
  position: relative;
}
.art-link::after {
  content: "";
  position: absolute;
  inset-inline-start: 50%;
  top: 50%;
  width: 0.34rem; height: 0.34rem;
  transform: translate(-50%, -50%) rotate(45deg);
  border-top: 2px solid rgba(255, 255, 255, 0.9);
  border-inline-start: 2px solid rgba(255, 255, 255, 0.9);
}

/* The signature line — the reason a browser believes any of it. */
.art-sig {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--font-mono);
  /* 0.85rem, not 0.78: 12.48px falls below the 13.1px floor. It is the only line in the
     drawing that states *why* a browser believes the answer, so it is read, not scenery. */
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.92);
  background: rgba(0, 0, 0, 0.22);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 10px;
  padding: 0.5rem 0.75rem;
  white-space: nowrap;
  overflow: hidden;
}
.art-sig-label { color: rgba(255, 255, 255, 0.7); }
.art-sig-val { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.art-sig-dot {
  width: 0.5rem; height: 0.5rem;
  border-radius: 50%;
  background: var(--verdigris);
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.18);
  flex: none;
}

/* ---------------------------------------------------------------- hostline */

/* The address, assembled. Not a logo lockup and not a badge: a name, its gateway suffix
   and its apex, written the way a browser writes them, in the mono face that carries every
   other hostname on this site (`--font-mono`, the same one `.preview-url` and `.res-details`
   use). It sits above the record because it answers "where does my name live" without
   requiring a click, and it is the one element here that is *interactive by adjacency* —
   it always displays exactly what the dig line below would return.

   Colour comes from the existing tokens and nothing new: the typed name is ink because it
   is the subject, the suffix is faint because it is the constant the operator supplies, and
   the connecting dot is the one place the sovereign apex accent is spent.

   `dir="ltr"` on the element itself, not on the children. In an RTL page a flex row of
   bare Latin spans lays out right-to-left and the address reads `.jemo.dev yourname` —
   backwards. The container direction fixes the order once, and the children keep their own
   `unicode-bidi: isolate` so a name with Arabic-alike characters does not leak direction. */
.hostline {
  display: inline-flex;
  align-items: baseline;
  justify-content: flex-start;
  flex-wrap: wrap;
  /* No `gap`. The name and the suffix are one hostname — a flex `gap` between them
     renders `yourname .gw.jemo.dev`, which is not a name anything can resolve. The dot
     carries the separation instead (`.hostline-dot` below). */
  gap: 0;
  margin: 0.35rem auto 0;
  padding: 0.30rem 0.95rem;
  max-width: 100%;
  font-family: var(--font-mono);
  font-size: var(--step-1);
  line-height: 1.5;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--surface);
  /* The softest neutral shadow on the page: this strip is a surface, not paper, and one
     1px hairline says "typed" while the 2px ink ring on `.dig` says "type here". */
  box-shadow: var(--shadow);
}
.hostline-dot {
  width: 0.42rem; height: 0.42rem;
  border-radius: 50%;
  margin-inline-end: 0.5rem;
  background: var(--verdigris);
  flex: none;
  align-self: center;
}
/* The typed name, and it must be able to be long: a 32-character guest name is allowed,
   and a strip that pushed it out of its box would break the layout before it broke the
   sentence. `min-width: 0` plus `anywhere` is what keeps a 32ch label from scrolling. */
.hostline-name {
  color: var(--ink);
  font-weight: 600;
  min-width: 0;
  overflow-wrap: anywhere;
  direction: ltr;
  unicode-bidi: isolate;
}
.hostline-suffix {
  color: var(--faint);
  direction: ltr;
  unicode-bidi: isolate;
  /* The suffix must never be clipped. A strip showing `name.jemo...` is an address that
     does not exist, and the ellipsis hides it behind three characters. `white-space:
     nowrap` plus `overflow: hidden` is what did that; the strip wraps the line instead,
     which costs two rows at the 32-character limit and tells the truth at every length. */
  overflow-wrap: anywhere;
}


/* The dig line — the one memorable element on the page, and the reason it looks like
   this product. A lookup written the way lookups are written: prompt, query, comment.
   Mono because it is a literal rather than because monospace is decorative, and the
   only place on the page where a display face and a mono face read as one unit. */
.dig {
  max-width: 40rem;
  margin: 1.75rem 0 0;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  background: linear-gradient(180deg, var(--surface-2) 0%, var(--surface) 100%);
  box-shadow: var(--shadow-lg);
  text-align: start;
  font-family: var(--font-mono);
  font-size: var(--step-0);
  line-height: 1.7;
  overflow: hidden;
  position: relative;
}
/* The record is crowned by a 2px bar of the accent — the single bold moment the design
   is allowed. A solid rule, not the old gradient: a gradient implies light falling on
   the page, which is the dark identity's premise. On a printed record the accent is a
   printed rule and it prints solid. */
.dig::before {
  content: "";
  position: absolute; inset: 0 0 auto; height: 2px;
  background: var(--seal);
}
/* The dig input drops its own ring (`.dig-field input:focus` below), so the record has
   to say it is focused the way `.search` does. Without this the only focusable thing in
   the dig line is invisible while focused. */
.dig:focus-within { border-color: var(--seal); box-shadow: var(--shadow-lg), 0 0 0 1px var(--seal-wash); }
.dig-line { display: flex; align-items: baseline; gap: 0.65rem; padding: 0.6rem 0.9rem; }
.dig-line + .dig-line { border-top: 1px solid var(--rule); }
/* The prompt sigil is the only non-letter on the line, so it is the only thing coloured. */
.sig { color: var(--seal); flex: none; }
.dig-text { font-family: var(--font-mono); color: var(--dim); min-width: 0; overflow-wrap: anywhere; }
.dig-query .dig-text { color: var(--ink); }
/* The name being looked up is the subject of the sentence, so it carries the accent. */
.dig-verb { color: var(--faint); flex: none; }
.dig-field { flex: 1; min-width: 0; display: flex; }
.dig-field input {
  width: 100%; min-width: 0;
  border: 0; background: transparent;
  font: inherit; font-family: var(--font-mono);
  direction: ltr; text-align: start;
  color: var(--seal);
  padding: 0;
  /* 2.75rem, which is 44px — the same target every other control on the site is held to
     by the coarse-pointer block. This rule was missing, so the field rendered at about
     26px while its own submit sat at 33px: the two halves of one row, sized by nothing
     in particular. It lives here rather than in a page because `index.html` and
     `register.html` both render this row and each had to fix it for itself. */
  min-height: 2.75rem;
}
.dig-field input:focus { outline: none; }
.dig-field input::placeholder { color: var(--faint); }
/* The go control is text, not a filled block: the record is a document, and a black slab
   in the middle of it would be the loudest thing here — louder than the answer. */
.dig-go {
  font: inherit; font-family: var(--font-body); font-weight: 600;
  color: var(--dim); background: transparent;
  border: 1px solid var(--rule); border-radius: var(--radius-sm);
  padding: 0.15rem 0.6rem; cursor: pointer; flex: none;
  /* Matches the field beside it. A submit half the height of the input it belongs to
     reads as a control that belongs to something else. */
  min-height: 2.75rem;
}
.dig-go:hover { color: var(--ink); border-color: var(--ink); }
/* The meta row is where the full answer goes — hostname and sovereign apex — and it is
   deliberately the quietest text on the page. */
/* 0.85rem, not 0.82rem: the answer's addresses are the payload of a lookup, and 13.1px
   was the size they were set at — the floor every other text on the site clears. */
.dig-meta { padding: 0 0.9rem 0.7rem 2.2rem; font-size: 0.85rem; }
/* The row exists only to carry an answer, and it is hidden only when there is no answer
   in it. `:empty` on the DIV never fires — the `<span id="dig-answer-body">` child is
   always there, so the strip drew 11px of padding under the record for a whole idle page
   and nobody saw it. The selector is on the span that actually holds the text. */
.dig-meta:has(#dig-answer-body:empty) { display: none; }
.dig-meta .host { font-size: 0.92em; }

/* Three states, and each carries a word as well as a colour — never colour alone.
   `ok` is verdigris because an answer came back; `bad` is the accent because the answer
   was no; `pending` is faint because nothing has been asked yet. */
.dig-answer.ok .dig-text { color: var(--verdigris); }
.dig-answer.bad .dig-text { color: var(--seal); }
/* A failed question is not a wrong answer, and it must not look like one — `bad` means
   "this name is taken, pick another", `error` means "we did not get to ask". */
.dig-answer.error .dig-text { color: var(--dim); font-style: italic; }
.dig-answer.pending .dig-text { color: var(--dim); }

/* The page's single orchestrated moment: the record answers once, on the first lookup,
   and never again. `prefers-reduced-motion` is honoured by the blanket rule at the end
   of this file, which shortens every animation to 0.01ms rather than removing the
   property — so the change is still announced, it just does not travel. */
@keyframes dig-answer-in {
  from { opacity: 0; transform: translateY(-2px); }
  to { opacity: 1; transform: none; }
}
.dig-answer.answered { animation: dig-answer-in 0.22s ease-out; }

/* The eyebrow pill was a tracked-out ALL-CAPS label above the heading — the single most
   templated element on the old page. The hierarchy now comes from size alone. */
.eyebrow-pill { display: none; }

/* The dig line: the memorable element, and the reason this page looks like this
   product. It is a record of a lookup, in the face that records are written in. */
.search-wrap { max-width: 46rem; margin-inline: auto; }

.search {
  display: flex;
  gap: 0.6rem;
  align-items: stretch;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: 0.35rem;
}
.search:focus-within { border-color: var(--ink); }

.input-wrap {
  display: flex; align-items: center; gap: 0.5rem;
  flex: 1; min-width: 0;
  padding-inline: 0.7rem;
}
.search input {
  flex: 1; min-width: 0;
  border: 0; background: transparent;
  font: inherit;
  font-family: var(--font-mono);
  direction: ltr;
  text-align: start;
  color: var(--ink);
  padding: 0.6rem 0;
}
.search input:focus { outline: none; }
.search input::placeholder { color: var(--faint); font-family: var(--font-body); }

.domain-addon {
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  color: var(--faint);
  font-size: var(--step-0);
  white-space: nowrap;
}

/* Buttons are a solid ink block or nothing. No gradient, no arrow glyph, no glow. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
  font: inherit;
  font-weight: 600;
  padding: 0.7rem 1.2rem;
  min-height: 2.75rem;
  border: 1px solid var(--ink);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink);
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.12s ease, color 0.12s ease;
}
.btn.primary { background: var(--ink); color: var(--paper); }
.btn.primary:hover { background: var(--seal-ink); }
.btn.ghost { border-color: var(--rule-strong); color: var(--dim); }
.btn.ghost:hover { border-color: var(--ink); color: var(--ink); }
.btn.danger { border-color: var(--seal); color: var(--seal); }
.btn.danger:hover { background: var(--seal); color: #fff; }
.btn.small { padding: 0.45rem 0.8rem; min-height: 2.25rem; font-size: var(--step-0); }
.btn[disabled] { opacity: 0.5; cursor: not-allowed; }
.btn-arrow { display: none; }        /* an arrow on every CTA is a template tell */
.row-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }

/* The dashboard's four figures. Each is a LINK, not a badge: a count the reader cannot
   act on is a decoration, and these answer "is anything wrong with my names" in one
   glance — which is the question a returning owner actually opens this page with.

   The warning tint is on a rule and a left border rather than on the whole tile, so the
   number itself stays the same ink as every other number. Colour alone would fail
   WCAG 1.4.1 anyway, and the counts are already distinguished by their labels. */
.overview-grid {
  display: grid; gap: 0.75rem; margin: 0;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
}
.ov-card {
  display: flex; flex-direction: column; align-items: center; gap: 0.2rem;
  padding: 1rem 0.75rem; text-decoration: none; color: inherit;
  border: 1px solid var(--rule); border-radius: 3px; background: var(--paper);
}
.ov-card:hover { border-color: var(--ink); }
.ov-n { font-size: var(--step-4); font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.ov-l { font-size: var(--step-0); color: var(--dim); }
/* A left rule plus a tint, never a tint alone. `border-inline-start` because the page is
   RTL and the leading edge is the right one. */
.ov-card.ov-warn { border-inline-start: 3px solid var(--seal); background: var(--bg-2, #f6f4f1); }
.ov-warn { color: var(--seal); font-weight: 600; }

/* The registry column: a word, not a colour. `.pill` carries the tint; this is why. */
.history-row td { padding-top: 0.4rem; }
.history-row h3 { font-size: var(--step-1); margin: 0 0 0.5rem; }
.history-row ul { list-style: none; padding: 0; margin: 0; }
.history-row li { padding: 0.3rem 0; border-bottom: 1px solid var(--rule); font-size: var(--step-0); }
.history-row li:last-child { border-bottom: 0; }

/* The ring is drawn 2px OUTSIDE the box, so it always lands on the page ground whatever
   the control's own fill is — `--seal` on `--paper` is 7.5:1 from either side. No
   `border-radius` here: it would re-square the corners of every focused element on the
   site, which is the one place a 3px and a 2px radius both being real is a bug. */
/* Drawn 2px OUTSIDE the box so it lands on whatever ground the control sits on, whichever
   that is. The offset is drawn in the page ground so the ring reads on a filled button as
   well as on the paper. */
:focus-visible { outline: 2px solid var(--seal-ink); outline-offset: 3px; border-radius: 4px; }

/* The answer. Two states, and the state is carried by a word as well as a colour —
   never by colour alone. */
/* The verdict is not repeated here — the record above already said it. What is left in
   this card is the thing the record cannot do: offer the next move. */
/* One element, two classes: `#quick-result` is itself the card (`quick-result res-card
   ok`), so the old `.quick-result .res-card` selector matched nothing and this box was
   the plain full-width `.res-card` the rest of the site uses. */
.quick-result { margin-top: 0.9rem; }
.quick-result.res-card {
  box-shadow: none;
  /* Hugging its own content rather than stretching: in the available state this card
     carries ONE next move — the register link — and a 40rem panel holding a single
     button at its start edge is an empty box that reads as a layout fault. The taken
     state fills it (a title, a sentence, a row of suggestions), so `fit-content` there
     costs nothing and keeps the two states in the same column. */
  width: fit-content;
  max-width: min(40rem, 100%);
  /* Centred under the record, which is also centred. */
  margin-inline: auto;
}
.res-card {
  border: 1px solid var(--rule);
  border-inline-start: 3px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: 1rem 1.1rem;
}
.res-card.ok { border-inline-start-color: var(--verdigris); }
.res-card.bad { border-inline-start-color: var(--seal); }
.res-header { display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; }
/* The title is the record's verdict line, so it aligns to the baseline with the icon —
   an `align-items: flex-start` row with an em-dash-less middle card would rope the text
   down. `.ic` is `1em` and optically sits at the cap height, which is the baseline. */
.res-title { font-weight: 600; font-family: var(--font-display); display: inline-flex; align-items: baseline; gap: 0.45rem; }
.res-title .ic { color: var(--seal); }
.res-details {
  font-family: var(--font-mono);
  font-size: var(--step-0);
  color: var(--dim);
  margin-top: 0.4rem;
}
.res-card.ok .res-details { color: var(--verdigris); }

.quick-tags { display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; margin-top: 0.9rem; }
.tag-chip, .chip, .sugg-btn {
  font: inherit;
  font-size: var(--step-0);
  font-family: var(--font-mono);
  direction: ltr;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--dim);
  cursor: pointer;
}
.tag-chip:hover, .chip:hover, .sugg-btn:hover { border-color: var(--ink); color: var(--ink); }
/* The chosen apex is marked in the markup (`aria-pressed`, or `.active` on the index
   page's chips) and nothing in this file drew it — the pressed chip was pixel-identical
   to the unpressed one, which is WCAG 4.1.2 showing a state no sighted user can read. */
.chip[aria-pressed="true"], .sugg-btn.active {
  border-color: var(--ink);
  color: var(--ink);
  font-weight: 600;
}
.chips-row { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.apex-picker { display: flex; align-items: center; gap: 0.6rem; margin-top: 0.7rem; flex-wrap: wrap; }

/* ------------------------------------------------------- facts, as a ruled row */

/* Three cards about ONE name. This comment previously argued the opposite — that a box
   was "the most recognisable tell of a generated page" and that the rule was the
   structure — while describing a ruled frame. The frame is gone and the boxes are back,
   so the reasoning is restated rather than left to contradict the rule below it.

   The frame's real argument survives in the part that was about arithmetic, not about
   taste: knowing where each row begins is `:nth-child(3n+1)` at three columns and
   `:nth-child(2n+1)` at two, every count then has to undo the previous count's resets,
   and a sweep across `/`, `/paper`, `/services` and `/extension` at five widths found
   exactly that leaking — columns missing their rule at 900px and at 700px. Cards need
   none of it: a gap is a gap at any count, so there is no row boundary to locate and a
   single `:nth-child` anywhere in this block. That is the part worth keeping, and it is
   why this is a grid gap rather than a frame with rules to place. */
.trio {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  /* Cards, with a gap the frame did not have. The ruled frame was correct for a document
     and wrong for the page it sits on: three hairline cells on one flat ground read as a
     table, and a table is not what three product claims are. A gap, a surface and one
     shadow is what makes them three objects instead of three paragraphs. */
  gap: clamp(1rem, 1.8vw, 1.5rem);
}
.trio > * {
  /* A column, so the source note can sit on its floor and the notes share a baseline.
     As a block they landed wherever the copy ended — 297px, 239px, 239px. */
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: clamp(1.4rem, 2.2vw, 2rem);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 16px;
  box-shadow: var(--shadow-md);
  /* Transform-only, so the lift moves no layout and re-fires nothing the
     `prefers-reduced-motion` block has not already clamped to a frame. */
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.trio > *:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
}
.card { min-width: 0; }
.card h2, .card h3 { font-size: var(--step-2); }
.card p { color: var(--dim); font-size: var(--step-0); }

/* The foot is a source note, not a marketing tag — hence mono and quiet. 0.85rem, not
   0.8rem: at 12.8px this was the smallest text on the page and it sits under the
   longest claim on the card, where it is read most carefully. */
.card-foot {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--dim);
  margin-top: 0.7rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--rule);
}
/* In the ruled row the note is the last thing in the card, so it goes to the floor of
   it and the three notes share one baseline. `.card-foot` on its own keeps the 0.7rem
   above, for the cards that are not columns. */
.trio > .card > .card-foot { margin-top: auto; }
/* The icon sits in a tinted plate — the one piece of colour rhythm the card grid gets.
   `display: grid` on a span that was already a block-level mark; no wrapper, no extra
   box in the markup. */
.card-icon {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 10px;
  background: var(--brand-wash);
  color: var(--brand-ink);
  font-size: 1.5rem;
  margin-bottom: 0.9rem;
}

/* `featured` is the path that works today. On a card that means a band across the top
   edge rather than a differently shaped card. It is not `--seal`: vermilion marks the
   sovereign apex and nothing else, and "this signup path works now" is a product fact
   about a route, not a zone boundary. */
.card.featured {
  position: relative;
  padding-top: 2rem;
  overflow: hidden;
}
.card.featured::before {
  content: "";
  /* Spanning the box, not the text block inside it. On a ruled column the accent was an
     annotation on a row and had to clear the frame's own rule at the column edge; on a
     card the edge is where a band belongs, and `overflow: hidden` keeps the band's
     square ends inside the 16px corner instead of hanging over them. */
  position: absolute; inset: 0 0 auto 0; height: 4px;
  background: var(--brand-grad);
}
.card.featured .card-icon { background: var(--brand); color: #fff; }
.card.featured .card-foot { color: var(--verdigris); border-top-color: var(--rule); }

/* Two columns. Now that the frame needs no arithmetic to place its rules, this is one
   declaration instead of a column-count rewrite — the same structure at a narrower
   measure, which is what it always claimed to be. */
@media (max-width: 1000px) {
  .trio { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --------------------------------------------------------------- comparison */

/* Two PATHS to the same name. A path is a vertical line with two ends, which is what
   the layout draws: one rule, two labelled ends. */
.compare-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2.5rem; }
.compare-card { display: flex; flex-direction: column; gap: 0.7rem; }
.compare-card + .compare-card { border-inline-start: 1px solid var(--rule); padding-inline-start: 2.5rem; }

.badge-sub {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--faint);
}
.badge-sub.sov { color: var(--seal); }

.preview-url {
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  text-align: start;
  font-size: var(--step-2);
  padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--rule);
}
.preview-url .highlight { color: var(--seal); }
.preview-url.sovereign-url .highlight { color: var(--seal); }

.feat-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.55rem; }
.feat-list li { display: flex; gap: 0.55rem; align-items: flex-start; color: var(--dim); font-size: var(--step-0); }
.feat-list .ic { color: var(--verdigris); margin-top: 0.35rem; }

/* ------------------------------------------------------- sovereign apexes */

/* The roots you can register under — one ruled row each, not a grid of tiles. Same
   discipline as `.trio`: a document row, divided by hairlines, with the accent spent
   only on the apex literal itself. A row is a whole-width hit target so a thumb finds it. */
.apex-list { list-style: none; margin: 1.5rem auto 0; padding: 0; max-width: var(--measure); border-top: 1px solid var(--rule); }
.apex-row { border-bottom: 1px solid var(--rule); }
.apex-pick { width: 100%; display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  background: none; border: 0; padding: 0.95rem 0.3rem; cursor: pointer; text-align: start; min-height: 2.75rem; }
.apex-pick:hover .apex-try { color: var(--ink); }
.apex-name { font-size: var(--step-2); color: var(--seal); }
.apex-try { flex: none; }

/* ---------------------------------------------------- verify command block */

/* The command a visitor copies to check their own name. A single bordered mono line in
   the face records are written in — not a syntax-highlighted "code card". The sigil is
   the only coloured glyph (same rule as the dig line); the copy control is text with a
   hairline, never a filled slab. LTR+isolated because a shell command is a literal. */
.cmd { max-width: var(--measure); margin: 1.5rem auto 0; display: flex; align-items: center; gap: 0.7rem;
  padding: 0.85rem 1rem; border: 1px solid var(--rule-strong); background: var(--surface);
  box-shadow: var(--shadow); overflow-x: auto; }
.cmd .sig { color: var(--seal); flex: none; }
.cmd-text { font-family: var(--font-mono); color: var(--ink); flex: 1; min-width: 0;
  white-space: nowrap; direction: ltr; unicode-bidi: isolate; }
.cmd-copy { flex: none; display: inline-flex; align-items: center; gap: 0.4rem; cursor: pointer;
  font-family: var(--font-body); font-size: var(--step-0); color: var(--dim);
  background: none; border: 1px solid var(--rule-strong); border-radius: var(--radius-sm);
  padding: 0.4rem 0.7rem; min-height: 2.25rem; transition: border-color 0.12s ease, color 0.12s ease; }
.cmd-copy:hover { border-color: var(--ink); color: var(--ink); }
.cmd-copy .ic { color: var(--seal); }
.verify-note { margin-top: 0.8rem; }

/* -------------------------------------------------- resolution flow (how it works) */

/* The delegation chain, drawn as what it is: a vertical path from a typed name down to a
   rendered page, each hop a signed step. Not a row of marketing tiles — a sequence, so it
   is numbered, connected by a single accent line, and read top to bottom. Each node is a
   lifted surface; the connector is the one continuous accent on the page's body. */
.flow { max-width: 46rem; margin: 2rem auto 0; display: flex; flex-direction: column; gap: 0; position: relative; }
.flow-step { position: relative; display: flex; gap: 1.1rem; padding-bottom: 2rem; }
.flow-step:last-child { padding-bottom: 0; }
/* The spine: a line down the marker column, drawn behind the dots. */
.flow-step::before {
  content: ""; position: absolute; inset-inline-start: 1.1rem; top: 2.3rem; bottom: -0.2rem;
  width: 2px; background: linear-gradient(180deg, var(--seal) 0%, var(--rule) 100%);
}
.flow-step:last-child::before { display: none; }
.flow-dot {
  flex: none; width: 2.3rem; height: 2.3rem; border-radius: 50%;
  display: grid; place-items: center; position: relative; z-index: 1;
  font-family: var(--font-mono); font-size: var(--step-0); font-weight: 600;
  color: var(--ink); background: var(--surface-2);
  border: 1px solid var(--rule-strong);
}
.flow-step:first-child .flow-dot,
.flow-step:last-child .flow-dot { border-color: var(--seal); color: var(--seal-ink); }
.flow-body {
  flex: 1; min-width: 0;
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: linear-gradient(180deg, var(--surface) 0%, var(--bg-2) 100%);
  box-shadow: var(--shadow); padding: 1.1rem 1.3rem;
}
.flow-body h3 { font-size: var(--step-2); margin-bottom: 0.3rem; }
.flow-body p { color: var(--dim); font-size: var(--step-0); margin: 0; }
.flow-body .host { margin-top: 0.5rem; display: inline-block; }
.flow-icon { color: var(--seal-ink); float: inline-end; font-size: 1.3rem; margin-inline-start: 0.6rem; }

/* ------------------------------------------------------------------- steps */

/* The content IS a sequence, so the numbers are information rather than decoration. */
.steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 2rem; }
.step { padding-inline-start: 1.2rem; border-inline-start: 2px solid var(--rule); position: relative; }
.step-num {
  font-family: var(--font-mono);
  font-size: var(--step-0);
  color: var(--seal);
  display: block;
  margin-bottom: 0.4rem;
}
.step h3 { font-size: var(--step-2); }
.step p { color: var(--dim); font-size: var(--step-0); }

/* --------------------------------------------------------------------- faq */

.faq-list { max-width: var(--measure); border-top: 1px solid var(--rule); }
.faq-item { border-bottom: 1px solid var(--rule); }
/* The row is the hit target, not the question text: an Arabic heading at 1.3rem is a
   ~40px band, and a question you have to aim at is a question fewer people open. The
   padded row is the control; the marker and text inside it inherit the hover. */
.faq-q { padding-inline-end: 0.75rem; transition: color 0.15s ease; }
.faq-item > .faq-q:hover { color: var(--seal-ink); }
/* `details` cannot animate height without a wrapper, so the answer fades and rises — the
   state change is announced, it just does not slide open like a scripted accordion. */
.faq-a { animation: faq-open 0.2s ease-out; }
@keyframes faq-open {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
.faq-q {
  cursor: pointer;
  padding: 1rem 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--step-2);
  list-style: none;
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
}
.faq-q::-webkit-details-marker { display: none; }
/* The marker is a real character, not a drawn triangle, so it inherits the text. */
.faq-q::before { content: "+"; font-family: var(--font-mono); color: var(--seal); }
.faq-item[open] > .faq-q::before { content: "\2212"; }
.faq-a { padding-bottom: 1.1rem; color: var(--dim); max-width: var(--measure); }

/* --------------------------------------------------------------------- cta */

/* The frame is back, and the reason the previous version removed one is the reason it is
   not repeated here. What failed before was a 1px *ink* border sitting one pixel below
   the section hairline — two rules saying the same thing in different voices, with the
   heavier one contributing nothing of its own. That is a double rule, not a box, so the
   hairline that used to underline this section goes too, and none of it comes back.

   What this is now is a box with something in it: a colour field, an edge that is
   genuinely its own rather than a heavier copy of the rule above it, and lift. A frame
   that carries colour is a different object from a frame that only repeats a hairline,
   and only the second one was ever the problem. */
/* The closing call, as a panel rather than another centred block on the same beige.
   It is the last thing on the page and the only place a full-strength colour field can
   sit without competing with anything — so it takes the gradient the hero only washes. */
.cta-banner {
  padding: clamp(2.5rem, 5vw, 4rem) clamp(1.25rem, 4vw, 3rem);
  border-radius: 24px;
  background: var(--brand-grad);
  color: #fff;
  box-shadow: var(--shadow-lg);
  position: relative;
  overflow: hidden;
}
/* One sheen, same construction as the hero panel: a gradient on the surface itself, with
   an edge and a direction, which is not what §13's ban on coloured radiance describes. */
.cta-banner::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(150deg, rgba(255, 255, 255, 0.22) 0%, rgba(255, 255, 255, 0) 55%);
  pointer-events: none;
}
.cta-banner h2 { margin-bottom: 0.4rem; color: #fff; position: relative; }
.cta-banner .lead { margin-bottom: 1.6rem; color: rgba(255, 255, 255, 0.9); position: relative; }
/* Inverted: the page's solid ink button would sit muddy on indigo. White ground with the
   brand ink on it keeps the same weight and clears contrast comfortably. */
.cta-banner .btn.primary {
  background: #fff;
  color: var(--brand-ink);
  border-color: #fff;
  position: relative;
}

/* ------------------------------------------------------------ back to top */

/* On a page this long the scroll position is state the reader cannot see, and the header
   is sticky so the way back is not at the top of the viewport. This is a fixed control,
   not a footer link, precisely so it stays reachable. It fades in only once there is
   somewhere to go back to — a control that is visible over a 400px page is decoration. */
.to-top {
  position: fixed;
  inset-block-end: 1.25rem;
  inset-inline-start: 1.25rem;
  z-index: 30;
  display: grid;
  place-items: center;
  width: 2.75rem; height: 2.75rem;
  border: 1px solid var(--rule-strong);
  border-radius: 50%;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--dim);
  cursor: pointer;
  box-shadow: var(--shadow);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.18s ease, visibility 0.18s ease, color 0.15s ease, border-color 0.15s ease;
}
.to-top .ic { transform: rotate(-90deg); }   /* the arrow glyph points up in an RTL page */
.to-top.show { opacity: 1; visibility: visible; }
.to-top:hover { color: var(--ink); border-color: var(--ink); }

/* ------------------------------------------------------------------- forms */

.form-card, .live-card, .login-card {
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: 1.75rem;
  max-width: 34rem;
  margin-inline: auto;
}
.login-card { max-width: 28rem; }

label { display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: var(--step-0); }
input[type="text"], input[type="email"], input[type="url"], input[type="password"], select, textarea {
  width: 100%;
  font: inherit;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
}
input:focus, select:focus, textarea:focus { border-color: var(--ink); }

.radio { display: flex; gap: 0.6rem; align-items: flex-start; font-weight: 400; }
.radio input { margin-top: 0.45rem; }

input[type="file"] {
  font: inherit;
  font-size: var(--step-0);
  color: var(--dim);
}
input[type="file"]::file-selector-button {
  font: inherit;
  font-weight: 600;
  margin-inline-end: 0.7rem;
  padding: 0.35rem 0.8rem;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
input[type="file"]::file-selector-button:hover { border-color: var(--ink); }

.drop {
  border: 1px dashed var(--rule-strong);
  border-radius: var(--radius-sm);
  padding: 1.5rem;
  text-align: center;
  color: var(--dim);
  cursor: pointer;
}
.drop.hot, .drop:hover { border-color: var(--seal); color: var(--ink); }

.filelist { list-style: none; margin: 0.8rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.filelist li {
  display: flex; align-items: center; justify-content: space-between; gap: 0.6rem;
  font-family: var(--font-mono); font-size: var(--step-0);
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--rule); border-radius: var(--radius-sm);
}
.rm {
  font: inherit; cursor: pointer;
  border: 1px solid transparent; background: transparent;
  color: var(--faint); padding: 0.2rem 0.4rem; border-radius: var(--radius-sm);
}
.rm:hover { color: var(--seal); border-color: var(--rule); }

/* --------------------------------------------------------- states and notices */

.notice {
  border: 1px solid var(--rule);
  border-inline-start: 3px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  padding: 1rem 1.15rem;
  margin-block: 1rem;
  background: var(--surface);
  box-shadow: var(--shadow);
}
/* A state notice is also a colour, so it carries a faint ground of that colour — enough
   to read at a glance in a column of them, not enough to shout. */
.notice.ok { background: linear-gradient(90deg, var(--verdigris-wash) 0%, var(--surface) 60%); }
.notice.err, .notice.bad { background: linear-gradient(90deg, var(--seal-wash) 0%, var(--surface) 60%); }
.notice.ok { border-inline-start-color: var(--verdigris); }
.notice.err, .notice.bad { border-inline-start-color: var(--seal); }
.notice.info { border-inline-start-color: var(--dim); }
.notice a { font-weight: 600; }

.pill, .count {
  display: inline-block;
  font-family: var(--font-mono);
  /* 0.85rem, not 0.78rem: 12.48px was below the 13.1px floor this sweep holds every
     other piece of text to, on a pill that carries a state word. */
  font-size: 0.85rem;
  padding: 0.22rem 0.55rem;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  color: var(--dim);
  background: var(--surface);
  transition: border-color 0.15s ease, color 0.15s ease;
}
/* Its own rule with literal colours, because this is the one badge that is text on a
   filled ground rather than a border on white — the only pair on the site whose
   contrast is not a comparison between two tokens. #2f5648 on #e6eeea is 5.9:1. */
.card-badge {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  padding: 0.2rem 0.5rem;
  border: 1px solid #3d6b5c;
  border-radius: var(--radius-sm);
  background: #e6eeea;
  color: #2f5648;
}
/* Depth, and only depth: a shadow here is neutral — it darkens, it does not glow, and
   it never carries a colour. The raised surfaces are the ones that read as a sheet laid
   on the paper (the search field, the answer card, the forms), and the value is small
   enough that the page still reads as ruled rather than tiled. */
.search, .res-card, .form-card, .live-card, .login-card, .note, .filelist li {
  box-shadow: var(--shadow);
}
/* Hover lifts a sheet by one step and no more — a shadow that travels on hover is the
   cheapest way to make a page feel like a template.

   `box-shadow` is APPENDED here, not the whole `transition` shorthand. A `transition`
   shorthand RESETS every property not named, so declaring it in a rule that comes after
   `.btn`'s own `transition: background-color, color` (:555) silently deleted those two —
   the button's fill snapped instantly while only the shadow eased, which is the opposite
   of what both rules asked for. Same for `.tag-chip`/`.chip`/`.sugg-btn` (:648) and for
   `.status-badge`, whose hover changes only `border-color`/`color` and therefore had no
   transition at all. Longhand, so nothing here can un-declare a rule further down. */
.btn, .tag-chip, .chip, .sugg-btn, .status-badge, .toc a {
  transition-property: box-shadow, border-color, color, background-color;
  transition-duration: 0.12s;
  transition-timing-function: ease;
}
.btn:hover, .tag-chip:hover, .chip:hover, .sugg-btn:hover { box-shadow: 0 2px 10px rgba(0,0,0,0.4); }
.pill.active { border-color: var(--verdigris); color: var(--verdigris); background: var(--verdigris-wash); }
.pill.suspended, .pill.err { border-color: var(--seal); color: var(--seal); background: var(--seal-wash); }

/* Custody is not a status a row can be "good" at — the operator holds these keys today,
   so the cell says so rather than colouring it like a success. */
.custody-badge { font-size: var(--step-0); color: var(--faint); }

.note {
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  padding: 1.25rem 1.4rem;
  margin-top: 1.5rem;
  background: var(--surface);
  color: var(--dim);
  font-size: var(--step-0);
}
.note strong { color: var(--ink); }

/* 0.85rem across this family. At 0.8rem these were 12.8px — and on paper they are harder
   to read than the near-black version was, because the ground is brighter and the
   contrast ratio is already spent carrying the hierarchy. */
.paper-meta, .src, .alt, .col-h {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--faint);
}
.refs { font-size: var(--step-0); color: var(--dim); max-width: var(--measure); }

/* The paper's abstract. It is a paragraph of prose, so it gets the reading measure like any
   other — an inline `max-width: none` on it once let it run the full 72rem column, about 130
   characters per line, which is roughly double where Arabic prose stays readable. Slightly
   larger than body copy because an abstract is read once, carefully. */
.paper-abstract {
  font-size: var(--step-2);
  line-height: 1.95;
  color: var(--ink);
  max-width: 46rem;
  margin-inline: auto;
}

/* -------------------------------------------------------------------- tables */

table { width: 100%; border-collapse: collapse; font-size: var(--step-0); }
th, td { text-align: start; padding: 0.6rem 0.5rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
th { font-family: var(--font-display); font-weight: 600; color: var(--ink); }
/* The header row is sticky on a long table: the column names stay with their data
   instead of scrolling away and leaving the reader to remember what column 4 was. */
thead th { position: sticky; top: 3.6rem; background: var(--bg); z-index: 1; }
tbody tr { transition: background-color 0.12s ease; }
tbody tr:hover { background: color-mix(in srgb, var(--surface) 60%, transparent); }
tbody td::before { content: none; }

/* Bars in the paper's charts are data, so they are thin and labelled — not decoration. */
.bar-row { display: grid; grid-template-columns: minmax(6rem, 10rem) 1fr auto; gap: 0.7rem; align-items: center; }
.bar-label { font-family: var(--font-mono); font-size: 0.85rem; color: var(--dim); }
/* Bars are a grid: label, meter, value. The meter's track is the one element whose
   height must not come from the row, so it is a block with a fixed height and the fill
   resolves against it. (The paper overrides both with its own figure-scale styling.) */
.bar-track { display: block; height: 0.5rem; background: var(--rule); border-radius: 2px; overflow: hidden; }
.bar-fill { height: 100%; background: var(--verdigris); border-radius: 2px; }
.bar-val { font-family: var(--font-mono); font-size: 0.85rem; color: var(--faint); }
.chart { margin-block: 1.25rem; }
.toc ol { list-style: none; display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0; padding: 0; }
.toc a {
  display: inline-block; padding: 0.5rem 0.7rem;
  border: 1px solid var(--rule); border-radius: var(--radius-sm);
  color: var(--dim); text-decoration: none; font-size: var(--step-0);
}
.toc a:hover, .toc a:focus-visible { border-color: var(--ink); color: var(--ink); }

.se { overflow-wrap: anywhere; }
.zero { color: var(--rule-strong); }
.starter { color: var(--faint); font-family: var(--font-mono); font-size: 0.85rem; }
.nm { font-family: var(--font-mono); direction: ltr; unicode-bidi: isolate; display: inline-block; }
.case { font-family: var(--font-mono); }
.icon-row { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.token-box, .token-row {
  display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
  padding: 0.8rem 1rem;
  border: 1px solid var(--rule); border-radius: var(--radius-sm); background: var(--surface);
  font-family: var(--font-mono); font-size: var(--step-0);
}

/* ------------------------------------------------------------------- footer */

/* The footer is the last thing on the page, so it carries the one gradient — the same bar
   that crowns the dig record, closing the loop with the same mark. It is the only other
   place that gradient appears, and it is the only place it should. */
.main-footer {
  position: relative;
  border-top: 0;
  margin-top: 4rem;
}
.main-footer::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: var(--seal);
}
.footer-top { position: relative; }
.footer-top {
  max-width: 72rem; margin: 0 auto;
  padding: 2.5rem var(--gutter);
  display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: 2.5rem;
}
.footer-brand p { color: var(--dim); font-size: var(--step-0); max-width: 28rem; margin-top: 0.9rem; }
/* The column label in the markup is a `<span class="col-h">`, not an `h4` — so this rule
   used to match nothing, and every label sat on the same line as the first link under
   it. One selector for the label that is actually there, and it is a block, because a
   label heads a column. */
.footer-col h4, .footer-col .col-h {
  display: block;
  font-family: var(--font-display); font-size: var(--step-0); font-weight: 600;
  color: var(--ink);
  margin-bottom: 0.75rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--rule);
}
/* The three groups are nested inside `.footer-links`, so `.footer-top`'s third track
   held nothing on any page and all three groups stacked in one 292px column. The links
   block now spans the last two tracks and lays its groups across them. */
.footer-links { grid-column: 2 / -1; display: flex; gap: 2.5rem; }
.footer-links a {
  display: block;
  color: var(--dim);
  text-decoration: none;
  font-size: var(--step-0);
  padding-block: 0.22rem;
  transition: color 0.15s ease;
}
.footer-links a:hover { color: var(--seal-ink); }
.footer-bottom {
  max-width: 72rem; margin: 0 auto;
  padding: 1.2rem var(--gutter) 2.5rem;
  border-top: 1px solid var(--rule);
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  color: var(--faint); font-size: var(--step-0);
}

/* --------------------------------------------------------------- responsive */

@media (max-width: 940px) {
  /* Below this the two columns each drop under ~300px and the record card starts
     wrapping its `dig +short` prompt. Stacked instead: the record, then the picture,
     in the order the DOM already puts them — no reordering to rescue. */
  .hero-body { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
  .hero h1 { max-width: 22ch; }
  .art-panel { max-width: 34rem; }
}

@media (max-width: 760px) {
  /* Arabic glyphs are wider than Latin ones, so the display size has to come down
     further on a phone than a Latin scale would suggest. */
  h1 { font-size: var(--step-3); }
  .dig { font-size: 0.86rem; }
  .dig-go { padding: 0.15rem 0.45rem; }

  /* The header on a phone was 247px of an 844px screen: three wrapped rows of the nav,
     because five destinations, a status fact and a button do not fit on one 350px line.
     The rows did not cost 44px each — they cost 44px each PLUS the hero's top edge, so
     a third of the first screen went to chrome before the headline. One scrollable
     strip instead: the CTA is ordered first so it is the thing in view rather than the
     thing a swipe finds, and the destinations scroll off to the left, which is the
     ordinary phone-nav pattern. Nothing is removed — only its row count changes. */
  .top-inner { flex-wrap: nowrap; align-items: center; gap: 0.85rem; padding-block: 0.65rem; }
  .logo { flex: none; }
  .top-nav {
    flex-wrap: nowrap;
    min-width: 0;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    /* A strip that scrolls has to say so. The fade is on the trailing edge only, so the
       inline start — where the CTA sits — stays fully opaque and legible. */
    mask-image: linear-gradient(to left, #000 82%, transparent 100%);
  }
  .top-nav::-webkit-scrollbar { display: none; }
  .top-nav > * { flex: none; }
  /* RTL: `order` is the block direction, so -1 puts the button at the inline START —
     the side the strip starts scrolled from, and the side the eye lands on. */
  .top-nav .btn { order: -1; }

  .compare-grid { grid-template-columns: 1fr; gap: 2rem; }
  .compare-card + .compare-card { border-inline-start: 0; padding-inline-start: 0; border-top: 1px solid var(--rule); padding-top: 2rem; }

  /* One column. Cards need no frame arithmetic at any count: there is no row boundary to
     locate, so stacking is the same grid with a single track, and the gap that separated
     them sideways now separates them vertically. The padding stays — it is each card's
     own interior, not indentation borrowed from a frame that is no longer there. */
  .trio { grid-template-columns: 1fr; }

  /* One track on a phone, so the links block stops spanning two that are not there and
     goes back to being a stack of groups. */
  .footer-top { grid-template-columns: 1fr; gap: 1.75rem; }
  .footer-links { grid-column: auto; flex-direction: column; gap: 0.4rem; }

  /* Tables become stacked blocks. `td::before` carries the column name from
     `data-label`, so the header row is not repeated per row on a phone. */
  table, thead, tbody, tr, th, td { display: block; }
  thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  tbody tr { border-bottom: 1px solid var(--rule); padding-block: 0.75rem; }
  td { border-bottom: 0; padding: 0.3rem 0; display: flex; gap: 0.6rem; }
  td::before {
    content: attr(data-label);
    font-family: var(--font-display); font-weight: 600;
    color: var(--faint); font-size: 0.85rem;
    flex: none; min-width: 6.5rem;
  }
  td[data-label=""], td[colspan] { display: block; }
  td[data-label=""]::before, td[colspan]::before { content: none; }
  td .btn, td .row-actions { width: 100%; }
  td .row-actions .btn { flex: 1 1 0; }

  .search { flex-direction: column; }
  .search .btn { width: 100%; }

  /* The command block cannot fit a sigil, a ~30ch literal and a labelled button on one
     390px row. `white-space: nowrap` on `.cmd-text` means the command must stay intact —
     a shell command broken mid-token is not a command — but a `min-width: 0` flex item
     that is nowrap PAINTS OVER its `flex: none` sibling rather than scrolling, which is
     what put "انسخ الأمر" on top of "yourname.jemo.dev". So: the literal gets its own
     scroll container, and the button drops to its own full-width row underneath. */
  .cmd { flex-wrap: wrap; overflow: visible; }
  .cmd-text { flex: 1 1 100%; overflow-x: auto; }
  .cmd-copy { flex: 1 1 100%; justify-content: center; min-height: 2.75rem; }
}

/* A 44px hit area for every control a thumb has to find: WCAG 2.5.5 and both platform
   HIGs ask for it, and the audit measured 29px nav links and 21px apex chips at 390px.
   Padding and min-height do it — the type keeps its size, because an Arabic glyph
   enlarged to fill a box is a control that has started shouting.
   Two ways to be a phone, one block: a narrow viewport, or any coarse pointer (a tablet
   at full width is the same thumb). `.btn` is already 2.75rem and stays out of it. */
@media (max-width: 760px), (pointer: coarse) {
  .nav-link, .status-badge, .dig-go, .toc a, .logo, .footer-links a,
  .tag-chip, .chip, .sugg-btn, .rm, .btn.small {
    min-height: 2.75rem;
  }
  /* `::file-selector-button` takes padding reliably and min-height not at all. */
  input[type="file"]::file-selector-button { padding: 0.55rem 0.8rem; }
  /* The dig input is a bare field inside a record line, so it has no padding of its own;
     on a thumb it is a 23px strip. `.search input` is already 0.6rem and needs nothing. */
  .dig-field input { padding-block: 0.7rem; }
  /* The radio itself is a 13px native control; the label that wraps it is the hit area. */
  .radio { min-height: 2.75rem; }
}

/* --------------------------------------------------------------- the quiet bits */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    /* Durations collapse to a frame so the STATE CHANGE is still applied and still
       announced; delays collapse to zero, because a clamp on duration alone leaves the
       hero's stagger intact — a reader who asked for stillness would sit through six
       60ms waits before anything appeared. */
    animation-duration: 0.01ms !important;
    animation-delay: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================== motion

   The house motion rule, in one place. Two things move and one does not: a state change
   the visitor caused (a hover, a press, an answer arriving) eases; nothing else does. No
   parallax, no scroll-jacking, no looping decoration — a records console that drifts
   while you read it is a records console you cannot trust to be still.

   Every duration here is short on purpose. 120-220ms is long enough to read as motion and
   short enough that it never delays the next click, and it matches the literals already in
   this file rather than introducing a token the rest of the sheet would then have to
   migrate to. */

/* -- entrance ------------------------------------------------------------------

   Sections arrive rather than exist. `.rise` starts 8px low and transparent and is left
   by `.shown`; the observer in app.js adds that class.

   The hiding is scoped to `html.js-reveal`, a class app.js adds to the root ONLY when it
   is actually going to observe. The ungated version — `.rise { opacity: 0 }` with no such
   guard — is the bug `reduced-motion-scroll-reveal-fix` exists to name: no script, a
   blocked CDN, or a browser without IntersectionObserver and every below-the-fold section
   on the site is permanently blank. Gating on the class makes the failure mode "no
   animation" instead of "no content", and the page is fully rendered with JavaScript off.

   The hero is the exception: it is above the fold on every screen, so it cannot wait for
   an observer. Its children stagger off one shared keyframe with a per-child `delay`,
   which reads as one gesture instead of six. Both are one-shot entrances. */

@keyframes rise-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.js-reveal .rise { opacity: 0; transform: translateY(8px); }
.js-reveal .rise.shown { animation: rise-in 0.32s ease-out forwards; }

/* The hero's own entrance. `fill-mode: backwards` matters: it applies the FIRST keyframe
   during the delay, so an element waiting its turn is hidden rather than sitting at full
   opacity and then jumping. */
.hero .hero-step {
  animation: rise-in 0.4s ease-out backwards;
  animation-delay: calc(var(--step-i, 0) * 60ms);
}

/* -- attention, once ------------------------------------------------------------

   Two dots on the site say something is live: the header's `.pulse-dot` and the hero's
   `.hostline-dot`. A live indicator that does not move is indistinguishable from a
   decoration, so each breathes — slowly, at a scale the eye reads as "alive" rather than
   "alerting". `prefers-reduced-motion` clamps the duration to 0.01ms at the bottom of
   this file, which freezes them on the resting frame rather than removing the dot. */

@keyframes breathe {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.55; transform: scale(0.82); }
}
.pulse-dot, .hostline-dot { animation: breathe 2.6s ease-in-out infinite; }
/* The hostline dot sits on the paper and the header dot on the header wash; both are
   small enough that the transform does not shift layout. */
.hostline-dot { transform-origin: center; }

/* -- state changes ---------------------------------------------------------------

   The remaining hover/focus transitions. Each one is a property its own rule ALREADY
   changes, added here rather than scattered so the whole motion surface is one list. The
   colours are the tokens the hover rules use; nothing new is invented. */

.ov-card,
.toc a,
.drop,
.rm,
input:focus, select:focus, textarea:focus,
input[type="file"]::file-selector-button,
.dig-go,
.apex-pick,
.cmd-copy,
.status-badge,
.nav-link {
  transition-property: border-color, color, background-color, box-shadow, outline-color, opacity;
  transition-duration: 0.15s;
  transition-timing-function: ease;
}

/* Focus is the one transition that must never be the slow one. A keyboard user tabs at
   a fixed cadence, so a ring that eases in at 150ms is behind their eye on every stop —
   outline and box-shadow only, which is what these rules animate, and the duration is
   already 0.15s from the block above; this just says the ring is the reason. */
:focus-visible { outline-offset: 2px; transition-duration: 0.08s; }
:focus:not(:focus-visible) { outline: none; }

/* The FAQ marker is a `+` that becomes `−` in `[open]`. Swapping the glyph is instant and
   reads as a jump cut; a short fade lands it. `transition` cannot interpolate between two
   `content` values, so the marker is a span-width crossfade instead — opacity only, which
   is the cheapest thing that still reads as a change. */
.faq-q::before { transition: opacity 0.15s ease, color 0.15s ease; }
.faq-item[open] > .faq-q::before { color: var(--verdigris); }

/* The drop zone is a dashed invitation; a drag over it should feel like the invitation
   being taken up, so its colour eases rather than snapping to the accent. */
.drop { transition-property: border-color, color, background-color, box-shadow; }
.drop.hot { background: var(--seal-wash); }

/* A row in the registry table is a link in all but name. The lift is one border-step,
   the same one-step rule the cards use — never a coloured shadow. */
tbody tr { transition: background-color 0.12s ease, box-shadow 0.12s ease; }
tbody tr:hover { box-shadow: inset 2px 0 0 var(--rule-strong); }
tbody tr:hover td:first-child { box-shadow: inset 2px 0 0 var(--rule-strong); }

/* -- the answer, arriving ---------------------------------------------------------

   The record's own answer has a keyframe already (`dig-answer-in`). What it lacked was
   the moment where the answer row is REPLACED: the class is rewritten wholesale on every
   lookup, so a second answer re-runs the same 0.22s entrance — which is correct, and is
   why the rule reads `.dig-answer.answered` and not `.dig-answer`.

   The meta line under it — the hostnames, the quietest text on the page — had no
   transition at all, so a new answer replaced the old one with no acknowledgement. It
   fades rather than rises: it is a footnote to the answer above it, not a new record. */
.dig-meta { transition: opacity 0.15s ease; }
.dig-answer-body-swap { animation: rise-in 0.18s ease-out; }

/* A notched phone's home indicator sits over the footer's last line. */
@supports (padding: max(0px)) {
  main { padding-bottom: max(4rem, env(safe-area-inset-bottom)); }
}