/*
app.css — project-wide custom styles that extend Bootstrap 5.3.3.
Loaded in <head> after the framework stylesheet so it can override/extend
Bootstrap utilities and component classes. Keep this file small and
component-oriented; prefer Bootstrap utilities in markup over adding rules
here. Custom properties follow Bootstrap's --bs-* token convention.
*/

:root {
  /* btn-icon: fixed square size for icon-only action buttons. Named as a
     design token so all icon buttons share one knob and stay in sync. The
     value matches the natural .btn-lg height (3.5rem). */
  --bs-btn-icon-size: 3.5rem;
}

/* btn-icon: square icon-only action button with explicit, font-independent
   sizing. Bootstrap ships no icon-button utility, and Bootstrap Icons glyphs
   have differing advance widths (circle vs bar glyphs), so a fixed width and
   height decouple the button box from the glyph. display:inline-flex centers
   the glyph; padding:0 neutralizes .btn-lg's 0.5/1rem padding so the box is
   exact; line-height:1 prevents the inline-block glyph baseline from adding
   vertical depth. Compose with .btn .btn-{color} .btn-lg in markup. */
.btn-icon {
  width: var(--bs-btn-icon-size);
  height: var(--bs-btn-icon-size);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  line-height: 1;
}

/* btn-icon-subtle: light tint for the active-log secondary icon buttons
   (pause / forward / note / resume). Uses Bootstrap's 200-step palette
   tokens — --bs-{color}-bg-subtle (the "200" tint) for the fill and
   --bs-{color}-text-emphasis (the "900" ink) for the glyph — so the icons
   read as one weight step lighter than the solid abort (danger) and end
   (success) buttons, and adapt to dark mode for free. Compose with
   .btn .btn-lg .btn-icon plus a per-color modifier (.btn-icon-{warning,
   primary, teal}) that maps the --bs-btn-subtle-* vars to the right
   Bootstrap tokens; see below. */
.btn-icon-subtle {
  --bs-btn-font-weight: 400;
  --bs-btn-color: var(--bs-btn-subtle-ink, var(--bs-secondary-text-emphasis));
  --bs-btn-bg: var(--bs-btn-subtle-fill, var(--bs-secondary-bg-subtle));
  --bs-btn-border-width: var(--bs-border-width);
  --bs-btn-border-color: var(--bs-btn-subtle-border, var(--bs-secondary-border-subtle));
  --bs-btn-hover-color: var(--bs-btn-subtle-ink, var(--bs-secondary-text-emphasis));
  --bs-btn-hover-bg: var(--bs-btn-subtle-fill-hover, var(--bs-secondary-bg-subtle));
  --bs-btn-hover-border-color: var(--bs-btn-subtle-border, var(--bs-secondary-border-subtle));
  --bs-btn-focus-shadow-rgb: var(--bs-btn-subtle-rgb, 108, 117, 125);
  --bs-btn-active-color: var(--bs-btn-subtle-ink, var(--bs-secondary-text-emphasis));
  --bs-btn-active-bg: var(--bs-btn-subtle-fill, var(--bs-secondary-bg-subtle));
  --bs-btn-active-border-color: var(--bs-btn-subtle-border, var(--bs-secondary-border-subtle));
  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
  --bs-btn-disabled-color: var(--bs-btn-subtle-ink, var(--bs-secondary-text-emphasis));
  --bs-btn-disabled-bg: var(--bs-btn-subtle-fill, var(--bs-secondary-bg-subtle));
  --bs-btn-disabled-border-color: var(--bs-btn-subtle-border, var(--bs-secondary-border-subtle));
}
/* Per-color modifiers: map --bs-btn-subtle-* to Bootstrap's 200/900 tokens,
   which auto-flip in dark mode ([data-bs-theme=dark]). */
.btn-icon-warning {
  --bs-btn-subtle-ink: var(--bs-warning-text-emphasis);
  --bs-btn-subtle-fill: var(--bs-warning-bg-subtle);
  --bs-btn-subtle-border: var(--bs-warning-border-subtle);
  --bs-btn-subtle-rgb: var(--bs-warning-rgb);
}
.btn-icon-primary {
  --bs-btn-subtle-ink: var(--bs-primary-text-emphasis);
  --bs-btn-subtle-fill: var(--bs-primary-bg-subtle);
  --bs-btn-subtle-border: var(--bs-primary-border-subtle);
  --bs-btn-subtle-rgb: var(--bs-primary-rgb);
}
.btn-icon-info {
  --bs-btn-subtle-ink: var(--bs-info-text-emphasis);
  --bs-btn-subtle-fill: var(--bs-info-bg-subtle);
  --bs-btn-subtle-border: var(--bs-info-border-subtle);
  --bs-btn-subtle-rgb: var(--bs-info-rgb);
}
/* Two-tone notebook icon: the bookmark gets an orange fill so it reads as a
   distinct accent against the cyan cover+rings (which use currentColor = the
   button's --bs-info-text-emphasis ink). Light mode uses --bs-orange
   (#fd7e14); dark mode lightens to #ffb07c for contrast on dark bg.
   The cover <rect> normally follows currentColor too, but in dark mode that
   resolves to the bright info-text-emphasis (#6edff6), which overpowers the
   icon.    --note-cover-fill (dark-only) dims the cover to a muted cyan
   (#1a86a0) so the cover softens while the binding+rings stay bright; light
   mode is unchanged via the var()'s currentColor fallback. */
:root { --note-bookmark-fill: var(--bs-orange); }
[data-bs-theme="dark"] { --note-bookmark-fill: #ffb07c; }
[data-bs-theme="dark"] { --note-cover-fill: #1a86a0; }
/* Teal is not a Bootstrap theme color (no --bs-teal-* tokens), so its 200/900
   pair is hardcoded and mirrored for dark mode. */
.btn-icon-teal {
  --bs-btn-subtle-ink: #0a5042;
  --bs-btn-subtle-fill: #d1f0e8;
  --bs-btn-subtle-border: #9fe6d3;
  --bs-btn-subtle-rgb: 32, 201, 151;
}
[data-bs-theme="dark"] .btn-icon-teal {
  --bs-btn-subtle-ink: #5ae5b5;
  --bs-btn-subtle-fill: #0e3b32;
  --bs-btn-subtle-border: #1a6b56;
}
.btn-icon-success {
  --bs-btn-subtle-ink: var(--bs-success-text-emphasis);
  --bs-btn-subtle-fill: var(--bs-success-bg-subtle);
  --bs-btn-subtle-border: var(--bs-success-border-subtle);
  --bs-btn-subtle-rgb: var(--bs-success-rgb);
}
.btn-icon-danger {
  --bs-btn-subtle-ink: var(--bs-danger-text-emphasis);
  --bs-btn-subtle-fill: var(--bs-danger-bg-subtle);
  --bs-btn-subtle-border: var(--bs-danger-border-subtle);
  --bs-btn-subtle-rgb: var(--bs-danger-rgb);
}
:root {
  --resume-ring-fill: var(--bs-primary);
  --skip-forward-fill: #20c997;
  --reorder-handle-fill: #f59e0b; /* amber-500 (Tailwind) / yellow-500 (Material) */
}
[data-bs-theme="dark"] {
  --skip-forward-fill: #5ae5b5;
  --reorder-handle-fill: #fbbf24; /* amber-400: lighter for contrast on dark bg */
}

/* Series-view reorder handle (bi-arrow-down-up, hidden via d-none until reorder
   mode is toggled): the 1em glyph is far too small for a touch target, so
   font-size enlarges the icon and padding widens the hit area to a
   finger-sized box while negative margins keep the row height unchanged.
   touch-action:none guarantees the browser never interprets a touch on the
   handle as a scroll, so Sortable always claims the drag; touches starting
   on the rest of the row still scroll normally (the Sortable handle guard
   rejects them before a drag ever starts). Colored amber/yellow-500
   (--reorder-handle-fill, lightened in dark mode) to signal the drag
   affordance; distinct from the primary Blue of the Reorder button so the
   handle and its toggle button read as two different affordances.
   font-weight needs !important because .bi::before hard-pins 400 with
   !important, which beats any inherited weight. */
.reorder-handle {
  cursor: grab;
  color: var(--reorder-handle-fill);
  font-size: 1.5rem;
  font-weight: 700 !important;
  padding: 0.75rem;
  margin: -0.75rem;
  touch-action: none;
}

/* Char-level diff highlighting (inline, within changed TOC fields).
   Uses Bootstrap RGB tokens so tints adapt to dark mode. */
ins { text-decoration: none; background-color: rgba(var(--bs-success-rgb), 0.15); }
del { text-decoration: line-through; background-color: rgba(var(--bs-danger-rgb), 0.15); }

/* Line-level diff rows (raw-text overview). */
.diff-line { padding: 0 0.5rem; white-space: pre-wrap; font-family: monospace; }
.diff-equal { color: var(--bs-body-color); }
.diff-del { background-color: rgba(var(--bs-danger-rgb), 0.1); color: var(--bs-danger-text-emphasis); }
.diff-ins { background-color: rgba(var(--bs-success-rgb), 0.1); color: var(--bs-success-text-emphasis); }

/* Readboard spine title styling:
   - Normal leaf entries: no bold, no italic (default)
   - Non-leaf (branch) entries: italic via .toc-title
   - Actionable entries: bold via .fw-semibold on the selectable row. */
.focused-spine-row--branch .toc-title { font-style: italic; }

/* TOC subtitle: rendered inside .toc-title as a nested span. Uses opacity
   (not text-muted) so it works on top of any parent color — text-secondary
   for non-readable entries, toc-completed for done entries, or the default
   body color. font-weight: 400 overrides the row's bold for the subtitle
   portion. On mobile (< 576px), .toc-title gets nowrap+ellipsis
   (readboard.gohtml), which naturally truncates the subtitle first since
   it's at the end. */
.toc-subtitle { font-size: 0.85em; opacity: 0.65; font-weight: 400; }

/* Chapter images on TOC entries (docs/30_cloudflare.md §18). The thumb
   strip sits under the entry title; each thumb is a fixed 2:3 tile (the
   edge serves the transform_toc crop in production) linking to the full
   image. The delete button is a small round badge overlapping the top-right
   corner, ringed with the page background so it reads on any image. */
.toc-images img {
  width: 72px;
  height: 108px;
  object-fit: cover;
}
.toc-image-delete {
  position: absolute;
  top: -0.4rem;
  right: -0.4rem;
}
.toc-image-delete .btn {
  --bs-btn-padding-y: 0;
  --bs-btn-padding-x: 0.25rem;
  --bs-btn-font-size: 0.6rem;
  --bs-btn-line-height: 1.2;
  border-radius: 50rem;
  box-shadow: 0 0 0 2px var(--bs-body-bg);
}

/* Consistent separator borders. Bootstrap's list-group-flush only assigns
   bottom borders, so the 1st child inside a nested <ul> and the first
   visible row of a book's spine have no top separator. Two rules:
   1. border-top on the <ul> itself — draws a line above the entire list
      regardless of which child is first visible (spine windowing hides
      early rows with display:none, so :first-child may be hidden).
   2. :first-child on each nested <ul>'s first <li> — creates a separator
      between parent content (selectable row/toc-toggle) and its first child.
   Scoped to .focused-spine-list so other list-groups on the page are
   unaffected. */
.focused-spine-list,
.focused-spine-list .list-group-item > ul.list-group-flush > .list-group-item:first-child {
  border-top: var(--bs-list-group-border-width) solid var(--bs-list-group-border-color);
}

/* Embla Carousel base (v8.6.0). Only these three rules are required by the
   library. Inert on pages that don't use Embla. Kept for future carousel
   clusters (readboard, etc.). overflow:hidden on the viewport is mandatory;
   touch-action on the container lets the browser handle vertical scroll
   natively while Embla owns the horizontal drag. */
.embla__viewport { overflow: hidden; }
.embla__container { display: flex; touch-action: pan-y pinch-zoom; }
.embla__slide { flex: 0 0 100%; min-width: 0; }

/* Press-and-hold confirm: a footer button (#hold-confirm-btn) in the TOC
   modal arms a "start reading" action on the next-fresh-part TOC row.
   While held, a progress fill overlay (.hold-confirm-fill) grows
   left-to-right behind the TOC entry text. Two-phase rate: fast 0→60%
   (~1.2s), slow 60→100% (~1.8s). Release ≥60% auto-completes; <60% resets. */

/* Footer button: large circular play icon, centered in the modal footer. */
.hold-confirm-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  border: none;
  background: var(--bs-primary);
  color: #fff;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
  position: relative;
}
.hold-confirm-btn:focus-visible {
  outline: 0.15rem solid var(--bs-primary);
  outline-offset: 0.2rem;
}
.hold-confirm-btn--holding {
  transform: scale(0.95);
  box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25);
}
.hold-confirm-btn--committed {
  opacity: 0.6;
  pointer-events: none;
}
.hold-confirm-btn[data-accent="primary"] { background: var(--bs-primary); }
.hold-confirm-btn[data-accent="success"] { background: var(--bs-success); }
.hold-confirm-btn[data-accent="info"] { background: var(--bs-info); }
.hold-confirm-btn[data-accent="danger"] { background: var(--bs-danger); }
.hold-confirm-btn[data-accent="warning"] {
  background: var(--bs-warning);
  color: #fff;
}
.hold-confirm-btn[data-accent="warning"].hold-confirm-btn--holding {
  box-shadow: 0 0 0 0.25rem rgba(var(--bs-warning-rgb), 0.3);
}
.hold-confirm-btn[data-accent="success"].hold-confirm-btn--holding {
  box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.3);
}
.hold-confirm-btn[data-accent="info"].hold-confirm-btn--holding {
  box-shadow: 0 0 0 0.25rem rgba(var(--bs-info-rgb), 0.3);
}
.hold-confirm-btn[data-accent="danger"].hold-confirm-btn--holding {
  box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.3);
}

/* Compact variant for inline rows outside the modal footer (e.g. the
   change queue's approve/reject buttons). Keeps the circular hold button
   but at button-sm scale so it sits beside a card's text. */
.hold-confirm-btn--sm {
  width: 2.25rem;
  height: 2.25rem;
  font-size: 1rem;
}

/* Disabled state: Bootstrap's .disabled class is visual + a11y only (the
   button is still a real event target), so hold-confirm.js also guards
   onPointerDown/onKeyDown against it. Visually, gray overrides every
   accent variant so the disabled button reads "no action", not "primary
   action you can't use yet". */
.hold-confirm-btn.disabled,
.hold-confirm-btn[disabled] {
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
  opacity: 0.6;
  cursor: not-allowed;
  box-shadow: none;
}
.hold-confirm-btn.disabled.hold-confirm-btn--holding {
  box-shadow: none;
}

/* Expand toggle + note trigger color treatment. The toggle is a
   navigation control (small 2.5rem subtle outline chip); the note
   trigger is an action button — full base size (3.5rem, 1.5rem icon,
   inherited from .hold-confirm-btn) on the informational accent
   (teal), the same palette the armed action buttons get via their
   data-accent at arm() time — set statically here because the note
   trigger is never armed (a collapse trigger, not a hold action). */
.hold-confirm-btn--toggle {
  width: 2.5rem;
  height: 2.5rem;
  font-size: 1.1rem;
  background: var(--bs-body-tertiary-bg);
  color: var(--bs-body-color);
  border: 1px solid var(--bs-border-color);
}
.hold-confirm-btn--toggle:hover {
  background: var(--bs-secondary-bg);
}
.hold-confirm-btn--note {
  background: var(--bs-info);
  color: #fff;
}
.hold-confirm-btn--toggle:active {
  transform: scale(0.95);
}
/* Footer button row: the active slot fills the row; the expand toggle
   is absolutely positioned at the far right corner. When the toggle is
   visible the row reserves its space (footer-button-row--has-toggle)
   so spread buttons never slide under it; when it is hidden a lone
   centered button lands on the true midline. */
.footer-button-row--has-toggle {
  padding-right: 3.5rem;
}
.hold-confirm-btn--toggle {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
}

/* Slot spread: the show.bs.modal handler counts visible buttons per
   slot and stamps slot-1/slot-2/slot-3. One button centers; two or
   three spread to the slot corners (space-between, gap-dominant) with
   side padding that keeps buttons clear of the modal's rounded edges
   and (via the row reserve above) the toggle. */
#footer-primary-slot, #footer-secondary-slot {
  width: 100%;
  /* Base centering; slot-2/slot-3 override (ID specificity beats this). */
  justify-content: center;
}
#footer-primary-slot.slot-1, #footer-secondary-slot.slot-1 {
  justify-content: center;
}
#footer-primary-slot.slot-2, #footer-secondary-slot.slot-2 {
  justify-content: space-between;
  padding-inline: 1.5rem;
}
#footer-primary-slot.slot-3, #footer-secondary-slot.slot-3 {
  justify-content: space-between;
  padding-inline: 0.5rem;
}

/* Footer slot animation: primary and secondary slots fade/slide
   when swapping. */
#footer-primary-slot, #footer-secondary-slot {
  transition: opacity 0.2s ease, transform 0.2s ease;
}
#footer-primary-slot.footer-slot--hidden, #footer-secondary-slot.footer-slot--hidden {
  opacity: 0;
  transform: translateY(0.5rem);
  pointer-events: none;
}

/* TOC row targeted by the hold button: position relative for the fill. */
.hold-confirm-target {
  position: relative;
  overflow: hidden;
  border-radius: 0.25rem;
}

/* Progress fill overlay: absolutely positioned, grows from left. Sits
   behind the text (z-index: 0) with a semi-transparent accent wash. */
.hold-confirm-fill {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: 0%;
  z-index: 0;
  transition: none;
  pointer-events: none;
  border-radius: 0.25rem;
}
.hold-confirm-fill[data-accent="primary"] {
  background: rgba(var(--bs-primary-rgb), 0.2);
}
.hold-confirm-fill[data-accent="success"] {
  background: rgba(var(--bs-success-rgb), 0.2);
}
.hold-confirm-fill[data-accent="warning"] {
  background: rgba(var(--bs-warning-rgb), 0.2);
}
.hold-confirm-fill[data-accent="info"] {
  background: rgba(var(--bs-info-rgb), 0.2);
}
.hold-confirm-fill[data-accent="danger"] {
  background: rgba(var(--bs-danger-rgb), 0.2);
}
.hold-confirm-fill--active {
  background: rgba(var(--bs-primary-rgb), 0.3);
}
.hold-confirm-fill[data-accent="primary"].hold-confirm-fill--active {
  background: rgba(var(--bs-primary-rgb), 0.3);
}
.hold-confirm-fill[data-accent="success"].hold-confirm-fill--active {
  background: rgba(var(--bs-success-rgb), 0.3);
}
.hold-confirm-fill[data-accent="warning"].hold-confirm-fill--active {
  background: rgba(var(--bs-warning-rgb), 0.3);
}
.hold-confirm-fill[data-accent="info"].hold-confirm-fill--active {
  background: rgba(var(--bs-info-rgb), 0.3);
}
.hold-confirm-fill[data-accent="danger"].hold-confirm-fill--active {
  background: rgba(var(--bs-danger-rgb), 0.3);
}
.hold-confirm-fill--committed {
  background: rgba(var(--bs-success-rgb), 0.4) !important;
}
/* Ensure text stays above the fill. */
.hold-confirm-target > *:not(.hold-confirm-fill):not(.hold-confirm-hint-overlay) {
  position: relative;
  z-index: 1;
}

/* Post-pause note form: shown in the modal footer when the session is
   paused. The form is moved from active_session_controls into
   #tocModalFooter by the modal show handler. Single-row layout:
   textarea (flex-grow) + pagenum (narrow) + Save button. Auto-focused
   on modal open so the user can type immediately. */
.hold-confirm-pause-form {
  width: 100%;
}
.hold-confirm-pause-textarea {
  resize: none;
  flex: 1 1 auto;
  min-width: 0;
}
.hold-confirm-pause-pagenum {
  flex: 0 0 auto;
}

/* Current part highlight: the TOC entry currently being read stands out
   in the part-context card with a primary bg-subtle wash + left border. */
.current-part-highlight {
  background: rgba(var(--bs-primary-rgb), 0.1);
  border-left: 3px solid var(--bs-primary);
}

/* TOC selectable rows (Phase 1): actionable entries in the TOC modal
   are tappable rows, not inline sliders. The user must explicitly tap
   a row to select it; once selected, the footer play button arms.
   - .toc-selectable-row: left border accent indicates "eligible for
     selection". On hover, a subtle wash reinforces tappability.
   - .toc-selectable-row--selected: stronger left border + wash shows
     the current selection. The hold fill overlay will appear here when
     the footer button is held.
   - .toc-selectable-body: the actual tap zone (excludes .toc-toggle
     chevron, which is a separate collapse/expand click zone for branch
     nodes). */
.toc-selectable-row {
  border-left: 3px solid transparent;
  border-radius: 0 0.25rem 0.25rem 0;
  padding: 0.25rem 0.5rem 0.25rem 0.5rem;
  margin-left: -0.5rem;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.toc-selectable-row:hover {
  background: rgba(var(--bs-primary-rgb), 0.05);
  border-left-color: rgba(var(--bs-primary-rgb), 0.3);
}
.toc-selectable-row--selected {
  background: rgba(var(--bs-primary-rgb), 0.1);
  border-left-color: var(--bs-primary);
}

/* Hold-confirm hint overlays: a compact pill (verb label) shown while a
   footer button is held. Positioned just below the row's bottom-right
   corner so it sits beside the row's content without overlapping the TOC
   title or any badge above. The pill carries its own opaque background so
   it never visually collides with a list-group-flush border or any other
   element behind it — full-width bands overlapped the spine row separators. */
.hold-confirm-hint-overlay {
  position: absolute;
  left: auto;
  right: 0.5rem;
  top: 100%;
  bottom: auto;
  transform: none;
  margin-top: -0.35rem;
  padding: 0 0.35rem;
  border-radius: 0.2rem;
  background: var(--bs-body-bg);
  font-size: 0.7rem;
  font-weight: 900;
  line-height: 1.1rem;
  color: var(--bs-secondary-color);
  z-index: 3;
  pointer-events: none;
  white-space: nowrap;
}
@media (max-width: 575.98px) {
  .hold-confirm-hint-overlay {
    font-size: 0.6rem;
    line-height: 1rem;
    right: 0.35rem;
    margin-top: -0.3rem;
  }
}
/* The hint pill sits below the row (top: 100%), outside the row's own
   box. .hold-confirm-target retains overflow: hidden to clip the fill
   overlay's rounded corners; relax it to visible only while a hint is
   actually present. :has() is already required elsewhere on this page
   (doc 20 §9.2). */
.hold-confirm-target:has(.hold-confirm-hint-overlay) {
  overflow: visible;
}
.hold-confirm-hint--completing {
  color: var(--bs-secondary-color);
}
.hold-confirm-hint--starting {
  color: var(--bs-secondary-color);
  animation: hold-hint-blink 1s ease-in-out infinite;
}
@keyframes hold-hint-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}
