/* …as He leads… — statement of faith
   Direction: scholarly critical edition. See DESIGN-DIRECTION.md.
   Palette roles: paper, ink, apparatus, rubric. Nothing else. */

:root {
  --paper:        #f3f4f7;
  --paper-edge:   #e6e8ee;
  --ink:          #11141d;
  --ink-soft:     #454b5c;
  --apparatus:    #17284a;
  --on-app:       #e7eaf1;
  --on-app-soft:  #a8b4cc;
  --rubric:       #b4232a;
  --rubric-light: #e8878c;
  /* The ministry's own colour, from its booklets. Not the darkest purple, not
     the lightest: this one measures 8.0:1 on paper, so it carries body text as
     well as display. The tint is for the apparatus field, where the full
     strength sits at 1.6:1 and disappears. */
  --purple:       #6d2f86;
  --purple-light: #d7a9e8;

  --pad: clamp(1.25rem, 5vw, 4.5rem);

  /* One family in two registers, which is how an edition is actually set:
     Vollkorn for everything read, Vollkorn SC where small caps do the work of
     a second voice. A serif paired with a sans is the reflex every site
     reaches for; a text face and its own small caps is a decision. */
  --face-text: "Vollkorn", Georgia, "Times New Roman", serif;
  --face-furn: "Vollkorn", Georgia, "Times New Roman", serif;
  --face-sc:   "Vollkorn SC", "Vollkorn", Georgia, serif;
}

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

html {
  background: var(--paper);
  -webkit-text-size-adjust: 100%;
  scrollbar-color: var(--apparatus) var(--paper-edge);
}

body {
  margin: 0;
  timeline-scope: --folio-leaf, --scene;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--face-text);
  font-size: clamp(1.0625rem, 0.95rem + 0.5vw, 1.1875rem);
  line-height: 1.62;
  caret-color: var(--rubric);
  text-rendering: optimizeLegibility;
}

::selection { background: var(--apparatus); color: var(--paper); }

:focus-visible {
  outline: 2px solid var(--rubric);
  outline-offset: 3px;
}

p { margin: 0; }

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10;
  background: var(--apparatus);
  color: var(--on-app);
  font-family: var(--face-furn);
  padding: 0.75rem 1.25rem;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

.skip:focus {
  left: 0;
}

[data-state="draft"] [data-query="true"]::after {
  content: "under query";
  display: block;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--rubric-light);
  margin-top: 0.15rem;
}

/* ---- running head ----
   An edition puts the work's name at the top of every page after the title
   leaf. This one materialises exactly as the title leaf scrolls away, driven
   by that element's own view timeline. Where scroll-driven animation is not
   supported it is simply absent: the title leaf still names the ministry, so
   nothing is lost, and a permanently visible bar would only duplicate it. */

.runner__name {
  font-family: var(--face-text);
  font-size: 1.0625rem;
  color: var(--ink);
}

.runner__place {
  font-family: var(--face-furn);
  font-size: 0.9375rem;
  color: var(--ink-soft);
}

.ell { color: var(--purple); }

/* ---- the edition ---- */

.edition__title,
.movement__head {
  font-family: var(--face-furn);
  margin: 0;
}

.edition__title {
  font-size: clamp(2rem, 1.3rem + 3.2vw, 3.5rem);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.05;
  color: var(--ink);
  max-width: 40rem;
  padding: 0 0 clamp(2rem, 4vw, 3rem);
}

/* Set in a real small-caps face rather than uppercase micro-type with tracking.
   A rubric is literally the red heading of a liturgical book, so red here is
   the historically exact use, not an accent. */

.movement__head {
  font-family: var(--face-sc);
  font-size: clamp(1.375rem, 1.15rem + 0.9vw, 1.75rem);
  font-weight: 700;
  letter-spacing: 0.015em;
  line-height: 1.15;
  color: var(--rubric);
  padding: 0 0 1.35rem;
}

/* A thick-and-thin rule: the oldest way of closing a heading on a page, and
   the reason it survives is that it separates without shouting. */

.movement__head::after {
  content: "";
  display: block;
  margin-top: 0.5rem;
  height: 3px;
  border-top: 2px solid var(--rubric);
  border-bottom: 1px solid var(--rubric);
  max-width: 7.5rem;
}

.movement { scroll-margin-top: 4.5rem; }

/* Rubrication marks section heads only. Citations were red too, and
   the client's verdict was that the eye went to them instead of the
   sentence. They are ink now, a size down, in brackets. */
.cite {
  font-family: var(--face-furn);
  font-size: 0.86em;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}

/* ---- colophon: the page closes mid-sentence ---- */

.colophon__ell {
  font-family: var(--face-text);
  font-size: clamp(2.5rem, 1.4rem + 4vw, 4.5rem);
  line-height: 0.8;
  color: var(--rubric);
  padding: 0;
  margin: 0;
  text-indent: -0.06em;
}

/* ---- narrow: the apparatus drops under its line as a full-bleed field ---- */

@media print {
.sign { page-break-inside: avoid; }

a[href]::after { content: ""; }

body { background: #fff; font-size: 11pt; }
}

/* ---- 404 ---- */

.lost__back a {
  color: var(--rubric);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

.lost__back a:hover { text-decoration-thickness: 2px; }

/* ---- plates ----
   Engravings after the photographs, set the way an edition sets a plate: a
   plate mark (the pressed rule around the image), then a centred caption in
   small caps with the engraver's credit line, "after a photograph by". They
   sit on paper, in the text column's width, with the apparatus running past. */

.plate {
  margin: 0;
  max-width: 46rem;
  padding: 0 var(--pad);
}

.plate__mark {
  margin: clamp(2rem, 5vw, 3.5rem) 0 0;
  padding: 0.55rem;
  border: 1px solid var(--ink);
  box-shadow: inset 0 0 0 0.35rem var(--paper), inset 0 0 0 calc(0.35rem + 1px) var(--ink-soft);
  background: var(--paper);
}

.plate__mark img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--paper);
}

.plate__cap {
  margin: 0.9rem 0 clamp(2rem, 5vw, 3.5rem);
  text-align: center;
  font-family: var(--face-furn);
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.plate__title {
  font-family: var(--face-sc);
  font-weight: 700;
  font-size: 1.0625rem;
  letter-spacing: 0.02em;
  color: var(--ink);
}

.plate__credit { display: block; }

/* ---- run-in labels in the policy ---- */

.runin {
  font-family: var(--face-furn);
  font-weight: 700;
  color: var(--ink);
}

/* ---- signature block ---- */

.sign {
  border-top: 2px solid var(--ink);
  padding-top: 1.4rem;
  max-width: 62ch;
}

.sign__text { max-width: 62ch; }

.sign__org { white-space: nowrap; }

.sign__fields {
  margin: 2rem 0 0;
  display: grid;
  gap: 1.9rem 2.5rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.sign__field dt {
  font-family: var(--face-furn);
  font-size: 0.875rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 1.6rem;
}

.sign__field dd {
  margin: 0;
  border-bottom: 1px solid var(--ink);
}

/* ============================================================================
   THE CRANE SCENE - an engraved plate
   ----------------------------------------------------------------------------
   Drawn, not photographed. The crane geometry is traced from the alpha matte
   derived from the real Fremantle photograph, so the lattice, legs and booms
   are the actual ones, then rendered as line-work with two hatch densities the
   way an engraving carries tone. The cumulus are drawn as the upper envelope
   of overlapping circles. See tools/build-engraving.py.

   An edition puts an engraved plate opposite its title page, so this is the
   form the rest of the site was already asking for.

   Motion: the clouds cross the viewport in 50 seconds, which is slow enough to
   read as weather and fast enough to be visible within a second or two. The
   first attempt ran at 8 pixels per second and was invisible. The cranes shift
   only slightly, and only on scroll.
   ========================================================================= */

:root {
  --sky-high:  #c4bca0;
  --sky-low:   #fdce74;
  --horizon:   #e8b56a;
}

.scene {
  position: relative;
  width: 100%;
  max-width: 100%;
  height: auto;
  aspect-ratio: 31 / 20;
  max-height: 980px;
  min-height: 18rem;
  overflow: hidden;
  background: linear-gradient(to bottom, var(--sky-high) 0%, var(--sky-low) 74%, var(--horizon) 100%);
  container-type: size;
  isolation: isolate;
}

.scene__layer {
  position: absolute;
  left: 0;
  right: 0;
  pointer-events: none;
  will-change: transform;
}

.scene__layer img { display: block; width: 100%; }

/* ---- sky lines ----
   Engravers line a sky horizontally and let the lines thin out toward the
   zenith. Constant spacing, faded by a mask, reads the same at this scale. */

.scene__skylines {
  inset: 0;
  z-index: 0;
  background: repeating-linear-gradient(to bottom, var(--ink) 0 1px, transparent 1px 11px);
  opacity: 0.14;
  mask-image: linear-gradient(to bottom, transparent 0%, #000 45%, #000 70%, transparent 88%);
}

/* ---- the sun ----
   Low on the right, where the photograph's light came from. Behind the
   clouds, in front of the sky lines. */

.scene__sun {
  left: auto;
  right: -7cqw;
  bottom: 2cqh;
  width: 46cqw;
  z-index: 1;
  opacity: 0.68;
}

.scene__sun img { height: auto; }

/* ---- clouds ---- */

.scene__clouds {
  top: 4%;
  height: 34%;
  z-index: 2;
  overflow: hidden;
  mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
}

.clouds__track {
  display: flex;
  width: max-content;
  height: 100%;
  animation: cloud-drift 50s linear infinite;
  will-change: transform;
}

.clouds__track img {
  flex: none;
  width: 100vw;
  height: 100%;
  max-width: none;
  object-fit: cover;
  object-position: 50% 30%;
}

@keyframes cloud-drift {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* ---- cranes, wharf, sea ---- */

.scene__cranes { bottom: 13%; z-index: 3; }

.scene__cranes img { height: auto; }

.scene__wharf {
  bottom: 13%;
  height: 0;
  z-index: 4;
  border-top: 0.35cqh solid var(--ink);
}

.scene__sea {
  bottom: 0;
  height: 13%;
  z-index: 4;
  background: repeating-linear-gradient(to bottom,
    var(--ink) 0 1px, transparent 1px 9px);
  opacity: 0.3;
}

@supports (animation-timeline: scroll()) {
.scene__cranes {
    animation-timeline: scroll(root block);
    animation-range: 0% 28%;
    animation-fill-mode: both;
    animation-timing-function: linear;
    animation-name: drift-cranes;
  }
}

@keyframes drift-cranes { to { transform: translate3d(-0.8cqw, -2.5cqh, 0); } }

@media (prefers-reduced-motion: reduce) {
.clouds__track, .scene__cranes { animation: none; }
}

@media (max-width: 60rem) {
.scene { max-height: 520px; }

.scene__cranes { bottom: 12%; }

.scene__clouds { height: 30%; }
}

@media print {
.scene { display: none; }
}

/* ---- the cartouche ----
   Maps and engraved plates carry their title in a cartouche: a paper panel
   with a ruled border, set into the picture. That is what gives the wordmark
   its ground here. Not a scrim over the drawing; a piece of the drawing. Ink
   on paper inside it measures 16.7:1, whatever passes behind. */

.scene__type {
  position: relative;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  height: 100%;
  padding: clamp(1.75rem, 5vh, 3.75rem) var(--pad) 0;
  pointer-events: none;
}

.cartouche {
  /* .scene__type is a full-bleed overlay and is transparent to the pointer so
     it does not sit in front of the plate. The cartouche is the one thing in
     it a hand can actually be over, and without this its ellipses could never
     be hovered at all. */
  pointer-events: auto;
  background: var(--paper);
  border: 1px solid var(--ink);
  /* The outer shadow has an offset and a blur: a panel lifted off the plate,
     not a zero-blur block, which is a costume this world never chose. */
  box-shadow:
    inset 0 0 0 0.3rem var(--paper),
    inset 0 0 0 calc(0.3rem + 1px) var(--ink),
    0.5rem 0.55rem 1.4rem -0.35rem color-mix(in srgb, var(--ink) 34%, transparent);
  padding: clamp(1.1rem, 2.6vw, 1.9rem) clamp(1.4rem, 3.2vw, 2.6rem) clamp(1rem, 2.4vw, 1.7rem);
  max-width: min(100%, 44rem);
}

/* Italic, because a cartouche titles itself in italic: on a map the roman is
   for places and the italic is for the work's own name. The tracking eases off
   from the roman setting, since italic Cardo is already narrower. */

.scene__name {
  margin: 0;
  font-family: var(--face-text);
  font-weight: 400;
  font-style: italic;
  font-size: clamp(2.5rem, 1rem + 6.6vw, 5.5rem);
  line-height: 0.98;
  letter-spacing: -0.015em;
  color: var(--ink);
  text-indent: 0;
  max-width: 15ch;
}

.scene__place {
  margin: clamp(0.6rem, 1.6vh, 1.2rem) 0 0;
  font-family: var(--face-sc);
  font-weight: 700;
  font-size: clamp(0.8125rem, 0.5rem + 1.35vw, 1.375rem);
  letter-spacing: 0.02em;
  color: var(--rubric);
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
.clouds__track { animation: none; }
}

@media print {
.scene__type { display: none; }
}

/* ---- parallax fallback ----
   The declarative path above runs off the main thread and is the primary one.
   It is not universal: Firefox has not shipped scroll-driven animations, and a
   scrollport reporting no height leaves the timeline with no scroll range, so
   the animation sits attached, running, and inert.

   scene-parallax.js detects that and drives --p instead. Only ever one of the
   two is live. The clouds are not in here: their drift is a plain infinite
   animation that needs no timeline. */

@property --p {
  syntax: "<number>";
  initial-value: 0;
  inherits: true;
}

html[data-js-parallax] .scene__cranes {
  animation: none;
  transform: translate3d(calc(var(--p) * -0.8cqw), calc(var(--p) * -2.5cqh), 0);
}

/* The running head used to fade in as the title leaf left, and this rule drove
   that fade where the scroll timeline never activated. The name lives in a
   permanent masthead now, so the rule was leaving the ministry's own name at
   opacity 0 on every page in every browser that takes the fallback path. */

@media (prefers-reduced-motion: reduce) {
html[data-js-parallax] .scene__cranes { transform: none; }
}

@media (max-width: 60rem) {
.plate { grid-template-columns: 1fr; }

.plate__mark { margin: clamp(1.5rem, 6vw, 2.5rem) var(--pad) 0; }

.plate__cap { margin-inline: var(--pad); }

.scene__sun { width: 70cqw; right: -18cqw; bottom: 6cqh; }

.cartouche { max-width: 100%; }
}

/* ============================================================================
   THE WORDMARK
   ----------------------------------------------------------------------------
   The ministry's name is always purple and always slanted, wherever it stands:
   the cartouche, the running head, the declaration, the copyright line. The
   generator wraps it, so it cannot drift from page to page. */

.wordmark,
.scene__name,
.runner__name,
.sign__org {
  font-style: italic;
  color: var(--purple);
}

.wordmark .ell,
.scene__name .ell,
.runner__name .ell,
.sign__org .ell { color: var(--purple); }

/* ---- the reading rule ----
   How far through the page you are, in the ministry's colour. Scroll-driven,
   so it runs off the main thread; scene-parallax.js drives the same custom
   property where the timeline never activates. Decoration on the top edge, it
   never covers content and never gates it. */

.reading-rule {
  position: fixed;
  inset: 0 0 auto;
  height: 3px;
  z-index: 20;
  pointer-events: none;
  background: color-mix(in srgb, var(--ink) 8%, transparent);
}

.reading-rule span {
  display: block;
  height: 100%;
  width: 100%;
  background: var(--purple);
  transform-origin: 0 50%;
  transform: scaleX(0);
}

@supports (animation-timeline: scroll()) {
.reading-rule span {
    animation: read-through linear both;
    animation-timeline: scroll(root block);
  }
}

@keyframes read-through { to { transform: scaleX(1); } }

html[data-js-parallax] .reading-rule span {
  animation: none;
  transform: scaleX(var(--read, 0));
}

/* ---- contact ---- */

.contact {
  list-style: none;
  margin: 0.4rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 2rem;
}

.contact a {
  display: inline-block;
  padding: 0.55rem 0;
  font-family: var(--face-sc);
  font-weight: 700;
  font-size: clamp(1.25rem, 1.1rem + 0.7vw, 1.6rem);
  letter-spacing: 0.03em;
  color: var(--purple);
  border-bottom: 1px solid var(--purple);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}

.contact a:hover { border-bottom-width: 3px; padding-bottom: calc(0.55rem - 2px); }

@media (prefers-reduced-motion: reduce) {
.reading-rule span { animation: none; }
}


/* ============================================================================
   THE READING LAYOUT
   ----------------------------------------------------------------------------
   What this replaced, and why.

   The text used to sit in a column with a deep blue apparatus field beside it
   carrying every scripture reference in red. It was a real form, and it failed
   with the one reader who matters. Her notes, verbatim: 'Not easy to read',
   'it's all disjointed', 'no scriptures in red - eyes go to that and not text',
   'all looks like a Geelong footy shirt', and, on the contents column that
   stood between the title and the first sentence, 'difficult to find stuff -
   I only just stumbled across your loaded stuff because I accidentally
   scrolled down'.

   So: one column, one ink, references in brackets where the ministry writes
   them, and the navigation on a single line at the top like dividers in a
   folder. The engraved plates, the type and the palette stay.
   ========================================================================= */

.masthead {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--paper);
  border-bottom: 1px solid var(--paper-edge);
  padding: 0.55rem var(--pad) 0;
}

.runner {
  display: flex;
  gap: 1rem;
  align-items: baseline;
  justify-content: space-between;
  margin: 0 0 0.4rem;
}

/* ---- tabs ----
   'I prefer tabs for each page at the top like dividers than a contents page.'
   Dividers in a folder: the current one joins the page below it by breaking
   the rule that separates the rest, which is the whole visual argument a tab
   makes and the reason a tab is not a button. */

.tabs__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.tab {
  display: block;
  position: relative;
  padding: 0.6rem 0.9rem;
  min-height: 2.75rem;
  font-family: var(--face-sc);
  font-size: 0.9375rem;
  letter-spacing: 0.015em;
  color: var(--ink-soft);
  text-decoration: none;
  border: 1px solid transparent;
  border-bottom: none;
  border-radius: 3px 3px 0 0;
  transition: color 140ms ease, background-color 140ms ease, transform 140ms ease;
}

.tab:hover,
.tab:focus-visible {
  color: var(--ink);
  background: var(--paper-edge);
}

.tab[aria-current="page"] {
  color: var(--ink);
  background: var(--paper);
  border-color: var(--paper-edge);
}

/* The divider tab sits over the masthead rule, so the current page is joined
   to its own tab rather than boxed off beneath a continuous line. */
.tab[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: var(--paper);
}

.tab[aria-current="page"]::before {
  content: "";
  position: absolute;
  left: 0.9rem;
  right: 0.9rem;
  top: 0;
  height: 2px;
  background: var(--rubric);
}

/* ---- the reading column ---- */

.folio {
  padding: clamp(2rem, 5vw, 3.25rem) var(--pad) 0;
}

.folio__title {
  font-family: var(--face-sc);
  font-weight: 700;
  font-size: clamp(1.75rem, 1.25rem + 2.2vw, 2.875rem);
  line-height: 1.05;
  margin: 0;
  color: var(--ink);
}

.edition {
  padding: 0 var(--pad);
}

.movement {
  max-width: 40rem;
  padding: clamp(1.75rem, 4vw, 2.5rem) 0;
}

.line,
.para,
.reasons,
.creed,
.booklets,
.contact,
.await,
.sign {
  max-width: 40rem;
}

.para { margin: 0 0 0.9em; }
.para:last-child { margin-bottom: 0; }

.sub {
  font-family: var(--face-sc);
  font-weight: 600;
  font-size: 1.0625rem;
  letter-spacing: 0.02em;
  margin: 1.6em 0 0.5em;
  color: var(--ink);
}
.sub:first-child { margin-top: 0; }

/* ---- a line and its reference ----
   The reference goes back where the ministry puts it: in brackets, at the end
   of the sentence, in the same ink a size down. Nothing to jump to. */

.line { margin: 0 0 0.55em; }

.ref {
  font-size: 0.8125em;
  color: var(--ink-soft);
}

/* One citation. It may not break in half — 'Jn' on one line and '1:34' on the
   next is not a reference — but the list it sits in may break around it. */
.cn { white-space: nowrap; }

.line--quote,
.line--item {
  padding-left: 1.35rem;
  text-indent: -1.35rem;
}

.letter {
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}

/* ---- the creed ----
   'Apostles creed not split up and divided.' One block, one rule down its
   left, the clauses running on. It reads as one statement again. */

.creed {
  border-left: 2px solid var(--paper-edge);
  padding-left: clamp(0.9rem, 2vw, 1.4rem);
  margin: 0.4rem 0 0;
}
.creed .line {
  margin-bottom: 0.35em;
  padding-left: 1.15rem;
  text-indent: -1.15rem;
}
.creed .line:last-child { margin-bottom: 0; }

/* ---- dot points ---- */

.reasons {
  margin: 0.4rem 0 0.9rem;
  padding-left: 1.15rem;
}
.reasons li { margin-bottom: 0.6em; }
.reasons li::marker { color: var(--ink-soft); }

.runin { font-weight: 600; }

/* ---- booklets ----
   'No summary - too wordy - just title.' Title, then the two facts worth
   knowing before opening a PDF on a phone. */

.booklets {
  list-style: none;
  margin: 0.6rem 0 1.2rem;
  padding: 0;
}

.booklet {
  border-top: 1px solid var(--paper-edge);
  padding: 0.85rem 0;
}
.booklet:last-child { border-bottom: 1px solid var(--paper-edge); }

.booklet__title {
  display: block;
  font-family: var(--face-sc);
  font-size: 1.0625rem;
  color: var(--ink);
  text-decoration-color: var(--paper-edge);
  text-underline-offset: 0.18em;
  min-height: 1.5rem;
  transition: color 140ms ease;
}
.booklet__title:hover,
.booklet__title:focus-visible {
  color: var(--rubric);
  text-decoration-color: currentColor;
}

.booklet__collation {
  display: block;
  font-size: 0.8125rem;
  color: var(--ink-soft);
  letter-spacing: 0.02em;
  margin-top: 0.15rem;
}

/* ---- awaiting the ministry ----
   One quiet line, not a boxed editorial note. The long placeholders were
   letters to the client sitting on a public page. */

.await {
  color: var(--ink-soft);
  font-style: italic;
  margin: 0 0 0.9em;
}

/* ---- the ellipses ----
   The name is three dots, a phrase, three dots. Asked for: the dots in the
   title, at the top and at the foot should bounce when you hover them, in the
   sequence a typing indicator bounces — each dot up and back on a loop, the
   next starting a beat later.

   The problem is that the name is spelt with one character, U+2026, and three
   dots that move separately need three boxes. Three elements carrying "." as
   text would turn "…as He leads…" into "...as He leads..." and fail the
   content check, correctly. So the dots are pseudo-element content on empty
   elements: pseudo content is not part of the DOM text, so the name still
   reads exactly as the ministry writes it, and there are still three boxes to
   move. The real ellipsis stays in the markup, visually hidden, for anything
   that reads rather than looks — a screen reader, a copy-paste, the verifier.

   They are full stops in the same face, not drawn circles, so they carry
   Vollkorn's own dot at whatever size the name is set. */

.ell {
  display: inline-block;
  position: relative;
  color: var(--purple);
  white-space: nowrap;
}

/* Present for anything reading the page, absent from the layout. Not
   display:none, which takes it out of the accessibility tree as well. */
.ell__t {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Three full stops set solid are tighter than one ellipsis: 45px against 59px
   at the title size. The tracking puts the metric back, so swapping the glyph
   for three boxes does not quietly reset the name. */
.ell__d {
  display: inline-block;
  font-style: inherit;
  font-weight: inherit;
  letter-spacing: 0.036em;
}
.ell__d::before { content: "."; }

/* Travel and timing from the reference: up and back, 600ms, each dot a fifth
   of a second behind the one before it. It loops while hovered rather than
   firing once, because a bounce that happens once is a flinch. */
@keyframes ell-bounce {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-0.42em); }
}

.scene__name:hover .ell__d,
.scene__name:focus-visible .ell__d,
.runner__name:hover .ell__d,
.colophon__ell:hover .ell__d {
  animation: ell-bounce 600ms ease-in-out infinite;
}

.scene__name:hover .ell__d:nth-of-type(2),
.runner__name:hover .ell__d:nth-of-type(2),
.colophon__ell:hover .ell__d:nth-of-type(2) { animation-delay: 200ms; }

.scene__name:hover .ell__d:nth-of-type(3),
.runner__name:hover .ell__d:nth-of-type(3),
.colophon__ell:hover .ell__d:nth-of-type(3) { animation-delay: 400ms; }

/* The trailing ellipsis is offset by half a beat so the two groups interleave.
   The obvious continuation — 600, 800, 1000 — is wrong: a 600ms delay against
   a 600ms loop is one whole cycle, which puts the second ellipsis in exact
   unison with the first and throws the wave away. */
.scene__name:hover .ell:last-of-type .ell__d,
.runner__name:hover .ell:last-of-type .ell__d { animation-delay: 100ms; }
.scene__name:hover .ell:last-of-type .ell__d:nth-of-type(2),
.runner__name:hover .ell:last-of-type .ell__d:nth-of-type(2) { animation-delay: 300ms; }
.scene__name:hover .ell:last-of-type .ell__d:nth-of-type(3),
.runner__name:hover .ell:last-of-type .ell__d:nth-of-type(3) { animation-delay: 500ms; }

/* The foot is one ellipsis in a block. Without this the hover target is the
   whole width of the page rather than the three dots. */
.colophon__ell { display: inline-block; }

@media (prefers-reduced-motion: reduce) {
  .scene__name:hover .ell__d,
  .runner__name:hover .ell__d,
  .colophon__ell:hover .ell__d { animation: none; }
}

/* ---- colophon ---- */

.colophon {
  border-top: 1px solid var(--paper-edge);
  margin-top: clamp(2.5rem, 6vw, 4rem);
  padding: clamp(1.5rem, 4vw, 2.25rem) var(--pad);
}

.colophon__note {
  max-width: 40rem;
  margin: 0.5rem 0 0;
  font-size: 0.8125rem;
  color: var(--ink-soft);
}
[data-state="draft"] .colophon__note { display: block; }
.colophon__note { display: none; }
