/* ============================================================================
   world.css — a printed card, and the world it unfolds into.

   Scoped to the homepage. The client, authentication and family pages keep
   their own styles.

   The page is a state machine on <html data-state>:

       (none)    no JavaScript, or before connect: a linear document.
       card      the calling card alone on paper. It tilts and it turns over.
       opening   the press: depress, ink flood out of the card.
       field     one composed still life of engraved objects on the sheet.
       plate     one object picked. The whole sheet transforms around it.
       reversing the flood running backwards to the card.

   Layers, in paint order:

       L0  paper, tokens, resets
       L1  the ink surface (canvas, and the CSS wash that replaces it)
       L2  the card: tilt, specular, letterpress, the turn
       L3  the field: head, watermark, the still life
       L4  the picked object: focal transform, words set on the sheet
       L5  the transformations, one per chapter
       L6  colophon and the reel

   Two rules hold the whole thing up: nothing listens to scroll, and every
   layer degrades to a readable static page.
   ========================================================================= */

/* --- L0  paper, tokens, resets ------------------------------------------ */

.world {
  /* The watermark bleeds off the right edge on purpose; clip it without
     making .world a scroll container. */
  overflow-x: clip;
  --paper: #f2efe6;
  --paper-lit: #faf8f2;
  --ink: #1f211d;
  --ink-soft: #5e6258;
  --rule: #cdcbbe;
  --vermillion: #a43c24;
  /* Wet ink, not set type: near black with a little warmth in it. The shader
     and the CSS wash both take their colour from here. */
  --flood-ink: #1b1a17;

  /* The sheet's working ink. A picked object drives it to that chapter's. */
  --sheet-ink: var(--ink);
  --chapter-ink: var(--vermillion);

  --depress: 120ms;
  --flood: 520ms;
  --flight: 420ms;
  --draw: 380ms;
  --focal: 720ms;
  --reverse: 480ms;
  --press: cubic-bezier(.2, .7, .2, 1);
  --swing: cubic-bezier(.34, 1.3, .44, 1);

  position: relative;
  min-height: 100dvh;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif);
  -webkit-font-smoothing: antialiased;
}

.world *,
.world *::before,
.world *::after { box-sizing: border-box; min-width: 0; }
.world :is(h1, h2, h3, p, figure, dl, dd) { margin: 0; }
.world :is(ul, ol) { margin: 0; padding: 0; list-style: none; }
.world button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
.world a { color: inherit; }
.world ::selection { background: #f3cbb7; color: var(--ink); }
.world :is(a, button, summary):focus-visible {
  outline: 3px solid var(--vermillion);
  outline-offset: 5px;
  border-radius: 2px;
}
.world .sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.world-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* The grain already in the asset pipeline, reused rather than reinvented. */
.world::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: url("/assets/overlay-pattern-be4f8506.png");
  background-size: 180px;
  opacity: .32;
  mix-blend-mode: multiply;
}

/* --- L1  the ink surface ------------------------------------------------ */

.world__ink {
  position: fixed;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 240ms linear;
}
html[data-state] .world__ink { opacity: 1; }
html[data-state="opening"] .world__ink,
html[data-state="reversing"] .world__ink { z-index: 60; }

/* No WebGL: the same gesture as a clip-path circle opening from the press. */
html[data-ink="css"] .world__ink { display: none; }
.world__wash {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: none;
  pointer-events: none;
  /* Not a flat screen of ink even here: the wash carries its own pooling. */
  background:
    radial-gradient(58% 48% at 30% 34%, rgba(0, 0, 0, .35), transparent 72%),
    radial-gradient(52% 44% at 74% 68%, rgba(255, 255, 255, .06), transparent 70%),
    var(--flood-ink);
  clip-path: circle(0% at var(--press-x, 50%) var(--press-y, 50%));
}
html[data-ink="css"][data-state="opening"] .world__wash {
  display: block;
  animation: wash-in var(--flood) var(--press) forwards;
}
html[data-ink="css"][data-state="reversing"] .world__wash {
  display: block;
  animation: wash-out var(--reverse) var(--press) forwards;
}
@keyframes wash-in {
  from { clip-path: circle(0% at var(--press-x, 50%) var(--press-y, 50%)); opacity: 1; }
  70%  { clip-path: circle(150% at var(--press-x, 50%) var(--press-y, 50%)); opacity: 1; }
  to   { clip-path: circle(150% at var(--press-x, 50%) var(--press-y, 50%)); opacity: 0; }
}
@keyframes wash-out {
  from { clip-path: circle(150% at var(--press-x, 50%) var(--press-y, 50%)); opacity: 1; }
  to   { clip-path: circle(0% at var(--press-x, 50%) var(--press-y, 50%)); opacity: 1; }
}

/* --- L2  the card ------------------------------------------------------- */

.world__card-stage {
  position: relative;
  z-index: 2;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 1.4rem;
  min-height: 100dvh;
  padding: 2rem;
}
html[data-state="field"] .world__card-stage,
html[data-state="plate"] .world__card-stage { display: none; }

.card-shell { position: relative; perspective: 1500px; }

/* The card casts its own shadow. A separate shadow element sorts into the
   perspective context and prints through the stock, so there isn't one: the
   stock's box-shadow leans with the card instead, off the unitless lean the
   controller writes beside the tilt. */

.card-faces {
  position: relative;
  z-index: 1;
  width: min(86vw, 440px);
  aspect-ratio: 1.75 / 1;
  transform-style: preserve-3d;
  transform:
    translateY(var(--hover-lift, 0px))
    rotateX(var(--tilt-x, 0deg))
    rotateY(calc(var(--tilt-y, 0deg) + var(--flip, 0deg)));
  transition: transform 520ms var(--press);
}
html[data-tilt="live"] .card-faces { transition: transform 110ms linear; }
.card-shell:hover { --hover-lift: -4px; }

.card {
  position: absolute;
  inset: 0;
  overflow: hidden;
  text-align: left;
  background: var(--paper-lit);
  box-shadow:
    0 0 0 1px var(--ink) inset,
    0 1px 0 rgba(31, 33, 29, .25),
    0 2px 5px -4px rgba(31, 33, 29, .45),
    calc(var(--lean-x, 0) * -16px) calc(17px - var(--lean-y, 0) * 7px) 32px -18px rgba(31, 33, 29, .6);
  transition: box-shadow 260ms var(--press);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
/* `.world button` resets padding, and it outranks a single class: the front
   face has to be written at that weight or it loses its own layout. */
.world .card--front {
  display: grid;
  grid-template-columns: 42% minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0;
  /* The portrait is printed to the edge of the stock; only the type is set in
     from it, so the padding lives on the type rather than on the card. */
  padding: 0;
}
html[data-state] .card--front { cursor: pointer; }
.card--back {
  display: grid;
  grid-template-columns: 34% minmax(0, 1fr);
  align-items: center;
  gap: 1.2rem;
  padding: clamp(1rem, 3.1vw, 1.5rem);
  padding-right: calc(clamp(1rem, 3.1vw, 1.5rem) + 8px);
  transform: rotateY(180deg);
}
html[data-state="opening"] .card-faces { --hover-lift: 2px; }
html[data-state="opening"] .card--front { box-shadow: 0 0 0 1px var(--ink) inset; }

/* The one vermillion edge, as an element so it cannot bleed into a corner. */
.card__edge {
  position: absolute;
  inset: 0 0 0 auto;
  width: 4px;
  background: var(--vermillion);
}

/* Specular. A highlight that follows the pointer across the stock, plus a
   fixed sheen that only shows when the card is turned away from square. */
.card__gloss {
  position: absolute;
  inset: 0;
  pointer-events: none;
  mix-blend-mode: soft-light;
  opacity: var(--glow, 0);
  background:
    radial-gradient(42% 58% at var(--glow-x, 50%) var(--glow-y, 32%),
      rgba(255, 255, 255, .95), rgba(255, 255, 255, 0) 68%),
    linear-gradient(104deg, transparent 36%, rgba(255, 255, 255, .5) 48%, transparent 60%);
  transition: opacity 320ms var(--press);
}

.card__mark {
  grid-column: 1;
  grid-row: 1 / -1;
  align-self: stretch;
  display: block;
  width: 100%;
  overflow: hidden;
}
.card__mark img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
  mix-blend-mode: multiply;
}
.card__type {
  grid-column: 2;
  grid-row: 1;
  align-self: center;
  padding: clamp(1rem, 3vw, 1.5rem) calc(clamp(1rem, 3vw, 1.5rem) + 8px) .2rem clamp(1rem, 3.2vw, 1.6rem);
}
.card__name {
  display: block;
  font-family: var(--font-display, Georgia, serif);
  font-size: clamp(1.25rem, 4.3vw, 1.7rem);
  font-weight: 500;
  letter-spacing: -.022em;
  line-height: 1.1;
  /* Letterpress: the type sits a hair below the surface of the stock. */
  text-shadow: 0 1px 0 rgba(255, 255, 255, .85), 0 -1px 1px rgba(31, 33, 29, .14);
}
.card__line {
  display: block;
  margin-top: .5rem;
  color: var(--ink-soft);
  font-size: clamp(.72rem, 2.3vw, .83rem);
  line-height: 1.5;
  text-shadow: 0 1px 0 rgba(255, 255, 255, .7);
}
.card__press {
  grid-column: 2;
  grid-row: 2;
  align-self: end;
  justify-self: start;
  padding: 0 clamp(1rem, 3vw, 1.5rem) clamp(1rem, 3vw, 1.4rem) clamp(1rem, 3.2vw, 1.6rem);
  font: .67rem/1 ui-monospace, SFMono-Regular, Consolas, monospace;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--vermillion);
}
.card__press::before {
  content: "";
  display: inline-block;
  width: 2.2em;
  margin-right: .8em;
  vertical-align: .3em;
  border-top: 1px solid currentColor;
}

.card__portrait { display: block; width: 100%; }
.card__portrait img { display: block; width: 100%; height: auto; mix-blend-mode: multiply; }
.card__back-type { display: grid; justify-items: start; gap: .1rem; }
.card__back-heading {
  font-family: var(--font-display, Georgia, serif);
  font-size: clamp(1.05rem, 3.4vw, 1.35rem);
  font-weight: 500;
  letter-spacing: -.02em;
  margin-bottom: .3rem;
}
.card__contact {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  min-height: 34px;
  font-size: .82rem;
  text-decoration: underline;
  text-decoration-color: var(--rule);
  text-underline-offset: 5px;
}
.card__contact:hover { color: var(--vermillion); text-decoration-color: currentColor; }

.card-turn {
  font: .6rem/1 ui-monospace, SFMono-Regular, Consolas, monospace;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-soft);
  min-height: 44px;
  padding-inline: .4rem;
}
.card-turn:hover { color: var(--vermillion); }
html:not([data-state]) .card-turn { display: none; }
html:not([data-state]) .card--back { display: none; }

/* --- L3  the field ------------------------------------------------------ */

.world__sheet {
  position: relative;
  z-index: 1;
  width: min(100% - 3rem, 1240px);
  margin-inline: auto;
  padding-block: clamp(2rem, 4.5vw, 3.4rem) 3rem;
  color: var(--sheet-ink);
  transition: color 600ms var(--press);
}
html[data-state="card"] .world__sheet,
html[data-state="opening"] .world__sheet { display: none; }
html[data-state="field"] .world__sheet,
html[data-state="plate"] .world__sheet { animation: sheet-in 320ms ease both; }
@keyframes sheet-in { from { opacity: 0; } to { opacity: 1; } }

.world__head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: .6rem 1.4rem;
  padding-bottom: 1.4rem;
  border-bottom: 1px solid var(--rule);
}
.world__mark {
  display: block;
  width: 52px;
  aspect-ratio: 1;
  box-shadow: 0 0 0 1px currentColor inset;
  overflow: hidden;
  transition: transform 200ms var(--press);
}
.world__mark img { display: block; width: 100%; height: 100%; object-fit: contain; mix-blend-mode: multiply; }
.world__mark:hover { transform: translateY(-2px); }
html:not([data-state]) .world__mark { display: none; }

.world__heading {
  align-self: center;
  max-width: 16em;
  font-family: var(--font-display, Georgia, serif);
  font-size: clamp(1.7rem, 4.6vw, 3rem);
  font-weight: 500;
  line-height: 1.04;
  letter-spacing: -.035em;
  text-wrap: balance;
}
.world__hint {
  grid-column: 2;
  font: .68rem/1.6 ui-monospace, SFMono-Regular, Consolas, monospace;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--vermillion);
  min-height: 1.6em;
}
html[data-state="plate"] .world__hint { opacity: 0; }

.world__watermark {
  position: absolute;
  z-index: -1;
  top: 2%;
  right: -22%;
  width: min(70vh, 52%);
  opacity: .018;
  pointer-events: none;
  mix-blend-mode: multiply;
  mask-image: radial-gradient(ellipse 58% 54% at 46% 44%, #000 30%, transparent 72%);
}
.world__watermark img { display: block; width: 100%; height: auto; }

/* --- L3b  the still life ------------------------------------------------ */

/* With JavaScript the three shelves stop being layout and become labels for
   assistive technology only; every object takes the place authored for it in
   config/resume.yml. The composition is one scene, not a grid. */
.still-life { position: relative; }
.still-life__box { position: relative; }
html[data-state] .still-life__box { aspect-ratio: 100 / 50; margin-top: 1.5rem; }
html[data-state] .shelf,
html[data-state] .shelf__objects { display: contents; }
html[data-state] .shelf__label {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
}

/* The case. Non-scaling strokes, because the box is stretched to the sheet and
   a scaled shelf line would be a different weight from the objects on it. */
.still-life__case {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  color: var(--sheet-ink);
  opacity: .42;
  pointer-events: none;
  transition: opacity 420ms var(--press);
}
.still-life__case path {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.still-life__case .case__thin { stroke-width: .7; opacity: .45; }
.still-life__case--narrow { display: none; }
html:not([data-state]) .still-life__case { display: none; }
/* Nothing dims the case for an open plate: it sits under the scrim already. */
/* The case is printed rather than drawn: a non-scaling stroke measures dashes
   in screen pixels, so `pathLength` cannot drive it the way it drives the
   objects. It fades up under them instead. */
html[data-state="field"] .still-life__case,
html[data-state="plate"] .still-life__case { animation: case-in 560ms var(--press) both; }
@keyframes case-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: .42; transform: none; }
}

html[data-state] .chapter {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w);
  rotate: var(--turn);
  transition:
    left var(--focal) var(--press),
    top var(--focal) var(--press),
    width var(--focal) var(--press),
    rotate var(--focal) var(--press),
    opacity 300ms linear;
}

.object {
  display: block;
  width: 100%;
  text-align: center;
}
/* The objects stand on the paper rather than float above it. */
html[data-state] .object__art { position: relative; }
html[data-state] .object__art::after {
  content: "";
  position: absolute;
  left: calc(50% - var(--iw, 76%) * .44);
  right: calc(50% - var(--iw, 76%) * .44);
  bottom: calc(var(--foot, 2%) - 3px);
  height: 8px;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(31, 33, 29, .3), transparent 76%);
  opacity: .5;
}
html[data-state="plate"] .chapter:not([data-open]) .object__art::after { opacity: 0; }
.object__art { display: block; }
.object .engraving { display: block; width: 100%; height: auto; }
html[data-state] .object {
  transition: transform 260ms var(--swing), filter 260ms linear;
}
html[data-state] .object:hover,
html[data-state] .object:focus-visible { transform: translateY(-6px); }
html[data-state] .object .engraving { transition: transform 260ms var(--swing); transform-origin: 50% 88%; }
html[data-state] .object:hover .engraving,
html[data-state] .object:focus-visible .engraving { transform: scale(1.04); }

/* Every object on the shelf carries its own label, printed on the shelf edge
   under it the way a case in a museum is labelled. --foot is the empty space
   below the engraving inside its own 120 box, so the label sits under the
   object's feet rather than under its bounding box. */
html[data-state] .object__caption {
  position: absolute;
  left: 50%;
  top: calc(100% - var(--foot, 0%) + .7rem);
  width: max(100%, 6.6rem);
  translate: -50% 0;
  pointer-events: none;
}
/* The fact stays in the object's accessible name; the shelf only has room for
   the label. */
html[data-state] .object__fact {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}
html[data-state="plate"] .chapter[data-open] .object__caption { opacity: 0; }

html[data-state] .object__label {
  font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif);
  font-size: clamp(.55rem, .72vw, .66rem);
  font-weight: 500;
  letter-spacing: .09em;
  line-height: 1.35;
  text-transform: uppercase;
  text-wrap: balance;
  color: color-mix(in srgb, currentColor 58%, transparent);
  transition: color 200ms linear;
}
/* The affordance: the object lifts, and its label is underlined in ink. */
html[data-state] .object__label::after {
  content: "";
  display: block;
  width: 0;
  height: 1px;
  margin: .28em auto 0;
  background: currentColor;
  transition: width 220ms var(--press);
}
html[data-state] .object:hover .object__label,
html[data-state] .object:focus-visible .object__label { color: currentColor; }
html[data-state] .object:hover .object__label::after,
html[data-state] .object:focus-visible .object__label::after { width: 62%; }

.object__label {
  display: block;
  font-family: var(--font-display, Georgia, serif);
  font-size: .9rem;
  font-weight: 500;
  letter-spacing: -.012em;
}
.object__fact {
  display: block;
  margin-top: .2rem;
  color: var(--ink-soft);
  font-size: .72rem;
  line-height: 1.45;
  text-wrap: pretty;
}

/* The engraving itself. Contours carry the weight; hatching is half of it. */
.engraving {
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.engraving .engraving__contour { stroke-width: 2.4; }
.engraving .engraving__hatch { stroke-width: 1.1; }
.engraving .engraving__blot { fill: currentColor; stroke: none; }
.engraving .engraving__dotted { stroke-dasharray: 3 5; }

/* Draw-on. Only ever applied once JavaScript owns the page, so the static
   document keeps complete, visible line art. */
html[data-state] .object .engraving :is(path, circle):not(.engraving__blot) {
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
}
html[data-state="field"] .object .engraving :is(path, circle):not(.engraving__blot),
html[data-state="plate"] .object .engraving :is(path, circle):not(.engraving__blot) {
  animation: draw-on var(--draw) ease-out forwards;
  animation-delay: var(--draw-delay, 0ms);
}
html[data-state] .object .engraving .engraving__blot { opacity: 0; }
html[data-state="field"] .object .engraving .engraving__blot,
html[data-state="plate"] .object .engraving .engraving__blot {
  animation: blot-on 200ms linear forwards;
  animation-delay: calc(var(--draw-delay, 0ms) + var(--draw));
}
@keyframes draw-on { to { stroke-dashoffset: 0; } }
@keyframes blot-on { to { opacity: 1; } }

/* --- L4  the picked object ---------------------------------------------- */

.world__scrim {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: none;
  background: var(--paper);
  opacity: 0;
  pointer-events: none;
}
html[data-state="plate"] .world__scrim {
  display: block;
  animation: scrim-in 420ms var(--press) forwards;
}
@keyframes scrim-in { to { opacity: .88; } }

html[data-state="plate"] .chapter:not([data-open]) { opacity: .12; }

html[data-state="plate"] .chapter[data-open] {
  left: 6%;
  top: 50%;
  width: 26%;
  rotate: 0deg;
  translate: 0 -50%;
  z-index: 50;
  color: var(--chapter-ink);
}

/* The words are set on the transformed sheet. No card, no border, no shadow:
   this is typography on paper, not a dialog.

   Positioned against the object rather than the viewport, because the open
   chapter carries a `rotate` and is therefore the containing block for
   anything inside it. The percentages are of the chapter's own 26% width. */
html[data-state] .plate {
  position: absolute;
  z-index: 50;
  left: 128%;
  width: 215%;
  top: 50%;
  translate: 0 -50%;
  display: none;
}
html[data-state="plate"] .chapter[data-open] .plate {
  display: block;
  animation: words-in 420ms var(--press) 160ms both;
}
@keyframes words-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}
html[data-state] .plate__sheet { position: relative; background: none; border: 0; box-shadow: none; padding: 0; }

.plate__label {
  font-family: var(--font-display, Georgia, serif);
  font-size: clamp(1.6rem, 3.6vw, 2.5rem);
  font-weight: 500;
  letter-spacing: -.03em;
  line-height: 1.04;
  color: var(--ink);
}
.plate__fact {
  margin-top: .6rem;
  color: var(--chapter-ink);
  font: .74rem/1.6 ui-monospace, SFMono-Regular, Consolas, monospace;
  letter-spacing: .06em;
}
.plate__body {
  margin-top: 1.1rem;
  font-size: clamp(1rem, 1.5vw, 1.12rem);
  line-height: 1.7;
  text-wrap: pretty;
  color: var(--ink);
}
.plate__media { margin-top: 1.2rem; max-width: 22rem; }
.plate__media img { display: block; width: 100%; height: auto; }
html[data-state] .plate__media { display: none; }
.plate__link {
  display: inline-flex;
  align-items: center;
  gap: .7rem;
  min-height: 44px;
  margin-top: 1rem;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--rule);
  text-underline-offset: 6px;
}
.plate__link:hover { color: var(--chapter-ink); text-decoration-color: currentColor; }
.plate__close {
  position: absolute;
  top: -3.2rem;
  right: 0;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--ink-soft);
}
.plate__close:hover { color: var(--chapter-ink); }
html:not([data-state]) .plate__close { display: none; }

/* --- L5  the transformations -------------------------------------------- */

/* One layer per chapter. Only the open chapter's layer is ever in the
   document's paint, and every one of them is under 1.2 seconds.
   --fx-x / --fx-y are the open object's centre, written by the controller. */
.world__effects {
  position: absolute;
  inset: 0;
  z-index: 46;
  pointer-events: none;
}
.fx { position: absolute; inset: 0; display: none; }
.world__effects[data-active="pour"] .fx--pour,
.world__effects[data-active="fill"] .fx--fill,
.world__effects[data-active="gel"] .fx--gel,
.world__effects[data-active="sail"] .fx--sail,
.world__effects[data-active="swim"] .fx--swim,
.world__effects[data-active="riffle"] .fx--riffle,
.world__effects[data-active="step"] .fx--step,
.world__effects[data-active="strike"] .fx--strike,
.world__effects[data-active="resonate"] .fx--resonate,
.world__effects[data-active="tick"] .fx--tick,
.world__effects[data-active="open"] .fx--open,
.world__effects[data-active="chalk"] .fx--chalk,
.world__effects[data-active="scan"] .fx--scan,
.world__effects[data-active="film"] .fx--film { display: block; }
html[data-state="field"] .world__effects[data-active] { animation: fx-out 300ms ease forwards; }
@keyframes fx-out { to { opacity: 0; } }

/* Tim Hortons — the portafilter pours into a cup, and the page takes the
   colour. The stream has a body and a lit edge, the cup fills under it, and
   the coffee that reaches the sheet keeps a surface. */
.fx--pour .fx__stream {
  position: absolute;
  left: var(--fx-x, 50%);
  top: calc(var(--fx-y, 50%) + 9%);
  width: clamp(8px, .8vw, 12px);
  margin-left: calc(clamp(8px, .8vw, 12px) / -2);
  height: 0;
  border-radius: 0 0 50% 50% / 0 0 10px 10px;
  background:
    linear-gradient(90deg,
      rgba(120, 74, 40, .30) 0 12%,
      rgba(44, 24, 10, .92) 34% 58%,
      rgba(150, 99, 56, .55) 84% 100%),
    linear-gradient(to bottom, rgba(44, 24, 10, .65), rgba(63, 35, 17, .95) 30%);
  animation: pour-stream 420ms var(--press) 60ms forwards;
}
/* The cup. Stock, not a dialog: one ink contour, a shadow under it, and the
   coffee rising inside with crema standing on top. */
.fx--pour .fx__cup {
  position: absolute;
  left: var(--fx-x, 50%);
  top: calc(var(--fx-y, 50%) + 21%);
  width: 7.4%;
  height: 16%;
  translate: -50% 0;
  overflow: hidden;
  border: 2px solid rgba(63, 35, 17, .72);
  border-top-width: 2px;
  border-radius: 6% 6% 34% 34% / 4% 4% 26% 26%;
  background: linear-gradient(90deg, rgba(31, 33, 29, .06), transparent 24% 76%, rgba(31, 33, 29, .07));
  box-shadow: 0 10px 18px -14px rgba(31, 33, 29, .8);
  opacity: 0;
  animation: cup-in 260ms var(--press) forwards;
}
.fx--pour .fx__coffee {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0;
  background:
    linear-gradient(90deg, rgba(31, 16, 6, .55), rgba(74, 42, 20, .9) 40%, rgba(46, 25, 11, .72)),
    linear-gradient(to top, rgba(31, 16, 6, .9), rgba(84, 49, 24, .82));
  animation: cup-fill 760ms var(--press) 300ms forwards;
}
/* Crema: the lighter head that stands on the surface while it is poured. */
.fx--pour .fx__crema {
  position: absolute;
  left: 0;
  right: 0;
  top: -5px;
  height: 10px;
  border-radius: 50% / 100%;
  background: linear-gradient(to bottom, rgba(214, 170, 120, .95), rgba(166, 110, 63, .7));
  opacity: 0;
  animation: crema-in 420ms linear 520ms forwards;
}
/* What reaches the sheet: a standing surface, not a flat band. */
.fx--pour .fx__pool {
  position: absolute;
  left: -5%;
  right: -5%;
  bottom: 0;
  height: 0;
  background:
    radial-gradient(44% 150% at 20% 100%, rgba(26, 13, 4, .40), transparent 72%),
    radial-gradient(38% 130% at 58% 100%, rgba(26, 13, 4, .30), transparent 74%),
    radial-gradient(40% 140% at 86% 100%, rgba(26, 13, 4, .34), transparent 72%),
    linear-gradient(to top, rgba(33, 17, 6, .88), rgba(74, 42, 20, .72) 58%, rgba(132, 86, 47, .58));
  border-radius: 46% 54% 0 0 / 14% 10% 0 0;
  /* The crema that stands on the surface where it has just been poured. */
  box-shadow: inset 0 2px 0 rgba(214, 170, 120, .7), inset 0 7px 12px -7px rgba(240, 212, 176, .45);
  animation: pour-pool 820ms var(--press) 240ms forwards;
}
@keyframes pour-stream { from { height: 0; } to { height: 13%; } }
@keyframes pour-pool { from { height: 0; } to { height: 12%; } }
@keyframes cup-in { to { opacity: 1; } }
@keyframes cup-fill { from { height: 0; } to { height: 74%; } }
@keyframes crema-in { to { opacity: 1; } }

/* Milk — the page fills white. The surface carries a meniscus and the body of
   it takes a highlight, so it reads as liquid rather than a white rectangle. */
.fx--fill .fx__milk {
  position: absolute;
  left: -5%;
  right: -5%;
  bottom: 0;
  height: 0;
  background:
    linear-gradient(90deg,
      rgba(214, 208, 190, .55) 0, rgba(255, 255, 255, 0) 15%,
      rgba(255, 255, 255, 0) 72%, rgba(214, 208, 190, .5) 100%),
    linear-gradient(to top, #f5f3ea, #fdfcf8 72%, #ffffff);
  animation: milk-fill 900ms var(--press) forwards;
}
.fx--fill .fx__milk::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -9px;
  height: 18px;
  border-radius: 50% / 100%;
  background: linear-gradient(to bottom, rgba(255, 255, 255, .55), #fdfcf8 60%);
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, .95), 0 3px 7px -3px rgba(31, 33, 29, .18);
}
/* The highlight down the body of it, where the light would stand. */
.fx--fill .fx__gleam {
  position: absolute;
  left: 22%;
  bottom: 0;
  width: 6%;
  height: 0;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, .95) 50%, rgba(255, 255, 255, 0));
  opacity: 0;
  animation: milk-fill 900ms var(--press) forwards, gleam-in 420ms linear 260ms forwards;
}
@keyframes milk-fill { from { height: 0; } to { height: 112%; } }
@keyframes gleam-in { to { opacity: .85; } }

/* Lobo — the gel runs: the samples leave the wells and the bands develop down
   the lanes, the heavy fragments first. The whole plate is held to the
   object's own column, so no lane is ever drawn through the paragraph. */
.fx--gel .fx__plate {
  position: absolute;
  left: 2.5%;
  right: 65%;
  top: 6%;
  height: 84%;
  border-radius: 3px;
  background: linear-gradient(to bottom, rgba(45, 96, 96, .12), rgba(45, 96, 96, .03));
  box-shadow: inset 0 0 0 1px rgba(45, 96, 96, .26);
  opacity: 0;
  animation: gel-plate 320ms ease forwards;
}
.fx--gel .fx__lane {
  position: absolute;
  left: calc(4.6% + var(--lane) * 4.3%);
  top: 8%;
  width: 3.1%;
  height: 80%;
  border-left: 1px dashed rgba(45, 96, 96, .34);
  border-right: 1px dashed rgba(45, 96, 96, .34);
  border-left: 1px dashed rgba(45, 96, 96, .34);
  border-right: 1px dashed rgba(45, 96, 96, .34);
}
/* The well the sample is loaded into. */
.fx--gel .fx__lane::before {
  content: "";
  position: absolute;
  left: -1px;
  right: -1px;
  top: 0;
  height: 7px;
  background: rgba(45, 96, 96, .55);
  box-shadow: inset 0 -2px 0 rgba(45, 96, 96, .3);
}
.fx--gel .fx__lane i {
  position: absolute;
  left: -3%;
  right: -3%;
  top: 2%;
  border-radius: 2px;
  background: linear-gradient(to bottom,
    rgba(45, 96, 96, .18), rgba(45, 96, 96, .92) 45%, rgba(45, 96, 96, .20));
  opacity: 0;
  animation: gel-band 1150ms var(--press) forwards;
  animation-delay: calc(var(--lane) * 40ms);
}
.fx--gel .fx__lane i:nth-child(1) { height: 8px; --to: calc(17% + var(--lane) * 6%); --peak: .92; }
.fx--gel .fx__lane i:nth-child(2) { height: 5px; --to: calc(43% + var(--lane) * 4%); --peak: .62;
  animation-delay: calc(var(--lane) * 40ms + 70ms); }
.fx--gel .fx__lane i:nth-child(3) { height: 3px; --to: calc(66% + var(--lane) * 2.5%); --peak: .4;
  animation-delay: calc(var(--lane) * 40ms + 130ms); }
@keyframes gel-plate { to { opacity: 1; } }
@keyframes gel-band {
  0% { top: 2%; opacity: 0; transform: scaleX(.55); }
  22% { opacity: var(--peak, .8); transform: scaleX(1); }
  100% { top: var(--to, 40%); opacity: var(--peak, .8); transform: scaleX(1); }
}

/* Caravel — a hatched sea arrives under the composition and the ship crosses
   it, leaving a wake. The hatching is the engravings' own, laid flat. */
.fx--sail { overflow: hidden; }
.fx--sail .fx__sea {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 0;
  border-top: 2px solid rgba(31, 95, 74, .55);
  background:
    repeating-linear-gradient(76deg, rgba(31, 95, 74, .26) 0 1.2px, transparent 1.2px 14px),
    repeating-linear-gradient(104deg, rgba(31, 95, 74, .10) 0 1px, transparent 1px 34px),
    linear-gradient(to bottom, rgba(31, 95, 74, .13), rgba(31, 95, 74, .02));
  animation: sea-rise 620ms var(--press) forwards;
}
/* Crests, not a bar: the surface is broken where the swell lifts it. */
.fx--sail .fx__swell {
  position: absolute;
  left: -20%; right: -20%; bottom: 29%;
  height: 16px;
  background:
    radial-gradient(34% 120% at 12% 100%, rgba(31, 95, 74, .30) 58%, transparent 60%),
    radial-gradient(26% 110% at 34% 100%, rgba(31, 95, 74, .20) 58%, transparent 60%),
    radial-gradient(32% 120% at 58% 100%, rgba(31, 95, 74, .26) 58%, transparent 60%),
    radial-gradient(24% 110% at 80% 100%, rgba(31, 95, 74, .18) 58%, transparent 60%);
  animation: swell 1200ms linear infinite;
}
/* The water the hull has already been through. */
.fx--sail .fx__wake {
  position: absolute;
  left: calc(var(--fx-x, 50%) - 15%);
  width: 16%;
  bottom: 29%;
  height: 8px;
  background: repeating-linear-gradient(90deg,
    rgba(31, 95, 74, .34) 0 7px, transparent 7px 15px);
  opacity: 0;
  animation: wake-in 1150ms ease-out 180ms forwards;
}
@keyframes sea-rise { to { height: 32%; } }
@keyframes swell { to { transform: translateX(12%); } }
@keyframes wake-in { 0% { opacity: 0; } 35% { opacity: .9; } 100% { opacity: .25; } }
html[data-state="plate"] .chapter[data-open][data-effect="sail"] .object__art {
  animation: sail 1200ms ease-in-out both;
}
/* It actually crosses: in from the left of its own column, out to the right. */
@keyframes sail {
  0% { transform: translateX(-42%) rotate(-4deg); }
  28% { transform: translateX(-20%) rotate(2.4deg); }
  62% { transform: translateX(2%) rotate(-2.2deg); }
  100% { transform: translateX(18%) rotate(1deg); }
}

/* Fishtail — it swims, and the sheet rings out from where it was. */
.fx--swim i {
  position: absolute;
  left: var(--fx-x, 50%);
  top: var(--fx-y, 50%);
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  border: 1px solid rgba(29, 90, 107, .55);
  border-radius: 50%;
  opacity: 0;
  animation: ripple 1150ms ease-out forwards;
  animation-delay: calc(var(--ring) * 170ms);
}
@keyframes ripple {
  0% { transform: scale(1); opacity: .8; }
  100% { transform: scale(46); opacity: 0; }
}
html[data-state="plate"] .chapter[data-open][data-effect="swim"] .object__art {
  animation: swim 1150ms ease-in-out both;
}
@keyframes swim {
  0% { transform: translateX(-16%) rotate(4deg); }
  40% { transform: translateX(4%) rotate(-4deg); }
  70% { transform: translateX(-2%) rotate(3deg); }
  100% { transform: none; }
}

/* City Lifestyle — the pages riffle. */
.fx--riffle { perspective: 900px; }
.fx--riffle i {
  position: absolute;
  left: var(--fx-x, 50%);
  top: var(--fx-y, 50%);
  width: 15%;
  height: 20%;
  margin: -10% 0 0 0;
  transform-origin: left center;
  background: linear-gradient(100deg, var(--paper-lit), #efece2);
  box-shadow: 0 0 0 1px rgba(31, 33, 29, .18);
  opacity: 0;
  animation: riffle 1100ms ease-in-out forwards;
  animation-delay: calc(var(--page) * 90ms);
}
@keyframes riffle {
  0% { transform: rotateY(0deg); opacity: 0; }
  12% { opacity: .95; }
  85% { opacity: .95; }
  100% { transform: rotateY(-168deg); opacity: 0; }
}

/* AI Growth Labs — the dividers step off the sheet, mark by mark. */
.fx--step i {
  position: absolute;
  left: calc(8% + var(--step) * 9.4%);
  top: calc(var(--fx-y, 50%) + 12%);
  width: 1px;
  height: 0;
  background: var(--chapter-ink);
  opacity: .7;
  animation: step-mark 160ms ease-out forwards;
  animation-delay: calc(var(--step) * 95ms);
}
@keyframes step-mark { to { height: 22px; } }
html[data-state="plate"] .chapter[data-open][data-effect="step"] .object__art {
  animation: step-walk 1150ms ease-in-out both;
}
@keyframes step-walk {
  0% { transform: rotate(0deg); }
  25% { transform: rotate(13deg); }
  50% { transform: rotate(-11deg); }
  75% { transform: rotate(9deg); }
  100% { transform: rotate(0deg); }
}

/* Blackline — two rules are struck across the document, not across the words
   that describe it: they stay in the object's own column. */
.fx--strike i,
.fx--strike::after {
  content: "";
  position: absolute;
  left: calc(var(--fx-x, 50%) - 15%);
  top: calc(var(--fx-y, 50%) - 4%);
  height: 2px;
  width: 0;
  background: var(--chapter-ink);
  animation: strike 520ms var(--press) forwards;
}
.fx--strike::after { top: calc(var(--fx-y, 50%) + 6%); height: 1px; animation-delay: 220ms; }
@keyframes strike { to { width: 30%; } }

/* Greenroom — the question carries. */
.fx--resonate i {
  position: absolute;
  left: var(--fx-x, 50%);
  top: var(--fx-y, 50%);
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border: 1px solid var(--chapter-ink);
  border-radius: 50%;
  opacity: 0;
  animation: resonate 1100ms ease-out forwards;
  animation-delay: calc(var(--ring) * 200ms);
}
@keyframes resonate {
  0% { transform: scale(1); opacity: .75; }
  100% { transform: scale(30); opacity: 0; }
}

/* Slate — the rubric is marked. */
.fx--tick i {
  position: absolute;
  left: calc(var(--fx-x, 50%) + 7%);
  top: calc(var(--fx-y, 50%) - 8% + var(--mark) * 9%);
  width: 20px;
  height: 11px;
  border-left: 2px solid var(--chapter-ink);
  border-bottom: 2px solid var(--chapter-ink);
  transform: rotate(-45deg) scale(.2);
  opacity: 0;
  animation: tick-in 300ms var(--swing) forwards;
  animation-delay: calc(var(--mark) * 180ms);
}
@keyframes tick-in { to { transform: rotate(-45deg) scale(1); opacity: 1; } }

/* Lintel — light comes through the opening. */
.fx--open .fx__shaft {
  position: absolute;
  left: var(--fx-x, 50%);
  top: var(--fx-y, 50%);
  width: 0;
  height: 60%;
  margin-left: 0;
  background: linear-gradient(to bottom, rgba(255, 252, 240, .85), rgba(255, 252, 240, 0));
  transform: translateX(-50%) skewX(-9deg);
  animation: shaft 760ms var(--press) forwards;
}
@keyframes shaft { to { width: 26%; } }

/* Savvica — two lines are written on the board, in the board's own ink. */
.fx--chalk i,
.fx--chalk::after {
  content: "";
  position: absolute;
  left: calc(var(--fx-x, 50%) - 7%);
  top: calc(var(--fx-y, 50%) - 5%);
  height: 2px;
  width: 0;
  border-radius: 2px;
  background: var(--chapter-ink);
  opacity: .75;
  animation: chalk 620ms var(--press) forwards;
}
.fx--chalk::after { top: calc(var(--fx-y, 50%) + 1%); animation-delay: 240ms; }
@keyframes chalk { to { width: 14%; } }

/* Barclays — the grid is swept. */
.fx--scan i {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 2px;
  background: var(--chapter-ink);
  box-shadow: 0 0 22px 10px rgba(31, 63, 107, .18);
  animation: scan 1000ms linear forwards;
}
@keyframes scan { from { top: -2%; opacity: 0; } 12% { opacity: .85; } to { top: 100%; opacity: 0; } }

/* The Ontario bar — the bands drop and settle. */
html[data-state="plate"] .chapter[data-open][data-effect="fall"] .object__art {
  animation: fall 1000ms var(--swing) both;
  transform-origin: 50% 8%;
}
@keyframes fall {
  0% { transform: translateY(-38%) rotate(-11deg); }
  55% { transform: translateY(0) rotate(7deg); }
  78% { transform: rotate(-4deg); }
  100% { transform: rotate(0deg); }
}

/* Not a job — the real footage, in a paper frame, standing where the engraved
   fire engine was. The engraving steps aside; the words do not. */
.fx--film .fx__film {
  position: absolute;
  left: var(--fx-x, 50%);
  top: var(--fx-y, 50%);
  width: min(22%, 230px);
  height: auto;
  translate: -50% -50%;
  background: var(--paper-lit);
  padding: 8px;
  box-shadow: 0 0 0 1px rgba(31, 33, 29, .35), 0 24px 44px -28px rgba(31, 33, 29, .7);
  filter: saturate(.72) sepia(.14) contrast(1.04);
  animation: film-in 620ms var(--press) both;
}
@keyframes film-in {
  from { opacity: 0; transform: scale(.94); }
  to { opacity: 1; transform: none; }
}
html[data-state="plate"] .chapter[data-open][data-effect="film"] .object__art {
  animation: film-swap 420ms var(--press) forwards;
}
@keyframes film-swap { to { opacity: 0; transform: scale(.9); } }

/* --- L6  colophon and the reel ------------------------------------------ */

.world__rail {
  color: var(--ink-soft);
  font-size: .78rem;
  line-height: 1.8;
}
.colophon {
  margin-top: clamp(2rem, 4vw, 3.5rem);
  padding-top: 1.1rem;
  border-top: 1px solid var(--rule);
  color: var(--ink-soft);
  font-size: .75rem;
}
.colophon__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .4rem 2rem;
  margin-top: .5rem;
}
.colophon nav { display: flex; flex-wrap: wrap; gap: .2rem 1.3rem; }
.colophon a { display: inline-flex; align-items: center; min-height: 40px; }
.colophon a:hover { color: var(--vermillion); }
html[data-state="plate"] .colophon,
html[data-state="plate"] .world__rail { opacity: .12; }

.reel {
  border: 0;
  padding: 0;
  background: var(--paper-lit);
  box-shadow: 0 0 0 1px var(--ink), 0 40px 80px -40px rgba(31, 33, 29, .8);
}
.reel::backdrop { background: rgba(31, 33, 29, .62); }
.reel video { display: block; width: min(86vw, 540px); height: auto; }
.reel__close {
  position: absolute;
  top: .2rem;
  right: .2rem;
  width: 44px;
  height: 44px;
  font-size: 1.4rem;
  color: var(--paper-lit);
  mix-blend-mode: difference;
}
html:not([data-state]) .reel { display: none; }

/* --- the phone ---------------------------------------------------------- */

@media (max-width: 760px) {
  .world__sheet { width: min(100% - 1.8rem, 100%); }
  html[data-state] .still-life__box { aspect-ratio: 100 / 152; }
  .still-life__case--wide { display: none; }
  .still-life__case--narrow { display: block; }
  html[data-state] .chapter {
    left: var(--sx);
    top: var(--sy);
    width: var(--sw);
    rotate: var(--sturn);
  }
  html[data-state="plate"] .chapter[data-open] {
    left: 50%;
    top: 16%;
    width: 42%;
    translate: -50% 0;
  }
  html[data-state] .plate {
    left: -62%;
    width: 224%;
    top: calc(100% + 1.6rem);
    translate: 0 0;
  }
  .plate__close { top: -3rem; right: 0; }
  .fx--film .fx__film { width: 46%; }
  /* The boxes are wider than the ink on a phone, so the label is measured
     against the object's own ink instead: two or three short lines that cannot
     reach the next object's label. */
  html[data-state] .object__caption {
    width: max(var(--iw, 100%), 4.2rem);
    top: calc(100% - var(--foot, 0%) + .45rem);
  }
  html[data-state] .object__label { font-size: .52rem; letter-spacing: .04em; line-height: 1.3; }
  /* The words sit under the object on a phone, so a transformation that runs
     down the sheet has to stop before it reaches them. */
  @keyframes pour-stream { from { height: 0; } to { height: 9%; } }
  @keyframes pour-pool { from { height: 0; } to { height: 14%; } }
  /* No room for the cup between the spout and the words: the sheet takes the
     pour instead. */
  .fx--pour .fx__cup { display: none; }
  @keyframes milk-fill { from { height: 0; } to { height: 40%; } }
  .fx--fill .fx__gleam { left: 26%; width: 9%; }
  @keyframes strike { to { width: 26%; } }
  .fx--strike i, .fx--strike::after { left: calc(var(--fx-x, 50%) - 13%); }
  .fx--chalk i, .fx--chalk::after { left: calc(var(--fx-x, 50%) - 6%); }
  @keyframes chalk { to { width: 12%; } }
  /* The words take the sheet below the object on a phone, so the gel runs in
     the clear band above it instead. */
  .fx--gel .fx__plate { left: 4%; right: 4%; top: 1%; height: 14.5%; }
  .fx--gel .fx__lane { left: calc(7% + var(--lane) * 12.4%); width: 8%; top: 2.5%; height: 11.5%; }
  .fx--gel .fx__lane i:nth-child(1) { height: 5px; }
  .fx--gel .fx__lane i:nth-child(2) { height: 4px; }
  .fx--gel .fx__lane i:nth-child(3) { height: 2px; }
  @keyframes sea-rise { to { height: 23%; } }
  .fx--sail .fx__swell { bottom: 19%; height: 20px; }
  .fx--sail .fx__wake { bottom: 21%; left: calc(var(--fx-x, 50%) - 22%); width: 20%; }
  .card-faces { width: min(90vw, 380px); }
  .colophon__row { flex-direction: column; align-items: flex-start; }
}

/* The narrowest phones still in use: the shelf labels get smaller rather than
   wider, because the objects they belong to are only a finger apart. */
@media (max-width: 400px) {
  html[data-state] .object__caption { width: max(var(--iw, 100%), 3.4rem); }
  html[data-state] .object__label { font-size: .47rem; letter-spacing: .02em; }
}

/* --- reduced motion ----------------------------------------------------- */

/* No flood, no flight, no stroke-on, no transformation. The press swaps the
   card for the field, picking an object moves it and sets the words, and
   everything arrives already drawn. */
@media (prefers-reduced-motion: reduce) {
  .world *,
  .world *::before,
  .world *::after {
    animation-duration: 1ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
  .world__ink,
  .world__wash { display: none !important; }
  html[data-state] .object .engraving :is(path, circle):not(.engraving__blot) {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
  }
  html[data-state] .object .engraving .engraving__blot { opacity: 1; }
  html[data-state] .object:hover, html[data-state] .object:focus-visible,
  .world__mark:hover { transform: none; }
  .card-shell:hover { --hover-lift: 0px; }
  html[data-state="plate"] .world__scrim { opacity: .88; }
  .world__effects { display: none; }
}

/* --- no JavaScript ------------------------------------------------------ */

/* The same content as a plain document: the card at the top, then one chapter
   after another, engraving beside its paragraph. The object already carries
   the label and the fact, so the plate contributes only the body and link. */
html:not([data-state]) .world__card-stage { min-height: auto; padding-block: 3rem 1rem; }
html:not([data-state]) .world__effects { display: none; }
html:not([data-state]) .shelf { padding-top: 2rem; }
html:not([data-state]) .shelf__label {
  margin-bottom: 1rem;
  font: .7rem/1 ui-monospace, SFMono-Regular, Consolas, monospace;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--rule);
  padding-bottom: .8rem;
}
html:not([data-state]) .chapter {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr);
  align-items: start;
  gap: 0 1.6rem;
  padding-block: 1.5rem;
  border-top: 1px solid var(--rule);
}
html:not([data-state]) .object { display: contents; }
html:not([data-state]) .object__art { grid-column: 1; grid-row: 1 / 3; }
html:not([data-state]) .object .engraving { max-height: 92px; }
html:not([data-state]) .object__caption { grid-column: 2; text-align: left; }
html:not([data-state]) .plate { grid-column: 2; }
html:not([data-state]) :is(.plate__label, .plate__fact) { display: none; }
html:not([data-state]) .plate__body { margin-top: .6rem; font-size: .95rem; }
html:not([data-state]) .world__watermark { display: none; }

/* --- print -------------------------------------------------------------- */

@media print {
  .world::before,
  .world__ink,
  .world__wash,
  .world__scrim,
  .world__watermark,
  .world__effects,
  .world__mark,
  .card-turn,
  .card--back,
  .card__gloss,
  .plate__close,
  .reel { display: none !important; }
  .world__card-stage { min-height: auto; padding: 0 0 1.5rem; }
  .card-faces { width: 100%; max-width: 420px; aspect-ratio: auto; transform: none; }
  .card { position: static; box-shadow: none; }
  .world__sheet { display: block !important; }
  .still-life__box { aspect-ratio: auto !important; }
  .chapter {
    position: static !important;
    display: block;
    width: auto !important;
    rotate: none !important;
    break-inside: avoid;
    padding-bottom: 1rem;
  }
  .plate { position: static !important; display: block !important; max-width: none; translate: none; }
  .object { display: block; text-align: left; }
  .object__art { display: none; }
  .object__caption {
    position: static !important;
    opacity: 1 !important;
    width: auto !important;
    translate: none !important;
  }
  /* On paper the label is a heading again, not a tag on a shelf edge. */
  .object__label {
    font-family: var(--font-display, Georgia, serif) !important;
    font-size: 1rem !important;
    letter-spacing: -.012em !important;
    text-transform: none !important;
    color: inherit !important;
  }
  .object__label::after { display: none; }
  .object__fact { display: block !important; }
  .plate__link::after { content: " (" attr(href) ")"; font-size: .8em; }
}
