/* ==========================================================================
   ROAD TO EMMAUS — timeline styles
   Warm-dark adaptation of the OnMeaning palette. Color belongs to threads;
   genre is communicated by glyph construction only.
   Layout rules (from the spec, learned the hard way):
   - All text is DOM text sized in CSS px. SVG is only for fixed-size glyphs.
   - Vertical spine. Thread lanes render per row from book-order indices.
   - One breakpoint: 900px. Below it detail is a full-screen view.
   ========================================================================== */

:root {
  --em-ground: #0b0906;
  --em-ground-2: #131009;
  --em-bone: #efe8d7;
  --em-bone-dim: #b3a88f;
  --em-rule: #332b1a;
  --em-rule-soft: #221c10;
  --em-dim: #8a7d61;
  /* Was #5c5340 at 2.62:1 on the ground and 2.50 on raised surfaces — well
     under AA. It carries era years, genre tags and menu subtitles, all of
     which are informational, not decorative. */
  --em-faint: #847d6f;
  --em-lens: #ffce8a;
  --em-lens-soft: #f2a65a;
  --em-serif: "Newsreader", Georgia, serif;
  --em-sans: "Inter", -apple-system, "Segoe UI", sans-serif;
  --em-mono: "IBM Plex Mono", ui-monospace, "Cascadia Mono", monospace;
}

/* Channels, so the accent and the overlay washes can change with the theme
   rather than being written into each rule. */
:root {
  --em-lens-rgb: 255 206 138;   /* gold, for glows and washes */
  --em-tint: 255 255 255;       /* hover overlay: light over dark */
  --em-scrim: 5 4 2;            /* modal backdrop */
  --em-raised: 24 20 12;        /* toast and floating surfaces */
  --em-plate: 0 0 0;            /* inset control backgrounds */

  /* Depth. On black a shadow is a black smear and reads as depth; on cream
     the same smear reads as dirt, so light uses a warm brown at a fraction
     of the opacity. */
  --em-shadow-rgb: 0 0 0;
  --em-shadow-a: 0.55;
  --em-shadow-a2: 0.25;

  /* The wash behind the header and the bloom at the top of the page. Both
     were hardcoded browns that turned into a dark stain on cream. */
  --em-glow: #1a1408;
  --em-top-wash: #120e07;

  /* The book list sits a step off the reading panel so the two read as
     separate surfaces rather than one sheet. */
  --em-list-bg: #0e0b07;
  --em-list-rgb: 14 11 7;
  --em-hero-core: #171208;
}

/* ---------- Light theme ----------
   The dark timeline reads as light in darkness: books glow, promises light
   up. That metaphor cannot survive a straight inversion, so light is its own
   design — ink and highlighter rather than lamp and halo. The accent drops
   from pale gold to a deep one that can hold its own on cream, and the
   overlay washes flip from white to ink. Thread colours are darkened in JS,
   where they live. */
:root[data-theme="light"] {
  --em-ground: #faf7f0;
  --em-ground-2: #f1ebdc;
  --em-bone: #241f16;
  --em-bone-dim: #4d4535;
  --em-rule: #e2dac6;
  --em-rule-soft: #ece5d5;
  /* A quiet tone on cream still has to clear 4.5:1, so the faint tier cannot
     be as faint as it is on black. Both are solved against --em-ground-2, the
     darker of the two surfaces, so they hold in menus and sheets as well as
     on the page. --em-dim is pushed further than it needs to be, to keep a
     visible step between the two tiers. */
  --em-dim: #5a5342;
  /* Solved to 5.0 rather than 4.5: tags and quiet labels sit on the lead
     section wash, which is tinted with the thread colour and costs the last
     fraction of contrast. */
  --em-faint: #696356;
  --em-lens: #815c16;
  --em-lens-soft: #85631b;
  --em-lens-rgb: 169 126 43;
  --em-tint: 36 31 22;
  --em-scrim: 60 52 36;
  --em-raised: 255 253 247;
  --em-plate: 120 108 84;

  --em-shadow-rgb: 92 74 42;
  --em-shadow-a: 0.14;
  --em-shadow-a2: 0.07;

  /* A warm highlight rather than a dark bloom, and a header wash barely
     distinguishable from the page — the point is a hairline of separation,
     not a banner. */
  --em-glow: #fffaf958;
  --em-top-wash: #f3ede0;

  --em-list-bg: #f5f1e7;
  --em-list-rgb: 245 241 231;
  --em-hero-core: #fffdf6;
}

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

/* Author `display` rules outrank the UA sheet's [hidden] { display: none } */
[hidden] {
  display: none !important;
}

/* The browser's default body margin would inset the whole app by 8px and
   clip the bottom of the 100dvh shell. This page loads no other stylesheet,
   so the reset has to live here. */
html.em-page,
.em-page body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
  background: var(--em-ground);
}

.em-app {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  background:
    radial-gradient(90rem 30rem at 50% -18rem, var(--em-glow) 0%, transparent 65%),
    var(--em-ground);
  color: var(--em-bone);
  font-family: var(--em-sans);
  -webkit-font-smoothing: antialiased;
}

.em-app ::selection {
  background: rgb(var(--em-lens-rgb) / 0.25);
}

.em-app :focus-visible,
.em-dialog :focus-visible {
  outline: 2px solid var(--em-lens);
  outline-offset: 2px;
  border-radius: 3px;
}

.em-app button,
.em-dialog button {
  -webkit-tap-highlight-color: transparent;
  font-family: inherit;
  cursor: pointer;
}

.em-mono {
  font-family: var(--em-mono);
}

/* Text links used inside the app (lede, endcap, dialogs) */
.em-link {
  display: inline-block;
  background: none;
  border: 0;
  padding: 6px 0;
  font-family: var(--em-mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--em-dim);
  text-decoration: none;
  border-bottom: 1px solid var(--em-rule);
  transition: color 0.2s ease, border-color 0.2s ease;
}

.em-link:hover {
  color: var(--em-lens);
  border-bottom-color: var(--em-lens-soft);
}

/* Solid + ghost buttons (welcome panel) */
.em-btn-solid,
.em-btn-ghost {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  font-weight: 600;
  padding: 11px 20px;
  min-height: 44px;
  border-radius: 999px;
  border: 1px solid transparent;
  transition: transform 0.15s ease, background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

/* The accent inverts between themes — pale gold on black, deep gold on cream
   — so the label on it has to invert too, or it reads as dark on dark. */
.em-btn-solid {
  background: var(--em-lens);
  color: var(--em-on-lens);
}

:root {
  --em-on-lens: #17120a;
  --em-lens-hover: #ffdba6;
}

:root[data-theme="light"] {
  --em-on-lens: #fffdf7;
  --em-lens-hover: #75530f;
}

.em-btn-solid:hover {
  background: var(--em-lens-hover);
  transform: translateY(-1px);
}

.em-btn-ghost {
  background: transparent;
  border-color: var(--em-rule);
  color: var(--em-bone-dim);
}

.em-btn-ghost:hover {
  border-color: var(--em-dim);
  color: var(--em-bone);
}

/* ---------- Top bar ---------- */

.em-top {
  flex-shrink: 0;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px 12px;
  border-bottom: 1px solid var(--em-rule);
  background: linear-gradient(180deg, var(--em-top-wash) 0%, transparent 100%);
}

@media (min-width: 900px) {
  .em-top {
    padding: 14px 24px 14px;
  }
}

.em-top-left {
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 12px;
}

.em-theme-toggle {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  border: 1px solid var(--em-rule);
  background: transparent;
  color: var(--em-dim);
  transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}

.em-theme-toggle svg {
  width: 16px;
  height: 16px;
}

.em-theme-toggle:hover {
  color: var(--em-lens);
  border-color: var(--em-lens);
  background: rgb(var(--em-lens-rgb) / 0.08);
}

.em-theme-toggle .icon-sun { display: none; }
[data-theme="light"] .em-theme-toggle .icon-sun { display: block; }
[data-theme="light"] .em-theme-toggle .icon-moon { display: none; }

/* Obvious way back out of the app */
.em-home-btn {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 999px;
  border: 1px solid var(--em-rule);
  background: transparent;
  color: var(--em-bone-dim);
  text-decoration: none;
  transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease, transform 0.2s ease;
}

.em-home-btn svg {
  width: 19px;
  height: 19px;
}

.em-home-btn:hover {
  color: var(--em-lens);
  border-color: var(--em-lens);
  background: rgb(var(--em-lens-rgb) / 0.08);
  /* A lift, not a nudge left — the leftward slide belonged to the back arrow
     this replaced and would still suggest stepping backwards. */
  transform: translateY(-1px);
}

.em-title-block {
  min-width: 0;
}

.em-home-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--em-mono);
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--em-dim);
  text-decoration: none;
  padding: 2px 0;
  transition: color 0.2s ease;
}

.em-home-label svg {
  width: 13px;
  height: 13px;
  color: var(--em-lens-soft);
}

.em-home-label:hover {
  color: var(--em-lens);
}

.em-title {
  font-family: var(--em-serif);
  font-size: clamp(15px, 4.9vw, 22px);
  font-weight: 400;
  line-height: 1.1;
  margin: 3px 0 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  letter-spacing: 0.005em;
}

.em-title em {
  font-style: normal;
  color: var(--em-lens);
}

@media (min-width: 900px) {
  .em-title {
    font-size: 30px;
  }
}

.em-top-actions {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Transient confirmation of what just changed, so the collapsed icon does not
   have to carry a permanent label. */
/* left+right with auto margins centers it while leaving the full viewport
   width available; anchoring at left:50% would cap it at half the screen. */
.em-toast {
  position: fixed;
  left: 1rem;
  right: 1rem;
  bottom: 30px;
  z-index: 80;
  width: fit-content;
  max-width: min(30rem, 100%);
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 13px 22px;
  border-radius: 15px;
  text-align: center;
  background: rgb(var(--em-raised) / 0.97);
  border: 1px solid #4a3f28;
  box-shadow: 0 16px 44px rgb(var(--em-shadow-rgb) / var(--em-shadow-a));
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  transform: translateY(14px);
  pointer-events: none;
  transition: opacity 0.28s ease, transform 0.28s ease;
}

.em-toast.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.em-toast-title {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--em-lens);
}

.em-toast-sub {
  font-family: var(--em-serif);
  font-size: 13px;
  line-height: 1.45;
  color: var(--em-bone-dim);
}

@media (max-width: 560px) {
  .em-toast {
    bottom: 22px;
    padding: 12px 18px;
  }

  .em-toast-sub {
    font-size: 12.5px;
  }
}

/* Order axis: segmented control where there is room, compact swap where not */
.em-order {
  display: inline-flex;
  padding: 3px;
  border-radius: 999px;
  border: 1px solid var(--em-rule);
  background: rgb(var(--em-plate) / 0.25);
}

.em-order button {
  padding: 6px 13px;
  min-height: 32px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  font-size: 12px;
  font-weight: 500;
  color: var(--em-dim);
  white-space: nowrap;
  transition: color 0.18s ease, background-color 0.18s ease;
}

.em-order button:hover {
  color: var(--em-bone-dim);
}

.em-order button[aria-pressed="true"] {
  background: rgb(var(--em-lens-rgb) / 0.14);
  color: var(--em-lens);
}

/* A 14% gold tint reads as a lit plate on black. On cream it is just tan, and
   gold-on-tan is 3.26:1 — so the selected segment fills properly instead. */
[data-theme="light"] .em-order button[aria-pressed="true"] {
  background: var(--em-lens);
  color: var(--em-on-lens);
}

.em-order-swap {
  display: none;
  align-items: center;
  gap: 7px;
  padding: 8px 13px 8px 10px;
  min-height: 38px;
  border-radius: 999px;
  border: 1px solid var(--em-rule);
  background: transparent;
  font-size: 12px;
  font-weight: 500;
  color: var(--em-bone-dim);
  white-space: nowrap;
  transition: color 0.2s ease, border-color 0.2s ease;
}

.em-order-swap svg {
  width: 14px;
  height: 14px;
  color: var(--em-lens-soft);
  flex-shrink: 0;
}

.em-order-swap:hover {
  color: var(--em-lens);
  border-color: var(--em-dim);
}

/* Below this width the segmented control and the help label cannot both fit */
@media (max-width: 780px) {
  .em-order {
    display: none;
  }

  .em-order-swap {
    display: inline-flex;
  }
}

/* Phone: trim the chrome so the title has room */
@media (max-width: 460px) {
  .em-top {
    gap: 8px;
    padding-inline: 12px;
  }

  .em-home-btn {
    width: 36px;
    height: 36px;
  }

  .em-top-left {
    gap: 9px;
  }
}

/* Smallest phones still in use (320px): tighten enough to keep the name */
@media (max-width: 360px) {
  .em-top {
    gap: 6px;
    padding-inline: 10px;
  }

  .em-top-left {
    gap: 7px;
  }

  .em-top-actions {
    gap: 6px;
  }
}

.em-help-btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 13px;
  font-weight: 500;
  padding: 9px 16px 9px 11px;
  min-height: 40px;
  border-radius: 999px;
  border: 1px solid var(--em-rule);
  background: transparent;
  color: var(--em-bone-dim);
  transition: color 0.2s ease, border-color 0.2s ease;
}

.em-help-mark {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border-radius: 999px;
  border: 1px solid currentColor;
  font-family: var(--em-mono);
  font-size: 11px;
  opacity: 0.8;
}

.em-help-btn:hover {
  color: var(--em-lens);
  border-color: var(--em-dim);
}

/* Narrow: both actions collapse to matching round icon buttons so the
   title is not squeezed. State is carried by the sort icon's tint. */
@media (max-width: 560px) {
  .em-help-label,
  .em-order-swap-label {
    display: none;
  }

  .em-help-btn,
  .em-order-swap {
    width: 36px;
    height: 36px;
    min-height: 36px;
    padding: 0;
    gap: 0;
    justify-content: center;
  }

  .em-help-mark {
    width: 20px;
    height: 20px;
  }

  .em-order-swap svg {
    width: 16px;
    height: 16px;
  }

  /* Canonical is the non-default axis — show that something is switched */
  .em-order-swap.is-alt {
    border-color: var(--em-lens);
    background: rgb(var(--em-lens-rgb) / 0.12);
  }

  .em-order-swap.is-alt svg {
    color: var(--em-lens);
  }
}

/* ---------- Thread bar + ribbon ---------- */

.em-chipbar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 4px;
  border-bottom: 1px solid var(--em-rule);
}

.em-chipbar-label {
  flex-shrink: 0;
  padding: 0 4px 0 16px;
  font-family: var(--em-mono);
  font-size: 8.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--em-faint);
}

@media (min-width: 900px) {
  .em-chipbar-label {
    padding-left: 24px;
  }
}

/* Chips never scroll now. fitChips() hides the ones that will not fit and
   moves them into the More menu, so overflow here is only a safety net. */
.em-chips {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 8px 10px 8px;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
}

/* ---------- More menu ----------
   The chip row is measured against the space it actually has, and the
   remainder collapses into this. The active chip is always kept in the row,
   so the menu never hides the thread you are reading. */

.em-more {
  position: relative;
  flex-shrink: 0;
  padding-right: 16px;
}

.em-chip-more {
  gap: 5px;
}

.em-chip-more[aria-expanded="true"] {
  color: var(--em-bone);
  border-color: var(--em-bone-dim, var(--em-dim));
}

.em-more-count::before {
  content: " ";
}

.em-more-count {
  font-family: var(--em-mono);
  font-size: 11px;
  color: var(--em-faint);
}

.em-more-chevron {
  width: 13px;
  height: 13px;
  transition: transform 0.18s ease;
}

.em-chip-more[aria-expanded="true"] .em-more-chevron {
  transform: rotate(180deg);
}

.em-more-menu {
  position: absolute;
  top: calc(100% - 4px);
  right: 16px;
  z-index: 40;
  width: 17rem;
  max-height: min(60vh, 26rem);
  overflow-y: auto;
  padding: 6px;
  border: 1px solid var(--em-rule);
  border-radius: 14px;
  background: var(--em-ground-2);
  box-shadow: 0 18px 50px rgb(var(--em-shadow-rgb) / var(--em-shadow-a));
  animation: em-more-in 0.14s ease-out both;
}

@keyframes em-more-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}

.em-more-menu button {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--em-dim);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  text-align: left;
}

.em-more-menu button:hover,
.em-more-menu button:focus-visible {
  background: rgb(var(--em-tint) / 0.06);
  color: var(--em-bone);
}

.em-more-menu button[aria-pressed="true"] {
  color: var(--em-bone);
}

.em-more-dot {
  flex-shrink: 0;
  width: 9px;
  height: 9px;
  border-radius: 999px;
  background: var(--row-color);
}

.em-more-sub {
  display: block;
  margin-top: 1px;
  font-size: 11.5px;
  font-weight: 400;
  color: var(--em-faint);
}

/* ---------- Thread picker ----------
   The chip row did not survive the thread count growing: six chips needed
   ~700px, eight ~860px, and fifteen need ~1630px — past any laptop. Raising
   the breakpoint each time only delayed the problem, so the chip row is now
   retired at every width and the picker is the only control.

   It names the current thread and opens a sheet listing every thread at once,
   which costs one tap and scales to any number of them. The chips are left in
   the markup but never shown; nothing reads them. */

/* A <button> shrink-wraps even as a flex container, so it needs an explicit
   width — and the horizontal inset lives on .em-chipbar rather than here,
   since width:100% plus side margins would push the right edge off screen. */
.em-picker {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 12px;
  margin: 9px 0 11px;
  padding: 12px 14px;
  border-radius: 13px;
  border: 1px solid #56492e;
  background: rgb(var(--em-tint) / 0.05);
  box-shadow: inset 0 1px 0 rgb(var(--em-tint) / 0.045), 0 2px 10px rgb(var(--em-shadow-rgb) / var(--em-shadow-a2));
  text-align: left;
  color: inherit;
  transition: border-color 0.18s ease, background-color 0.18s ease;
}

.em-picker:hover {
  border-color: var(--em-bone-dim);
  background: rgb(var(--em-tint) / 0.08);
}

.em-picker-dot {
  flex: none;
  width: 12px;
  height: 12px;
  border-radius: 999px;
  border: 1.5px dashed var(--em-lens-soft);
  transition: background-color 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
}

.em-picker.is-active .em-picker-dot {
  border: 0;
  background: var(--picker-color);
  box-shadow: 0 0 9px color-mix(in srgb, var(--picker-color) 75%, transparent);
}

.em-picker-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.em-picker-title {
  font-size: 14.5px;
  font-weight: 600;
  color: var(--em-bone);
  line-height: 1.2;
}

.em-picker.is-active .em-picker-title {
  color: var(--picker-color);
}

.em-picker-sub {
  font-family: var(--em-serif);
  font-size: 12.5px;
  line-height: 1.35;
  color: var(--em-bone-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Names the interaction outright, so the control cannot be mistaken for a label */
.em-picker-action {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 5px 9px 5px 10px;
  border-radius: 999px;
  background: rgb(var(--em-lens-rgb) / 0.14);
  font-family: var(--em-mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--em-lens);
}

.em-picker-chevron {
  flex: none;
  width: 14px;
  height: 14px;
  color: var(--em-lens);
}

/* Opens the book drawer. Floats clear of the header rather than sitting in
   it — the top bar was already carrying the title, the order swap and the
   help button, and a fourth control there read as clutter. Bottom left is
   the side the drawer arrives from, and it is within thumb reach.
   Shown wherever the list is a drawer: the 900px layout breakpoint, not the
   768px chip one. */
.em-drawer-fab {
  display: none;
  position: fixed;
  left: 16px;
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  z-index: 45;
  width: 54px;
  height: 54px;
  place-items: center;
  border-radius: 999px;
  border: 1px solid var(--em-rule);
  background: rgb(var(--em-raised) / 0.94);
  color: var(--em-bone);
  box-shadow: 0 10px 30px rgb(var(--em-shadow-rgb) / var(--em-shadow-a));
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: opacity 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
}

.em-drawer-fab svg {
  width: 22px;
  height: 22px;
}

.em-drawer-fab:hover {
  border-color: var(--em-dim);
  transform: translateY(-1px);
}

/* Stays put while the drawer is open and inverts, so it reads as pressed.
   Keeping it visible is what ties the button to the panel it opened — and it
   gives a second way out alongside the scrim, a swipe, and Escape. */
.em-app.drawer-open .em-drawer-fab {
  background: var(--em-bone);
  border-color: var(--em-bone);
  color: var(--em-ground);
  box-shadow: 0 6px 22px rgb(var(--em-shadow-rgb) / var(--em-shadow-a));
}

.em-app.drawer-open .em-drawer-fab:hover {
  background: #fff;
  transform: none;
}

@media (max-width: 899.98px) {
  .em-drawer-fab {
    display: grid;
  }

  /* The toast is centred at the bottom and would otherwise share that space. */
  .em-toast {
    bottom: 88px;
  }

  /* The button now sits over the open drawer, so the last book in the list
     needs room to clear it. */
  .em-list {
    padding-bottom: 84px;
  }
}

/* Below the chip breakpoint the whole row collapses into the picker — one
   control, one sheet. Kept as a single menu on phones deliberately: a
   measured row of chips is not worth the horizontal space there.
   The JS mirrors this breakpoint in `mqChips`; change both together. */
@media (max-width: 767.98px) {
  /* Inset matches .em-top so the controls line up with the header */
  .em-chipbar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding-inline: 16px;
  }

  .em-chipbar-label,
  .em-chips,
  .em-more {
    display: none !important;
  }

  .em-picker {
    display: flex;
    flex: 1;
    min-width: 0;
    margin-block: 9px 11px;
  }

  /* The picker already invites the choice; the hint would repeat it */
  .em-ribbon.is-hint {
    display: none;
  }
}

/* From tablet up the chips return, fitted to the width by fitChips(). */
@media (min-width: 768px) {
  .em-picker {
    display: none;
  }
}

/* Keep the picker's inset in step with the header's at every width. These
   must follow the rule above to win the cascade. */
@media (max-width: 460px) {
  .em-chipbar {
    padding-inline: 12px;
  }
}

@media (max-width: 360px) {
  .em-chipbar {
    padding-inline: 10px;
  }
}

/* ---------- Thread sheet ---------- */

.em-sheet {
  margin: auto auto 0;
  width: 100%;
  max-width: 34rem;
  max-height: 88dvh;
  padding: 0;
  border: 1px solid var(--em-rule);
  border-bottom: 0;
  border-radius: 20px 20px 0 0;
  background: var(--em-ground-2);
  color: var(--em-bone);
  font-family: var(--em-sans);
  box-shadow: 0 -12px 60px rgb(var(--em-shadow-rgb) / var(--em-shadow-a));
  animation: em-sheet-up 0.26s cubic-bezier(0.2, 0.7, 0.3, 1) both;
}

.em-sheet::backdrop {
  background: rgb(var(--em-scrim) / 0.72);
  backdrop-filter: blur(3px);
}

@keyframes em-sheet-up {
  from { transform: translateY(18px); opacity: 0.4; }
  to { transform: translateY(0); opacity: 1; }
}

@media (min-width: 1024px) {
  .em-sheet {
    margin: auto;
    border-radius: 20px;
    border-bottom: 1px solid var(--em-rule);
  }
}

.em-sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 20px 12px;
}

.em-sheet-head h2 {
  font-family: var(--em-serif);
  font-size: 21px;
  font-weight: 400;
  margin: 0;
}

.em-sheet-close {
  flex: none;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  border: 1px solid var(--em-rule);
  background: transparent;
  color: var(--em-dim);
  font-size: 14px;
}

.em-sheet-close:hover {
  color: var(--em-bone);
  border-color: var(--em-dim);
}

.em-sheet-intro {
  margin: 0;
  padding: 0 20px 14px;
  font-family: var(--em-serif);
  font-size: 14px;
  line-height: 1.5;
  color: var(--em-bone-dim);
}

.em-sheet-list {
  list-style: none;
  margin: 0;
  padding: 0 12px 20px;
  overflow-y: auto;
  max-height: calc(88dvh - 130px);
}

.em-sheet-list button {
  display: flex;
  align-items: center;
  gap: 13px;
  width: 100%;
  padding: 13px 12px;
  border: 0;
  border-radius: 12px;
  background: transparent;
  text-align: left;
  color: inherit;
  transition: background-color 0.15s ease;
}

.em-sheet-list button:hover {
  background: rgb(var(--em-tint) / 0.05);
}

.em-sheet-list button[aria-pressed="true"] {
  background: color-mix(in srgb, var(--row-color) 13%, transparent);
}

.em-sheet-dot {
  flex: none;
  width: 12px;
  height: 12px;
  border-radius: 999px;
  background: var(--row-color);
  opacity: 0.55;
}

.em-sheet-list button[aria-pressed="true"] .em-sheet-dot {
  opacity: 1;
  box-shadow: 0 0 10px color-mix(in srgb, var(--row-color) 80%, transparent);
}

.em-sheet-copy {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.em-sheet-name {
  font-size: 15.5px;
  font-weight: 600;
  color: var(--em-bone);
}

.em-sheet-list button[aria-pressed="true"] .em-sheet-name {
  color: var(--row-color);
}

.em-sheet-sub {
  font-family: var(--em-serif);
  font-size: 13px;
  line-height: 1.4;
  color: var(--em-bone-dim);
}

.em-sheet-meta {
  flex: none;
  font-family: var(--em-mono);
  font-size: 9.5px;
  letter-spacing: 0.06em;
  color: var(--em-faint);
  white-space: nowrap;
}

.em-sheet-clear {
  margin-top: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--em-rule);
}

.em-sheet-clear button {
  color: var(--em-dim);
  font-size: 14px;
  justify-content: center;
}

.em-sheet-clear button:hover {
  color: var(--em-bone-dim);
}

.em-chip {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 500;
  padding: 8px 14px;
  min-height: 38px;
  border-radius: 999px;
  border: 1px solid var(--em-rule);
  background: transparent;
  color: var(--em-dim);
  transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
}

.em-chip:hover {
  border-color: var(--em-dim);
  color: var(--em-bone-dim);
}

/* Each chip's dot always wears its thread color — dimmed until active —
   so the colors on the timeline are learnable at a glance. */
.em-chip .em-chip-dot {
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--chip-color);
  opacity: 0.5;
  transition: opacity 0.15s ease, box-shadow 0.15s ease;
}

.em-chip[aria-pressed="true"] {
  border-color: var(--chip-color);
  background: color-mix(in srgb, var(--chip-color) 11%, transparent);
  color: var(--chip-color);
  box-shadow: 0 0 16px color-mix(in srgb, var(--chip-color) 14%, transparent);
}

.em-chip[aria-pressed="true"] .em-chip-dot {
  opacity: 1;
  box-shadow: 0 0 7px color-mix(in srgb, var(--chip-color) 80%, transparent);
}

/* Ribbon: one line introducing the thread just turned on */
.em-ribbon {
  flex-shrink: 0;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding: 9px 16px 10px;
  border-bottom: 1px solid var(--em-rule-soft);
  background: var(--em-ground-2);
  animation: em-fade 0.3s ease-out both;
}

.em-ribbon[hidden] {
  display: none;
}

@media (min-width: 900px) {
  .em-ribbon {
    padding-inline: 24px;
  }
}

.em-ribbon-name {
  font-family: var(--em-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ribbon-color);
}

.em-ribbon-blurb {
  font-family: var(--em-serif);
  font-size: 14px;
  font-style: italic;
  color: var(--em-bone-dim);
}

/* Keep the ribbon to a single line: the blurb absorbs the slack and
   truncates, so the link to the thread's page is never pushed to a new row. */
@media (min-width: 900px) {
  .em-ribbon {
    flex-wrap: nowrap;
    align-items: baseline;
  }

  .em-ribbon-blurb {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .em-ribbon-name,
  .em-ribbon-sub,
  .em-ribbon-meta {
    flex-shrink: 0;
  }
}

/* Mobile: keep chrome compact — thread name and extent only */
@media (max-width: 899.98px) {
  .em-ribbon-blurb,
  .em-ribbon-sub {
    display: none;
  }
}

.em-ribbon-sub {
  font-family: var(--em-mono);
  font-size: 9.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--em-faint);
}

.em-ribbon-meta {
  font-family: var(--em-mono);
  font-size: 9.5px;
  color: var(--em-dim);
  white-space: nowrap;
}

/* Hint state: shown until the first thread is chosen */
.em-ribbon-hint {
  font-family: var(--em-serif);
  font-style: italic;
  font-size: 13.5px;
  color: var(--em-dim);
}

/* ---------- Body: list + detail ---------- */

.em-body {
  flex: 1;
  min-height: 0;
  display: flex;
}

/* Offset from the reading panel by one step, so the index and the text you
   are reading register as two surfaces rather than one continuous sheet.
   Deliberately slight — a shade, not a border. */
.em-list {
  height: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  flex: 1;
  min-width: 0;
  scroll-padding-top: 44px;
  background: var(--em-list-bg);
}

.em-list::-webkit-scrollbar,
.em-detail::-webkit-scrollbar {
  width: 6px;
}

.em-list::-webkit-scrollbar-thumb,
.em-detail::-webkit-scrollbar-thumb {
  background: var(--em-rule);
  border-radius: 99px;
}

.em-detail {
  display: none;
  height: 100%;
  overflow-y: auto;
}

@media (min-width: 900px) {
  .em-list {
    flex: none;
    width: 460px;
    border-right: 1px solid var(--em-rule);
  }

  .em-detail {
    display: block;
    flex: 1;
    min-width: 0;
  }
}

/* Below 900px the reading panel is the page and the book list is a drawer
   that pulls out over it. The old model swapped the two views, which meant
   every move between a book and the list was a full-screen change and the
   way back was a text link that scrolled away. The drawer keeps the reading
   surface put and the index one tap away, from a control that never moves. */
@media (max-width: 899.98px) {
  .em-detail {
    display: block;
    flex: 1;
  }

  .em-list {
    position: absolute;
    z-index: 30;
    inset-block: 0;
    left: 0;
    width: min(84vw, 380px);
    /* The browser keeps vertical panning; horizontal is left for the
       swipe-to-close handler, which therefore needs no preventDefault. */
    touch-action: pan-y;
    border-right: 1px solid var(--em-rule);
    background: var(--em-ground-2);
    box-shadow: 0 0 60px rgb(var(--em-shadow-rgb) / var(--em-shadow-a));
    transform: translateX(-101%);
    /* visibility keeps the closed drawer out of the tab order. It flips on
       immediately when opening — a focus() call cannot land on a hidden
       element — and only after the slide when closing. */
    visibility: hidden;
    transition: transform 0.28s cubic-bezier(0.2, 0.7, 0.3, 1), visibility 0s 0.28s;
  }

  .em-app.drawer-open .em-list {
    transform: none;
    visibility: visible;
    transition: transform 0.28s cubic-bezier(0.2, 0.7, 0.3, 1), visibility 0s;
  }

  .em-scrim {
    position: absolute;
    inset: 0;
    z-index: 20;
    background: rgb(var(--em-scrim) / 0.6);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    opacity: 0;
    transition: opacity 0.28s ease;
  }

  .em-app.drawer-open .em-scrim {
    opacity: 1;
  }
}

/* The drawer is positioned against this, and it clips the slide. */
.em-body {
  position: relative;
}

@media (min-width: 900px) {
  .em-scrim {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .em-list {
    transition: none;
  }
}

/* ---------- List lede (mobile orientation; scrolls away) ---------- */

.em-lede {
  padding: 16px 16px 4px;
  max-width: 34em;
}

.em-lede-title {
  font-family: var(--em-serif);
  font-size: 18px;
  font-weight: 400;
  color: var(--em-bone);
  margin: 0 0 5px;
}

.em-lede-text {
  font-size: 13px;
  line-height: 1.6;
  color: var(--em-dim);
  margin: 0 0 8px;
}

@media (min-width: 900px) {
  .em-lede {
    display: none;
  }
}

/* ---------- Era headers ---------- */

/* Era headers sit ON the line: lanes and spine run straight through them,
   and a diamond tick marks the era boundary like a milestone. */
.em-era {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: stretch;
  background: var(--em-list-bg);
}

/* soft scrim so rows fade as they pass beneath the sticky header */
.em-era::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: 10px;
  background: linear-gradient(180deg, rgb(var(--em-list-rgb) / 0.9), transparent);
  pointer-events: none;
}

.em-era-spine {
  position: relative;
  flex-shrink: 0;
  width: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.em-era-tick {
  position: relative;
  display: block;
  width: 6px;
  height: 6px;
  background: var(--em-dim);
  transform: rotate(45deg);
  outline: 3px solid var(--em-list-bg);
}

.em-era.is-silence .em-era-tick {
  background: var(--em-bone-dim);
}

.em-era-text {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 14px 7px 0;
  border-bottom: 1px solid var(--em-rule-soft);
}

.em-era-label {
  font-family: var(--em-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--em-dim);
}

.em-era-years {
  font-family: var(--em-mono);
  font-size: 9px;
  letter-spacing: 0.06em;
  color: var(--em-faint);
  white-space: nowrap;
}

.em-era.is-silence .em-era-label {
  color: var(--em-bone-dim);
}

/* ---------- Book rows ---------- */

.em-row {
  display: flex;
  align-items: stretch;
  width: 100%;
  text-align: left;
  border: 0;
  padding: 0;
  background: transparent;
  color: inherit;
  transition: background-color 0.15s ease;
}

.em-row:hover {
  background: rgb(var(--em-tint) / 0.035);
}

/* Books the active thread touches. The halo alone did not carry across a long
   list, so the row itself takes the thread's colour: a wash that fades to the
   right, and a bar on the leading edge to square up the column. */
.em-row.is-onthread {
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--thread-color) 17%, transparent),
    color-mix(in srgb, var(--thread-color) 4%, transparent) 62%,
    transparent);
  box-shadow: inset 2px 0 0 var(--thread-color);
}

.em-row.is-onthread:hover {
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--thread-color) 26%, transparent),
    color-mix(in srgb, var(--thread-color) 7%, transparent) 62%,
    transparent);
}

/* Where the promise arrives, gold overrides the thread colour. */
.em-row.is-arrival {
  background: linear-gradient(90deg,
    rgb(var(--em-lens-rgb) / 0.2),
    rgb(var(--em-lens-rgb) / 0.05) 62%,
    transparent);
  box-shadow: inset 2px 0 0 var(--em-lens);
}

.em-row.is-arrival:hover {
  background: linear-gradient(90deg,
    rgb(var(--em-lens-rgb) / 0.29),
    rgb(var(--em-lens-rgb) / 0.08) 62%,
    transparent);
}

/* The selection reads over either of them. */
.em-row.is-sel,
.em-row.is-sel.is-onthread,
.em-row.is-sel.is-arrival {
  background: linear-gradient(90deg, rgb(var(--em-lens-rgb) / 0.07), rgb(var(--em-tint) / 0.03));
}

/* Thread lanes (decorative; membership is conveyed by inline text).
   Row internals are <span>s (buttons allow only phrasing content),
   so display is set explicitly throughout. */
.em-lanes {
  display: block;
  position: relative;
  flex-shrink: 0;
  align-self: stretch;
}

.em-lane {
  display: block;
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  opacity: 0.75;
}

.em-lane-dot {
  display: block;
  position: absolute;
  top: 50%;
  margin-top: -3.5px;
  width: 7px;
  height: 7px;
  border-radius: 999px;
}

/* Where a promise lands in Christ — ringed so the arrival reads at a glance */
.em-lane-dot.is-fulfillment {
  box-shadow: 0 0 0 2px var(--em-list-bg), 0 0 0 3.5px var(--em-lens), 0 0 9px rgb(var(--em-lens-rgb) / 0.7);
}

/* Spine */
.em-spine {
  position: relative;
  flex-shrink: 0;
  width: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.em-spine-rule {
  display: block;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 19.5px;
  width: 1px;
  background: var(--em-rule);
}

.em-halo {
  display: block;
  position: absolute;
  border-radius: 999px;
  pointer-events: none;
  animation: em-bloom 0.55s ease-out both;
}


.em-glyph {
  display: block;
  position: relative;
  background: var(--em-list-bg);
  border-radius: 99px;
  padding: 1px;
}

.em-glyph svg {
  display: block;
}

/* Row content */
.em-row-main {
  flex: 1;
  min-width: 0;
  padding: 9px 12px 9px 0;
  min-height: 46px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

@media (min-width: 900px) {
  .em-row-main {
    padding-block: 7px;
    min-height: 36px;
  }
}

.em-row-line {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}

.em-book-name {
  font-size: 15.5px;
  line-height: 1.3;
  color: var(--em-bone-dim);
  transition: color 0.15s ease;
}

@media (min-width: 900px) {
  .em-book-name {
    font-size: 14px;
  }
}

.em-row:hover .em-book-name,
.em-row.is-lit .em-book-name,
.em-row.is-sel .em-book-name {
  color: var(--em-bone);
}

/* Must follow .is-lit above: same specificity, so source order decides.
   A book on the active thread reads brighter; an arrival reads gold. */
.em-row.is-onthread .em-book-name {
  color: var(--em-bone);
  font-weight: 500;
}

.em-row.is-arrival .em-book-name {
  color: var(--em-lens);
  font-weight: 500;
}

.em-row.is-sel .em-book-name {
  font-family: var(--em-serif);
  font-weight: 500;
  font-size: 16.5px;
}

@media (min-width: 900px) {
  .em-row.is-sel .em-book-name {
    font-size: 15px;
  }
}

.em-genre-tag {
  display: none;
  font-family: var(--em-mono);
  font-size: 8.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--em-faint);
}

@media (min-width: 900px) {
  .em-genre-tag {
    display: inline;
  }
}

.em-star {
  font-family: var(--em-mono);
  font-size: 9.5px;
  color: var(--em-lens);
  opacity: 0.8;
}

@media (min-width: 900px) {
  .em-star {
    display: none;
  }
}

/* A promise arriving in Christ. The thread keeps its color (color belongs to
   threads); the gold star marks the arrival. */
.em-entry.is-fulfillment .em-entry-ref::before {
  content: "\2726\00a0";
  color: var(--em-lens);
}

.em-entry.is-fulfillment {
  background: linear-gradient(90deg, rgb(var(--em-lens-rgb) / 0.06), transparent 75%);
  border-radius: 0 8px 8px 0;
  padding-block: 8px;
  margin-left: 0;
}

/* ---------- The Silence ---------- */

.em-silence {
  display: flex;
  align-items: stretch;
  height: 150px;
}

.em-silence-text {
  flex: 1;
  display: flex;
  align-items: center;
  font-family: var(--em-serif);
  font-style: italic;
  font-size: 15px;
  letter-spacing: 0.08em;
  color: var(--em-faint);
}

/* ---------- End cap ---------- */

.em-endcap {
  padding: 44px 24px 64px;
  text-align: center;
}

.em-endcap-star {
  display: block;
  font-size: 13px;
  color: var(--em-lens);
  text-shadow: 0 0 14px rgb(var(--em-lens-rgb) / 0.7);
  margin-bottom: 18px;
}

.em-endcap-line {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--em-dim);
  margin: 0 0 6px;
}

.em-endcap-quote {
  font-family: var(--em-serif);
  font-style: italic;
  font-size: 19px;
  color: var(--em-bone);
  margin: 0 0 22px;
}

.em-endcap-links {
  display: flex;
  justify-content: center;
  gap: 22px;
  flex-wrap: wrap;
}

/* ---------- Detail view ---------- */

.em-detail-inner {
  padding: 22px 20px 56px;
  max-width: 660px;
  animation: em-fade-up 0.32s ease-out both;
}

@media (min-width: 900px) {
  .em-detail-inner {
    padding: 36px 40px 64px;
  }
}

/* Must follow the shorthand above, or it is overwritten by it. Clears the
   floating drawer button so the last line of a passage is never under it. */
@media (max-width: 899.98px) {
  .em-detail-inner {
    padding-bottom: 96px;
  }
}

/* Route from a book to the thread's full arc. A button rather than a back
   link: the reader may have opened this book first and turned the thread on
   afterwards, in which case there is nothing to go "back" to — it is an
   invitation to somewhere they have not been. */
.em-thread-cta {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 20px;
  padding: 9px 15px 9px 13px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--thread-color) 42%, transparent);
  background: color-mix(in srgb, var(--thread-color) 11%, transparent);
  color: var(--thread-color);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.005em;
  transition: background-color 0.16s ease, border-color 0.16s ease, transform 0.16s ease;
}

.em-thread-cta:hover {
  background: color-mix(in srgb, var(--thread-color) 20%, transparent);
  border-color: var(--thread-color);
  transform: translateX(2px);
}

.em-thread-cta svg {
  width: 14px;
  height: 14px;
  opacity: 0.75;
}

.em-thread-cta-dot {
  width: 9px;
  height: 9px;
  border-radius: 999px;
  background: var(--thread-color);
  box-shadow: 0 0 8px color-mix(in srgb, var(--thread-color) 70%, transparent);
}

/* ---------- Thread overview (the panel a thread opens into) ---------- */

.em-ov-hero {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 18px;
}

.em-ov-dot {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  border-radius: 999px;
  background: var(--thread-color);
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--thread-color) 18%, transparent);
}

.em-ov-sub {
  margin-top: 3px;
  font-size: 14px;
  color: var(--em-dim);
}

.em-ov-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--em-rule);
  font-family: var(--em-mono);
  font-size: 11.5px;
  color: var(--em-faint);
}

.em-ov-meta b {
  color: var(--em-bone-dim, var(--em-dim));
  font-weight: 500;
}

.em-ov-intro p {
  margin: 16px 0 0;
  font-family: var(--em-serif);
  font-size: 16.5px;
  line-height: 1.72;
  color: var(--em-bone-dim, var(--em-dim));
}

.em-ov-arc-label {
  margin: 30px 0 4px;
}

/* The arc. A rule runs the length of it in the thread's colour, so the beats
   read as one sequence rather than as a stack of separate cards. */
.em-beats {
  position: relative;
  margin: 0;
  padding: 6px 0 0 20px;
  list-style: none;
}

.em-beats::before {
  content: "";
  position: absolute;
  left: 3px;
  top: 14px;
  bottom: 14px;
  width: 2px;
  border-radius: 2px;
  background: linear-gradient(to bottom,
    color-mix(in srgb, var(--thread-color) 15%, transparent),
    var(--thread-color) 12%,
    var(--thread-color) 88%,
    color-mix(in srgb, var(--thread-color) 15%, transparent));
  opacity: 0.5;
}

.em-beat {
  position: relative;
  padding: 0 0 26px;
}

.em-beat::before {
  content: "";
  position: absolute;
  left: -21px;
  top: 7px;
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--thread-color);
}

/* A landing is where the promise arrives; ring it and turn it gold. */
.em-beat.is-fulfillment::before {
  left: -23px;
  top: 5px;
  width: 12px;
  height: 12px;
  background: var(--em-lens);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--em-lens) 22%, transparent);
}

.em-beat-book {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--em-mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--em-faint);
  transition: color 0.15s ease;
}

.em-beat-book:hover,
.em-beat-book:focus-visible {
  color: var(--em-bone);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.em-beat.is-fulfillment .em-beat-book {
  color: var(--em-lens);
}

.em-beat-body {
  margin-top: 5px;
}

.em-beat-ref {
  display: inline-block;
  font-size: 13px;
  font-weight: 600;
  color: var(--em-bone-dim, var(--em-dim));
  text-decoration: none;
}

.em-beat-ref:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.em-beat-quote {
  margin: 7px 0 0;
  padding-left: 12px;
  border-left: 2px solid var(--em-rule);
  font-family: var(--em-serif);
  font-size: 15.5px;
  font-style: italic;
  line-height: 1.62;
  color: var(--em-bone);
}

.em-beat-text {
  margin: 9px 0 0;
  font-size: 14px;
  line-height: 1.68;
  color: var(--em-dim);
}

.em-beat .em-tags {
  margin-top: 9px;
}

.em-ov-fulfillment {
  margin-top: 12px;
  padding: 18px 20px 20px;
  border: 1px solid color-mix(in srgb, var(--em-lens) 26%, var(--em-rule));
  border-radius: 14px;
  background: color-mix(in srgb, var(--em-lens) 5%, transparent);
}

.em-ov-fulfillment p {
  margin: 8px 0 0;
  font-family: var(--em-serif);
  font-size: 16px;
  line-height: 1.72;
  color: var(--em-bone);
}

.em-detail-hero {
  display: flex;
  align-items: center;
  gap: 18px;
}

.em-detail-glyph {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 74px;
  height: 74px;
  border-radius: 999px;
  border: 1px solid var(--em-rule);
  background:
    radial-gradient(circle at 50% 40%, var(--em-hero-core) 0%, var(--em-ground) 75%);
}

.em-detail-kicker {
  font-family: var(--em-mono);
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--em-dim);
}

.em-detail-title {
  font-family: var(--em-serif);
  font-size: 31px;
  font-weight: 400;
  line-height: 1.08;
  margin: 6px 0 7px;
  text-wrap: balance;
}

@media (min-width: 900px) {
  .em-detail-title {
    font-size: 37px;
  }
}

.em-detail-pos {
  font-family: var(--em-mono);
  font-size: 9.5px;
  letter-spacing: 0.1em;
  color: var(--em-faint);
}

/* Dating line and theme tags, between the title and the body */
.em-dates {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
  margin-top: 18px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--em-rule);
  font-family: var(--em-mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--em-bone-dim);
}

.em-dates b {
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--em-faint);
  margin-right: 6px;
}

.em-themes {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 14px;
}

.em-themes span {
  padding: 4px 10px;
  border-radius: 999px;
  background: rgb(var(--em-tint) / 0.06);
  border: 1px solid var(--em-rule);
  font-size: 10.5px;
  letter-spacing: 0.02em;
  color: var(--em-bone-dim);
}

.em-detail-threads {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 18px;
}

/* Each dot selects its thread, so a reader can move from a book into the
   promise running through it without leaving the page. */
.em-detail-threads .em-tdot {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 11px 5px 9px;
  border-radius: 999px;
  border: 1px solid var(--em-rule);
  background: transparent;
  font-family: var(--em-mono);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--em-dim);
  transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}

.em-detail-threads .em-tdot:hover {
  color: var(--em-bone-dim);
  border-color: var(--em-dim);
}

.em-detail-threads .em-tdot i {
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--tdot-color);
  opacity: 0.55;
  transition: opacity 0.15s ease, box-shadow 0.15s ease;
}

.em-detail-threads .em-tdot.is-on {
  border-color: var(--tdot-color);
  background: color-mix(in srgb, var(--tdot-color) 12%, transparent);
  color: var(--tdot-color);
}

.em-detail-threads .em-tdot.is-on i {
  opacity: 1;
  box-shadow: 0 0 7px color-mix(in srgb, var(--tdot-color) 80%, transparent);
}

/* Held back by the cap until the +N is tapped. */
.em-detail-threads:not(.is-expanded) .em-tdot.is-extra {
  display: none;
}

.em-tdot-more {
  display: inline-flex;
  align-items: center;
  padding: 5px 11px;
  border-radius: 999px;
  border: 1px dashed var(--em-rule);
  background: transparent;
  font-family: var(--em-mono);
  font-size: 9.5px;
  letter-spacing: 0.1em;
  color: var(--em-faint);
  transition: color 0.15s ease, border-color 0.15s ease;
}

.em-tdot-more:hover {
  color: var(--em-bone-dim);
  border-color: var(--em-dim);
}

.em-detail-hint {
  font-family: var(--em-serif);
  font-style: italic;
  font-size: 14px;
  color: var(--em-dim);
  margin: 0;
}

.em-fields {
  margin-top: 30px;
  display: grid;
  gap: 26px;
}

.em-field-label {
  font-family: var(--em-mono);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--em-dim);
  margin-bottom: 8px;
}

.em-field-text {
  font-family: var(--em-serif);
  font-size: 18px;
  line-height: 1.6;
  margin: 0;
}

@media (min-width: 900px) {
  .em-field-text {
    font-size: 17.5px;
  }
}

.em-detail-section {
  margin-top: 34px;
  padding-top: 26px;
  border-top: 1px solid var(--em-rule);
}

.em-detail-section > .em-field-label {
  margin-bottom: 18px;
}

/* The active thread now sits above the book's own description, so it reads as a
   panel in the thread's colour rather than a section divided off by a rule. */
.em-detail-section.is-lead {
  margin-top: 22px;
  padding: 18px 20px 20px;
  border-top: 0;
  border-left: 2px solid var(--lead-color);
  border-radius: 0 14px 14px 0;
  background: color-mix(in srgb, var(--lead-color) 7%, transparent);
}

.em-detail-section.is-lead > .em-field-label {
  color: var(--lead-color);
  margin-bottom: 14px;
}

/* Entries inside the panel drop their own left rule — the panel supplies it */
.em-detail-section.is-lead .em-entry {
  border-left: 0;
  padding-left: 0;
}

.em-detail-section.is-lead .em-entry.is-fulfillment {
  background: none;
  border-radius: 0;
  padding-block: 0;
}

.em-entry {
  border-left: 2px solid var(--entry-color, var(--em-lens-soft));
  padding-left: 14px;
  margin-bottom: 18px;
}

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

.em-entry-ref {
  font-family: var(--em-mono);
  font-size: 11px;
  letter-spacing: 0.03em;
  color: var(--entry-color, var(--em-lens));
  margin-bottom: 4px;
}

.em-entry-ref a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, currentColor 35%, transparent);
  transition: border-color 0.15s ease;
}

.em-entry-ref a:hover {
  border-bottom-color: currentColor;
}

.em-entry-ref a::after {
  content: "\2197";
  font-size: 8.5px;
  margin-left: 4px;
  opacity: 0.65;
}

.em-entry-ref .em-entry-thread {
  color: var(--em-dim);
}

/* The scripture itself, set apart from the commentary on it */
.em-quote {
  margin: 8px 0 9px;
  padding: 0;
  font-family: var(--em-serif);
  font-size: 16px;
  line-height: 1.55;
  color: var(--em-bone);
}

@media (min-width: 900px) {
  .em-quote {
    font-size: 15.5px;
  }
}

.em-entry-text {
  font-size: 15px;
  line-height: 1.62;
  color: var(--em-bone-dim);
  margin: 0;
}

/* What kind of claim this is, and how firmly it is grounded */
.em-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 9px;
}

.em-tag {
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--em-rule);
  font-family: var(--em-mono);
  font-size: 8.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--em-faint);
  cursor: help;
}

.em-tag.is-stated {
  border-color: color-mix(in srgb, var(--em-lens) 40%, transparent);
  color: var(--em-lens);
}

.em-tag.is-pattern {
  color: var(--em-dim);
}

.em-tag.is-traditional {
  border-style: dashed;
}

@media (min-width: 900px) {
  .em-entry-text {
    font-size: 14px;
  }
}

/* Testimony gets its own gold-washed panel */
.em-detail-section.is-testimony {
  border-top: 0;
  padding: 22px 20px 20px;
  border-radius: 16px;
  background: rgb(var(--em-lens-rgb) / 0.045);
  border: 1px solid rgb(var(--em-lens-rgb) / 0.14);
}

.em-detail-section.is-testimony > .em-field-label {
  color: var(--em-lens);
}

/* Prev / next book navigation */
.em-booknav {
  margin-top: 40px;
  padding-top: 22px;
  border-top: 1px solid var(--em-rule);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.em-booknav button {
  display: block;
  text-align: left;
  padding: 13px 16px;
  border: 1px solid var(--em-rule);
  border-radius: 14px;
  background: transparent;
  color: inherit;
  transition: border-color 0.15s ease, transform 0.15s ease, background-color 0.15s ease;
}

.em-booknav button:hover {
  border-color: var(--em-lens-soft);
  background: rgb(var(--em-lens-rgb) / 0.05);
  transform: translateY(-1px);
}

.em-booknav .is-next {
  text-align: right;
}

.em-booknav .nav-label {
  display: block;
  font-family: var(--em-mono);
  font-size: 8.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--em-faint);
  margin-bottom: 4px;
}

.em-booknav .nav-title {
  display: block;
  font-family: var(--em-serif);
  font-size: 16px;
  color: var(--em-bone);
}

/* ---------- Welcome panel (desktop empty state) ---------- */

.em-empty {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 100%;
  padding: 48px 48px 56px;
  max-width: 620px;
}

/* A phone cannot spare 48px a side — the lede was breaking after three words.
   The deep bottom padding keeps the panel clear of the floating button, which
   would otherwise sit on top of the first step. */
@media (max-width: 599.98px) {
  .em-empty {
    padding: 26px 20px 100px;
  }

  .em-empty-lede {
    font-size: 23px;
  }

  .em-empty-glyphs {
    gap: 18px;
    margin-bottom: 26px;
  }
}

.em-empty-glyphs {
  position: relative;
  display: flex;
  align-items: center;
  gap: 26px;
  margin-bottom: 34px;
  width: fit-content;
}

.em-empty-glyphs::before {
  content: "";
  position: absolute;
  left: -12px;
  right: -12px;
  top: 50%;
  height: 1px;
  background: var(--em-rule);
}

.em-empty-glyphs > span {
  position: relative;
  display: block;
  background: var(--em-list-bg);
  border-radius: 99px;
  padding: 2px;
}

/* The panel opens by telling the story the timeline is named after. The title
   has always promised Emmaus and nothing on the page explained it. */
.em-empty-kicker {
  font-family: var(--em-mono);
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--em-lens-soft);
  margin: 0 0 14px;
}

.em-empty-lede {
  font-family: var(--em-serif);
  font-size: 26px;
  font-weight: 300;
  line-height: 1.4;
  color: var(--em-bone);
  margin: 0;
  text-wrap: balance;
}

.em-empty-quote {
  margin: 22px 0 0;
  padding-left: 16px;
  border-left: 2px solid var(--em-lens-soft);
  font-family: var(--em-serif);
  font-size: 17px;
  font-style: italic;
  line-height: 1.62;
  color: var(--em-bone-dim);
}

.em-empty-quote cite {
  display: block;
  margin-top: 8px;
  font-family: var(--em-mono);
  font-size: 10px;
  font-style: normal;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--em-faint);
}

.em-empty-tie {
  margin: 22px 0 0;
  font-size: 15px;
  line-height: 1.68;
  color: var(--em-bone-dim);
}

.em-empty-steps {
  list-style: none;
  margin: 30px 0 0;
  padding: 0;
  display: grid;
  gap: 14px;
}

.em-empty-steps li {
  display: flex;
  gap: 14px;
  align-items: baseline;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--em-bone-dim);
}

.em-empty-steps .n {
  font-family: var(--em-mono);
  font-size: 10px;
  color: var(--em-lens-soft);
}

/* The three ways in, ahead of the explanation rather than below it. Most of
   this navigation is not conventional — a drawer behind a floating button, a
   thread picker — so the panel leads with the doors and explains afterwards. */
.em-empty-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 30px;
  padding-bottom: 30px;
  border-bottom: 1px solid var(--em-rule);
}

/* On a phone they stack full width, which reads as a menu rather than as a
   row of chips and gives each a comfortable tap target. */
@media (max-width: 599.98px) {
  .em-empty-actions {
    display: grid;
    gap: 10px;
  }

  .em-empty-actions button {
    width: 100%;
    justify-content: center;
    min-height: 50px;
  }
}

.em-btn-spine {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  margin-right: -2px;
}

.em-empty-steps {
  margin-top: 28px;
}

/* ---------- Help dialog ---------- */

.em-dialog {
  margin: auto;
  width: min(92vw, 600px);
  max-height: min(86dvh, 720px);
  padding: 0;
  border: 1px solid var(--em-rule);
  border-radius: 20px;
  background: var(--em-ground-2);
  color: var(--em-bone);
  font-family: var(--em-sans);
  box-shadow: 0 24px 80px rgb(var(--em-shadow-rgb) / var(--em-shadow-a));
}

.em-dialog::backdrop {
  background: rgb(var(--em-scrim) / 0.72);
  backdrop-filter: blur(3px);
}

/* Icons shown inside a sentence, so the instruction points at the mark the
   reader is hunting for rather than describing it. Boxed like a keycap so it
   reads as "this thing on your screen" and not as decoration. */
.em-inline-icon {
  display: inline-block;
  width: 24px;
  height: 24px;
  padding: 3px;
  vertical-align: -7px;
  margin: 0 2px;
  border-radius: 7px;
  border: 1px solid var(--em-rule);
  background: rgb(var(--em-plate) / 0.18);
  color: var(--em-bone);
}

/* Disclosure chevrons sit inside a label, so they stay bare and small. */
.em-inline-icon.is-tight {
  width: 13px;
  height: 13px;
  padding: 0;
  vertical-align: -2px;
  margin: 0 0 0 2px;
  border: 0;
  background: none;
  border-radius: 0;
}

/* A miniature of the button that carries a reader from a book to its thread. */
.em-inline-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 7px 2px 6px;
  margin: 0 1px;
  vertical-align: -3px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--pill-color) 45%, transparent);
  background: color-mix(in srgb, var(--pill-color) 12%, transparent);
  color: var(--pill-color);
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
}

.em-inline-pill i {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: currentColor;
}

.em-dialog-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 24px 14px;
  border-bottom: 1px solid var(--em-rule);
}

.em-dialog-head h2 {
  font-family: var(--em-serif);
  font-size: 22px;
  font-weight: 400;
  margin: 0;
}

.em-dialog-close {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  border: 1px solid var(--em-rule);
  background: transparent;
  color: var(--em-dim);
  font-size: 15px;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.em-dialog-close:hover {
  color: var(--em-bone);
  border-color: var(--em-dim);
}

.em-dialog-body {
  padding: 20px 24px 28px;
  overflow-y: auto;
  max-height: calc(min(86dvh, 720px) - 76px);
}

.em-dialog-body::-webkit-scrollbar {
  width: 6px;
}

.em-dialog-body::-webkit-scrollbar-thumb {
  background: var(--em-rule);
  border-radius: 99px;
}

.em-dialog-body > p {
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--em-bone-dim);
  margin: 0 0 14px;
}

.em-dialog-body h3 {
  font-family: var(--em-mono);
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--em-lens);
  margin: 24px 0 8px;
}

/* ---------- Guide sections ----------
   Collapsible so the dialog can be scanned rather than read. Built on
   <details>, which brings its own keyboard handling and open/close semantics
   and needs no script. */

.em-dialog-lede {
  font-family: var(--em-serif);
  font-size: 17px;
  line-height: 1.6;
  color: var(--em-bone);
  margin: 0 0 6px;
}

.em-guide {
  border-top: 1px solid var(--em-rule);
}

.em-guide:last-of-type {
  border-bottom: 1px solid var(--em-rule);
}

.em-guide > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 0;
  cursor: pointer;
  list-style: none;
  font-family: var(--em-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--em-bone-dim);
  transition: color 0.15s ease;
}

.em-guide > summary::-webkit-details-marker {
  display: none;
}

.em-guide > summary:hover {
  color: var(--em-bone);
}

.em-guide[open] > summary {
  color: var(--em-lens);
}

/* A chevron drawn from two borders, so it needs no glyph or asset. */
.em-guide > summary::after {
  content: "";
  flex-shrink: 0;
  width: 7px;
  height: 7px;
  margin-right: 3px;
  border-right: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.18s ease;
}

.em-guide[open] > summary::after {
  transform: translateY(1px) rotate(225deg);
}

.em-guide > *:not(summary) {
  margin-bottom: 16px;
}

.em-guide p {
  font-size: 14px;
  line-height: 1.65;
  color: var(--em-bone-dim);
  margin: 0 0 10px;
}

.em-guide-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 9px;
}

.em-guide-list li {
  position: relative;
  padding-left: 16px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--em-bone-dim);
}

.em-guide-list li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 9px;
  width: 4px;
  height: 4px;
  border-radius: 999px;
  background: var(--em-faint);
}

.em-guide-defs {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 14px;
  margin: 0;
  font-size: 14px;
  line-height: 1.55;
}

.em-guide-defs dt {
  font-family: var(--em-mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--em-lens-soft);
  padding-top: 2px;
  white-space: nowrap;
}

.em-guide-defs dd {
  margin: 0;
  color: var(--em-bone-dim);
}

@media (max-width: 460px) {
  .em-guide-defs {
    grid-template-columns: 1fr;
    gap: 3px;
  }

  .em-guide-defs dd {
    margin-bottom: 10px;
  }
}

/* Navigation is what someone is stuck without, so it reads as instructions:
   the action in bold, the method after it. */
.em-nav-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 11px;
}

.em-nav-list li {
  font-size: 14px;
  line-height: 1.6;
  color: var(--em-bone-dim);
}

.em-nav-list b {
  color: var(--em-bone);
  font-weight: 600;
}

/* Needs two classes to outrank `.em-dialog-body > p`, which otherwise sets
   this at body size and left it looking like a paragraph someone forgot. */
.em-dialog-body > .em-dialog-foot {
  margin: 20px 0 0;
  font-size: 11.5px;
  line-height: 1.7;
  color: var(--em-faint);
}

.em-dialog-body > .em-dialog-foot a {
  color: var(--em-dim);
  text-decoration: none;
  border-bottom: 1px solid var(--em-rule);
  transition: color 0.15s ease, border-color 0.15s ease;
}

.em-dialog-body > .em-dialog-foot a:hover {
  color: var(--em-lens);
  border-bottom-color: var(--em-lens-soft);
}

.em-dialog-legend {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px 10px;
  margin: 14px 0 6px;
}

@media (max-width: 480px) {
  .em-dialog-legend {
    grid-template-columns: repeat(2, 1fr);
  }
}

.em-legend-item {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12.5px;
  color: var(--em-bone-dim);
}

/* ---------- Animations ---------- */

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

@keyframes em-fade-up {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes em-slide-in {
  from { opacity: 0.4; transform: translateX(28px); }
  to { opacity: 1; transform: translateX(0); }
}

@keyframes em-bloom {
  from { opacity: 0; transform: scale(0.55); }
  to { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .em-app *,
  .em-app *::before,
  .em-app *::after,
  .em-dialog * {
    transition-duration: 0.001s !important;
    animation-duration: 0.001s !important;
  }
}
