/* Karana, Learn-journey roadmap (krm-)
   Rendered inside #detail-body (padding: 0 22px calc(120px + safe-area) 22px).
   Flat system: the backdrop is the token deep navy (both modes),
   nodes are flat circles, the lit path is the single saffron accent.
   No gradients, no glows. */

/* Throwaway wrapper used only while a screen-slide transition is in
   flight (see mountScreen in roadmap.js) — holds the outgoing and
   incoming .krm-wrap absolutely-positioned on top of each other so the
   swap never touches the real host's own layout/scroll role.
   No overflow:hidden here on purpose: .krm-wrap bleeds 22px past ITS
   immediate parent via negative margins to reach the true edge of the
   scrolling host (whose own overflow-x:hidden does the real clipping,
   same as before this wrapper existed) — clipping at the stage's own
   (narrower, still-padded) box would chop off exactly that bleed strip
   on each side, showing the host's bare background through it. */
.krm-transition-stage {
  position: relative;
}

.krm-wrap {
  position: relative;
  /* `100%` alone silently resolves to nothing when this is mounted straight
     into the Learn tab: its parent (`#learn`, a plain auto-height `.page`,
     not a flex item) never gives percentage heights anything to resolve
     against, per spec. That was never visible before — every existing
     screen's own content was already taller than the viewport regardless —
     until a short screen (the character profile) exposed it as a hard
     cutoff with dead page-background showing underneath. The `100dvh` term
     is a real, ancestor-independent floor that fixes it in both this and
     the detail-overlay mount (where `100%` does resolve, and dvh is simply
     the same value again). */
  min-height: calc(max(100%, 100dvh) + 120px + env(safe-area-inset-bottom));
  margin: 0 -22px calc(-120px - env(safe-area-inset-bottom)) -22px;
  padding: calc(max(16px, env(safe-area-inset-top)) + 68px) 22px calc(140px + env(safe-area-inset-bottom)) 22px;
  background: var(--indigo);
  color: #EFF1F0;
  font-family: var(--font-sans, system-ui, sans-serif);
}

/* ---------------- Header ---------------- */

.krm-header {
  position: relative; /* anchors .krm-header-save-btn on the legacy path, which never gets .krm-path-mode's own position:relative */
  text-align: left;
  padding: 0 8px 6px;
}

.krm-title {
  margin: 0 0 6px;
  font-family: var(--font-title, inherit);
  font-size: 1.625rem;
  font-weight: 700;
  font-style: normal;
  color: #ffffff;
}
.krm-title em, .krm-title i { font-style: normal; }

.krm-tagline {
  margin: 0 0 14px;
  font-size: 0.875rem;
  line-height: 1.5;
  color: rgba(236, 236, 240, 0.66);
}

.krm-day-line {
  margin-bottom: 10px;
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: normal;
  text-transform: none;
  color: rgba(236, 236, 240, 0.75);
}

.krm-day-complete {
  font-size: 1.125rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: normal;
  color: #EFF1F0;
}

.krm-progress {
  height: 6px;
  margin: 0 0 14px;
  border-radius: var(--radius-round, 99px);
  background: rgba(255, 255, 255, 0.14);
  overflow: hidden;
}

.krm-progress-fill {
  height: 100%;
  border-radius: inherit;
  background: var(--brand-primary);
  transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

/* draft-banner base class lives in theme.css; restyle for the indigo backdrop */
.krm-wrap .draft-banner {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 2px 0 0;
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.06);
  font-size: 0.75rem;
  font-weight: 500;
  text-transform: none;
  letter-spacing: normal;
  color: rgba(236, 236, 240, 0.85);
}

.krm-hint {
  margin-top: 12px;
  font-size: 0.8125rem;
  color: rgba(236, 236, 240, 0.7);
}

/* ---------------- Winding path ---------------- */

.krm-track {
  position: relative;
  padding: 28px 0 6px;
}

.krm-path-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  /* Explicit, not relied on via DOM order: the connecting line must always
     paint behind the node circles, never over them. */
  z-index: 0;
}

.krm-path-base {
  fill: none;
  stroke: rgba(255, 255, 255, 0.24);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 0.5 11;
}

.krm-path-lit {
  fill: none;
  stroke: var(--brand-primary);
  stroke-width: 3.5;
  stroke-linecap: round;
  stroke-dasharray: 0.5 11;
}

/* ---------------- Nodes ---------------- */

.krm-node {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  width: 100%;
  min-height: 150px;
  margin: 0 0 8px;
  padding: 0;
  /* This is a <button>; suppress the default focus rectangle (a hard-edged
     box around the whole node clashes badly with the circular design) in
     favor of a deliberate focus-visible ring scoped to the circle itself. */
  outline: none;
  border: none;
  background: none;
  cursor: pointer;
  font-family: inherit;
  color: inherit;
  -webkit-tap-highlight-color: transparent;
}

.krm-pos-l { transform: translateX(-64px); }
.krm-pos-c { transform: translateX(0); }
.krm-pos-r { transform: translateX(64px); }

.krm-circle {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.08);
  font-size: 1.875rem;
  line-height: 1;
  color: #EFF1F0;
  transition: transform 0.15s ease, border-color 0.2s ease, background-color 0.2s ease;
}
.krm-circle .k-icon { width: 30px; height: 30px; }

.krm-node:not(.krm-locked):active .krm-circle {
  transform: scale(0.93);
}

/* Keyboard-focus ring, scoped to the round circle instead of the outline
   default's hard rectangle around the whole button */
.krm-node:focus-visible .krm-circle {
  box-shadow: 0 0 0 3px var(--brand-primary, #F85E00);
}
.krm-path-mode .krm-node:focus-visible .krm-circle {
  box-shadow: 0 0 0 3px var(--krm-fg, #fff), 0 0 0 5px var(--krm-accent, #FC9E4F);
}

.krm-node-badge {
  position: absolute;
  right: -5px;
  bottom: -5px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  font-size: 0.875rem;
  line-height: 1;
}

/* done, accent ring + accent-tinted flat fill */
.krm-done .krm-circle {
  background: rgba(232, 137, 28, 0.18);
  border-color: var(--brand-primary);
  color: var(--brand-primary);
}

.krm-node-badge-fire {
  background: var(--indigo);
  border: 2px solid var(--brand-primary);
  color: var(--brand-primary);
  font-size: 0.8125rem;
}

/* current, accent ring + subtle scale pulse (no shadow glow) */
.krm-current .krm-circle {
  background: rgba(232, 137, 28, 0.12);
  border-color: var(--brand-primary);
  animation: krm-pulse 2.4s ease-in-out infinite;
}

@keyframes krm-pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.045); }
}

.krm-current .krm-cap-day {
  color: var(--brand-primary);
}

/* Mayu floats beside the current stone (flips sides for right-leaning nodes) */
.krm-mayu {
  position: absolute;
  top: 8px;
  left: calc(50% + 44px);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--brand-primary);
  color: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  font-size: 1.25rem;
  line-height: 1;
  pointer-events: none;
  animation: krm-bob 2.8s ease-in-out infinite;
}
.krm-mayu .k-icon { width: 22px; height: 22px; }

.krm-mayu-left {
  left: auto;
  right: calc(50% + 44px);
}

@keyframes krm-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-7px); }
}

/* locked, dulled */
.krm-locked {
  cursor: default;
}

/* Grayscale the ART, never the TEXT. This rule used to include .krm-cap and
   set opacity: 0.4 on both, which is the thing the long comment further down
   this file explicitly forbids ("NEVER opacity ... on anything that covers real
   area"). On Mahabharata's #048A81 plate it took the locked episode titles to
   1.29:1 — invisible, and nowhere near the 3:1 AA needs for 21px bold. The
   caption's own locked colour is set in the path-mode block below, which is
   where that decision belongs. */
.krm-locked .krm-circle {
  filter: grayscale(1);
  opacity: 0.4;
}

.krm-node-badge-lock {
  border: 2px solid rgba(255, 255, 255, 0.25);
  background: rgba(37, 36, 34, 0.92);
  color: rgba(255, 255, 255, 0.85);
}

.krm-node-badge-lock .material-symbols-rounded {
  font-size: 0.9375rem;
  font-variation-settings: 'FILL' 1, 'wght' 500;
}
.krm-node-badge-lock .k-icon { width: 14px; height: 14px; }

/* ---------------- Karana Plus ----------------
   A Plus node is deliberately NOT drawn like a progression lock. The grey-out
   above says "not yet, keep going", which is true of a locked node and false of
   a paywalled one: there is nothing to keep going at. So a Plus node stays in
   full colour and legible, with a gold star, and reads as "this is here, and it
   costs something" rather than "you have failed to earn this". It is also
   tappable, which a locked node is not, because the tap is the entire point. */
.krm-plus { cursor: pointer; }

/* Beats .krm-locked's grey-out: a Plus node that is ALSO further down the path
   still shows in colour, because the paywall is the fact that matters about it.
   filter, never opacity, per the note on .krm-path-mode .krm-locked below: the
   connecting-line SVG sits behind every node, and a translucent circle lets it
   bleed straight through. */
.krm-plus .krm-circle,
.krm-plus .krm-cap {
  filter: none;
  opacity: 1;   /* resets .krm-locked's 0.4, and 1 cannot bleed the path line through */
}

.krm-node-badge-plus {
  border: 2px solid #F0C860;
  background: linear-gradient(150deg, #F6D678 0%, #D99A2B 100%);
  color: #4A2F00;
  box-shadow: 0 1px 5px rgba(180, 130, 20, 0.45);
}

.krm-node-badge-plus .material-symbols-rounded {
  font-size: 0.9375rem;
  font-variation-settings: 'FILL' 1, 'wght' 600;
}

/* A hairline gold ring, so the row scans as Plus at a glance without having to
   find the badge. Kept to inset+shadow rather than a border, which would shift
   the circle's layout by 2px and break the SVG path's node alignment. */
.krm-plus .krm-circle {
  box-shadow: 0 0 0 2px rgba(240, 200, 96, 0.85), 0 2px 10px rgba(180, 130, 20, 0.25);
  border-radius: 50%;
}

/* ---------------- Captions ---------------- */

.krm-cap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  max-width: 150px;
}

/* sentence case, e.g. "Part 3 · ~7 swipes" */
.krm-cap-day {
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: normal;
  text-transform: none;
  color: rgba(236, 236, 240, 0.6);
}

.krm-cap-title {
  font-size: 0.84375rem;
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
  color: #EFF1F0;
}

/* ---------------- Milestone flags ---------------- */

.krm-flag-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 2px 0 18px;
  padding: 0 6px;
}

.krm-flag-line {
  flex: 1;
  height: 1px;
  background: rgba(255, 255, 255, 0.16);
}

.krm-flag {
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(255, 255, 255, 0.06);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: normal;
  text-transform: none;
  color: rgba(236, 236, 240, 0.65);
  white-space: nowrap;
}

.krm-flag-done .krm-flag {
  border-color: var(--brand-primary);
  background: rgba(232, 137, 28, 0.14);
  color: var(--brand-primary);
}

/* Section header (each Upanishad): reads as a heading, not a milestone tally. */
.krm-section-row {
  margin: 14px 0 20px;
}

.krm-section-flag {
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.3px;
  color: rgba(236, 236, 240, 0.9);
  background: rgba(255, 255, 255, 0.09);
}

.krm-flag-done .krm-section-flag {
  color: var(--brand-primary);
}

/* ---------------- Loading state ---------------- */

/* .krm-wrap's own background is the fixed navy backdrop (see its own
   comment: deliberately the same in both themes, once the roadmap itself
   is showing). Loading is shown BEFORE any of that content exists, so
   there's nothing yet that needs the navy for contrast — overridden here
   back to the ordinary theme surface so the skeleton reads as "the app is
   loading" instead of flashing a dark panel no matter which theme is
   active, then swapping to a real background once content lands.
   .krm-wrap's min-height/margin/padding (100dvh + 120px, and a matching
   negative bottom margin, 68px/140px asymmetric top/bottom padding) exist
   to reserve room for the bottom nav under REAL, scrollable roadmap
   content — the loading spinner isn't that, and centering it with
   justify-content inside a box taller than the viewport (with far more
   padding below than above) centered it against that oversized box, not
   the screen you're actually looking at, so it sat visibly low/off-center.
   Reset to a plain, symmetric, viewport-height box instead. */
.krm-wrap.krm-loading {
  background: var(--bg-app);
  color: var(--text-main);
  min-height: 100dvh;
  margin: 0;
  padding: 0;
}

.krm-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding-top: 40px;
  width: 100%;
}

.krm-load-node {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  width: 100%;
  min-height: 150px;
  margin: 0 0 8px;
}

.krm-load-circle {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: linear-gradient(100deg, var(--surface-alt) 40%, var(--border) 50%, var(--surface-alt) 60%);
  background-size: 250% 100%;
  animation: krm-shimmer 1.4s ease-in-out infinite;
}

.krm-load-node:nth-child(2) .krm-load-circle { animation-delay: 0.12s; }
.krm-load-node:nth-child(3) .krm-load-circle { animation-delay: 0.24s; }
.krm-load-node:nth-child(4) .krm-load-circle { animation-delay: 0.36s; }

@keyframes krm-shimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

/* ---------------- Reduced motion ---------------- */

@media (prefers-reduced-motion: reduce) {
  .krm-current .krm-circle,
  .krm-mayu,
  .krm-load-dot {
    animation: none;
  }
  .krm-progress-fill {
    transition: none;
  }
}

/* ---------------- Completion fork (review / next text) ---------------- */

.krm-fork {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 16px;
}

.krm-fork-btn {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 14px 16px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(255, 255, 255, 0.06);
  color: #EFF1F0;
  text-align: left;
  cursor: pointer;
  transition: background-color 0.15s ease, transform 0.1s ease;
}

.krm-fork-btn:active { transform: scale(0.985); background: rgba(255, 255, 255, 0.1); }

.krm-fork-btn > .material-symbols-rounded,
.krm-fork-btn > .k-icon {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(232, 137, 28, 0.16);
  color: var(--brand-primary);
  padding: 9px;
  box-sizing: border-box;
}

.krm-fork-txt { display: flex; flex-direction: column; gap: 2px; }
.krm-fork-txt b { font-size: 0.9375rem; font-weight: 700; font-family: var(--font-title); }
.krm-fork-txt small { font-size: 0.75rem; opacity: 0.75; }

.krm-fork-next > .material-symbols-rounded,
.krm-fork-next > .k-icon {
  background: rgba(232, 137, 28, 0.16);
  color: var(--brand-primary);
}

/* ---------------- header explore link (flat ghost) ---------------- */

.krm-explore-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 10px 0 0;
  padding: 8px 14px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  background: transparent;
  color: rgba(236, 236, 240, 0.85);
  font-size: 0.8125rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease, transform 0.1s ease;
}

.krm-explore-link .material-symbols-rounded { font-size: 1.0625rem; }
.krm-explore-link .k-icon { width: 16px; height: 16px; }
.krm-explore-link:active { transform: scale(0.97); background: rgba(255, 255, 255, 0.08); }

/* ============================================================
   CHAPTER LIST + PER-CHAPTER PATH (courses with spine.chapters[])
   Themed from the course/chapter's own palette via --krm-bg / --krm-fg /
   --krm-accent / --krm-glow, set inline by applyPalette() in roadmap.js —
   the CSS below only ever reads those custom properties, never a color
   literal, so this whole block re-themes automatically per course.
   ============================================================ */

.krm-chapters-mode,
.krm-path-mode {
  /* Soft organic color blooms (same painterly-placeholder technique used in
     lesson-player.css for episodes without art yet) instead of a repeating
     diagonal-stripe pattern — a stripe tile reads fine in a small header
     card, but tiled across a tall scrolling page it just looks like
     wallpaper. Radial blooms don't repeat, so they read as ambient light
     and color instead of a pattern, and stay in character with the rest of
     the app's hand-painted feel rather than generic flat-UI geometry. */
  background:
    radial-gradient(85% 40% at 20% 0%,
      color-mix(in srgb, #EFF1F0 26%, var(--krm-bg, var(--indigo))) 0%, transparent 62%),
    radial-gradient(65% 50% at 105% 28%,
      color-mix(in srgb, var(--krm-secondary, var(--krm-bg)) 50%, var(--krm-bg, var(--indigo))) 0%, transparent 65%),
    radial-gradient(70% 46% at -8% 72%,
      color-mix(in srgb, var(--krm-secondary, var(--krm-bg)) 35%, black) 0%, transparent 68%),
    radial-gradient(75% 38% at 55% 108%,
      color-mix(in srgb, var(--krm-bg, var(--indigo)) 65%, black) 0%, transparent 72%),
    var(--krm-bg, var(--indigo));
  color: var(--krm-fg, #EFF1F0);
  /* The base .krm-wrap padding-top (+68px) was sized for the old denser
     legacy header (title+tagline+day-line+progress+hint+fork all stacked).
     This new header is shorter and lives in its own card, so that much
     clearance just reads as a dead gap before anything appears. */
  padding-top: calc(max(12px, env(safe-area-inset-top)) + 10px);
}

/* ---------------- shared header card ---------------- */

.krm-course-header,
.krm-path-mode .krm-header {
  position: relative;
  overflow: hidden;
  margin: 0 0 22px;
  padding: 20px 20px 22px;
  border-radius: 22px;
  background: color-mix(in srgb, var(--krm-bg, var(--indigo)) 88%, black);
  /* Grounds the card against the now-textured body instead of it reading as
     a flat sticker sitting on top of the same-colored surface. */
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.2);
  isolation: isolate;
  transition: transform 0.1s ease, background-color 0.15s ease;
  -webkit-tap-highlight-color: transparent;
}

/* Whole card is the back/close target when it carries the role (chapter-list
   omits it when mounted in the Learn tab, since that screen IS the tab root
   and has nowhere to go "back" to) — same tap-the-whole-card pattern as the
   chapter cards below, not a separate floating button. Extra top padding
   clears the small back-arrow icon in the corner. */
.krm-course-header[role="button"],
.krm-path-mode .krm-header[role="button"] {
  cursor: pointer;
  padding-top: 40px;
}
.krm-course-header[role="button"]:active,
.krm-path-mode .krm-header[role="button"]:active {
  transform: scale(0.99);
  background: color-mix(in srgb, var(--krm-bg, var(--indigo)) 78%, black);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.16);
}

.krm-course-header-stripes,
.krm-header-stripes {
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: repeating-linear-gradient(
    -18deg,
    rgba(255, 255, 255, 0.07) 0 10px,
    transparent 10px 26px
  );
  -webkit-mask-image: linear-gradient(to bottom left, black, transparent 65%);
  mask-image: linear-gradient(to bottom left, black, transparent 65%);
}

.krm-course-title,
.krm-path-mode .krm-title {
  margin: 0 0 6px;
  font-family: var(--font-title, inherit);
  font-size: 1.5rem;
  font-weight: 700;
  color: #fff;
}

.krm-course-tagline {
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.5;
  color: color-mix(in srgb, var(--krm-fg, #fff) 78%, transparent);
}

.krm-chapter-crumb {
  margin: 0 0 12px;
  font-size: 0.875rem;
  font-weight: 600;
  color: color-mix(in srgb, var(--krm-fg, #fff) 75%, transparent);
}

.krm-path-mode .krm-progress-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.krm-path-mode .krm-progress {
  flex: 1;
  height: 8px;
  border-radius: 99px;
  background: rgba(0, 0, 0, 0.22);
  overflow: hidden;
}

.krm-path-mode .krm-progress-fill {
  height: 100%;
  border-radius: inherit;
  background: #fff;
  transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

.krm-progress-pct {
  flex: 0 0 auto;
  font-size: 0.8125rem;
  font-weight: 700;
  color: #fff;
  font-variant-numeric: tabular-nums;
}

/* ---------------- back arrow (small icon, not a separate button) ---------------- */

.krm-header-back-icon {
  position: absolute;
  top: 16px;
  left: 18px;
  z-index: 2;
  font-size: 1.25rem;
  color: color-mix(in srgb, var(--krm-fg, #fff) 65%, transparent);
}

/* ---------------- chapter list ---------------- */

.krm-chapter-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.krm-chapter-card {
  position: relative;
  display: flex;
  align-items: stretch;
  height: 128px;
  border: none;
  border-radius: 20px;
  overflow: hidden;
  background: color-mix(in srgb, var(--krm-bg, var(--indigo)) 70%, black);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  transition: transform 0.15s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.15s ease;
  -webkit-tap-highlight-color: transparent;
  contain: content; /* isolates layout/paint per card during scroll */
}

.krm-chapter-card:active { transform: scale(0.98); box-shadow: 0 3px 10px rgba(0, 0, 0, 0.14); }

.krm-chapter-thumb {
  position: absolute;
  inset: 0;
  width: 62%;
  overflow: hidden;
}

.krm-chapter-thumb::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to right,
    transparent 35%,
    color-mix(in srgb, var(--krm-bg, var(--indigo)) 70%, black) 92%);
}

.krm-chapter-thumb .krm-art-img,
.krm-chapter-thumb .krm-emoji {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* both node-art contexts fade the loaded photo in over its emoji
   placeholder (see wireArtImages() in roadmap.js) rather than popping in */
.krm-art-img {
  transition: opacity 0.35s ease;
}

.krm-chapter-thumb .krm-art-img { object-fit: cover; object-position: center center; }
.krm-chapter-thumb .krm-emoji { display: flex; align-items: center; justify-content: center; font-size: 2.125rem; }

.krm-chapter-locked .krm-chapter-thumb .krm-art-img,
.krm-chapter-locked .krm-chapter-thumb .krm-emoji {
  filter: grayscale(1) brightness(0.6);
}

.krm-chapter-info {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  margin-left: auto;
  width: 54%;
  /* extra right padding clears the absolutely-positioned chevron/lock/plus-mark
     icon — 52px (not 34px) since the Plus mark runs at 36px wide now. */
  padding: 14px 52px 14px 10px;
}

.krm-chapter-num {
  position: absolute;
  top: 10px;
  right: 14px;
  font-size: 2.125rem;
  font-weight: 800;
  line-height: 1;
  color: color-mix(in srgb, var(--krm-fg, #fff) 22%, transparent);
  font-variant-numeric: tabular-nums;
}

.krm-chapter-title-text {
  max-width: 80%;
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.25;
  color: #fff;
}

.krm-chapter-sub {
  max-width: 82%;
  font-size: 0.75rem;
  line-height: 1.4;
  color: color-mix(in srgb, var(--krm-fg, #fff) 65%, transparent);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.krm-chapter-progress-track {
  display: block;
  margin-top: 6px;
  height: 5px;
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.18);
  overflow: hidden;
}

/* Category row meta caption (Browse screen): tells you the RULE before you
   commit — "8 journeys, in order" vs "6 deities, any order" vs "guided
   audio" — visible proof of the lock-vs-recommend split from LEARN-PLAN.md. */
.krm-cat-meta {
  display: inline-block;
  margin-top: 6px;
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--krm-accent, #FC9E4F);
  background: color-mix(in srgb, var(--krm-accent, #FC9E4F) 16%, transparent);
  padding: 3px 8px;
  border-radius: 99px;
}

.krm-chapter-progress-fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--krm-accent, #FC9E4F);
  transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Vertically centered on the card, not bottom-anchored — bottom anchoring
   collided with the progress bar sitting at the base of .krm-chapter-info. */
.krm-chapter-lock,
.krm-chapter-chevron,
.krm-chapter-plus-mark {
  position: absolute;
  top: 50%;
  right: 14px;
  transform: translateY(-50%);
  z-index: 1;
}
.krm-chapter-lock,
.krm-chapter-chevron {
  font-size: 1.25rem;
  color: color-mix(in srgb, var(--krm-fg, #fff) 55%, transparent);
}

.krm-chapter-locked { cursor: default; }
.krm-chapter-locked .krm-chapter-info { opacity: 0.55; }
.krm-chapter-locked:active { transform: none; }

/* A Plus chapter. Same reasoning as .krm-plus on the path: it stays in full
   colour and stays tappable, because it is available and the tap is the point.
   These three rules have to beat .krm-chapter-locked above, since a Plus chapter
   is nearly always progression-locked as well and would otherwise be greyed to
   55% and desaturated, which reads as unavailable rather than for sale. */
.krm-chapter-plus { cursor: pointer; }
.krm-chapter-plus .krm-chapter-info { opacity: 1; }
.krm-chapter-plus .krm-chapter-thumb .krm-art-img,
.krm-chapter-plus .krm-chapter-thumb .krm-emoji { filter: none; }

/* Used to be a gold pill with an icon + "Plus" label; that competed with the
   title/subtitle for attention on a card that's already busy. Just the mark
   now, same corner a lock or chevron would sit in on any other chapter card
   — recognizable as "this one's different" without shouting about it.
   Overrides .plus-mark's own (deliberately larger) row-badge size, since
   this needs to read as understated here, not as the headline. */
.krm-chapter-plus-mark {
  width: 36px;
  height: 36px;
  opacity: 0.92;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.35));
}

/* Save toggle (saveButtonHtml/wireSaveButtons in roadmap.js) — lives on a
   course's own header once it's open (krm-header-save-btn below), not on
   the Browse-screen cards. Circular icon button, base rule has no position
   of its own since every mount point positions it differently. */
.krm-save-btn {
  position: absolute;
  z-index: 2;
  width: 30px;
  height: 30px;
  border: none;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.32);
  color: #fff;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 0.15s ease, transform 0.15s ease;
}
.krm-save-btn .material-symbols-rounded { font-size: 1.0625rem; }
.krm-save-btn:active { transform: scale(0.9); }
.krm-save-btn.is-saved { background: var(--krm-accent, #FC9E4F); color: #2b1c08; }

/* Mirrors .krm-header-back-icon's top:16px/left:18px on the opposite
   corner — every course header (chapter-list, path, legacy path) is
   `position: relative` (see .krm-header/.krm-course-header), so this
   anchors correctly regardless of which of the three renders it. */
.krm-save-btn.krm-header-save-btn {
  top: 16px;
  right: 18px;
}

/* Characters entry point — same corner, sitting just left of the save
   button (30px wide + 18px inset + 8px gap). */
.krm-save-btn.krm-header-characters-btn {
  top: 16px;
  right: 56px;
}

/* ---------------- text selector (all courses, each card self-themed) ---------------- */

/* Each .krm-text-card carries its own --krm-bg/--krm-fg/--krm-accent inline
   (see paletteInlineStyle in roadmap.js), so it re-themes the shared
   .krm-chapter-card rules above without any new color logic here. */
.krm-text-card.krm-text-current {
  box-shadow: 0 0 0 2px var(--krm-accent, #FC9E4F), 0 8px 20px rgba(0, 0, 0, 0.18);
}

/* ---------------- path nodes (art circles + side-by-side text row) ---------------- */

/* Row layout: circle + text block side by side, alternating which side the
   circle sits on per node (.krm-row-l / .krm-row-r), text always reading
   left-to-right regardless of which side it's on. Overrides the base
   .krm-node's centered-column layout used by the legacy path. */
.krm-path-mode .krm-node {
  flex-direction: row;
  align-items: center;
  gap: 20px;
  min-height: auto;
  /* Bigger circles already fill more of the row; less extra margin is
     needed on top to keep the elbow connector's corners readable. */
  margin: 0 0 62px;
}
.krm-path-mode .krm-node:last-child {
  margin-bottom: 4px;
}
.krm-path-mode .krm-node.krm-row-r {
  flex-direction: row-reverse;
}

.krm-path-mode .krm-cap {
  flex: 1;
  min-width: 0;
  align-items: flex-start;
  text-align: left;
  max-width: none;
  gap: 0;
}

/* Row-reversed nodes (circle on the right) would otherwise leave the text
   hugging the far LEFT edge of the panel — flex:1 fills all remaining row
   width, and left-aligned text sits at the start of that space regardless
   of which side the circle is on. Right-align instead so the text hugs its
   own circle, matching the reference's tight grouping either direction. */
.krm-path-mode .krm-node.krm-row-r .krm-cap {
  align-items: flex-end;
  text-align: right;
}

.krm-cap-eyebrow {
  display: block;
  margin: 0 0 4px;
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--krm-fg, #fff) 55%, transparent);
}

.krm-path-mode .krm-cap-title {
  margin: 0;
  font-size: 1.3125rem;
  font-weight: 700;
  line-height: 1.25;
}

.krm-path-mode .krm-circle {
  width: 112px;
  height: 112px;
  border: none;
  /* Fully opaque, not the translucent 6% wash it was — this circle has to
     fully occlude the connecting line behind it on its own, as a second,
     independent guarantee alongside the z-index stacking order, not rely
     on the art image happening to cover every pixel with no gaps. */
  background: color-mix(in srgb, var(--krm-bg, var(--indigo)) 70%, black);
  overflow: hidden;
}

/* Scaled up to match the bigger 112px circle (base rule sizes the badge for
   the legacy path's smaller 72px one). */
.krm-path-mode .krm-node-badge {
  width: 30px;
  height: 30px;
  right: -4px;
  bottom: -4px;
  font-size: 1rem;
}

.krm-path-mode .krm-art-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  border-radius: 50%;
}

.krm-path-mode .krm-emoji {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  font-size: 2.625rem;
}

/* Connecting line: solid gold once completed, dim + dashed while still ahead
   (matches the same "not unlocked yet" language as the locked nodes below). */
.krm-path-mode .krm-path-lit {
  stroke: var(--krm-accent, #FC9E4F);
  stroke-width: 7;
  stroke-linecap: round;
  stroke-dasharray: none;
}
.krm-path-mode .krm-path-base {
  stroke: rgba(0, 0, 0, 0.28);
  stroke-width: 5;
  stroke-linecap: round;
  stroke-dasharray: 11 11;
}

/* unlocked (done or current): glowing accent ring around the art, plus a
   soft neutral drop shadow so the circle feels lifted off the page rather
   than a flat sticker printed on the same-colored surface. */
.krm-path-mode .krm-unlocked .krm-circle {
  box-shadow:
    0 0 0 3px var(--krm-accent, #FC9E4F),
    0 0 18px 2px var(--krm-glow, rgba(252, 158, 79, 0.55)),
    0 8px 16px rgba(0, 0, 0, 0.22);
}

.krm-path-mode .krm-current .krm-circle {
  animation: krm-glow-pulse 2.4s ease-in-out infinite;
}

@keyframes krm-glow-pulse {
  0%, 100% { box-shadow: 0 0 0 3px var(--krm-accent, #FC9E4F), 0 0 18px 2px var(--krm-glow, rgba(252, 158, 79, 0.55)), 0 8px 16px rgba(0, 0, 0, 0.22); }
  50%      { box-shadow: 0 0 0 3px var(--krm-accent, #FC9E4F), 0 0 26px 6px var(--krm-glow, rgba(252, 158, 79, 0.55)), 0 8px 16px rgba(0, 0, 0, 0.22); }
}

/* locked: dark, desaturated art and muted text — reads as "not unlocked
   yet" rather than merely "less colorful" — still grounded with the same
   soft drop shadow so it doesn't look like a cutout.

   Deliberately uses filter (grayscale/brightness) and color-mix toward the
   page background, NEVER opacity, on anything that covers real area (the
   art circle, the text). opacity blends an element with whatever is
   stacked behind it — here that's the connecting-line SVG, which sits
   correctly behind every node in DOM order, but a translucent locked
   circle let it visibly bleed through, and translucent text read as muddy,
   unpredictable contrast rather than a deliberately muted color. filter
   only reprocesses the element's own pixels, so it dims/desaturates
   without exposing anything underneath. */
.krm-path-mode .krm-locked .krm-circle {
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.08), 0 6px 14px rgba(0, 0, 0, 0.18);
}
.krm-path-mode .krm-locked .krm-art-img,
.krm-path-mode .krm-locked .krm-emoji {
  filter: grayscale(1) brightness(0.4);
}
/* 48% was the original mix and it measured 1.29:1 on Mahabharata's #048A81
   once the legacy opacity:0.4 above was also applied — locked titles were
   effectively invisible. 82% gives 3.28:1, which clears AA for the 21px bold
   title. The 13px eyebrow needs 4.5:1 and CANNOT reach it on this palette:
   even pure white on #048A81 is 4.24:1, so it is set as light as the theme
   allows and the remaining gap is a palette problem, not a colour-mix one.
   The locked state still reads as locked from the grayscaled art circle and
   the lock badge, which is where that signal belongs. */
.krm-path-mode .krm-locked .krm-cap-title {
  color: color-mix(in srgb, var(--krm-fg, #fff) 82%, var(--krm-bg, black));
}
.krm-path-mode .krm-locked .krm-cap-eyebrow {
  color: color-mix(in srgb, var(--krm-fg, #fff) 92%, var(--krm-bg, black));
}

/* Plus, inside the path.
   These repeat the .krm-plus rules further up at .krm-path-mode specificity
   (0,3,0) because the locked/unlocked rules immediately above are themselves
   0,3,0 and come later in the file, so the shorter selectors lose outright. A
   Plus node that is also progression-locked would otherwise be drawn fully
   greyed and ringed in white, which reads as unavailable when the entire point
   is that it is available for a price. */
.krm-path-mode .krm-plus .krm-circle {
  box-shadow:
    0 0 0 3px #F0C860,
    0 0 18px 2px rgba(226, 176, 64, 0.42),
    0 8px 16px rgba(0, 0, 0, 0.22);
}
.krm-path-mode .krm-plus .krm-art-img,
.krm-path-mode .krm-plus .krm-emoji {
  filter: none;
}
.krm-path-mode .krm-plus .krm-cap-eyebrow,
.krm-path-mode .krm-plus .krm-cap-title {
  color: var(--krm-fg, #fff);
}
/* The "up next" pulse animates box-shadow in its keyframes, so it overrides the
   gold ring outright on a node that is both current and Plus. Stopping it is
   also the honest thing: pulsing "do this next" at something the user cannot
   open is an invitation to a locked door. */
.krm-path-mode .krm-plus .krm-circle {
  animation: none;
}

/* Stays fully legible — it's the signal explaining why the rest is dim. */
.krm-path-mode .krm-locked .krm-node-badge-lock {
  background: rgba(37, 36, 34, 0.95);
  border-color: rgba(255, 255, 255, 0.3);
}

/* Permanent affordance on the current node only (roadmap.js only emits this
   markup when state === 'current') — any node click opens the player
   directly, so this is purely a visual "start here" cue, not a tap target
   with its own separate behavior. */
.krm-play-btn {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
  pointer-events: none;
}

.krm-play-btn::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.28);
}

.krm-play-btn .material-symbols-rounded {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background: #fff;
  color: var(--krm-bg, var(--indigo));
  font-size: 1.625rem;
  font-variation-settings: 'FILL' 1;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.35);
  pointer-events: auto;
  cursor: pointer;
  animation: krm-play-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes krm-play-pop {
  from { transform: scale(0.6); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}

.krm-path-mode .krm-cap-title {
  color: var(--krm-fg, #EFF1F0);
}

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

@media (prefers-reduced-motion: reduce) {
  .krm-path-mode .krm-current .krm-circle,
  .krm-play-btn .material-symbols-rounded {
    animation: none;
  }
  .krm-chapter-card,
  .krm-course-header,
  .krm-path-mode .krm-header {
    transition: none;
  }
}


/* ---------------- text selector: theme-aware, not palette-dark ----------------
   The selector deliberately has NO course palette (each card carries its
   own), so its chrome must follow the app's light/dark tokens — the old
   krm fallbacks pinned it to the dark charcoal even in light mode. */
.krm-texts-mode {
  background: var(--bg-app);
  color: var(--text-main);
}

/* Full-bleed band: cancels the wrap's own padding so it runs edge-to-edge
   and up under the status bar; a faint warm brand tint fading into the
   page (same treatment as the Today masthead), in both themes. */
.krm-texts-header {
  margin: calc(-1 * (max(12px, env(safe-area-inset-top)) + 10px)) -22px 20px;
  padding: calc(max(20px, env(safe-area-inset-top)) + 18px) 22px 20px;
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--brand-primary) 8%, var(--bg-app)) 0%,
    transparent 100%);
}

.krm-texts-header .krm-course-title { font-size: 2.125rem; color: var(--text-main); }

.krm-texts-header .krm-course-tagline { margin: 2px 0 16px; color: var(--text-muted); }

/* The main Browse header has no title/tagline anymore (just the search
   bar) — less bottom spacing is needed before the first shelf. Top padding
   is untouched on purpose: it handles status-bar clearance when this
   screen is mounted directly in the Learn tab (see the #learn-scoped
   padding-top override below), a mount-context concern unrelated to
   whether a title happens to be present. */
.krm-texts-header--plain {
  padding-bottom: 14px;
  margin-bottom: 14px;
}

.krm-texts-search {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 16px;
  height: 46px;
  border-radius: var(--radius-round, 99px);
  background: var(--surface);
  border: 1px solid var(--border);
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.krm-texts-search:focus-within {
  border-color: var(--brand-primary);
}
.krm-texts-search .material-symbols-rounded {
  font-size: 1.25rem;
  color: var(--text-muted);
}

.krm-texts-search-input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: none;
  font-family: var(--font-sans);
  /* 16px minimum on inputs, or iOS zooms the page — see styles.css. */
  font-size: 1rem;
  color: var(--text-main);
}
.krm-texts-search-input::placeholder {
  color: var(--text-muted);
}
.krm-texts-search-input::-webkit-search-cancel-button { -webkit-appearance: none; }

.krm-text-card[hidden] { display: none; }
.krm-text-list[hidden] { display: none; }

/* ---------------- Characters screens ---------------- */

.krm-char-search { margin: 0 0 14px; }

.krm-char-grid {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Same card shell as a chapter row (.krm-chapter-card), just themed by the
   course palette applied to .krm-wrap rather than a per-card inline style —
   there's one flat cast list per text, not one color per character. */
.krm-char-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  height: 72px;
  padding: 0 16px;
  border: none;
  border-radius: 18px;
  background: color-mix(in srgb, var(--krm-bg, var(--indigo)) 70%, black);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.16);
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.15s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.15s ease;
}
.krm-char-card:active { transform: scale(0.98); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12); }

.krm-char-portrait {
  position: relative;
  flex: none;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  overflow: hidden;
}
.krm-char-portrait .krm-char-initials,
.krm-char-portrait .krm-art-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.krm-char-portrait .krm-char-initials {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.9375rem;
  font-weight: 700;
  color: #fff;
  background: var(--char-color, #8a7ab0);
}
.krm-char-portrait .krm-art-img { object-fit: cover; }

.krm-char-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.krm-char-name {
  font-size: 0.96875rem;
  font-weight: 700;
  color: var(--krm-fg, #fff);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.krm-char-role {
  font-size: 0.8125rem;
  color: color-mix(in srgb, var(--krm-fg, #fff) 68%, transparent);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.krm-char-chevron {
  flex: none;
  font-size: 1.25rem;
  color: color-mix(in srgb, var(--krm-fg, #fff) 55%, transparent);
}

/* Profile screen — mirrors the "character intro" beat in lesson-player.css
   (.slp-charintro/.slp-chip-lg/.slp-char-epithet): a big portrait on a
   painterly field, bold centered name, an accent-colored epithet pill. Reuses
   this file's own --krm-* palette vars (already set by applyPalette on
   .krm-wrap) rather than lesson-player's --ep-* ones, so it doesn't need that
   file's beat/continue-button scaffolding to render correctly. */

.krm-char-profile-back {
  position: absolute;
  top: max(12px, env(safe-area-inset-top));
  left: 22px;
  z-index: 2;
  width: 40px;
  height: 40px;
  border: none;
  border-radius: var(--radius-round, 99px);
  background: rgba(0, 0, 0, 0.35);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: transform 0.1s ease, background-color 0.15s ease;
}
.krm-char-profile-back:active { transform: scale(0.92); }

.krm-char-profile-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 54px 12px 8px;
  box-sizing: border-box;
  /* Real centering height is set inline in JS (scroller.clientHeight, see
     renderCharacterProfile) — the actual scrolling container isn't
     reliably the same box a CSS viewport unit measures, and .krm-wrap
     itself intentionally runs taller than one screen (padding-bottom +
     the min-height floor's bleed, for a long list's last row to clear the
     fixed bottom nav), so sizing off either would drag centered content
     down past the real fold. This min-height is only the pre-JS fallback. */
  min-height: 60vh;
}

.krm-char-profile-portrait {
  position: relative;
  width: 172px;
  height: 172px;
  margin: 0 auto 16px;
  border-radius: 999px;
  overflow: hidden;
  border: 3px solid rgba(255, 255, 255, 0.12);
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.38);
}
.krm-char-profile-portrait .krm-char-initials,
.krm-char-profile-portrait .krm-art-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.krm-char-profile-portrait .krm-char-initials {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 3.25rem;
  font-weight: 700;
  color: #fff;
  background:
    radial-gradient(70% 70% at 30% 25%, rgba(255, 255, 255, 0.4) 0%, transparent 60%),
    radial-gradient(75% 75% at 50% 90%, rgba(0, 0, 0, 0.28) 0%, transparent 65%),
    var(--char-color, #8a7ab0);
}
.krm-char-profile-portrait .krm-art-img { object-fit: cover; }

.krm-char-profile-name {
  margin: 0 0 8px;
  font-family: var(--font-title, inherit);
  font-size: 1.875rem;
  font-weight: 700;
  line-height: 1.15;
  color: var(--krm-fg, #fff);
}

.krm-char-profile-epithet {
  display: inline-block;
  margin: 0 0 18px;
  padding: 5px 14px;
  border-radius: var(--radius-round, 99px);
  background: var(--krm-accent, var(--brand-primary, #F85E00));
  color: #fff;
  font-size: 0.78125rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.krm-char-profile-blurb {
  max-width: 320px;
  margin: 0 0 26px;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: color-mix(in srgb, var(--krm-fg, #fff) 88%, transparent);
}

.krm-char-appears-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 18px;
  border: none;
  border-radius: var(--radius-round, 99px);
  background: color-mix(in srgb, var(--krm-bg, var(--indigo)) 70%, black);
  color: var(--krm-fg, #fff);
  font-family: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.1s ease;
}
.krm-char-appears-toggle:active { transform: scale(0.97); }
.krm-char-appears-toggle .material-symbols-rounded { font-size: 1.125rem; }
.krm-char-appears-chevron { transition: transform 0.2s ease; }
.krm-char-appears-toggle.is-open .krm-char-appears-chevron { transform: rotate(180deg); }

.krm-char-appears-list { margin-top: 22px; }
.krm-char-appears-list[hidden] { display: none; }

/* .krm-texts-empty's default --text-muted assumes the neutral texts-selector
   surface; these two screens sit on the dark per-course palette instead. */
.krm-characters-mode .krm-texts-empty,
.krm-char-profile-mode .krm-texts-empty {
  color: color-mix(in srgb, var(--krm-fg, #fff) 65%, transparent);
}

.krm-texts-empty {
  margin: 26px 0 0;
  text-align: center;
  font-size: 0.90625rem;
  color: var(--text-muted);
}

/* ---------------- Continue Reading ----------------
   One big hero card, standalone above every shelf — whatever was last
   opened anywhere (a text or a collection episode). Same dark-palette
   card language as .krm-chapter-card (art + gradient + text), just
   sized as the single most prominent thing on the screen. */
.krm-continue-card {
  position: relative;
  display: flex;
  align-items: stretch;
  width: 100%;
  height: 108px;
  border: none;
  border-radius: 20px;
  overflow: hidden;
  background: color-mix(in srgb, var(--krm-bg, var(--indigo)) 70%, black);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  margin-bottom: 26px;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.15s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.15s ease;
}
.krm-continue-card:active { transform: scale(0.98); box-shadow: 0 3px 10px rgba(0, 0, 0, 0.14); }

.krm-continue-art { position: relative; flex: 0 0 108px; width: 108px; overflow: hidden; }
.krm-continue-art .krm-art-img,
.krm-continue-art .krm-emoji {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.krm-continue-art .krm-art-img { object-fit: cover; object-position: center center; }
.krm-continue-art .krm-emoji { display: flex; align-items: center; justify-content: center; font-size: 2rem; }

.krm-continue-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  padding: 14px 36px 14px 16px;
}
.krm-continue-label {
  font-size: 0.65625rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--krm-accent, #FC9E4F);
}
.krm-continue-title {
  font-size: 1.0625rem;
  font-weight: 700;
  color: #fff;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.krm-continue-sub {
  font-size: 0.78125rem;
  line-height: 1.35;
  color: color-mix(in srgb, var(--krm-fg, #fff) 68%, transparent);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.krm-continue-chevron {
  position: absolute;
  top: 50%;
  right: 14px;
  transform: translateY(-50%);
  font-size: 1.25rem;
  color: color-mix(in srgb, var(--krm-fg, #fff) 55%, transparent);
}

/* ---------------- Browse shelves ----------------
   Two densities of the same "shelf" idea: Sacred Texts gets big vertical
   posters (each of the 8 has real cover art + its own palette, worth the
   real estate); every collection/starter category gets a shelf of its own
   episodes as compact horizontal cards (episodeCardHtml) — the icon tile
   (categoryTileHtml) is only a fallback if that category's spine fails to
   load. All shelves scroll horizontally, same metaphor at different
   scales, rather than one long vertical stack.
   Left edges deliberately sit at the wrap's own 22px inset (no negative-
   margin/repad trick) so the first card in every shelf lines up exactly
   under its shelf title — the previous edge-to-edge version drifted out
   of alignment with the header. Trade-off: the last card stops shy of the
   true screen edge instead of bleeding off it, which is the safer of the
   two to get wrong. */
.krm-shelf { margin-bottom: 12px; }
.krm-shelf-title { margin: 0 0 2px; font-size: 1.1875rem; font-weight: 700; color: var(--text-main); }
.krm-shelf-sub { margin: 0 0 12px; font-size: 0.8125rem; line-height: 1.4; color: var(--text-muted); }

.krm-poster-shelf-scroll,
.krm-ep-shelf-scroll,
.krm-cat-shelf-scroll {
  display: flex;
  overflow-x: auto;
  padding: 4px 0 24px;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.krm-poster-shelf-scroll::-webkit-scrollbar,
.krm-ep-shelf-scroll::-webkit-scrollbar,
.krm-cat-shelf-scroll::-webkit-scrollbar { display: none; }
.krm-poster-shelf-scroll { gap: 14px; }
.krm-ep-shelf-scroll { gap: 12px; }
.krm-cat-shelf-scroll { gap: 12px; }

/* Episode cards: the same .krm-chapter-card visual language (art + gradient
   + text) as a chapter-list row, just fixed-width so a shelf of them can
   scroll side by side instead of stacking full-width. */
.krm-ep-card {
  flex: 0 0 auto;
  width: 220px;
  scroll-snap-align: start;
}

/* Completion ring: bottom-left, empty by default, draws itself shut into a
   full circle when the episode is done — see episodeCardHtml in
   roadmap.js for why this is a ring rather than a static checkmark icon. */
.krm-ep-ring {
  position: absolute;
  bottom: 8px;
  left: 8px;
  z-index: 2;
  width: 28px;
  height: 28px;
  display: block;
}
.krm-ep-ring::before {
  content: '';
  position: absolute;
  inset: 1px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.32);
  z-index: -1;
}
.krm-ep-ring svg {
  width: 100%;
  height: 100%;
  transform: rotate(-90deg); /* draw starting from 12 o'clock, not 3 o'clock */
}
.krm-ep-ring-track {
  fill: none;
  stroke: rgba(255, 255, 255, 0.3);
  stroke-width: 3;
}
.krm-ep-ring-fill {
  fill: none;
  stroke: var(--krm-accent, #FC9E4F);
  stroke-width: 3;
  stroke-linecap: round;
  transition: stroke-dashoffset 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.krm-ep-ring-check {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.875rem;
  font-weight: 700;
  color: #fff;
  opacity: 0;
  transform: scale(0.4);
  transition: opacity 0.2s ease, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.krm-ep-ring.is-done .krm-ep-ring-check {
  opacity: 1;
  transform: scale(1);
  transition-delay: 0.3s; /* lands just as the ring finishes drawing shut */
}
/* Locked state: a solid circle in the card's own theme color (same
   var(--krm-accent) the ring stroke above already reads) with the gold K+
   mark centered on it, instead of the ring — this is a paywall, not
   progress, so the dark scrim backing (::before, meant to keep an empty
   ring visible over any thumbnail) is redundant and switched off. */
.krm-ep-ring.is-locked {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--krm-accent, #FC9E4F);
  border-radius: 50%;
  border: none;
}
.krm-ep-ring.is-locked::before { content: none; }
.krm-ep-ring.is-locked .plus-mark-dark { width: 23px; height: 23px; }

.krm-poster-card {
  position: relative;
  flex: 0 0 auto;
  width: 152px;
  height: 226px;
  border: none;
  border-radius: 22px;
  overflow: hidden;
  background: color-mix(in srgb, var(--krm-bg, var(--indigo)) 70%, black);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  scroll-snap-align: start;
  transition: transform 0.15s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.15s ease;
  -webkit-tap-highlight-color: transparent;
  contain: content;
}
.krm-poster-card:active { transform: scale(0.97); box-shadow: 0 3px 10px rgba(0, 0, 0, 0.14); }

.krm-poster-art { position: absolute; inset: 0; }
.krm-poster-art .krm-art-img,
.krm-poster-art .krm-emoji {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.krm-poster-art .krm-art-img { object-fit: cover; object-position: center center; }
.krm-poster-art .krm-emoji { display: flex; align-items: center; justify-content: center; font-size: 2.75rem; }
.krm-poster-art::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom,
    transparent 34%,
    color-mix(in srgb, var(--krm-bg, var(--indigo)) 80%, black) 96%);
}

.krm-poster-body {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  padding: 14px 14px 16px;
}
.krm-poster-title {
  display: block;
  font-size: 0.9375rem;
  font-weight: 700;
  line-height: 1.25;
  color: #fff;
  margin-bottom: 3px;
}
.krm-poster-meta {
  display: block;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: color-mix(in srgb, var(--krm-fg, #fff) 75%, transparent);
}
.krm-poster-progress-track {
  display: block;
  margin-top: 7px;
  height: 4px;
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.22);
  overflow: hidden;
}
.krm-poster-progress-fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--krm-accent, #FC9E4F);
  transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Full Sacred Texts shelf: an actual book icon per scripture (roadmap.js's
   bookCoverSvg — a paper-colored page block behind a narrower front cover,
   leaving a sliver of pages peeking out past the fore-edge, plus an inset
   spine strip, all drawn in SVG), title set as real HTML text over it,
   left-aligned past the spine like type on a real dust jacket. No verse
   count, no fetch. */
.krm-book-shelf-scroll {
  display: flex;
  overflow-x: auto;
  padding: 4px 0 10px;
  gap: 14px;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.krm-book-shelf-scroll::-webkit-scrollbar { display: none; }

.krm-book-card {
  position: relative;
  flex: 0 0 auto;
  /* Same footprint as .krm-poster-card (the journey shelf right above this
     one) — two shelves of mismatched card sizes stacked back to back read
     as a layout bug even when each one is fine on its own. */
  width: 152px;
  height: 226px;
  border: none;
  border-radius: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
  scroll-snap-align: start;
  filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.22));
  transition: transform 0.15s cubic-bezier(0.22, 1, 0.36, 1), filter 0.15s ease;
  -webkit-tap-highlight-color: transparent;
}
.krm-book-card:active { transform: scale(0.96); filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.18)); }

.krm-book-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.krm-book-title {
  position: absolute;
  /* The spine strip ends around 27px in (see bookCoverSvg's x=18/100 of a
     152px-wide card) — this needs real clearance past it, not just enough
     to avoid overlapping. A flex box here (instead of top:50%/translateY)
     sizes its anonymous text item to max-content and never wraps at all —
     it just overflows past `right` silently, so plain block + vertical
     centering via transform it is. */
  left: 40px;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  text-align: left;
  overflow: hidden;
  overflow-wrap: break-word;
  font-size: 0.9375rem;
  font-weight: 800;
  line-height: 1.32;
  color: var(--krm-fg, #fff);
}

/* Fallback-only tile (a category whose spine failed to load) — icon-on-
   color, not art (see roadmap.js categoryTileHtml). */
.krm-cat-tile {
  position: relative;
  flex: 0 0 auto;
  scroll-snap-align: start;
  width: 108px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 16px 10px 14px;
  border: 1px solid var(--border);
  border-radius: 18px;
  background: var(--surface);
  text-align: center;
  cursor: pointer;
  font-family: inherit;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.15s cubic-bezier(0.22, 1, 0.36, 1);
}
.krm-cat-tile:active { transform: scale(0.96); }

.krm-cat-tile-glyph {
  width: 48px;
  height: 48px;
  border-radius: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.4375rem;
  background: color-mix(in srgb, var(--krm-cat-color, var(--brand-primary)) 18%, transparent);
}
.krm-cat-tile-title {
  font-size: 0.78125rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--text-main);
}
.krm-cat-tile-meta {
  font-size: 0.59375rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

/* Mounted in the Learn tab, roadmap screens must own the viewport's very
   top edge — the 48px top inset otherwise shows as a pale gap above the
   themed backdrop at scroll 0. Remove that inset while Learn hosts a
   roadmap (deterministic, unlike negative-margin cancellation which is at
   the mercy of margin collapsing), and re-clear the status bar with padding
   per screen. Other tabs and overlay-hosted roadmaps are unaffected.
   Targets .app, not .app-content: the inset moved up to the non-scrolling
   frame so that page headers stop scrolling under the status bar. */
.app:has(#learn.active .krm-wrap) {
  padding-top: 0;
}
#learn .krm-chapters-mode:not(.krm-texts-mode),
#learn .krm-path-mode {
  padding-top: calc(max(48px, env(safe-area-inset-top)) + 14px);
}
#learn .krm-texts-mode .krm-texts-header {
  padding-top: calc(max(48px, env(safe-area-inset-top)) + 16px);
}


/* The recoverable failure state. A spinner that never resolves used to be the
   whole of this: no message, no retry, and a hidden bottom nav underneath it. */
.krm-error { gap: 14px; text-align: center; padding: 0 24px; }
.krm-error .krm-load-text { margin-bottom: 4px; }
.krm-error .krm-retry { min-width: 180px; }
.krm-error .krm-back-texts { min-width: 180px; }
