/* ==========================================================================
   tarotdaemon — ink on paper
   One stylesheet, layered: tokens, reset, chrome, forms, wait, table.
   ========================================================================== */

@font-face {
  font-family: "Doves Type";
  src: url("/assets/DovesType-Regular-edfeaa85.woff2") format("woff2");
  /* One roman cut. Map the weights the page already asks for so the
     browser does not synthesise a light or a bold. Italic still slants. */
  font-weight: 300 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "TarotDaemon Dingbats";
  src: url("/assets/TarotDaemonDingbats-Regular-7b2e273e.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: block;
  unicode-range: U+2726;
}

:root {
  --paper:        #f4f1ea;
  --paper-panel:  #eae5da;
  --paper-raised: #fbf9f5;

  --ink:          #2f2c27;
  --ink-dim:      #6a655b;
  --ink-faint:    #97917f;

  --gold:         #856121;
  --gold-tint:    #c0a984;
  --ember:        #a3452e;

  --rule:         rgba(47, 44, 39, 0.13);
  --rule-strong:  rgba(47, 44, 39, 0.28);

  /* Cards sit on the page rather than glowing out of it. */
  --lift:         0 1px 2px rgba(47, 44, 39, 0.10), 0 6px 18px rgba(47, 44, 39, 0.13);
  --lift-high:    0 2px 4px rgba(47, 44, 39, 0.12), 0 12px 30px rgba(47, 44, 39, 0.20);

  /* The cloth the cards are dealt onto: dark warm wool, lit from the head of
     the page like the wash above it. Cards on cloth need their own elevation
     and their own dimming — a paper shadow is too soft to read against it, and
     a translucent card over cloth goes dark rather than faint. */
  --cloth:           #b3a48c;
  --cloth-sheen:     rgba(255, 250, 238, 0.34);  /* lit from the head */
  --cloth-shade:     rgba(74, 66, 52, 0.20);     /* pooling at the foot */
  --cloth-drape:     rgba(74, 66, 52, 0.16);     /* the fall into the edges */
  --cloth-mark:      rgba(133, 97, 33, 0.34);    /* the hem stitch on the cloth */
  --cloth-lift:      0 1px 2px rgba(41, 36, 27, 0.24), 0 8px 20px rgba(41, 36, 27, 0.26);
  --cloth-lift-high: 0 2px 4px rgba(41, 36, 27, 0.28), 0 14px 30px rgba(41, 36, 27, 0.34);
  --cloth-bleed:     clamp(1.75rem, 5vw, 3.5rem); /* cloth beyond the card box */
  --cloth-hem:       1.1rem;    /* the stitch line, inside the cloth's edge */
  --cloth-dim:       0.62;      /* how far a card not being read sinks in */

  --display: "Doves Type", Georgia, serif;
  --ornament: "TarotDaemon Dingbats", "Zapf Dingbats", "Segoe UI Symbol", sans-serif;
  --body: var(--display);
  /* Labels: real small caps from Doves Type (`smcp` + `c2sc`). Uppercase
     stays as the fallback if the face does not load. Small-cap glyphs read
     smaller than the old sans, so --label-scale doubles the label sizes. */
  --label-caps: all-small-caps;
  --label-scale: 2;
  --copy-size: 1.25rem;
  --copy-leading: 1.65;
  --prose-size: 1.55rem;
  /* A card's notes sit a step under the reading's own voice. */
  --note-size: 1.4rem;
  --note-leading: 1.4rem;
  --footnote-size: 1rem;
  --keyword-size: 1rem;

  --measure: 34rem;
  /* Operator tables: at least four-fifths of the viewport, never past the
     page's own gutter. */
  --wide-measure: 80vw;
  /* Pricing: two plans side by side, each a little wider than half a measure. */
  --plans-measure: 54rem;
  --gutter: clamp(1.25rem, 4vw, 3rem);
  /* A ruled row: a hairline, not a padded cell. Shared by .entries, .listing,
     .facts and .history. */
  --row-pad: 0.2rem;
  /* Tighter than --gutter: on a phone the reading is one column and the cloth
     already runs to the edges, so the page's own inset is excess. */
  --reading-narrow-gutter: 0.75rem;
  --prose-leading: 2.1rem;
  --passage-heading-size: calc(0.7rem * var(--label-scale));
  --conclusion-heading-size: calc(1.05rem * var(--label-scale));
  /* A card's position sits under "The Spread", and reads a step down from it. */
  --position-heading-size: calc(1.05rem * var(--label-scale));
  --conclusion-size: var(--prose-size);
  --conclusion-leading: var(--prose-leading);
  --conclusion-drop: 3.75rem;
  --conclusion-fade: 0.5s;
  --reading-stage-span: 2fr;
  --reading-stream-span: 1fr;
  --reading-width: 84rem;
  --reading-nav-width: 80%;
  --reading-nav-inset: 1.5rem; /* gap between the pill and the colophon */
  /* Reserved strip so last copy is not under the pinned colophon. */
  --colophon-space: 4.5rem;

  /* The wait's sky. Gold ink on paper, not light: the stars are drawn in the
     same two golds as every other mark on the page. pixel_sky.js reads these
     two off the canvas, so the palette is not duplicated in JavaScript. */
  --sky-ink: var(--gold-tint);
  --sky-ink-bright: var(--gold);

  --wait-corona-size: 64px;
  --wait-halo-border: 2px;
  --wait-breathe-duration: 2s;
  --wait-breathe-min: 0.2;
  --wait-stage-gap: 1.5rem;

  /* The pixel sky behind the wait. One art pixel, and the ink its marks are
     drawn in: gold at its faintest, its half state and its brightest. A star
     is ink on paper, so these are opacities and never a glow. */
  --sky-pixel: 2px;
  --sky-ink:     var(--gold-tint);
  --sky-ink-lit: var(--gold);
  --sky-dim: 0.18;
  --sky-mid: 0.46;
  --sky-lit: 0.9;
  /* The meteor's own pixel box, matching #px-meteor's viewBox, how far it
     travels, and how many jumps it takes to get there. */
  --sky-meteor-grid-w: 18;
  --sky-meteor-grid-h: 2;
  --sky-meteor-travel: 52vmin;
  --sky-meteor-steps: 16;

  /* Ornament. One stroke weight for every drawn mark, so the fleuron glyph,
     the corner flourish and the tailpiece all read as the same hand. The
     wait's question rules are the one exception: a heavier finish, set apart
     so the rest of the grammar stays a hairline. */
  --ornament-stroke:       1;
  --ornament-stroke-heavy: 2.5;
  --ornament-terminal:     clamp(2rem, 6vw, 3rem);  /* a terminal's box, 38 × 16 */
  --ornament-tailpiece:    clamp(8rem, 34%, 12rem);
  --ornament-rule-offset:  3rem;     /* the bound, out from the content column */
  --ornament-bound-inset:  1.5rem;   /* where the bound's rules stop short */
  /* Drops the masthead dingbat so its ink centre meets Doves Type's cap height. */
  --masthead-glyph-shift:  0.16em;
  --masthead-mark-gap:     0.35em;
  --colophon-rule-terminal: 1.15rem; /* a terminal's box in the footer */
  --colophon-rule-min:      3.5rem;  /* shortest a stretching footer rule may go */

  /* The placing table's beds. Ink is the only colour that reads on the
     cloth (5.7:1); gold, ember and the dim inks fall under 3:1 there. */
  --bed-stitch:      rgba(47, 44, 39, 0.45);   /* --ink, an empty bed's dash */
  --bed-stitch-firm: rgba(47, 44, 39, 0.8);    /* hovered or focused */
  --bed-fill:        rgba(255, 250, 238, 0.16);
  --bed-ring:        var(--ink);
  --deck-thumb:      4.75rem;  /* a deck card's narrowest column */
  --deck-thumb-narrow: 4.2rem;

  --card-ratio: 0.6;      /* 768 × 1280 card scans */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

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

html { -webkit-text-size-adjust: 100%; color-scheme: light; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  padding-bottom: var(--colophon-space);
  background: var(--paper);
  color: var(--ink);
  font: 300 var(--copy-size)/var(--copy-leading) var(--body);
  letter-spacing: 0.01em;
  overflow-x: hidden;
}

/* A single fixed wash behind everything: warm at the head, cooler and heavier
   at the foot and corners, the way a sheet ages. No image, no repaint. */
.wash {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 120% 70% at 50% -15%, rgba(196, 172, 118, 0.20), transparent 65%),
    radial-gradient(ellipse 100% 60% at 50% 115%, rgba(120, 112, 96, 0.16), transparent 65%),
    radial-gradient(circle at 8% 12%, rgba(47, 44, 39, 0.05), transparent 38%),
    radial-gradient(circle at 94% 84%, rgba(47, 44, 39, 0.05), transparent 38%);
}

h1, h2, h3 { font-family: var(--display); font-weight: 400; letter-spacing: 0.01em; line-height: 1.15; margin: 0; }
p { margin: 0 0 1em; }
a { color: var(--ink); text-decoration-color: var(--rule-strong); text-underline-offset: 0.25em; }
a:hover { color: var(--gold); }

/* The attribute wins over any component rule that sets display. Without
   this a flex or grid rule shows what [hidden] was meant to hide. */
[hidden] { display: none !important; }

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

.masthead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
  padding: 1.5rem var(--gutter);
  border-bottom: 1px solid var(--rule);
}

.masthead__mark {
  display: inline-flex;
  align-items: baseline;
  gap: var(--masthead-mark-gap);
  font-family: var(--display);
  font-size: 2.2rem;
  letter-spacing: 0.12em;
  line-height: 1;
  text-decoration: none;
  color: var(--ink);
}
.masthead__glyph {
  font-family: var(--ornament);
  font-style: normal;
  font-variant-caps: normal;
  letter-spacing: 0;
  line-height: 1;
  color: var(--gold);
  /* The dingbat sits high in its em against Doves Type's short cap height.
     This drop puts the star's ink centre on the word's cap-height centre. */
  transform: translateY(var(--masthead-glyph-shift));
}

.masthead__nav {
  display: flex;
  align-items: baseline;
  gap: 1.5rem;
  font-size: calc(0.8rem * var(--label-scale));
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.masthead__nav a {
  color: var(--ink-dim);
  text-decoration: none;
}
.masthead__nav a:hover { color: var(--gold); }
.masthead__nav a[aria-current="page"] { color: var(--ink); }

/* Clipped until focus, then a paper chip at the head of the sheet.
   .visually-hidden supplies the clip. These rules undo it. Print
   drops the link. The target outline is suppressed so the whole
   page does not ring when the skip link moves focus to main.
   The illustrated pages' link to the linear reading wears the same
   classes, inside a label line, so focus also restores body type. */
.skip:focus,
.skip:focus:hover {
  position: fixed;
  top: 0.75rem;
  left: var(--gutter);
  z-index: 2;
  width: auto;
  height: auto;
  padding: 0.6rem 1rem;
  overflow: visible;
  clip-path: none;
  white-space: normal;
  background: var(--paper-raised);
  color: var(--ink);
  outline: 1px solid var(--gold);
  outline-offset: 0.25rem;
  text-decoration: none;
  text-transform: none;
  letter-spacing: 0.01em;
  font: 300 var(--copy-size)/var(--copy-leading) var(--body);
}
@media print {
  .skip { display: none; }
}

.account-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 2.5rem;
}
.account-actions form { margin: 0; }
.account-actions .sub { margin: 0; }

/* Entitlement on the account page: apparatus, not prose and not a card.
   Plan and remaining readings sit on one centered line. #orn-rule under
   them is the separator between identity and the form — half the column,
   hairline stroke. The wait's heavier rule stays the wait's. */
.sub:has(+ .quota),
.account-actions:has(+ .quota) { margin-bottom: 0; }
.account-delete {
  margin-top: 2.5rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--rule);
}
.quota {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.65rem;
  margin: 1.75rem 0 2.5rem;
}
.quota__line {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  column-gap: 1rem;
  row-gap: 0.25rem;
  margin: 0;
  text-align: center;
}
.quota__plan {
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold);
}
.quota__usage {
  color: var(--ink-dim);
  font-size: 1.2rem;
}
.quota__usage em {
  font-style: italic;
  color: var(--gold);
  font-family: var(--display);
  font-size: 2em;
}
.quota__rule {
  display: block;
  width: 50%;
  aspect-ratio: 240 / 16;
  color: var(--gold-tint);
  fill: none;
  stroke: currentColor;
  stroke-width: var(--ornament-stroke);
  stroke-linecap: butt;
  overflow: visible;
  pointer-events: none;
}

.page {
  position: relative;   /* the ruled bound is positioned against it */
  flex: 1;
  width: 100%;
  padding: clamp(2rem, 6vw, 5rem) var(--gutter);
}
.page:focus { outline: none; }

/* One row, mirroring the masthead: labels at the left, credit at the right,
   and a finished hairline in each gap. On a phone the rules drop out and
   the row stays quiet. Pinned to the viewport foot so it stays in view;
   print puts it back in flow. Paper fill so the page cannot show through. */
.colophon {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 0.5rem 0.65rem;
  flex-wrap: wrap;
  padding: 0.9rem var(--gutter);
  border-top: 1px solid var(--rule);
  background: var(--paper);
  color: var(--ink-faint);
  font-size: 0.9rem;
  letter-spacing: 0.09em;
}
@media print {
  body { padding-bottom: 0; }
  .colophon { position: static; background: none; }
}
.colophon__credit { margin: 0; }
.colophon__rule {
  display: flex;
  align-items: center;
  flex: 1 1 var(--colophon-rule-min);
  min-width: var(--colophon-rule-min);
  color: var(--gold-tint);
  pointer-events: none;
}
.colophon__rule-hair {
  flex: 1 1 auto;
  min-width: 0;
  height: 0;
  border-top: 1px solid currentColor;
}
.colophon__rule-end {
  flex: 0 0 var(--colophon-rule-terminal);
  width: var(--colophon-rule-terminal);
  height: calc(var(--colophon-rule-terminal) * 16 / 38);
}
.colophon__rule-end--start { transform: scaleX(-1); }
@media (max-width: 40rem) {
  .colophon__rule { display: none; }
  .colophon__credit { margin-left: auto; }
}
.colophon__link {
  font-size: calc(0.7rem * var(--label-scale));
  text-transform: uppercase;
  text-decoration: none;
  letter-spacing: 0.12em;
  color: var(--ink-dim);
}
.colophon__link:hover { color: var(--gold); }
.colophon__credit-link {
  color: inherit;
  text-decoration: none;
}
.colophon__credit-link:hover { color: var(--gold); }

.flashes { padding: 0 var(--gutter); margin-top: 1rem; }
.flash {
  margin: 0 auto;
  max-width: var(--measure);
  padding: 0.7rem 1rem;
  border-left: 2px solid var(--gold);
  background: var(--paper-raised);
  font-size: 1.15rem;
}
.flash--alert { border-left-color: var(--ember); }

/* --- prose blocks -------------------------------------------------------- */

.centered { max-width: var(--measure); margin-inline: auto; }

.lede {
  font-family: var(--display);
  font-size: clamp(1.9rem, 5vw, 2.9rem);
  font-weight: 300;
  line-height: 0.95;
  margin-bottom: 0.6rem;
  /* Wrapped lines sit under the first word, so a long lede keeps one edge. */
  padding-inline-start: 0.5em;
  text-indent: -0.5em;
}
.lede em { font-style: italic; color: var(--gold); }

/* Session page: the heading names the active mode, and the switch below it
   is two text controls and a dark pipe. Stimulus sets aria-pressed and
   hidden; these rules decide what that looks like. Each
   label uses the lede's gold italic on the last word. The inactive label
   recedes. No underline. The pipe is larger and ink, never gold. Nothing
   here is ornament. */
.auth-switch {
  --inactive: 0.42;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 0.35em;
  margin-bottom: 1.75rem;
  /* Two controls, not a wrapping sentence: drop the lede's hanging indent. */
  padding-inline-start: 0;
  text-indent: 0;
}
.auth-switch__mode {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--ink);
  cursor: pointer;
  white-space: nowrap;
  opacity: var(--inactive);
}
.auth-switch__mode:hover {
  color: var(--gold);
  opacity: 1;
}
.auth-switch__mode[aria-pressed="true"] {
  cursor: default;
  opacity: 1;
}
.auth-switch__mode[aria-pressed="true"]:hover { color: var(--ink); }
.auth-switch__pipe {
  font-size: 1.35em;
  font-style: normal;
  font-weight: 400;
  line-height: 1;
  color: var(--ink);
  user-select: none;
}

/* The closed sign's own phrase, inside an ordinary lede. Its own block, so
   the sentence around it stays at reading size and this carries the weight.
   Upright rather than italic: Doves Type ships a real `smcp` table, and
   synthesised italic small capitals would fight it. */
.lede__notice {
  display: block;
  /* One line, always. The phrase is twenty-odd characters and the column is
     capped at --measure, so the size is bounded by what fits rather than by
     taste: 8vw on a phone, 3.2rem once the column stops growing. `nowrap`
     holds the line if a font substitution comes in wider than expected. */
  font-size: min(3.2rem, 8vw);
  font-style: normal;
  font-variant-caps: small-caps;
  letter-spacing: 0.04em;
  line-height: 1.15;
  white-space: nowrap;
}

.sub {
  color: var(--ink-dim);
  font-size: 1.4rem;
  line-height: 2rem;
}

/* Long-form pages: the colophon and anything else that is mostly words. */
.prose h2 {
  font-family: var(--display);
  font-size: 1.5rem;
  font-weight: 400;
  letter-spacing: 0.02em;
  margin: 1.6rem 0 0.75rem;
}
.prose p {
  line-height: 1.7;
  margin-bottom: 1.1rem;
}
.prose em { color: var(--gold); }

.eyebrow {
  font-size: calc(0.7rem * var(--label-scale));
  line-height: 1em;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 0.75rem;
}

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

.field { margin-bottom: 1.75rem; }
.field > label {
  display: block;
  font-size: calc(0.72rem * var(--label-scale));
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 0.6rem;
}
.field__row {
  display: flex;
  align-items: stretch;
  gap: 0.75rem;
}
.field__row input[type="text"] {
  width: auto;
  flex: 1 1 auto;
  min-width: 0;
}
.field__row .button { flex: 0 0 auto; }
.field__hint { font-size: 1rem; color: var(--ink-faint); margin: 0.5rem 0 0; }
/* The address is claimed and the mail has not come back. Larger than a
   hint, and marked like an error, because Subscribe cannot proceed. */
.account-waiting {
  /* The same space above the email field and below, before the resend
     button. A bare bottom would leave the button against the line. */
  margin: 0.85rem 0;
  padding-left: 0.85rem;
  border-left: 2px solid var(--ember);
  font-size: var(--prose-size);
  line-height: 1.35;
  color: var(--ink);
}
.field--check {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 0.75rem;
  align-items: start;
}
.field--check input[type="hidden"] { display: none; }
.field--check input[type="checkbox"] {
  width: auto;
  margin-top: 0.35em;
  accent-color: var(--gold);
}
.field--check > label {
  text-transform: none;
  letter-spacing: 0;
  font-variant-caps: normal;
  font-size: var(--copy-size);
  color: var(--ink);
  margin-bottom: 0;
}
.field--check > .field__hint { grid-column: 2; }
.field__value {
  margin: 0;
  font: 300 var(--copy-size)/1.5 var(--body);
  color: var(--ink);
}

.field__secret { position: relative; }
.field__unmask {
  position: absolute;
  right: 0.75rem;
  top: 50%;
  transform: translateY(-50%);
  padding: 0;
  background: none;
  border: 0;
  color: var(--gold);
  font: 400 calc(0.72rem * var(--label-scale))/1 var(--body);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  cursor: pointer;
}
.field__unmask:hover { color: var(--ink); }
.field__secret .field_with_errors { display: contents; }

input[type="text"], input[type="password"], input[type="email"], select, textarea {
  width: 100%;
  padding: 0.85rem 1rem;
  background: var(--paper-raised);
  color: var(--ink);
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  font: 300 var(--copy-size)/1.5 var(--body);
  transition: border-color 0.25s var(--ease);
}
textarea {
  font-family: var(--display);
  font-size: 1.5rem;
  line-height: 1.4;
  resize: vertical;
  min-height: 6rem;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--gold);
}
.field__secret input { padding-right: 4.5rem; }
select { appearance: none; background-image: none; cursor: pointer; }
select option { background: var(--paper-raised); }

.button {
  display: inline-block;
  padding: 0.85rem 2.25rem;
  background: transparent;
  color: var(--gold);
  border: 1px solid var(--gold);
  border-radius: 2px;
  font: 400 calc(0.78rem * var(--label-scale))/1 var(--body);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.3s var(--ease), color 0.3s var(--ease);
}
.button:hover { background: var(--gold); color: var(--paper); }
/* Disabled says so with the rule's own grey. What it waits for is written
   beside it. */
.button:disabled,
.button:disabled:hover {
  background: transparent;
  color: var(--rule-strong);
  border-color: var(--rule-strong);
  cursor: not-allowed;
}

/* A quiet action set as a label: Clear, Remove, All positions. */
.text-button {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: 400 calc(0.62rem * var(--label-scale))/1 var(--body);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-dim);
  cursor: pointer;
}
.text-button:hover { color: var(--gold); }
.text-button:focus-visible { outline: 1px solid var(--gold); outline-offset: 0.25rem; }
.text-button--ember { color: var(--ember); }

.ask {
  text-align: center;
  margin-bottom: 0;
}
.lede + .ask { margin-top: 1.75rem; }

/* Remaining-draw line on the library, grouped with Ask a Question rather
   than with the welcome. The account page's giant em stays on .quota. */
.ask-remaining {
  color: var(--ink-dim);
  font-size: 1.2rem;
  text-align: center;
  margin: 0 0 0.75rem;
}
.lede + .ask-remaining { margin-top: 1.75rem; }
.ask-remaining em {
  font-style: italic;
  color: var(--gold);
}

.claim {
  text-align: center;
  margin-top: 1.75rem;
}
.claim .button {
  white-space: nowrap;
}
.claim__note {
  color: var(--ink-dim);
  font-size: 1.2rem;
  margin: 0.5rem 0 0;
}

.errors {
  border-left: 2px solid var(--ember);
  padding: 0.6rem 1rem;
  margin-bottom: 1.5rem;
  font-size: 1.15rem;
  color: var(--ink-dim);
}
.errors ul { margin: 0; padding-left: 1.1rem; }

.form-aside {
  margin-top: 2rem;
  font-size: 1.05rem;
  color: var(--ink-faint);
}

/* --- wait (while the draw runs) ------------------------------------------ */

/* The sky fills the content area and nothing else: it is positioned against
   `.page`, which is already relative, so the masthead and the colophon stay
   clear of it. Everything drawn on it comes from pixel_sky.js.

   A canvas cannot be printed as ink and the sky is not part of the reading, so
   it simply is not there on paper. */
.sky {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
@media print {
  .sky { display: none; }
}

.wait {
  /* Above the sky, which shares the content area with it. */
  position: relative;
  z-index: 1;
  min-height: 60vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.wait__question {
  font-family: var(--display);
  font-style: italic;
  font-size: clamp(1.3rem, 6vw, 3rem);
  line-height: 1.2em;
  color: var(--ink);
  max-width: 75vw;
  margin: 0.85rem 0;
}

/* A finished rule as one SVG (#orn-rule), half the viewport. Heavier than the
   rest of the ornament grammar — the wait is the one place a rule has to hold
   against the sky. Height follows the symbol's 240 × 16 box. */
.wait__rule {
  display: block;
  width: 50vw;
  height: calc(50vw * 16 / 240);
  color: var(--gold-tint);
  fill: none;
  stroke: currentColor;
  stroke-width: var(--ornament-stroke-heavy);
  stroke-linecap: butt;
  overflow: visible;
  pointer-events: none;
}

.wait__corona {
  display: block;
  width: var(--wait-corona-size);
  height: var(--wait-corona-size);
  margin-top: var(--wait-stage-gap);
  overflow: visible;
  flex-shrink: 0;
}
.wait__corona-track {
  fill: none;
  stroke: var(--rule-strong);
  stroke-width: var(--wait-halo-border);
}
.wait__corona-moon {
  fill: var(--gold);
}
.wait__corona-halo {
  fill: none;
  stroke: var(--gold);
  stroke-width: var(--wait-halo-border);
  opacity: 0;
}
.wait__corona--overrun .wait__corona-halo {
  animation: wait-breathe var(--wait-breathe-duration) ease-in-out infinite;
}
@keyframes wait-breathe {
  0%, 100% { opacity: var(--wait-breathe-min); }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .wait__corona--overrun .wait__corona-halo {
    animation: none;
    opacity: 1;
  }
}

.wait__status {
  margin-top: var(--wait-stage-gap);
  font-size: calc(0.75rem * var(--label-scale));
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* --- the table ----------------------------------------------------------- */

.reading__header,
.reading-layout { animation: rise 0.9s var(--ease) both; }
@keyframes rise {
  from { opacity: 0; transform: translateY(1.5rem); }
  to   { opacity: 1; transform: none; }
}

.reading__header {
  max-width: 80%;
  margin: 0 auto 3rem;
  text-align: center;
  /* Above the table. The rise animation and the sticky stage both create
     stacking contexts; without this, an open share body sits under the cloth. */
  position: relative;
  z-index: 3;
}
.reading__question {
  font-family: var(--display);
  font-style: italic;
  font-size: clamp(1.5rem, 4vw, 2.3rem);
  line-height: 1.2;
  margin-bottom: 0.75rem;
}
.reading__meta {
  font-size: calc(0.72rem * var(--label-scale));
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.reading__meta span + span::before { content: "·"; margin: 0 0.6em; color: var(--gold); }

/* The owner's share disclosure. One quiet line under the meta until opened.
   Its body lies over the page beneath, so opening it shifts nothing. */
.share { position: relative; margin-top: 1rem; }
.share__summary {
  display: inline-block;
  list-style: none;
  cursor: pointer;
  font-size: calc(0.72rem * var(--label-scale));
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.share__summary::-webkit-details-marker { display: none; }
.share__summary:hover,
.share__disclosure[open] .share__summary { color: var(--gold); }
.share__body {
  position: absolute;
  top: calc(100% + 0.75rem);
  left: 50%;
  translate: -50% 0;
  z-index: 3;
  width: min(var(--measure), calc(100vw - 2rem));
  padding: 1.5rem;
  background: var(--paper-raised);
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  box-shadow: var(--lift);
}
.share__consent { margin: 0 0 1.25rem; line-height: 1.6; color: var(--ink-dim); }
.share__consent em { font-style: italic; color: var(--gold); }
.share__body form { margin: 0; }
.share__link {
  width: 100%;
  margin: 1.25rem 0 1rem;
  padding: 0.55rem 0.75rem;
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  font-size: 1rem;
  text-align: center;
}
.share__links { margin: 0; color: var(--ink-faint); font-size: 1.1rem; }
.share__quiet {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--rule-strong);
  text-underline-offset: 0.25em;
  cursor: pointer;
}
.share__quiet:hover { color: var(--gold); }
/* Set apart, last: the one control that ends something. */
.share__body .button_to:last-child {
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
}
.share__stop {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 1.05rem;
  color: var(--ember);
  cursor: pointer;
}
@media print {
  .share { display: none; }
}

/* The shared page. The eyebrow names whose reading it is; the invitation is
   the one call to action for a visitor who never reaches the end. */
.reading__header .eyebrow { margin-bottom: 1rem; }
/* An interpretation's way back to its table: the eyebrow's own gold. */
.reading__header .eyebrow a { color: inherit; text-decoration-color: var(--gold-tint); }
/* A placement's one quiet line down to its interpretation offer. */
.reading__interpret {
  margin: 1rem 0 0;
  font-size: calc(0.72rem * var(--label-scale));
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.reading__interpret a { color: var(--ink-dim); text-decoration: none; }
.reading__interpret a:hover { color: var(--gold); }
/* The way back from the linear page to the table (spec-blind-access, issue
   25). The share disclosure's weight: one quiet label line in the measure,
   never a button. The way forward, on the illustrated pages, is
   .reading__text--clipped and reuses the skip link's clip. */
.reading__text {
  max-width: var(--measure);
  margin: 0 auto 1.5rem;
  text-align: center;
  font-size: calc(0.72rem * var(--label-scale));
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.reading__text a { color: var(--ink-dim); }
.reading__text a:hover { color: var(--gold); }
/* No quiet-label gap: the link inside is out of flow until focus. */
.reading__text--clipped { margin: 0; }

/* Under a placement's closing note: the paid step, set as a closing too. */
.interpretation-offer {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
  scroll-margin-top: 2rem;
}
.interpretation-offer form { margin: 0; }
.interpretation-offer__allowance { color: var(--ink-dim); }
.interpretation-offer__allowance em { font-style: italic; color: var(--gold); }
@media print {
  .reading__interpret,
  .interpretation-offer { display: none; }
}

.reading__invitation {
  margin: 1.25rem 0 0;
  font-size: 1.15rem;
  color: var(--ink-dim);
}

/* The coda closes a shared reading under the whole grid, clear of the
   narrow stream column. It borrows the landing page's shape. */
.reading-layout:has(+ .coda) { padding-bottom: 3rem; }
.coda {
  max-width: var(--measure);
  margin-inline: auto;
  padding-bottom: 4rem;
  text-align: center;
  scroll-margin-top: 2rem;
}
.coda .ask { margin-top: 1.75rem; }
.coda__own { color: var(--ink-dim); font-size: 1.15rem; }

/* The owner's private note. Immediately after the conclusion, in the same
   column, whether that is the stream or the sticky stage. Not a passage the
   nav steps into, and not an ornamented heading. */
.reading-note {
  width: 100%;
  max-width: none;
  margin-inline: 0;
  padding-top: 1.25rem;
  padding-bottom: 2rem;
}
.reading-note .field { margin-bottom: 0; }
.reading-note textarea {
  font-size: var(--prose-size);
  line-height: var(--prose-leading);
  min-height: 8rem;
}
.reading-note__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1rem;
  padding-top: var(--row-pad);
}
.reading-note__status {
  min-height: 1.4em;
  margin: 0;
  font-size: var(--footnote-size);
  color: var(--ink-faint);
}
.reading-note .button:disabled,
.reading-note .button:disabled:hover {
  background: transparent;
  color: var(--ink-faint);
  border-color: var(--rule-strong);
  cursor: default;
}
/* Idle Save stays in the row as a blank of its own size. */
.reading-note .button[data-idle] {
  visibility: hidden;
  pointer-events: none;
}
@media print {
  .reading-note__actions { display: none; }
  .reading-note[data-blank] { display: none; }
  .reading-note textarea {
    border: 0;
    background: none;
    padding: 0;
    resize: none;
    white-space: pre-wrap;
  }
}

/* Wide layout: the stream sits beside the spread and sets the row height,
   which is what gives the sticky stage its range. The stage takes two thirds
   of the row so the cloth and the conclusion have room; the stream takes the
   rest. The conclusion lives under the spread inside that stage (the
   controller moves it there), and the owner's note follows it there.
   Closing notes follow under the stream once the cards run out. */
.reading-layout {
  display: grid;
  grid-template-columns: minmax(0, var(--reading-stage-span)) minmax(0, var(--reading-stream-span));
  column-gap: clamp(1.5rem, 4vw, 4rem);
  max-width: var(--reading-width);
  margin-inline: auto;
  padding-bottom: 6rem;
  transition: grid-template-columns var(--conclusion-fade) var(--ease);
}

.table-well { grid-column: 1; grid-row: 1; }
.stream     { grid-column: 2; grid-row: 1; margin-inline: 0; }
.closing    { grid-column: 2; grid-row: 2; margin-inline: 0; padding-top: 3rem; }

.passage { scroll-margin-top: 2rem; }

/* The well stretches with the stream; the stage sticks inside it. The lead-in
   is the controller's, and only sets where the spread rests before the stage
   pins — see #leadIn. */
.table-stage {
  position: sticky;
  top: 2rem;
  margin-top: var(--stage-lead, 0px);
}

.table {
  position: relative;
  width: 100%;
  aspect-ratio: var(--table-aspect);
  margin-inline: auto;
  transition: width var(--conclusion-fade) var(--ease);
}
.table-stage[data-pinned] .table { width: min(100%, var(--pinned-table-width, 100%)); }

/* Under the spread the conclusion keeps the column's measure, not the stream's.
   It stays out of flow until the stage pins, then fades in and out with the
   same opacity transition (display waits for the fade via allow-discrete). */
.table-stage .conclusion {
  max-width: none;
  margin-inline: 0;
  margin-top: 2rem;
  padding-bottom: 1rem;
  display: none;
  opacity: 0;
  transition:
    opacity var(--conclusion-fade) var(--ease),
    display var(--conclusion-fade) allow-discrete;
}
.table-stage[data-pinned] .conclusion {
  display: block;
  opacity: 1;
}
@starting-style {
  .table-stage[data-pinned] .conclusion { opacity: 0; }
}

/* The owner's note follows the conclusion inside the stage. Hide it until
   the conclusion is on screen, so it does not appear under the cloth alone. */
.table-stage:not([data-pinned]) .reading-note { display: none; }
.table-stage .reading-note { padding-bottom: 0; }
@media print {
  .table-stage:not([data-pinned]) .reading-note:not([data-blank]) { display: block; }
}

@media (max-width: 62rem) {
  .page:has(.reading) { padding-inline: var(--reading-narrow-gutter); }
  .reading__header { max-width: var(--measure); }
  .reading-layout { grid-template-columns: minmax(0, 1fr); }
  .table-well { grid-row: 1; margin-bottom: 3rem; }
  .stream { grid-column: 1; grid-row: 2; margin-inline: auto; }
  .closing { grid-column: 1; grid-row: 3; margin-inline: auto; border-top: 1px solid var(--rule); }
  .table-stage { position: relative; top: auto; margin-top: 0; }
  .table-stage .conclusion { margin-top: 0; }
}

/* --- the cloth ----------------------------------------------------------- */

/* The cards are dealt onto cloth, not onto the page. It is a box in the layout
   rather than a bleed off the table, so it cannot cover what sits beside or
   above it: the padding is the margin of cloth around the outermost card, and
   it has to be generous, because cards at the edge of a spread overhang the
   table box (the Horseshoe's arc rises above it). The conclusion is
   deliberately outside it — it pins below
   the cloth, on paper, because the reading is read on paper and dealt on
   cloth.

   Wool: grey fractal noise blended into a warm near-black, lit from the head
   and pooling dark at the foot, with the weight of a cloth laid on the page.
   The noise is a file, not a data: URI: the CSP allows images from 'self'
   only (decision 58), and blocked the inline weave. */
.cloth {
  position: relative;
  padding: var(--cloth-bleed);
  margin-inline: auto;
  border-radius: 2px;
  background-color: var(--cloth);
  background-image:
    url("/assets/cloth-weave-46277604.svg"),
    radial-gradient(ellipse 85% 60% at 50% -5%, var(--cloth-sheen), transparent 62%),
    radial-gradient(ellipse 120% 80% at 50% 108%, var(--cloth-shade), transparent 70%);
  background-blend-mode: soft-light, normal, normal;
  box-shadow:
    inset 0 0 0 1px var(--cloth-sheen),
    inset 0 0 5rem var(--cloth-drape),
    0 2px 4px rgba(47, 44, 39, 0.14),
    0 14px 30px rgba(47, 44, 39, 0.18);
  transition: width var(--conclusion-fade) var(--ease);
}

/* The hem, stitched inside the cloth's edge. */
.cloth::after {
  content: "";
  position: absolute;
  inset: var(--cloth-hem);
  border: 1px dashed var(--cloth-mark);
  border-radius: 1px;
  opacity: 0.5;
  pointer-events: none;
}

/* The cloth contracts with the spread it carries. --pinned-table-width is set
   on the stage rather than the table, so both can read it. */
.table-stage[data-pinned] .cloth {
  width: min(100%, calc(var(--pinned-table-width, 100%) + 2 * var(--cloth-bleed)));
}

/* On a narrow screen the cloth runs off both edges of the page, so there is no
   edge left to hem. */
@media (max-width: 62rem) {
  .cloth {
    margin-inline: calc(50% - 50vw);
    border-radius: 0;
  }
  .cloth::after { display: none; }
}

/* A browser prints no backgrounds, so nothing of the cloth survives; what was
   written on it has to come back to ink. */
@media print {
  .cloth { box-shadow: none; }
  .cloth::after { display: none; }
}

.slot {
  position: absolute;
  left: calc(var(--x) * 100%);
  top: calc(var(--y) * 100%);
  width: calc(var(--card-width) * 100%);
  aspect-ratio: var(--card-ratio);
  transform: translate(-50%, -50%) rotate(var(--rot));
  appearance: none;
  border: 0;
  padding: 0;
  background: none;
  outline: none;
  cursor: pointer;
  border-radius: 3px;
  /* Dealt one after another by --deal-delay. */
  animation: deal 1.4s var(--ease) both;
  animation-delay: var(--deal-delay);
  transition: filter 0.3s var(--ease);
}

@keyframes deal {
  from { opacity: 0; transform: translate(-50%, -50%) rotate(var(--rot)) translateY(-3rem) scale(0.86); }
  to   { opacity: 1; transform: translate(-50%, -50%) rotate(var(--rot)); }
}

.slot__art {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  border-radius: 3px;
  box-shadow: var(--cloth-lift);
  transition: box-shadow 0.35s var(--ease), filter 0.35s var(--ease);
}
.slot[data-reversed] .slot__art { transform: rotate(180deg); }

.slot:hover .slot__art,
.slot[data-active] .slot__art,
.slot:focus-visible .slot__art {
  box-shadow: 0 0 0 1px var(--gold), var(--cloth-lift-high);
}
/* Cards not being read sink into the cloth. On paper that was opacity; over
   cloth a translucent card only goes dark, so it is dimmed by light instead.
   A pattern being pointed at dims the cards it does not name the same way. */
.table[data-has-active] .slot:not([data-active]) .slot__art,
.table[data-has-pattern] .slot:not([data-pattern]) .slot__art {
  filter: brightness(var(--cloth-dim)) saturate(0.7);
}
/* Laid by the visitor, not dealt: the cards are simply there. */
.table[data-laid] .slot { animation: none; }

/* --- a card's entry in the stream ---------------------------------------- */

/* An h4 under "The Spread", so it takes the type the h1-h3 rule used to give
   it — including the zeroed margin the browser would otherwise supply. */
.entry__card {
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: 0.01em;
  font-size: 1.8rem;
  line-height: 1.1;
  margin: 0 0 0.3rem;
}

/* The card's name is the way into the card. It reads as the heading it is,
   with the eye mark alongside to say so. */
.entry__open {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition: color 0.25s var(--ease);
}
.entry__open:hover { color: var(--gold); }
.entry__open:focus-visible { outline: 1px solid var(--gold); outline-offset: 0.25rem; }

.entry__expand {
  width: 1.05rem;
  height: 1.05rem;
  flex: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  color: var(--gold-tint);
  transition: color 0.25s var(--ease);
}
.entry__expand circle {
  fill: currentColor;
  stroke: none;
}
.entry__open:hover .entry__expand { color: var(--gold); }
.entry__card small {
  display: block;
  font-family: var(--body);
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ember);
  margin-top: 0.45rem;
}
/* The position's question, set directly under the position that asks it and
   centred with it — the two are one header, and the card answers them. Beaten
   on size by `.prose p` unless it matches that selector's weight. */
.entry__ask,
.prose .entry__ask {
  text-align: center;
  font-family: var(--display);
  font-style: italic;
  font-size: 1.05rem;
  line-height: 1.5;
  color: var(--ink-dim);
  margin: 0 0 1.6rem;
}

/* A card's attributions belong to its name, so they follow it rather than
   waiting at the foot of the passage. */
.entry__credit,
.prose .entry__credit { margin: 0 ; }

.keywords { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0 0 1rem; padding: 0; list-style: none; }
.keywords li {
  font-size: var(--keyword-size);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-dim);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.1rem 0.4rem;
  line-height: 1rem;
}

.note { margin-bottom: 1.4rem; }

/* A card's notes are a set of short blocks in one narrow column. They set a
   step under the reading's own voice, and they read as a set when their right
   edges line up. */
.entry .note p {
  font-size: var(--note-size);
  line-height: var(--note-leading);
}

/* Justified only while the column can still hold the full measure — under
   that, not even hyphenation keeps the rivers out, so they go ragged right. */
@media (min-width: 34rem) {
  .entry .note p {
    text-align: justify;
    hyphens: auto;
  }
}
.note h4 {
  font: 400 calc(0.68rem * var(--label-scale))/1 var(--body);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 0.5rem;
}
.note p { margin: 0; }

/* --- the reading nav ------------------------------------------------------ */

/* Narrow screens only. On a wide screen the conclusion already sits under the
   sticky spread, so the foot pill would only crowd that column. */
.reading-nav {
  display: none;
  position: fixed;
  bottom: calc(var(--reading-nav-inset) + var(--colophon-space));
  left: 0;
  width: 100vw;
  justify-content: center;
  pointer-events: none;
  z-index: 2;
}

/* An open card makes the page inert; the nav should not linger under its
   scrim looking clickable. */
.reading:has(.lightbox[open]) .reading-nav { opacity: 0; }

.reading-nav__pill {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  width: var(--reading-nav-width);
  padding: 0.15rem 0.2rem;
  border: 1px solid var(--rule-strong);
  border-radius: 999px;
  background: var(--paper-raised);
  box-shadow: var(--lift);
  pointer-events: auto;
}

.reading-nav__step {
  width: 1.55rem;
  height: 1.55rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--ink-dim);
  cursor: pointer;
  transition: background 0.2s var(--ease), color 0.2s var(--ease);
}
.reading-nav__step:hover:not(:disabled) { background: var(--gold); color: var(--paper); }
.reading-nav__step:disabled { color: var(--ink-faint); opacity: 0.35; cursor: default; }
.reading-nav__step svg {
  width: 0.72rem;
  height: 0.72rem;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}

.reading-nav__where {
  display: flex;
  align-items: baseline;
  flex: 1;
  gap: 0.35rem;
  padding: 0 0.3rem;
  min-width: 0;
}
.reading-nav__title {
  flex: 1;
  min-width: 0;
  font-size: calc(0.55rem * var(--label-scale));
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.reading-nav__count {
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
  white-space: nowrap;
}

@media (max-width: 62rem) {
  .reading-nav { display: flex; }
  /* Clear the fixed pill, so the coda's button is never under it. */
  .coda { padding-bottom: calc(7rem + var(--colophon-space)); }
}

/* --- the lightbox --------------------------------------------------------- */

/* A <dialog>, for the top layer, Esc, and focus handling. The dialog itself is
   the whole viewport and transparent, so a click anywhere but the card or the
   close button lands on it and dismisses. It sets no overflow of its own, so a
   wheel over it still scrolls the reading underneath. */
.lightbox {
  width: 100vw;
  height: 100dvh;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
}
/* Nothing is dimmed. The scrim covers the card's own column and no more, so
   the reading beside it stays legible — which is the point of standing the
   card in that column rather than over the middle of the page. */
.lightbox::backdrop { background: transparent; }

/* On a wide screen the card takes the column the spread was in, so the reading
   stays where it was. --stage-left and --stage-width are measured from the
   spread's own well when the card is opened. */
.lightbox__frame {
  position: fixed;
  top: 0;
  left: var(--stage-left, 0px);
  width: var(--stage-width, 100vw);
  height: 100dvh;
  display: grid;
  place-items: center;
  /* Semi-transparent, so the fixed .wash behind shows through and the column's
     edge does not read as a seam against the rest of the page. */
  background: rgba(244, 241, 234, 0.94);
  pointer-events: none;
  animation: fade 0.25s var(--ease) both;
}

.lightbox__art {
  max-height: 100dvh;
  max-width: 100%;
  border-radius: 4px;
  box-shadow: var(--lift-high);
  pointer-events: auto;
}
.lightbox__art[data-reversed] { transform: rotate(180deg); }

@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

.lightbox__close {
  position: fixed;
  top: 1rem;
  left: 1rem;
  width: 1.9rem;
  height: 1.9rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--rule-strong);
  border-radius: 999px;
  background: var(--paper-raised);
  color: var(--ink);
  cursor: pointer;
  transition: background 0.25s var(--ease), color 0.25s var(--ease), border-color 0.25s var(--ease);
}
.lightbox__close:hover {
  background: var(--gold);
  border-color: var(--gold);
  color: var(--paper);
}
.lightbox__close svg {
  width: 0.75rem;
  height: 0.75rem;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  fill: none;
}

/* Narrow: the card gets the whole screen rather than a column of it. */
@media (max-width: 62rem) {
  .lightbox__frame {
    left: 0;
    width: 100vw;
    padding: 0 var(--gutter);
  }
}

/* --- the written reading -------------------------------------------------- */

.prose { max-width: var(--measure); margin-inline: auto; }
.prose > :first-child > h2:first-child,
.passage:first-child h2 { margin-top: 0; }

/* Section titles sit between hairlines broken by the same mark as the
   masthead: rule, diamond, title, diamond, rule.

   Top-level headings only. A card's position heading is a level under this
   one and is deliberately left bare — ornament thins as the hierarchy
   deepens, and a Celtic Cross would otherwise repeat this mark ten times
   inside a single section. */
h2 {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.65rem;
  text-align: center;
  margin: 2rem 0 1rem;
  font-size: var(--passage-heading-size);
  font-family: var(--body);
  font-weight: 400;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold);
}
h2::before,
h2::after {
  content: "✦";
  flex: 1 1 0;
  min-width: 2.5rem;
  font-family: var(--ornament);
  font-style: normal;
  font-variant-caps: normal;
  font-size: 1.0rem;
  letter-spacing: 0;
  line-height: 1;
  color: var(--gold);
}
h2::before {
  text-align: right;
  background:
    linear-gradient(var(--rule-strong), var(--rule-strong)) left center / calc(100% - 1.4em) 1px no-repeat;
}
h2::after {
  text-align: left;
  background:
    linear-gradient(var(--rule-strong), var(--rule-strong)) right center / calc(100% - 1.4em) 1px no-repeat;
}

/* A level under "The Spread": the same centred small capitals, without the
   diamonds, and with a rule that hugs the words rather than running the
   column's width — the section above it already owns the full-width rule. */
.entry__position {
  display: block;
  width: fit-content;
  margin-inline: auto;
  padding-bottom: 0.35rem;
  border-bottom: 1px solid var(--gold-tint);
  text-align: center;
  font-family: var(--display);
  font-size: var(--position-heading-size);
  font-weight: 400;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--gold);
  margin: 1.6rem auto 0.7rem;
}

/* "The Spread" and the first card's position are two titles in a row, with
   nothing between them to separate. */
h2 + .entry .entry__position { margin-top: 0.6rem; }

.conclusion {
  border-top: 1px solid var(--gold);
  border-bottom: 1px solid var(--gold);
  padding-block: 1.75rem 1.5rem;
  /* The rules are finished at both ends by a terminal, so the title's
     hairlines have to stand clear of the leaf. On a wide screen the whole
     block takes that inset; on a phone only the heading does, so the prose
     keeps the stream's measure. */
  padding-inline: min(calc(var(--ornament-terminal) * 0.9), 6%);
}
/* Same treatment as any passage title, carrying only the size that says which
   passage this is. */
.conclusion > h2 {
  font-size: var(--conclusion-heading-size);
  margin-top: 0;
}

/* In one column the conclusion sits in the stream, which already has the
   page gutter. A second inset made this block narrower than the passages
   around it. The heading still stands off the terminals. */
@media (max-width: 62rem) {
  .conclusion { padding-inline: 0; }
  .conclusion > h2 {
    padding-inline: min(calc(var(--ornament-terminal) * 0.9), 6%);
  }
}

.prose p,
.observations li {
  font-family: var(--display);
  font-size: var(--prose-size);
  line-height: var(--prose-leading);
  color: var(--ink);
}

/* After .prose p so size, weight and italic win on the conclusion. */
.conclusion.prose > p {
  font-size: var(--conclusion-size);
  line-height: var(--conclusion-leading);
  font-style: italic;
  font-weight: 600;
}
.conclusion.prose > p::first-letter {
  float: left;
  font-family: var(--display);
  font-size: var(--conclusion-drop);
  font-style: italic;
  font-weight: 600;
  line-height: 0.85;
  padding: 0.12rem 0.4rem 0 0;
  color: var(--gold);
}

.observations { list-style: none; margin: 0; padding: 0; }
.observations li {
  border-left: 1px solid var(--rule);
  padding: 0.15rem 0 0.15rem 1.1rem;
  margin-bottom: 1rem;
  color: var(--ink-dim);
}
/* The rule carries the emphasis, so the type does not have to. */
.observations li[data-stands-out] { border-left-color: var(--gold); color: var(--ink); }
/* A pattern that names cards lights them (table#highlight). */
.observations li[data-positions] { cursor: default; }
.observations li[data-positions]:hover,
.observations li[data-positions]:focus-visible { border-left-color: var(--ink); outline: none; }
.observations__more { margin-top: 0.5rem; }
.observations__more summary {
  display: inline-block;
  margin-bottom: 1rem;
  list-style: none;
  cursor: pointer;
  font-size: calc(0.72rem * var(--label-scale));
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.observations__more summary::-webkit-details-marker { display: none; }
.observations__more summary:hover,
.observations__more[open] summary { color: var(--gold); }
.observations__more summary:focus-visible { outline: 1px solid var(--gold); outline-offset: 0.25rem; }

.footnote,
.prose .footnote {
  font-family: var(--body);
  color: var(--ink-faint);
  font-size: var(--footnote-size);
  line-height: 1.7;
}

/* --- the placing table ---------------------------------------------------- */

/* /study: the reading page before the reading exists. It reuses the header,
   the grid, the cloth and the slot geometry; these rules cover only what a
   finished reading lacks — beds, the position list and the deck. On the
   cloth everything is ink: gold and ember fail contrast there. */

/* The header's question is the heading it will become. */
.placing__question {
  display: block;
  width: 100%;
  min-height: 0;
  margin: 0 auto 0.9rem;
  padding: 0.2rem 0 0.45rem;
  border: 0;
  border-bottom: 1px solid var(--rule-strong);
  border-radius: 0;
  background: transparent;
  resize: none;
  overflow: hidden;
  text-align: center;
  font-family: var(--display);
  font-style: italic;
  font-size: clamp(1.5rem, 4vw, 2.3rem);
  line-height: 1.2;
  color: var(--ink);
}
.placing__question::placeholder { color: var(--ink-faint); }
.placing__question:focus { outline: none; border-bottom-color: var(--gold); }

/* The spread choice, set as the meta line: .auth-switch's receding options. */
.placing__spreads { border: 0; margin: 0; padding: 0; min-width: 0; }
.placing__spread-options {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  column-gap: 0.2rem;
  row-gap: 0.35rem;
  font-size: calc(0.62rem * var(--label-scale));
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.spread-option { --inactive: 0.42; position: relative; cursor: pointer; white-space: nowrap; color: var(--ink); opacity: var(--inactive); }
.spread-option + .spread-option::before { content: "·"; margin: 0 0.6em; color: var(--gold); }
.spread-option input { position: absolute; opacity: 0; pointer-events: none; }
.spread-option span { padding-bottom: 0.15rem; border-bottom: 1px solid transparent; }
.spread-option small { font-size: 0.85em; color: var(--ink-faint); margin-left: 0.3em; letter-spacing: 0.08em; }
.spread-option:hover { opacity: 1; color: var(--gold); }
.spread-option:has(input:checked) { opacity: 1; }
.spread-option:has(input:checked) span { border-bottom-color: var(--gold); }
.spread-option:has(input:focus-visible) span { outline: 1px solid var(--gold); outline-offset: 0.2rem; }

/* One meta-line label with a chevron: the kind, and the phone's spread. */
.placing__kind-select,
.placing__spread-select {
  width: auto;
  border: 0;
  border-bottom: 1px solid var(--gold);
  border-radius: 0;
  /* A file, not a data: URI: the CSP allows images from 'self' only. */
  background: transparent url("/assets/chevron-down-487126f7.svg") no-repeat right 0.15rem center / 0.85rem;
  color: var(--ink);
}
.placing__kind-select:focus-visible,
.placing__spread-select:focus-visible { outline: 1px solid var(--gold); outline-offset: 0.2rem; }
.placing__kind {
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: 0.6rem;
  margin: -0.2rem 0 0.9rem;
  font-size: calc(0.62rem * var(--label-scale));
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.placing__kind-select {
  padding: 0.2rem 1.5rem 0.2rem 0.3rem;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
}
.placing__spread-select { display: none; }
.placing__noscript { text-align: center; }

/* The deck widens the stream; the cloth scales down with it and stays whole. */
.placing__layout[data-mode="deck"] { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
/* Laid, not dealt: no fall. A placed card sets down instead (data-landed). */
.placing .slot { animation: none; }

/* The bed: a stitched place the size of the card it will hold. */
/* A button does not inherit the page's face on its own. */
.slot--bed { container-type: size; border-radius: 3px; font: inherit; }
.slot--bed[data-empty] {
  border: 1px dashed var(--bed-stitch);
  background: var(--bed-fill);
  transition: border-color 0.2s var(--ease), filter 0.3s var(--ease);
}
.slot--bed[data-empty]:hover,
.slot--bed[data-empty]:focus-visible,
.slot--bed[data-empty][data-hover] {
  border-style: solid;
  border-color: var(--bed-stitch-firm);
}
.slot--bed[data-active] {
  border: 2px solid var(--bed-ring);
  box-shadow: 0 0 0 1px var(--paper);
  z-index: 2;
}
.slot--bed[data-active] .slot__art { box-shadow: var(--cloth-lift-high); }
.slot--bed:focus-visible { outline: none; }
/* A filled bed rings in ink, never gold: gold does not read on the cloth. */
.slot--bed:not([data-empty]):hover .slot__art,
.slot--bed:not([data-empty]):focus-visible .slot__art,
.slot--bed:not([data-empty])[data-hover] .slot__art {
  box-shadow: 0 0 0 1px var(--paper), 0 0 0 2px var(--ink), var(--cloth-lift-high);
}
.slot--bed[data-empty] .slot__art,
.slot--bed[data-empty] .bed__chip,
.slot--bed:not([data-empty]) .bed__label { display: none; }

/* Everything but the bed being filled sinks, as reading selection does. */
.table[data-has-active] .slot--bed:not([data-active]) { filter: brightness(var(--cloth-dim)) saturate(0.7); }
.table[data-has-active] .slot--bed:not([data-active]) .slot__art { filter: none; }

/* The label layer is counter-rotated, so a crossing bed still reads level. */
.bed__label {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 100cqw;
  height: 100cqh;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding: 9cqh 6cqw;
  color: var(--ink);
  text-align: center;
  pointer-events: none;
}
.slot--bed[data-rot="90"] .bed__label {
  width: 100cqh;
  height: 100cqw;
  transform: translate(-50%, -50%) rotate(-90deg);
  flex-direction: row;
  padding: 4cqw 8cqh;
}
.bed__numeral { font-family: var(--display); font-size: clamp(0.8rem, 18cqw, 1.7rem); line-height: 1; }
.bed__name {
  font-size: clamp(0.7rem, 13cqw, 1.2rem);
  line-height: 1.1;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.slot--bed[data-rot="90"] .bed__numeral { font-size: clamp(0.8rem, 11cqh, 1.4rem); }
.slot--bed[data-rot="90"] .bed__name { font-size: clamp(0.7rem, 8cqh, 1.1rem); }
/* Narrow beds drop the name. The numeral stays at the head, so The Heart's
   shows in the strip above The Crossing rather than under it. */
@container (max-width: 4.5rem) {
  .bed__name { display: none; }
  .bed__label { justify-content: flex-start; padding-top: 4cqh; }
  .slot--bed[data-rot="90"] .bed__label { justify-content: center; padding: 0; }
}

/* A filled card is bare until named. The pill sits under the card on hover,
   focus, or while that card's passage is the one selected. */
.bed__chip {
  position: absolute;
  left: 50%;
  top: 100%;
  translate: -50% 0.4rem;
  padding: 0.15rem 0.5rem;
  border: 1px solid var(--rule-strong);
  border-radius: 999px;
  background: var(--paper-raised);
  box-shadow: var(--lift);
  color: var(--ink);
  font-size: calc(0.5rem * var(--label-scale));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s var(--ease);
  z-index: 3;
}
.slot[data-rot="90"] .bed__chip { rotate: -90deg; top: 50%; left: 100%; translate: 0.4rem -50%; transform-origin: left center; }
.slot:hover .bed__chip,
.slot:focus-visible .bed__chip,
.slot[data-hover] .bed__chip,
.slot[data-active] .bed__chip { opacity: 1; }
/* The pill hangs past the card. Raise that card so the next one does not cover the name. */
.reading:not(.placing) .slot:hover,
.reading:not(.placing) .slot:focus-visible,
.reading:not(.placing) .slot[data-active] { z-index: 2; }

.slot--bed[data-landed] .slot__art { animation: set-down 0.25s var(--ease) both; }
.slot--bed[data-landed][data-reversed] .slot__art { animation-name: set-down-reversed; }
@keyframes set-down {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: none; }
}
@keyframes set-down-reversed {
  from { opacity: 0; transform: rotate(180deg) scale(0.96); }
  to   { opacity: 1; transform: rotate(180deg); }
}

/* The stream's positions: one ruled row per place, a way into the deck. */
.positions__note {
  font-family: var(--display);
  font-size: 1.25rem;
  line-height: 1.6;
  color: var(--ink-dim);
  text-align: center;
  max-width: var(--measure);
  margin: 0 auto 1.5rem;
}
.positions__note .text-button { margin-left: 0.5rem; }
.positions__list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); }
.positions__list li { border-bottom: 1px solid var(--rule); }
.position-row {
  appearance: none;
  width: 100%;
  display: grid;
  grid-template-columns: 2.4rem 1fr auto;
  column-gap: 0.75rem;
  align-items: center;
  padding: 0.9rem 0;
  border: 0;
  background: none;
  text-align: left;
  font: inherit;
  color: var(--ink);
  cursor: pointer;
}
.position-row__numeral { font-family: var(--display); font-size: 1.35rem; color: var(--ink-faint); text-align: center; }
.position-row__name {
  display: block;
  font-size: calc(0.66rem * var(--label-scale));
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  transition: color 0.2s var(--ease);
}
.position-row__ask { display: block; margin-top: 0.15rem; font-family: var(--display); font-style: italic; font-size: 1.05rem; line-height: 1.35; color: var(--ink-dim); }
.position-row__card { display: block; margin-top: 0.35rem; font-family: var(--display); font-size: 1.3rem; line-height: 1.2; }
.position-row__card small {
  margin-left: 0.4rem;
  font-family: var(--body);
  font-size: calc(0.55rem * var(--label-scale));
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ember);
}
.position-row[data-empty] .position-row__card,
.position-row:not([data-reversed]) .position-row__card small { display: none; }
.position-row__mini {
  width: 2.25rem;
  aspect-ratio: var(--card-ratio);
  border: 1px dashed var(--rule-strong);
  border-radius: 2px;
  overflow: hidden;
}
.position-row__mini img { display: block; width: 100%; height: 100%; object-fit: cover; }
.position-row[data-empty] .position-row__mini img { display: none; }
.position-row:not([data-empty]) .position-row__mini { border: 0; box-shadow: var(--lift); }
.position-row[data-reversed] .position-row__mini img { transform: rotate(180deg); }
.position-row:hover .position-row__name,
.position-row:focus-visible .position-row__name,
.position-row[data-hover] .position-row__name { color: var(--gold); }
.position-row[data-empty]:hover .position-row__mini,
.position-row[data-empty][data-hover] .position-row__mini { border-style: solid; border-color: var(--ink-dim); }
.position-row:focus-visible { outline: 1px solid var(--gold); outline-offset: -1px; }

.positions__foot { margin-top: 1.75rem; text-align: center; }
.positions__sign { text-align: left; }
.positions__count {
  margin-bottom: 1rem;
  font-size: calc(0.66rem * var(--label-scale));
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.positions__actions { display: flex; justify-content: center; align-items: center; gap: 1.5rem; }

/* The stream's deck: a sticky head naming the position, then the 78. */
.deck__head {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 1rem 0 0.75rem;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}
.deck__bar { display: flex; justify-content: space-between; align-items: center; }
.deck__back { display: inline-flex; align-items: center; gap: 0.35rem; }
.deck__back svg,
.deck__close svg { fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.deck__back svg { width: 0.8rem; height: 0.8rem; }
.deck__close,
.deck__strip { display: none; }
.deck__where { text-align: center; transition: opacity 0.2s var(--ease); }
.deck__where[data-changing] { opacity: 0; }
.deck__numeral { margin: 0.4rem 0 -1rem; font-family: var(--display); font-size: 1.5rem; color: var(--ink-faint); }
.deck__where .entry__ask { margin-bottom: 0.75rem; }

.deck__controls { display: flex; justify-content: center; align-items: baseline; flex-wrap: wrap; gap: 1.5rem; }
/* Upright | Reversed, in the .auth-switch idiom. Reversed is ember, the
   colour a reversal carries everywhere else. */
.orient {
  --inactive: 0.42;
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
  border: 0;
  margin: 0;
  padding: 0;
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.orient__mode { position: relative; cursor: pointer; opacity: var(--inactive); color: var(--ink); }
.orient__mode input { position: absolute; opacity: 0; pointer-events: none; }
.orient__mode:hover { opacity: 1; color: var(--gold); }
.orient__mode:has(input:checked) { opacity: 1; }
.orient__mode--reversed:has(input:checked) { color: var(--ember); }
.orient__mode:has(input:focus-visible) { outline: 1px solid var(--gold); outline-offset: 0.2rem; }
.orient__pipe { font-size: 1.3em; line-height: 1; color: var(--ink); }
.deck__current { display: inline-flex; align-items: baseline; gap: 0.75rem; }
.deck__current-name { font-family: var(--display); font-size: 1.15rem; }

.deck__suits {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0 1rem;
  margin-top: 0.75rem;
  font-size: calc(0.6rem * var(--label-scale));
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.deck__suits a { color: var(--ink-faint); text-decoration: none; }
.deck__suits a[aria-current="true"] { color: var(--ink); }
.deck__suits a:hover { color: var(--gold); }

.deck__section { scroll-margin-top: 16rem; padding-top: 1.25rem; }
.deck__section h3 {
  margin: 0 0 0.9rem;
  font: 400 calc(0.62rem * var(--label-scale))/1 var(--body);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-align: center;
}
.deck__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--deck-thumb), 1fr));
  gap: 0.9rem 0.6rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.deck-card {
  appearance: none;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--ink);
  text-align: center;
  cursor: pointer;
}
.deck-card__art {
  display: block;
  width: 100%;
  aspect-ratio: var(--card-ratio);
  object-fit: cover;
  border-radius: 3px;
  box-shadow: 0 1px 2px var(--rule);
  transition: transform 0.3s var(--ease), box-shadow 0.25s var(--ease), filter 0.25s var(--ease);
}
/* What you pick is what you get: Reversed turns the whole deck over. */
.deck[data-orientation="reversed"] .deck-card__art { transform: rotate(180deg); }
.deck-card__caption {
  display: block;
  margin-top: 0.3rem;
  font-family: var(--display);
  font-size: 0.95rem;
  line-height: 1.2;
  color: var(--ink-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.deck-card:hover .deck-card__art { box-shadow: var(--lift); }
.deck-card:focus-visible { outline: none; }
.deck-card:focus-visible .deck-card__art { box-shadow: 0 0 0 2px var(--gold), var(--lift); }
.deck-card[aria-disabled="true"] { cursor: not-allowed; }
.deck-card[aria-disabled="true"] .deck-card__art { filter: brightness(var(--cloth-dim)) saturate(0.5); opacity: 0.55; box-shadow: none; }
.deck-card[aria-disabled="true"] .deck-card__caption { font-style: italic; color: var(--ink-faint); }
.deck-card[data-here] .deck-card__art { box-shadow: 0 0 0 1px var(--paper), 0 0 0 2px var(--ink); }
.deck-card[data-here] .deck-card__caption { color: var(--ink); }

/* The sheet and the pill exist only on a narrow screen. */
.sheet { display: none; }
.place-pill { display: none; }

@media (max-width: 62rem) {
  .placing__layout[data-mode="deck"] { grid-template-columns: minmax(0, 1fr); }
  .placing .reading__header { margin-bottom: 1.5rem; }

  /* One line, not a stacked menu: the cloth has to start on the first screen. */
  .placing__spread-options { display: none; }
  .placing__spread-select {
    display: block;
    margin: 0 auto;
    padding: 0.35rem 1.6rem 0.35rem 0.6rem;
    text-align: center;
    font: 400 calc(0.62rem * var(--label-scale))/1.4 var(--body);
    letter-spacing: 0.16em;
    text-transform: uppercase;
  }

  /* The deck as a full-height modal sheet on paper. */
  .sheet[open] {
    display: block;
    width: 100vw;
    height: 100dvh;
    max-width: none;
    max-height: none;
    margin: 0;
    padding: 0 var(--gutter) 3rem;
    border: 0;
    background: var(--paper);
    color: var(--ink);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .sheet::backdrop { background: var(--paper); }
  .sheet :focus-visible { outline: 1px solid var(--gold); outline-offset: 2px; }
  /* The head's paper spans the sheet's gutter too, or the thumbnails'
     shadows show beside it as they scroll under. */
  .deck__head {
    margin-inline: calc(-1 * var(--gutter));
    padding: 0.75rem var(--gutter);
  }
  .deck__back { display: none; }
  .deck__bar { gap: 0.75rem; justify-content: flex-start; }
  .deck__close {
    display: grid;
    place-items: center;
    flex: none;
    width: 1.9rem;
    height: 1.9rem;
    padding: 0;
    border: 1px solid var(--rule-strong);
    border-radius: 999px;
    background: var(--paper-raised);
    color: var(--ink);
    cursor: pointer;
  }
  .deck__close svg { width: 0.75rem; height: 0.75rem; }
  /* One numeral per position: a filled disc when laid, a ring when active. */
  .deck__strip {
    display: flex;
    flex: 1;
    gap: 0.25rem;
    overflow-x: auto;
    list-style: none;
    margin: 0;
    padding: 0.2rem;
  }
  .deck__strip button {
    width: 2rem;
    height: 2rem;
    padding: 0;
    border: 1px solid var(--rule-strong);
    border-radius: 999px;
    background: none;
    font-family: var(--display);
    font-size: 0.8rem;
    color: var(--ink-dim);
    cursor: pointer;
  }
  .deck__strip button[data-filled] { background: var(--ink); border-color: var(--ink); color: var(--paper); }
  .deck__strip button[data-active] { box-shadow: 0 0 0 2px var(--paper), 0 0 0 4px var(--ink); color: var(--ink); }
  .deck__strip button[data-active][data-filled] { color: var(--paper); }
  .deck__suits { flex-wrap: nowrap; justify-content: flex-start; overflow-x: auto; }
  .deck__section { scroll-margin-top: 18rem; }
  .deck__grid { grid-template-columns: repeat(auto-fill, minmax(var(--deck-thumb-narrow), 1fr)); }

  /* The count and the action ride in a pill, shaped like the reading nav. */
  .placing .positions__foot > :not(.positions__sign) { display: none; }
  .place-pill {
    display: flex;
    position: fixed;
    left: 0;
    width: 100vw;
    bottom: calc(var(--reading-nav-inset) + var(--colophon-space));
    justify-content: center;
    pointer-events: none;
    z-index: 5;
  }
  .place-pill__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    width: min(94vw, 26rem);
    padding: 0.3rem 0.35rem 0.3rem 1rem;
    border: 1px solid var(--rule-strong);
    border-radius: 999px;
    background: var(--paper-raised);
    box-shadow: var(--lift);
    pointer-events: auto;
  }
  .place-pill__count {
    white-space: nowrap;
    font-size: calc(0.5rem * var(--label-scale));
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-dim);
  }
  .place-pill__go {
    padding: 0.45rem 0.9rem;
    border: 1px solid var(--gold);
    border-radius: 999px;
    background: var(--gold);
    color: var(--paper);
    font: 400 calc(0.5rem * var(--label-scale))/1 var(--body);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    white-space: nowrap;
    cursor: pointer;
  }
  .place-pill__go:disabled { background: none; border-color: var(--rule-strong); color: var(--ink-faint); cursor: not-allowed; }
  .placing__stream { padding-bottom: 6rem; }
}

/* Print the laid cards and the list; the beds, the deck and the actions are
   for laying, not for reading. */
@media print {
  .slot--bed { border: 0; background: none; }
  .bed__label,
  .bed__chip,
  .placing__spreads,
  .placing__kind,
  .deck,
  .sheet,
  .place-pill,
  .positions__foot,
  .positions__note { display: none; }
}

/* --- ornament ------------------------------------------------------------- */

/* The app ornaments with rule, break, and mark. The dingbat ✦ is the mark
   that BREAKS a rule — the masthead and the h2 headings. The
   terminal below is the mark that FINISHES one, and the tailpiece is the two
   of them together, once, at the end of a page.

   Ornament thins as the hierarchy deepens: h2 gets rules and diamonds, the
   card position under it gets none, and nothing interactive gets any. Buttons,
   inputs, the keyword chips, the masthead nav, the reading pill and the
   lightbox controls stay in the plain modern idiom on purpose. */

.ornament-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* One weight and one colour for every drawn mark. `stroke`, `stroke-width` and
   the cap joins are inherited properties, so they reach the cloned symbol
   through the <use> boundary; `vector-effect: non-scaling-stroke` sits on the
   paths themselves, which is what keeps the hairline identical whether the
   symbol is drawn at 42px or at 190px. */
.terminals__mark,
.tailpiece__mark,
.colophon__rule-end {
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: var(--ornament-stroke);
  stroke-linecap: butt;
  overflow: visible;
}

/* Four marks finishing a block's two rules, from one symbol. The drawn shape
   runs left to right; the rest are rotations and mirrors. Each is centred on
   its rule's end so the hairline it carries lies over the rule itself — the
   rule appears to swell into the leaf rather than to meet one. */
.terminals {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.terminals__mark {
  position: absolute;
  width: var(--ornament-terminal);
  height: calc(var(--ornament-terminal) * 16 / 38);   /* the symbol's own box */
}

/* Rules above and below: the marks point out to left and right. */
.terminals--h .terminals__mark--tl { top: 0;    left: 0;  transform: translateY(-50%) scaleX(-1); }
.terminals--h .terminals__mark--tr { top: 0;    right: 0; transform: translateY(-50%); }
.terminals--h .terminals__mark--bl { bottom: 0; left: 0;  transform: translateY(50%) scaleX(-1); }
.terminals--h .terminals__mark--br { bottom: 0; right: 0; transform: translateY(50%); }

/* Rules left and right: the same mark stood on end, pointing up and down.
   Centred on the rule's end first, then turned, then backed off along its own
   axis so the tip lands on the end rather than past it. */
.terminals--v .terminals__mark--tl { top: 0;    left: 0;  transform: translate(-50%, -50%) rotate(-90deg) translateX(-50%); }
.terminals--v .terminals__mark--tr { top: 0;    right: 0; transform: translate(50%, -50%)  rotate(-90deg) translateX(-50%); }
.terminals--v .terminals__mark--bl { bottom: 0; left: 0;  transform: translate(-50%, 50%)  rotate(90deg)  translateX(-50%); }
.terminals--v .terminals__mark--br { bottom: 0; right: 0; transform: translate(50%, 50%)   rotate(90deg)  translateX(-50%); }

/* The conclusion is the one block in the app framed by its own rules — gold,
   above and below — so it is the one block whose rules get finished. The cloth
   does not: its edge is already stitched with a hem. */
.conclusion { position: relative; }
.conclusion .terminals { color: var(--gold); }

/* The bound: two hairlines standing off the content column, stopped short at
   both ends and finished with the same mark. It draws only where there is
   margin to draw it in. The bound is --measure plus two offsets wide, so it
   needs about 44rem to exist at all and 52rem before there is any paper left
   outside it; below that the measured margin runs to a few pixels and then to
   none, so the bound is simply not there and the headings' own rules carry the
   boundaries. The reading page never opts in — its stream column
   faces the cloth, not paper, at every width. */
.page__bound { display: none; }

@media (min-width: 52rem) {
  /* A classic scrollbar narrows only the pages that overflow. The bound is
     centered, so a long page sat half a scrollbar left of a short one.
     Reserve the gutter on both edges while the bound can be drawn, and
     every page keeps the short page's column. Below this width the bound
     is not there, and the gutter would only steal measure from the phone. */
  html { scrollbar-gutter: stable both-edges; }

  .page--ruled .page__bound {
    display: block;
    position: absolute;
    top: var(--ornament-bound-inset);
    bottom: var(--ornament-bound-inset);
    left: 50%;
    width: min(
      100% - 2 * var(--gutter),
      calc(var(--measure) + 2 * var(--ornament-rule-offset))
    );
    transform: translateX(-50%);
    /* The rule and the mark that finishes it are one line, so they are one
       colour: --gold-tint, which the palette keeps for borders and fills. */
    border-left: 1px solid var(--gold-tint);
    border-right: 1px solid var(--gold-tint);
    color: var(--gold-tint);
    pointer-events: none;
  }

  .page--wide.page--ruled .page__bound {
    width: min(
      100% - 2 * var(--gutter),
      calc(var(--wide-measure) + 2 * var(--ornament-rule-offset))
    );
  }

  .page--plans.page--ruled .page__bound {
    width: min(
      100% - 2 * var(--gutter),
      calc(var(--plans-measure) + 2 * var(--ornament-rule-offset))
    );
  }
}

/* The end of a composition. Once per page, and never at a fixed interval. */
.tailpiece {
  display: flex;
  justify-content: center;
  margin-top: 3.5rem;
  color: var(--gold-tint);
}
.tailpiece__mark {
  width: var(--ornament-tailpiece);
  height: auto;
  aspect-ratio: 15 / 2;   /* the symbol's own 120 × 16 */
}

/* One opening letter per document, in ink. The conclusion's is gold and stays
   the only gold one, so the reading's destination is not competed with. */
.prose__opening::first-letter {
  float: left;
  font-family: var(--display);
  font-size: var(--conclusion-drop);
  line-height: 0.85;
  padding: 0.12rem 0.4rem 0 0;
  color: var(--ink);
}

/* --- the list ------------------------------------------------------------- */

.entries { list-style: none; margin: 0; padding: 0; max-width: var(--measure); margin-inline: auto; }
.entries li { display: flex; align-items: center; gap: 1rem; border-bottom: 1px solid var(--rule); }
.entries a {
  flex: 1;
  min-width: 0;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1.5rem;
  padding: var(--row-pad) 0;
  text-decoration: none;
}
.entries a:hover .entries__question { color: var(--gold); }
.entries__delete-form { margin: 0; flex: none; }
.entries__delete {
  width: 1.92rem;
  height: 1.92rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 999px;
  background: none;
  color: var(--ember);
  cursor: pointer;
}
.entries__delete:hover { border-color: var(--ember); }
.entries__delete svg {
  width: 1.02rem;
  height: 1.02rem;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}
.entries__question { font-family: var(--display); font-size: 1.2rem; line-height: 1.2rem; }
.entries__when { font-size: calc(0.7rem * var(--label-scale)); letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-faint); white-space: nowrap; }
.entries__meta {
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  min-width: 0;
  overflow-wrap: anywhere;
}

.empty { color: var(--ink-faint); font-style: italic; font-family: var(--display); font-size: 1.2rem; text-align: center; }

.listing-page {
  width: min(100%, var(--wide-measure));
  margin-inline: auto;
}
.listing-page .field { max-width: var(--measure); }
.page--wide .subnav {
  max-width: min(100%, var(--wide-measure));
}
.listing-wrap { overflow-x: auto; }
.listing {
  width: 100%;
  border-collapse: collapse;
  margin: 0.5rem 0 0;
}
.listing th,
.listing td {
  padding: var(--row-pad) 1.25rem var(--row-pad) 0;
  border-top: 1px solid var(--rule);
  text-align: left;
  vertical-align: baseline;
}
.listing th:last-child,
.listing td:last-child { padding-right: 0; }
.listing th {
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.listing th a {
  color: inherit;
  text-decoration: none;
}
.listing th a:hover,
.listing td a:hover { color: var(--gold); }
.listing th[aria-sort="ascending"] a,
.listing th[aria-sort="descending"] a { color: var(--ink); }
.listing td { overflow-wrap: anywhere; }
.listing td a { text-decoration: none; }
.listing-anon {
  margin: 1.5rem 0 0;
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.listing-anon a {
  color: var(--ink-dim);
  text-decoration: none;
}
.listing-anon a:hover { color: var(--gold); }
.listing-anon a[aria-pressed="true"] { color: var(--ink); }

/* --- the operator dashboard ------------------------------------------------
   Figures are ink, bars are gold-tint on a recessed track. Gold marks a
   warning, ember a failure or a closed window. Nothing else is coloured.
   Bars are inline SVG sized by attribute, so they print as ink. */

.dashboard {
  --dashboard-gap: clamp(2rem, 4vw, 3.5rem);
  --figure-size: 2.1rem;
  --gauge-figure-size: 1.6rem;
  --bar-height: 0.55rem;
  --stack-height: 1rem;
  --swatch-size: 0.7rem;
  --column-chart-height: 7rem;
  --bar-label-width: 10.5rem;
  --bar-count-width: 3.5rem;
}
.dashboard__head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem 1.5rem;
}
.dashboard__head .eyebrow { margin: 0; }
.dashboard__asof {
  margin: 0;
  color: var(--ink-faint);
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.panel { margin-top: var(--dashboard-gap); min-width: 0; }
.panel h2 { margin-top: 0; }
.panel__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  gap: var(--dashboard-gap);
}
.dashboard__pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr));
  column-gap: var(--dashboard-gap);
  align-items: start;
}
.panel__label {
  margin: 0 0 0.75rem;
  font-family: var(--body);
  font-size: calc(0.7rem * var(--label-scale));
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.panel__label:not(:first-child) { margin-top: 1.75rem; }
.panel__note {
  margin: 0.6rem 0 0;
  color: var(--ink-faint);
  font-size: var(--footnote-size);
}
.panel .empty { text-align: left; }
.panel .listing-wrap + .listing-wrap { margin-top: 1.5rem; }

.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.listing .num { text-align: right; }
.listing .alert,
.vital--alert .vital__figure,
.vital--alert dt { color: var(--ember); }
.listing__sub th { padding-left: 1rem; }
.listing__sub td { color: var(--ink-dim); }
.listing tfoot th,
.listing tfoot td { border-top-color: var(--rule-strong); }
/* The bar is the column that gives way, so it has no minimum width. */
.listing__bar { width: 25%; vertical-align: middle; }
/* Break a model id only where it cannot fit, never mid-word by default. */
.listing td.listing__name { overflow-wrap: break-word; }
/* A spend block's table answers to its own column, not the viewport: the
   grid's columns are narrowest just before they stack, around 60rem. In a
   narrow column the relative bar costs the table its fit, so it goes and the
   figure stays. */
.panel__grid > div { container-type: inline-size; }
@container (max-width: 25rem) {
  .listing .listing__bar { display: none; }
  .listing th,
  .listing td { padding-right: 0.75rem; }
  .listing th:last-child,
  .listing td:last-child { padding-right: 0; }
  .listing thead th { letter-spacing: 0.06em; }
  .listing td.listing__name { overflow-wrap: anywhere; }
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Vital signs: a label, one large figure, and what it counts. */
.vitals {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
  column-gap: 1.25rem;
  margin: 0;
}
.vitals--compact { grid-template-columns: repeat(auto-fit, minmax(min(100%, 8rem), 1fr)); }
.vital {
  padding: 0.6rem 0 0.9rem;
  border-top: 1px solid var(--rule);
}
.vital dt {
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.vital dd { margin: 0; }
.vital__figure {
  font-family: var(--display);
  font-size: var(--figure-size);
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.vital__note {
  color: var(--ink-faint);
  font-size: var(--footnote-size);
}
.vital--alert { border-top-color: var(--ember); }
.vital--callout { border-top-color: var(--gold-tint); }
.vital--callout dt { color: var(--gold); }

/* Bars. */
.bar {
  display: block;
  width: 100%;
  height: var(--bar-height);
  overflow: visible;
}
.bar__track { fill: var(--paper-panel); }
.bar__fill { fill: var(--gold-tint); }
.bar__mark { stroke: var(--ink-dim); stroke-width: 1; }
.bar--warn .bar__fill { fill: var(--gold); }
.bar--closed .bar__fill { fill: var(--ember); }

/* Headroom: a bullet gauge per SpendCap window, ticked at the warn line. */
.gauges {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1.5rem;
}
.gauge__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  margin-bottom: 0.4rem;
}
.gauge__label {
  font-size: calc(0.8rem * var(--label-scale));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.gauge__figure {
  font-family: var(--display);
  font-size: var(--gauge-figure-size);
  font-variant-numeric: tabular-nums;
}
.gauge__of { color: var(--ink-faint); font-size: var(--copy-size); }
.gauge__note {
  margin: 0.4rem 0 0;
  color: var(--ink-dim);
  font-size: var(--footnote-size);
}
.gauge--warn .gauge__label { color: var(--gold); }
.gauge--closed .gauge__label,
.gauge--closed .gauge__note { color: var(--ember); }

/* Seven days as columns, scaled to the week's tallest day. */
.columns {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 0.4rem;
}
.columns__day {
  display: grid;
  gap: 0.25rem;
  min-width: 0;
  text-align: center;
}
.columns__value {
  font-size: var(--footnote-size);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.columns__bar {
  display: block;
  width: 100%;
  height: var(--column-chart-height);
  border-bottom: 1px solid var(--rule-strong);
}
.columns__date {
  font-size: calc(0.6rem * var(--label-scale));
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.columns__count {
  color: var(--ink-faint);
  font-size: var(--footnote-size);
  font-variant-numeric: tabular-nums;
}
.columns__day--quiet .columns__value { color: var(--ink-faint); }

/* Cost per reading: one stacked bar, split by operation. */
.figure {
  margin: 0;
  font-family: var(--display);
  font-size: var(--figure-size);
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.figure__note {
  margin-left: 0.5rem;
  color: var(--ink-faint);
  font-size: var(--footnote-size);
}
.stack {
  display: block;
  width: 100%;
  height: var(--stack-height);
  margin: 0.75rem 0 1rem;
}
.stack__part--primary { fill: var(--gold); }
.stack__part--secondary { fill: var(--gold-tint); }
.legend { margin: 0; }
.legend > div {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: 0 0.75rem;
  align-items: baseline;
  padding: var(--row-pad) 0;
  border-top: 1px solid var(--rule);
}
.legend dt {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.legend dd { margin: 0; }
.legend__swatch { width: var(--swatch-size); height: var(--swatch-size); flex: none; }
.legend__share { color: var(--ink-faint); }

/* A labelled bar per category: plans and roles. */
.bars {
  list-style: none;
  margin: 0;
  padding: 0;
}
.bars li {
  display: grid;
  grid-template-columns: var(--bar-label-width) minmax(0, 1fr) var(--bar-count-width);
  gap: 0.75rem;
  align-items: center;
  padding: var(--row-pad) 0;
  border-top: 1px solid var(--rule);
}
.bars__label {
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-dim);
  overflow-wrap: anywhere;
}

/* In-page apparatus for a second nav and a labelled record. Named for
   what they look like, not where they are used. */
.subnav {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 1.5rem;
  max-width: var(--measure);
  margin: 0 auto 2rem;
  font-size: calc(0.8rem * var(--label-scale));
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.subnav a {
  color: var(--ink-dim);
  text-decoration: none;
}
.subnav a:hover { color: var(--gold); }

.facts {
  margin: 2rem 0 0;
}
.facts > div {
  display: grid;
  grid-template-columns: minmax(7rem, 11rem) minmax(0, 1fr);
  gap: 0.25rem 1.25rem;
  padding: var(--row-pad) 0;
  border-top: 1px solid var(--rule);
}
.facts dt {
  margin: 0;
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.facts dd {
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}
.facts dd.facts__warn { color: var(--ember); }
@media (max-width: 40rem) {
  .facts > div { grid-template-columns: 1fr; }
}

/* Pricing. Two plans laid side by side like two cards on the page, each
   headed by its trump, then the fine print as ruled facts. The plans stack
   below two comfortable columns. Emphasis is the gold italic figure. */
.pricing__head { text-align: center; }
.pricing__head .lede { padding-inline-start: 0; text-indent: 0; }
.plans {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
  gap: 3.5rem 3rem;
  max-width: var(--plans-measure);
  margin: 3rem auto 2.5rem;
}
.plan {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.plan__card {
  width: 7.5rem;
  height: auto;
  border-radius: 3px;
  box-shadow: var(--lift);
  margin-bottom: 0.5rem;
}
.plan__current {
  margin: 0.75rem 0 -1rem;
  font-size: calc(0.6rem * var(--label-scale));
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.plan__name {
  align-self: stretch;
  margin-bottom: 0.5rem;
  font-family: var(--display);
  font-size: 1.75rem;
  letter-spacing: 0.06em;
}
.plan__tagline {
  margin: 0 0 1.25rem;
  font-family: var(--display);
  font-style: italic;
  font-size: 1.4rem;
  color: var(--ink-dim);
}
.plan__terms,
.plan__prices { align-self: stretch; list-style: none; margin: 0; padding: 0; }
.plan__terms li {
  padding: 0.4rem 0;
  border-top: 1px solid var(--rule);
  font-size: 1.25rem;
}
.plan__terms em,
.plan__amount em {
  font-family: var(--display);
  font-style: italic;
  color: var(--gold);
}
.plan__terms em { font-size: 1.4em; line-height: 1; }
/* The prices sit at the foot, so both plans' rows line up across. */
.plan__prices { margin-top: auto; padding-top: 1.5rem; }
.plan__price {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem 0;
  border-top: 1px solid var(--rule-strong);
}
.plan__price:last-child { border-bottom: 1px solid var(--rule-strong); }
.plan__price:not(:has(form)) { justify-content: center; }
.plan__price form { margin: 0; flex: none; }
.plan__price .button { padding: 0.7rem 1.25rem; }
/* A phone's column cannot hold the annual saving and a full-width label. */
@media (max-width: 30rem) {
  .plan__price .button { padding: 0.7rem 0.9rem; letter-spacing: 0.14em; }
}
.plan__amount {
  margin: 0;
  text-align: left;
  font-size: 1.2rem;
  color: var(--ink-dim);
}
.plan__amount em { font-size: 2rem; line-height: 1; color: var(--gold); }
.plan__saving {
  display: block;
  font-size: calc(0.55rem * var(--label-scale));
  letter-spacing: 0.18em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--gold);
}
/* Study is free for everyone, so it answers before any price does: the
   gold italic of a lede's emphasis, between two hairlines. */
.pricing__study {
  margin: 1.5rem auto 0;
  padding: 0.9rem 0;
  border-block: 1px solid var(--rule);
  font-family: var(--display);
  font-style: italic;
  font-size: 1.4rem;
  line-height: 2rem;
  color: var(--ink);
}
.pricing__study a {
  font-style: normal;
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold);
}
@media print {
  .plan__price form { display: none; }
  .plan__card { box-shadow: none; }
}

.action {
  margin: 2.5rem 0 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
}
.action .field { margin-bottom: 1rem; }
.action .ask { margin-top: 1rem; }

.history {
  margin: 2.5rem 0 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
}
.history ol {
  list-style: none;
  padding: 0;
  margin: 1rem 0 0;
}
.history li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.25rem 1.25rem;
  padding: var(--row-pad) 0;
  border-top: 1px solid var(--rule);
}
.history__who {
  min-width: 0;
  overflow-wrap: anywhere;
}
.history__what {
  color: var(--ink-dim);
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.history__when {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
  color: var(--ink-faint);
  font-size: calc(0.7rem * var(--label-scale));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  overflow-wrap: anywhere;
}
@media (max-width: 40rem) {
  .history li { grid-template-columns: 1fr; }
  .history__when { grid-column: 1; grid-row: auto; }
}

/* After every `font:` shorthand, which resets variant. Uppercase in the
   element rules is the fallback when the face does not load. */
.masthead__nav,
.quota__plan,
.colophon__link,
.eyebrow,
.field > label,
.field__unmask,
.button,
.wait__status,
.reading__meta,
.share__summary,
.entry__card small,
.keywords li,
.note h4,
.reading-nav__title,
h2,
.entry__position,
.entries__when,
.entries__meta,
.observations__more summary,
.reading__interpret,
.reading__text,
.text-button,
.bed__name,
.bed__chip,
.spread-option,
.placing__kind,
.placing__spread-select,
.position-row__name,
.position-row__card small,
.positions__count,
.orient,
.deck__suits,
.deck__section h3,
.place-pill__count,
.place-pill__go,
.subnav,
.facts dt,
.plan__current,
.plan__saving,
.pricing__study a,
.history__what,
.history__when,
.listing th,
.listing-anon,
.dashboard__asof,
.panel__label,
.vital dt,
.gauge__label,
.columns__date,
.legend dt,
.bars__label {
  font-variant-caps: var(--label-caps);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
