@font-face {
  font-family: 'Ezra SIL';
  src: url('/static/fonts/EzraSIL.ttf') format('truetype');
  font-display: swap;
  font-weight: 400;
}

:root {
  --color-accent: #2b3f6b;
  --color-text: #1a1a1a;
  --color-muted: #666;
  --color-bg: #fdfdfb;
  --color-footer-bg: #111111;
  --color-footer-text: #cfcfcf;
  --font-serif: Georgia, "Iowan Old Style", "Palatino Linotype", "Book Antiqua", serif;
  --font-sans: system-ui, -apple-system, "Segoe UI", sans-serif;
}

body {
  font-family: var(--font-sans);
  margin: 0;
  color: var(--color-text);
  background: var(--color-bg);
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.site-header {
  position: relative;
  padding: 2rem 2rem 0.85rem;
  text-align: center;
}

/* Hamburger toggle -- hidden on desktop, where the full nav row already
   fits (see the mobile-nav media query below for the breakpoint and the
   collapse itself). Absolutely positioned over the header rather than
   inserted into a flex row alongside .site-title, so the desktop layout
   (a single centered title, text-align: center) needs no restructuring
   at all -- this button is simply display:none there. */
.nav-toggle {
  display: none;
}

.site-title {
  display: inline-block;
  margin-bottom: 1rem;
  font-family: var(--font-serif);
  font-size: 2rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--color-accent);
  text-decoration: none;
}

.site-header nav {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 2rem;
  border-top: 2px solid var(--color-accent);
  border-bottom: 2px solid var(--color-accent);
  padding: 0.85rem 0;
}

/* Every item -- active or not -- carries the SAME padding/radius at all
   times, so nothing shifts size when hover/active states toggle; only
   color/background change. */
.site-header nav a,
.site-header nav summary {
  display: inline-block;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  color: var(--color-text);
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition: color 0.15s ease, background 0.15s ease;
}

/* Hover = a light background tint, not an underline -- applies to
   every item alike (Read/Search/Help and Study's own summary), so
   Study's dropdown trigger no longer needs a special-cased exception
   from the plain links' own hover treatment. */
.site-header nav a:hover,
.site-header nav summary:hover {
  color: var(--color-accent);
  background: #eef2f7;
}

/* Active top-level section, e.g. Study while anywhere under /study/... --
   a filled rounded-rectangle pill, deliberately NOT the avatar's own
   perfect circle (border-radius: 50%, see .avatar-initials/.avatar-bg
   below) -- shape is the differentiator, so the two are never visually
   confusable even though both land in the same family of blues. A
   lighter sky blue on purpose, distinct from --color-accent's own dark
   navy (the site's "синий", used for borders/links/logo everywhere) --
   this pill reads as "голубой", its own identity rather than reusing
   the site's one existing blue for a new purpose. */
.site-header nav a.active,
.site-header nav summary.active {
  background: #4a90e2;
  color: #fff;
}

.site-header nav a.active:hover,
.site-header nav summary.active:hover {
  color: #fff;
  background: #4a90e2;
}

/* Study's own dropdown -- a native <details>/<summary> disclosure, no
   JS: click/tap to open (works the same on touch as on desktop, unlike
   a hover-only CSS dropdown), closes when the summary is clicked again
   or a link inside is followed (page navigation resets it). */
.nav-dropdown {
  position: relative;
  display: inline-block;
}

.nav-dropdown[hidden] {
  display: none;
}

.nav-dropdown summary {
  cursor: pointer;
  list-style: none;
}

.nav-dropdown summary::-webkit-details-marker {
  display: none;
}

.nav-dropdown summary::after {
  content: "▾";
  display: inline-block;
  margin-left: 0.35em;
  font-size: 0.8em;
}

.nav-dropdown-panel {
  position: absolute;
  top: calc(100% + 0.6rem);
  left: 0;
  background: #fff;
  border: 1px solid #e3ddcf;
  border-radius: 6px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
  padding: 0.3rem 0;
  display: flex;
  flex-direction: column;
  min-width: 210px;
  z-index: 20;
  /* .site-header itself sets text-align: center (for the page title) --
     inherited text-align cascades straight into these <a>s otherwise,
     silently centering every item's label instead of the plain
     left-aligned list a dropdown is expected to be. */
  text-align: left;
}

/* Specificity note: .site-header nav a (0,1,2 -- one class, two
   elements) otherwise beats a plain .nav-dropdown-panel a (0,1,1), so
   the panel's own items silently kept the top nav's uppercase/bold/
   letter-spacing styling despite this rule -- qualified with the
   .site-header nav ancestor here specifically to win that fight. */
.site-header nav .nav-dropdown-panel a {
  display: block;
  padding: 0.65rem 1.1rem;
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: normal;
  text-transform: none;
  color: var(--color-text);
  text-decoration: none;
  border-bottom: 1px solid #ece7da;
  white-space: nowrap;
}

.site-header nav .nav-dropdown-panel a:last-child {
  border-bottom: none;
}

/* :last-child goes by DOM position, not visibility -- when the actual
   last item is a hidden one (e.g. Read's not-yet-implemented
   Transliterated text), the rule above strips ITS border, leaving the
   real last VISIBLE item with a stray border-bottom. Strip the border
   from whichever item is immediately followed by a hidden sibling. */
.site-header nav .nav-dropdown-panel a:has(+ a[hidden]) {
  border-bottom: none;
}

.site-header nav .nav-dropdown-panel a:hover {
  background: #f4f8fb;
  color: var(--color-accent);
}

/* Same [hidden]-vs-specificity bug already fixed for .search-panel etc.
   below -- the rule above's own display:block outranks the UA
   [hidden] { display: none } default, so a hidden dropdown item (e.g.
   Read's not-yet-implemented Transliterated text) needs an explicit
   override to actually stay hidden. */
.site-header nav .nav-dropdown-panel a[hidden] {
  display: none;
}

.avatar-link {
  display: inline-flex;
  border-bottom: none !important;
}

.avatar-link[hidden] {
  display: none;
}

.avatar-bg {
  fill: var(--color-accent);
  transition: fill 0.15s ease;
}

.avatar-link:hover .avatar-bg {
  fill: #17223e;
}

.avatar-initials {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--color-accent);
  color: #fff;
  font-size: 0.8rem;
  font-weight: 700;
  transition: background 0.15s ease;
}

.avatar-link:hover .avatar-initials {
  background: #17223e;
}

/* Mobile nav: below this width the full row (4 dropdown triggers +
   avatar, each with generous padding/letter-spacing) genuinely doesn't
   fit -- measured empirically (Playwright, real viewport widths): the
   row starts overflowing the page around 520-540px, so 640px leaves a
   safe margin across phones/small tablets rather than cutting it close.
   Collapses into a hamburger toggle + a full-width vertical menu, reusing
   the existing <details>/<summary> dropdowns unchanged (still native,
   still closed/opened by tapping their own summary) -- only their
   CONTAINER goes from an absolutely-positioned floating panel (which,
   on a narrow screen, was overflowing off the right edge of the viewport
   regardless of which of the 4 buttons opened it -- confirmed via a real
   screenshot, "Getting Started" was showing as "Getting S…", cut off) to
   a plain in-flow block that pushes the rest of the stacked list down,
   which can never overflow horizontally by construction. */
@media (max-width: 640px) {
  /* Real bug, reported from an actual phone (not caught by the earlier
     Playwright checks, which all screenshotted 375px and happened to
     land the button just past the title's own longest line there): the
     button was position: absolute with a hand-picked top offset,
     assuming the title always wraps to a fixed two-line height at a
     fixed position. It doesn't -- checked programmatically afterward
     across 320-430px (the real range of phone widths), and the two
     boxes overlap at EVERY one of them, not just some. Root cause was
     genuinely structural (guessing a static offset for a title whose
     own wrapped height/width varies with viewport and font metrics),
     not a value to nudge -- so the header becomes a real flex row here
     instead: title and button as ordinary siblings that can never
     overlap by construction, whatever the title wraps to. */
  .site-header {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    text-align: left;
    padding: 1.25rem 1.25rem 0.85rem;
  }

  .site-title {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 1.4rem;
    margin-bottom: 0;
  }

  .nav-toggle {
    display: flex;
    flex: none;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    align-self: center;
    position: static;
    width: 34px;
    height: 34px;
    padding: 0;
    background: none;
    border: none;
    cursor: pointer;
  }

  .nav-toggle-bar {
    display: block;
    width: 100%;
    height: 2px;
    background: var(--color-accent);
    border-radius: 1px;
    transition: transform 0.15s ease, opacity 0.15s ease;
  }

  /* Hamburger -> X, while open, same three bars re-arranged rather than
     swapped for a second icon -- confirms open/closed state at a glance
     without a second asset. */
  .nav-toggle.is-open .nav-toggle-bar:nth-child(1) {
    transform: translateY(7px) rotate(45deg);
  }

  .nav-toggle.is-open .nav-toggle-bar:nth-child(2) {
    opacity: 0;
  }

  .nav-toggle.is-open .nav-toggle-bar:nth-child(3) {
    transform: translateY(-7px) rotate(-45deg);
  }

  /* Closed by default; #nav-toggle's own click handler (nav.js) toggles
     .nav-open. No JS needed for the dropdowns THEMSELVES -- they stay
     native <details>, only their container's display changes here. */
  .site-header nav {
    display: none;
    flex-basis: 100%;
    width: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    text-align: left;
    border-top: none;
    padding: 0.5rem 0 0;
  }

  .site-header nav.nav-open {
    display: flex;
  }

  .site-header nav .nav-dropdown {
    display: block;
    width: 100%;
  }

  /* Same [hidden]-vs-specificity bug already documented elsewhere in this
     file (e.g. .site-header nav .nav-dropdown-panel a[hidden] above) --
     the rule above's own display:block outranks the UA [hidden] {
     display: none } default, so a wholly hidden dropdown (rollout's own
     narrowed nav hides Read and Search entirely, not just individual
     links within them) would otherwise reappear in the mobile menu
     despite the attribute. Caught via a real cherry-pick onto rollout,
     not on main (which never hides a whole <details>, only individual
     links -- see the existing a[hidden] rule) -- kept here regardless of
     branch, since main could gain a fully-hidden dropdown later too. */
  .site-header nav .nav-dropdown[hidden] {
    display: none;
  }

  .site-header nav summary {
    display: block;
    width: 100%;
    box-sizing: border-box;
  }

  /* The one piece that actually fixes the overflow: was
     position: absolute (floating, anchored to whichever of the 4
     buttons opened it -- overflowing the viewport for any button not
     near the very left edge). Static + full-width means it simply
     flows in place under its own summary, indented one step to still
     read as "belonging to" that summary. */
  .nav-dropdown-panel {
    position: static;
    box-shadow: none;
    border: none;
    padding: 0;
    min-width: 0;
    width: 100%;
  }

  .site-header nav .nav-dropdown-panel a {
    padding-left: 2.5rem;
  }

  .avatar-link {
    align-self: flex-start;
    margin: 0.75rem 0 0.25rem;
  }
}

main {
  padding: 2rem;
  max-width: 1200px;
  margin: 0 auto;
  width: 100%;
  box-sizing: border-box;
  flex: 1;
}

main h1 {
  font-family: var(--font-serif);
  color: var(--color-accent);
}

.welcome-heading {
  font-family: var(--font-sans);
  font-size: 1.4rem;
  font-weight: 600;
  text-align: center;
}

.welcome-subtitle {
  font-size: 1.15rem;
  text-align: center;
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
  margin-top: 2.5rem;
}

.card {
  position: relative;
  display: block;
  padding: 2rem;
  background: #f4f8fb;
  color: var(--color-text);
  text-decoration: none;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}

.card::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 0 0 26px 26px;
  border-color: transparent transparent var(--color-accent) transparent;
  filter: drop-shadow(-1px -1px 2px rgba(0, 0, 0, 0.3));
  transition: border-color 0.15s ease;
}

.card:hover::after {
  border-color: transparent transparent #17223e transparent;
}

.card h2 {
  margin: 0 0 0.5rem;
  font-family: var(--font-serif);
  font-size: 1.35rem;
  color: var(--color-accent);
}

.card p {
  margin: 0;
  font-size: 1rem;
  color: var(--color-muted);
}

@media (max-width: 560px) {
  .card-grid {
    grid-template-columns: 1fr;
  }
}

.site-footer {
  background: var(--color-footer-bg);
  color: var(--color-footer-text);
  padding: 1.5rem 2rem;
}

.site-footer p {
  max-width: 1200px;
  margin: 0 auto;
  text-align: left;
  font-size: 1rem;
}

.site-footer-bottom {
  max-width: 1200px;
  margin: 0.35rem auto 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

/* Specificity note: the general .site-footer p rule above (one class +
   one element, 0-1-1) otherwise beats a bare .site-footer-note (0-1-0)
   for the margin property, since .site-footer-note is itself a <p> --
   its inherited "margin: 0 auto" gets treated as flex auto-margins once
   nested in .site-footer-bottom, silently centering it instead of
   letting justify-content: space-between push it flush left. Qualified
   with the parent here specifically to win that fight. */
.site-footer-bottom .site-footer-note {
  margin: 0;
  text-align: left;
  font-size: 0.8rem;
  opacity: 0.7;
}

.site-footer-note a {
  color: #fff;
}

.social-icons {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
}

.social-icon {
  display: inline-flex;
  color: var(--color-footer-text);
  opacity: 0.6;
  transition: color 0.15s ease, opacity 0.15s ease;
}

.social-icon svg {
  width: 18px;
  height: 18px;
  fill: currentColor;
}

.social-icon:hover {
  opacity: 1;
}

.social-icon-blogger:hover { color: #f57d00; }
.social-icon-linkedin:hover { color: #0a66c2; }
.social-icon-facebook:hover { color: #1877f2; }

/* Read page */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.read-toolbar {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin-bottom: 1.75rem;
}

.read-toolbar-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
}

.read-select {
  font-family: var(--font-sans);
  font-size: 0.9rem;
  padding: 0.4rem 0.6rem;
  border: 1px solid #ccc;
  border-radius: 4px;
  background: #fff;
  color: var(--color-text);
}

.read-select-narrow {
  min-width: 4.5rem;
}

/* Book selects (Read/Word Analyzer/Syntax Analyzer) have no explicit
   width otherwise, so a browser that sizes <select> to its currently
   selected option (Firefox) resizes -- often visibly jumping -- on
   every change; a placeholder option ("Book...") starting narrower
   than a real book name makes the FIRST selection jump hardest.
   Fixed at the width of the longest real book name ("Song of Songs"),
   with a little headroom, so the box never moves regardless of which
   book is selected or whether a placeholder option exists. */
.read-select-book {
  width: 9.5rem;
}

.read-nav-arrows {
  display: flex;
  gap: 0.4rem;
  margin-left: auto;
}

.read-nav-btn {
  font-family: var(--font-sans);
  font-size: 1rem;
  padding: 0.35rem 0.75rem;
  border: 1px solid var(--color-accent);
  border-radius: 4px;
  background: #fff;
  color: var(--color-accent);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.read-nav-btn:hover:not(:disabled) {
  background: var(--color-accent);
  color: #fff;
}

.read-nav-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.font-heb {
  font-family: "SBL Hebrew", "Ezra SIL", serif;
  font-weight: 400;
  font-synthesis: none;
}

.read-text {
  font-size: 1.6rem;
  line-height: 2.2;
  text-align: right;
}

.read-text p {
  margin: 0 0 0.75rem;
}

.verse-num {
  font-family: var(--font-sans);
  font-size: 0.8rem;
  color: var(--color-muted);
  vertical-align: super;
  margin-inline-end: 0.4rem;
  unicode-bidi: isolate;
}

.read-gated-message {
  font-family: var(--font-sans);
  font-size: 1.1rem;
  text-align: left;
  color: var(--color-muted);
}

.read-gated-message a {
  color: var(--color-accent);
}

.read-dev-note {
  margin-top: 2rem;
  font-size: 0.85rem;
  color: var(--color-muted);
  font-style: italic;
}

/* Account pages (register/login/account) */

.auth-form {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  max-width: 26rem;
  margin-top: 1.5rem;
}

.form-field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  font-size: 0.9rem;
  font-weight: 600;
}

.form-field input {
  font-family: var(--font-sans);
  font-size: 1rem;
  padding: 0.5rem 0.6rem;
  border: 1px solid #ccc;
  border-radius: 4px;
  background: #fff;
  color: var(--color-text);
  font-weight: normal;
}

.form-checkbox {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  font-size: 0.875rem;
  color: var(--color-muted);
}

.form-checkbox input {
  margin-top: 0.2rem;
}

.auth-submit {
  align-self: flex-start;
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 600;
  padding: 0.55rem 1.4rem;
  border: 1px solid var(--color-accent);
  border-radius: 4px;
  background: var(--color-accent);
  color: #fff;
  cursor: pointer;
  transition: background 0.15s ease;
}

.auth-submit:hover {
  background: #17223e;
}

.form-error {
  color: #a02020;
  font-size: 0.9rem;
  font-weight: 600;
}

.form-links {
  margin-top: 1.25rem;
  font-size: 0.9rem;
}

.form-links-disabled {
  color: var(--color-muted);
}

/* Search page */

.search-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  border-bottom: 2px solid var(--color-accent);
  margin-bottom: 1.5rem;
}

.search-tab {
  font-family: var(--font-sans);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.5rem 1rem;
  border: none;
  background: none;
  color: var(--color-muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.search-tab:hover {
  color: var(--color-accent);
}

.search-tab.active {
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
}

.search-panel {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.search-panel[hidden] {
  display: none;
}

.search-field {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.search-field label {
  font-family: var(--font-sans);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-text);
}

.search-field-row {
  display: flex;
  gap: 0.6rem;
}

.search-field input[type="text"] {
  font-family: var(--font-sans);
  font-size: 0.95rem;
  padding: 0.45rem 0.6rem;
  border: 1px solid #ccc;
  border-radius: 4px;
  flex: 1;
  min-width: 0;
}

.search-field input.font-heb {
  font-family: "SBL Hebrew", "Ezra SIL", serif;
  font-size: 1.35rem;
}

.search-select {
  font-family: var(--font-sans);
  font-size: 0.9rem;
  padding: 0.45rem 0.6rem;
  border: 1px solid #ccc;
  border-radius: 4px;
  background: #fff;
  color: var(--color-text);
}

.search-checkbox-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-sans);
  font-size: 0.9rem;
  font-weight: 600;
}

.search-radio-group {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.search-radio-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-sans);
  font-size: 0.9rem;
}

.search-coming-soon {
  color: var(--color-muted);
  font-style: italic;
}

.search-subheading {
  font-family: var(--font-sans);
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-muted);
  margin: 1rem 0 0.4rem;
}
.search-subheading:first-of-type {
  margin-top: 0.6rem;
}

.search-submit {
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 600;
  padding: 0.6rem 1.5rem;
  border: none;
  border-radius: 4px;
  background: var(--color-accent);
  color: #fff;
  cursor: pointer;
  transition: background 0.15s ease;
}

.search-submit:hover {
  background: #17223e;
}

#search-results {
  margin-top: 2rem;
  border-top: 1px solid #ddd;
  padding-top: 1.5rem;
}

#search-status {
  font-family: var(--font-sans);
  color: var(--color-muted);
}

#search-results-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1rem;
}

#search-results-header[hidden] {
  display: none;
}

#search-count {
  font-family: var(--font-sans);
  font-size: 0.9rem;
  color: var(--color-muted);
}

#search-group-row[hidden] {
  display: none;
}

.search-view-toggle {
  display: flex;
  gap: 0.4rem;
}

.search-view-btn {
  font-family: var(--font-sans);
  font-size: 0.85rem;
  padding: 0.3rem 0.75rem;
  border: 1px solid var(--color-accent);
  border-radius: 4px;
  background: #fff;
  color: var(--color-accent);
  cursor: pointer;
}

.search-view-btn.active {
  background: var(--color-accent);
  color: #fff;
}

.search-results-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

.search-results-table tr {
  border-bottom: 1px solid #eee;
}

.search-results-table td {
  padding: 0.6rem 0.5rem;
  vertical-align: middle;
}

.search-result-word {
  font-size: 1.6rem;
  text-align: right;
  width: 15%;
  white-space: nowrap;
}

.search-result-ref {
  font-family: var(--font-sans);
  font-size: 0.8rem;
  color: var(--color-muted);
  text-align: left;
  white-space: normal;
  overflow-wrap: break-word;
}

.search-verse-context {
  font-size: 1.6rem;
  line-height: 2;
  margin: 0 0 1rem;
}

.search-verse-context .search-result-ref {
  display: block;
  margin-bottom: 0.25rem;
}

.search-poetic-line {
  display: block;
  margin-bottom: 0.3rem;
}

.search-match-highlight {
  background: #fff2a8;
  border-radius: 2px;
}

#search-pagination {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 1rem;
  margin-top: 1.5rem;
  font-family: var(--font-sans);
  font-size: 0.9rem;
}

#search-pagination[hidden] {
  display: none;
}

#search-pagination button {
  font-family: var(--font-sans);
  font-size: 0.9rem;
  padding: 0.35rem 0.9rem;
  border: 1px solid var(--color-accent);
  border-radius: 4px;
  background: #fff;
  color: var(--color-accent);
  cursor: pointer;
}

/* Study section, 2026-09-08 (third revision): the tool switcher moved
   entirely into the main nav's own Study dropdown (see .nav-dropdown
   above) -- no more in-page tab bar/sidebar/segmented control fighting
   for attention. What's left on the page itself is just a plain
   breadcrumb for orientation ("Study / Word Analyzer"), not a nav
   control -- switching tools happens via the dropdown now. Read's own
   breadcrumb/title (2026-09-09) and Search's own (2026-09-09, stub
   pages) reuse this same pattern, one level below their own nav
   dropdown. Named generically (not .study-content) since it's no
   longer Study-specific. */
.page-content {
  min-width: 0;
}

.breadcrumb {
  margin: 0 0 1.25rem;
  font-family: var(--font-sans);
  font-size: 0.85rem;
  color: var(--color-muted);
}

.breadcrumb-sep {
  margin: 0 0.4rem;
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--color-muted);
  vertical-align: -0.05em;
}

.breadcrumb-current {
  color: var(--color-text);
  font-weight: 600;
}

/* Centered page title, brought back for the three Study tools -- the
   dropdown+breadcrumb combo (see above) is deliberately low-key for
   orientation/switching, so a real centered heading is what actually
   establishes "this is a whole page/tool", the same weight a title
   carries on every other section of the site. Named generically
   (not .study-title) since Read's own dropdown+breadcrumb+title
   (2026-09-09) reuses it too, keyed to the current reading mode
   instead of a fixed tool. */
.page-title {
  text-align: center;
  margin: 0 0 2.75rem;
}

/* Getting Started (Help section) -- same sidebar/content-pane shape as
   Reference Grammar's own .rg-* rules, kept as an independent .gs-*
   copy rather than sharing selectors: the two features are unrelated
   and evolve separately, so one page's own future styling change must
   never risk the other (same reasoning already applied to Full/Lexical
   Search's own renamed classes elsewhere in this file). */
.gs-layout {
  display: flex;
  gap: 2rem;
  align-items: stretch;
}

.gs-nav {
  flex: 0 0 220px;
  font-family: var(--font-sans);
  font-size: 1rem;
  color: var(--color-muted);
  padding-right: 1.5rem;
  border-right: 1px solid #e3ddcf;
}

.gs-nav-category-label {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-muted);
  margin: 1rem 0 0.25rem;
}

.gs-nav-category:first-child .gs-nav-category-label {
  margin-top: 0;
}

.gs-nav-link {
  display: block;
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
  font-size: 1rem;
  color: var(--color-text);
  text-decoration: none;
}

.gs-nav-link:hover {
  background: #f4f8fb;
}

.gs-nav-link.active {
  background: var(--color-accent);
  color: #fff;
  font-weight: 600;
}

.gs-content {
  flex: 1;
  min-width: 0;
  font-family: var(--font-sans);
  font-size: 1.05rem;
  line-height: 1.6;
}

.gs-content-placeholder {
  font-size: 0.85rem;
  color: var(--color-muted);
}

.gs-content h1 {
  font-size: 1.6rem;
  margin-bottom: 1rem;
}

.gs-content h2 {
  font-size: 1.2rem;
  margin: 1.5rem 0 0.5rem;
}

.gs-content p {
  margin-bottom: 0.85rem;
}

.gs-content a {
  color: var(--color-accent);
}

.gs-content ul.simple {
  margin: 0 0 0.85rem 1.25rem;
}

.gs-content table {
  border-collapse: collapse;
  margin: 1rem 0;
  font-size: 1rem;
}

.gs-content th,
.gs-content td {
  border: 1px solid #e5e5e0;
  padding: 0.4rem 0.7rem;
  text-align: left;
}

.gs-content th {
  background: #fafaf8;
}

.gs-error {
  color: #b3261e;
  font-family: var(--font-sans);
  font-size: 0.85rem;
}

/* Mobile: .gs-layout was a fixed-width flex row (220px sidebar + content,
   no breakpoint at all) -- unusable on a phone, the sidebar alone left
   almost no room for content. Stack sidebar above content instead, same
   640px breakpoint as the header nav's own mobile collapse below (empirically
   verified there; reused here rather than the narrower 560px .card-grid
   breakpoint above, since .rg-layout -- the identical sidebar+content shape,
   for Reference Grammar -- needs 640px to actually clear its own sidebar
   content, and there's no reason for the two matching layouts to switch at
   different widths). Tables (e.g. the Colometric Analyzer guide's Unicode
   cantillation-mark table) get their own horizontal scroll rather than
   forcing the whole page to scroll sideways. */
@media (max-width: 640px) {
  .gs-layout {
    flex-direction: column;
    gap: 0;
  }

  .gs-nav {
    flex: none;
    width: 100%;
    padding-right: 0;
    padding-bottom: 1rem;
    margin-bottom: 1rem;
    border-right: none;
    border-bottom: 1px solid #e3ddcf;
  }

  .gs-nav-link {
    padding: 0.5rem;
  }

  .gs-content table {
    display: block;
    overflow-x: auto;
  }
}

/* Help / About */

.about-prose {
  max-width: 640px;
  margin: 0 auto;
  font-size: 1.05rem;
  line-height: 1.7;
}

.about-prose p {
  margin: 0 0 1.25rem;
}

.about-prose a {
  color: var(--color-accent);
}

.about-updated {
  margin-top: 2rem;
  font-size: 0.85rem;
  color: var(--color-muted);
}

/* Help / Credits & Licenses -- independent copy of .about-prose's
   shape, not shared with it: the two pages evolve separately (Credits
   grows a new named section per source as features start using it),
   same "own prefix per page" discipline as .gs-*/.rg-*/.sf-*
   elsewhere in this file. */
.credits-prose {
  max-width: 640px;
  margin: 0 auto;
  font-size: 1.05rem;
  line-height: 1.7;
}

.credits-prose p {
  margin: 0 0 1.25rem;
}

.credits-prose h2 {
  font-size: 1.15rem;
  margin: 2rem 0 1rem;
}

.credits-prose a {
  color: var(--color-accent);
}

.credits-prose code {
  background: #f3f4f6;
  border-radius: 4px;
  padding: 0.1rem 0.3rem;
  font-size: 0.9em;
  font-family: ui-monospace, monospace;
}

/* Help / Contacts -- same own-prefix discipline as .about-prose/
   .credits-prose, even though today's content is a single line. */
.contacts-prose {
  max-width: 640px;
  margin: 0 auto;
  font-size: 1.05rem;
  line-height: 1.7;
}

.contacts-prose p {
  margin: 0 0 1.25rem;
}

.contacts-prose a {
  color: var(--color-accent);
}

#search-pagination button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Syntax Analyzer (Study section) */

.hidden {
  display: none;
}

.sv-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.sv-btn {
  font-family: var(--font-sans);
  font-size: 0.9rem;
  font-weight: 500;
  padding: 0.4rem 0.9rem;
  border-radius: 4px;
  cursor: pointer;
}

.sv-btn-primary {
  border: 1px solid var(--color-accent);
  background: var(--color-accent);
  color: #fff;
}

.sv-btn-primary:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Reciprocal "Open in X" button between Syntax Analyzer and Word
   Analyzer -- pushed to the toolbar's far right (margin-left: auto on
   a flex row), a plain secondary/link style rather than the primary
   accent color, since it navigates away rather than confirming an
   action on this page. */
.sv-btn-link {
  margin-left: auto;
  border: 1px solid #d6d6d6;
  background: #fff;
  color: var(--color-text);
}

.sv-btn-link:hover:not(:disabled) {
  background: #f5f5f3;
}

.sv-btn-link:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.sv-mode-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1rem;
  font-family: var(--font-sans);
  font-size: 0.9rem;
}

.sv-mode-label {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-muted);
}

.sv-mode-row label {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  cursor: pointer;
}

.sv-verse-box {
  background: #fff;
  border: 1px solid #e3ddcf;
  border-radius: 6px;
  padding: 1.1rem 1.3rem;
  margin-bottom: 0.75rem;
  min-height: 4rem;
}

.sv-verse-box #sv-verse-text,
.sv-verse-box #wa-verse-text {
  font-size: 1.5rem;
  line-height: 2.2;
}

.sv-placeholder {
  font-family: var(--font-sans);
  font-size: 0.9rem;
  color: var(--color-muted);
}

.sv-hint {
  font-family: var(--font-sans);
  font-size: 0.8rem;
  color: var(--color-muted);
  margin: 0 0 0.75rem;
}

.sv-accent-panel,
#wa-grammar-panel {
  margin-bottom: 1.5rem;
}

.sv-loading,
.sv-muted {
  font-family: var(--font-sans);
  font-size: 0.85rem;
  color: var(--color-muted);
}

.poetic-line {
  display: block;
  margin-bottom: 0.3rem;
}

/* static/js/verse_fit.js applies this class while it's actively fitting
   the verse to one line per colon -- nowrap is what makes .scrollWidth
   reflect the line's true natural width (a wrapping line's scrollWidth
   is just its own container width, useless for measuring "does this
   need to shrink"), and it also IS the fix once the right font-size is
   found: the same rule keeps enforcing single-line rendering at that
   size instead of letting the browser wrap again on the next reflow. */
.sv-fit-lines .poetic-line {
  white-space: nowrap;
}

.sv-w {
  cursor: pointer;
  border-radius: 3px;
  padding: 0.05rem 0;
}

.sv-w:hover {
  background: #e7edf6;
}

.sv-w.selected {
  background: #cddaf0;
}

/* Deep-link target highlight (?word=<slot> in Word Analyzer) -- yellow,
   distinct from the blue click-to-select .selected above, so both can
   never be confused: word-match marks "this is the word the link
   pointed at", selected marks "this is the word currently open in the
   grammar/accent panel". */
.sv-w.word-match {
  background: #fef08a;
}

/* Syntactic Search result match (?clause=/?phrase=/?group=, see
   syntax_analyzer.js's own revealTargets()) -- same pale-yellow fill
   convention as .sv-w.word-match above, applied to the whole matched
   Clause/Phrase/Group card rather than to text, so it stays legible
   layered over each card's own border/tag colors. Needed because
   revealTargets() reveals a WHOLE container's siblings (e.g. every
   phrase of a matched clause), not just the one actually matched --
   without this, there'd be no way to tell which of possibly several
   revealed cards is the real match. Border/left-accent colors are left
   untouched -- only the fill changes -- so a highlighted card still
   reads as "this is a Clause/Phrase/Group" via its own established
   color identity. */
.clause-block.search-match,
.sc-block.phrase-card.search-match,
.group-row.search-match {
  background: #fef08a;
}

/* Sentence / Clause / Phrase / Group tree */

.sentence-block {
  border: 1px solid #ecdfb8;
  border-left: 4px solid #b8860b;
  border-radius: 6px;
  padding: 1rem;
  margin-bottom: 1rem;
  background: #fdf8ec;
}

.sentence-header {
  font-family: var(--font-sans);
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-muted);
  margin-bottom: 0.6rem;
}

.sentence-header .sc-ref {
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
  margin-left: 0.4rem;
}

.sentence-text {
  font-size: 1.4rem;
  color: #4b4638;
  margin-bottom: 0.85rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid #ecdfb8;
}

.clause-block {
  border: 1px solid #d6dceb;
  border-left: 3px solid var(--color-accent);
  border-radius: 4px;
  padding: 0.6rem 0.85rem;
  margin-bottom: 0.6rem;
  background: #f5f7fb;
}

.clause-block:last-child {
  margin-bottom: 0;
}

.clause-header {
  font-family: var(--font-sans);
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--color-muted);
  margin-bottom: 0.15rem;
}

.clause-text {
  font-size: 1.4rem;
  margin-bottom: 0.4rem;
}

.clause-gap {
  color: #9ca3af;
}

.sc-block {
  padding: 0.75rem 1rem;
  border: 1px solid #e3ddcf;
  border-radius: 6px;
  background: #fafaf7;
}

.sc-block.phrase-card {
  background: #f1f8f3;
  border-color: #bfe0cb;
}

.sc-block-label {
  font-family: var(--font-sans);
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--color-muted);
  margin-bottom: 0.15rem;
}

.sc-heb {
  font-family: "SBL Hebrew", "Ezra SIL", serif;
  font-size: 1.4rem;
  font-weight: 400;
  font-synthesis: none;
  margin-bottom: 0.4rem;
}

.sc-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.sc-tag {
  display: inline-flex;
  flex-direction: column;
  font-family: var(--font-sans);
  font-size: 0.7rem;
  background: #e4e9f4;
  color: var(--color-accent);
  border-radius: 4px;
  padding: 0.2rem 0.5rem;
  line-height: 1.35;
}

.sc-tag-code {
  font-weight: 700;
}

.phrase-card .sc-tag {
  background: #dcefe1;
  color: #2f6b45;
}

/* Border moved from bottom back to top (2026-09-01, both Clause and
   Phrase now reordered the same way -- tags before Mother, see
   renderClause() in this file and app/syntax_card.py's
   phrase_html_by_id()) -- Mother now comes AFTER the type/domain/kind
   (or function/type/determination) tags in both cards, right before the
   Show Phrases/Groups button (which already supplies its own
   margin-top regardless of what's directly above it), so the dashed
   separator needs to divide Mother from what PRECEDES it (the tags)
   rather than what follows it. A short-lived .phrase-card-only override
   existed between the 2026-08-29 and 2026-09-01 reorders, when only
   Phrase had flipped back to tags-first and Clause still had
   Mother-first -- gone now that both cards agree, folded back into this
   one shared rule. */
.sc-mother {
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px dashed #d1cdc0;
  font-family: var(--font-sans);
  font-size: 0.8rem;
}

/* relation on its own line (2026-08-29 fix) -- a long mother Hebrew
   text (sometimes a whole clause) wrapping onto multiple lines was
   breaking the relation tag's alignment when both shared one line.
   Mother line first, relation below it (2026-08-29 follow-up) -- the
   gap now sits above relation as a small margin-top, not below it. */
.sc-relation-row {
  margin-top: 0.15rem;
}

.sc-relation-row:empty {
  display: none;
}

.sc-mother-label {
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #9a6a1f;
}

.sc-mother .sc-heb {
  display: inline;
  font-size: 1.15rem;
  margin: 0 0.3rem;
}

.sc-ref {
  color: var(--color-muted);
  font-size: 0.75rem;
}

.sc-error {
  color: #b3261e;
  font-family: var(--font-sans);
  font-size: 0.85rem;
}

.phrase-cards {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.show-clauses-btn,
.show-phrases-btn,
.show-groups-btn {
  font-family: var(--font-sans);
  margin-top: 0;
  margin-bottom: 0.6rem;
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border-radius: 4px;
  padding: 0.25rem 0.6rem;
  cursor: pointer;
}

.show-clauses-btn {
  color: #9a6a1f;
  background: #fbeecb;
  border: 1px solid #e8cf8e;
}

.show-clauses-btn:hover {
  background: #f6e2ac;
}

/* Page-level Show/Hide Sentences toggle -- same amber identity as
   .show-clauses-btn (color points at what it opens: the Sentence tree
   below it), just larger/margined to sit as its own toolbar-adjacent
   control rather than inline inside a Sentence card. */
.toggle-sentences-btn {
  font-family: var(--font-sans);
  font-size: 0.85rem;
  font-weight: 600;
  border-radius: 4px;
  padding: 0.4rem 0.9rem;
  margin-bottom: 1rem;
  cursor: pointer;
  color: #9a6a1f;
  background: #fbeecb;
  border: 1px solid #e8cf8e;
}

.toggle-sentences-btn:hover:not(:disabled) {
  background: #f6e2ac;
}

.toggle-sentences-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Green, not the site accent color (2026-09-02) -- this button reveals
   Phrase cards, so its color should point at what it opens rather than
   blend into the Clause card it sits on, same reasoning applied to
   .show-groups-btn right below. Exact values matched to .show-groups-btn
   and .sc-block.phrase-card's own border-color. */
.show-phrases-btn {
  color: #2f6b45;
  background: #dcefe1;
  border: 1px solid #bfe0cb;
}

.show-phrases-btn:hover {
  background: #c9e6d1;
}

.show-groups-btn {
  margin-top: 0.6rem;
  margin-bottom: 0;
  color: #2f6b45;
  background: #dcefe1;
  border: 1px solid #bfe0cb;
}

.show-groups-btn:hover {
  background: #c9e6d1;
}

.groups-container {
  margin-top: 0.5rem;
}

.groups-list {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.group-row {
  padding: 0.4rem 0.6rem;
  background: #ffffff;
  border: 1px solid #d1e9d8;
  border-radius: 4px;
}

.group-header {
  font-family: var(--font-sans);
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #2f6b45;
  margin-bottom: 0.25rem;
}

.group-content {
  font-family: "SBL Hebrew", "Ezra SIL", serif;
  font-size: 1.3rem;
  font-weight: 400;
  font-synthesis: none;
}

/* Word Analyzer (Study section) -- grammar card, ported from
   docs/word_analyzer.html's own <style> block (raw Tailwind hex values
   there) re-expressed with sh-web's own design tokens. */
.gc-wrap {
  display: flex;
  flex-direction: column;
  max-width: 42rem;
  margin: 0 auto;
  border: 1px solid #e5e5e0;
  border-radius: 6px;
  overflow: hidden;
  background: #fff;
}

.gc-row {
  display: grid;
  grid-template-columns: 9rem 1fr;
  gap: 0.75rem;
  padding: 0.4rem 0.9rem;
  border-bottom: 1px solid #f1f1ec;
}

.gc-row:last-child {
  border-bottom: none;
}

.gc-row:nth-child(odd) {
  background: #fafaf8;
}

.gc-label {
  font-family: var(--font-sans);
  font-size: 0.8rem;
  color: var(--color-muted);
}

.gc-value {
  font-family: var(--font-sans);
  font-size: 0.9rem;
  color: var(--color-text);
  font-weight: 600;
}

.gc-value-heb {
  font-family: "SBL Hebrew", "Ezra SIL", serif;
  font-size: 1.3rem;
  font-weight: 400;
  font-synthesis: none;
}

/* Grammar-reference badge (student-tier only, see queries_word_analyzer.py
   can_link_reference_grammar) -- links a card row into Reference Grammar.
   A small round pill (sh-web's own light blue-gray/navy tint, matching
   .nav-dropdown-panel's own hover tone, rather than the dev tool's raw
   indigo hex) with margin-left for breathing room from the value text --
   was rendered as a bare, unstyled "?" before this rule existed (this
   class had no CSS at all in sh-web until now). */
.gc-uhg-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.1rem;
  height: 1.1rem;
  margin-left: 0.4rem;
  border-radius: 999px;
  background: #eef2f7;
  color: var(--color-accent);
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1;
  text-decoration: none;
  flex-shrink: 0;
}

.gc-uhg-link:hover {
  background: #dbe4ef;
}

/* Locked variant (student-tier cross-link, see routers/study.py's
   word_analyzer_word_grammar) -- same badge shape, muted gray instead of
   the navy accent so it reads as "present but not currently usable"
   without being a completely different affordance. Still a real,
   clickable <a> (cursor stays a pointer) -- word_analyzer.js intercepts
   the click and shows data-locked-message inline instead of navigating,
   see .gc-uhg-locked-tooltip below. */
.gc-uhg-link-locked {
  background: #ececec;
  color: var(--color-muted);
}

.gc-uhg-link-locked:hover {
  background: #e0e0e0;
}

.gc-uhg-locked-tooltip {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  background: #fff4e5;
  color: #8a5a00;
  font-family: var(--font-sans);
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
}

.gc-empty,
.gc-error {
  padding: 0.75rem 1rem;
  font-family: var(--font-sans);
  font-size: 0.85rem;
  color: var(--color-muted);
}

.gc-error {
  color: #b3261e;
}

/* Reference Grammar (Study section) -- ported from docs/reference_
   grammar.html's own <style> block, re-expressed with sh-web's design
   tokens (var(--color-accent) for links/active state instead of the
   dev tool's raw #1d4ed8, var(--font-serif)/var(--font-sans) instead
   of raw Georgia) rather than a byte-for-byte port, since this page's
   markup/layout is genuinely different from the dev tool's own
   full-viewport SPA shell (nested inside the shared Study nav here,
   not a standalone page). mark.heb-highlight's font-weight:400 +
   font-synthesis:none and a.bible-ref's distinct color are kept as-is
   -- those aren't styling choices, they're the same hard Ezra-SIL
   correctness requirement and "this leaves the page" signal the dev
   tool already established. */
.rg-layout {
  display: flex;
  gap: 2rem;
  align-items: stretch;
}

.rg-nav {
  flex: 0 0 220px;
  font-family: var(--font-sans);
  font-size: 1rem;
  color: var(--color-muted);
  padding-right: 1.5rem;
  border-right: 1px solid #e3ddcf;
}

.rg-nav-category-label {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-muted);
  margin: 1rem 0 0.25rem;
}

.rg-nav-category:first-child .rg-nav-category-label {
  margin-top: 0;
}

.rg-nav-link {
  display: block;
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
  font-size: 1rem;
  color: var(--color-text);
  text-decoration: none;
}

.rg-nav-link:hover {
  background: #f4f8fb;
}

.rg-nav-link.active {
  background: var(--color-accent);
  color: #fff;
  font-weight: 600;
}

.rg-content {
  flex: 1;
  min-width: 0;
  /* Hebrew appears inline within ordinary prose here (no per-element
     tagging, unlike .gc-value-heb/.font-heb elsewhere) -- Georgia
     doesn't cover Hebrew glyphs, so 'Ezra SIL' must be IN this chain
     (not just the generic 'serif' fallback var(--font-serif) ends
     with) for Hebrew to reliably fall through to it rather than
     whatever unpredictable system serif the browser picks. */
  font-family: Georgia, "Iowan Old Style", "Palatino Linotype", "Book Antiqua", "Ezra SIL", serif;
  font-size: 1.05rem;
  line-height: 1.6;
}

.rg-content-placeholder {
  font-family: var(--font-sans);
  font-size: 0.85rem;
  color: var(--color-muted);
}

.rg-content h1 {
  font-size: 1.6rem;
  margin-bottom: 1rem;
}

.rg-content h2 {
  font-size: 1.2rem;
  margin: 1.5rem 0 0.5rem;
}

.rg-content p {
  margin-bottom: 0.85rem;
  line-height: 1.6;
}

.rg-content a {
  color: var(--color-accent);
}

/* Bible verse citations (queries_reference_grammar.py's own
   abbrev->abbrev_os href rewrite) -- a distinct color/underline from
   ordinary in-app cross-reference links, since these open a DIFFERENT
   tool (Word Analyzer, in its own shared tab) rather than navigating
   within this one. */
.rg-content a.bible-ref {
  color: #15803d;
  text-decoration: underline dotted;
  text-underline-offset: 0.15em;
}

.rg-content table {
  border-collapse: collapse;
  margin: 1rem 0;
  font-size: 1.2rem;
}

.rg-content th,
.rg-content td {
  border: 1px solid #e5e5e0;
  padding: 0.4rem 0.7rem;
  text-align: left;
}

.rg-content th {
  background: #fafaf8;
}

.rg-content code,
.rg-content .docutils.literal {
  background: #f3f4f6;
  border-radius: 4px;
  padding: 0.1rem 0.3rem;
  font-size: 0.85em;
  font-family: ui-monospace, monospace;
}

/* Replaces <strong> for a key word/phrase WITHIN Hebrew text (see
   notebooks/25_uhg_pages_migration.py's _dehighlight_hebrew_bold) --
   never bold: Ezra SIL has only one weight, and a browser-synthesized
   bold on it looks wrong (same "never make Ezra bold" rule already
   applied everywhere else in the project). */
.rg-content mark.heb-highlight {
  background: #fef08a;
  font-weight: 400;
  font-synthesis: none;
  padding: 0 0.1rem;
  border-radius: 0.15rem;
}

.rg-content .admonition {
  border-left: 3px solid #93c5fd;
  background: #eff6ff;
  padding: 0.6rem 1rem;
  margin: 1rem 0;
  border-radius: 4px;
}

.rg-content .admonition.warning {
  border-left-color: #fca5a5;
  background: #fef2f2;
}

.rg-content .admonition-title {
  font-weight: 700;
  display: block;
  margin-bottom: 0.25rem;
}

.rg-content ul.simple,
.rg-content .toctree-wrapper ul {
  margin: 0 0 0.85rem 1.25rem;
}

.rg-error {
  color: #b3261e;
  font-family: var(--font-sans);
  font-size: 0.85rem;
}

/* Mobile: same fixed-width-sidebar bug as .gs-layout above, same fix --
   stack instead of squeeze, same breakpoint. Reference Grammar's own
   nav is much longer (19 categories/87 pages vs. Getting Started's one
   guide), so a stacked sidebar sits above a LOT of content before
   reaching the article -- but loadPage()'s own existing
   contentBodyEl.scrollIntoView() call (reference_grammar.js) already
   scrolls straight past it to the article on every navigation,
   including the very first page load, so this was verified NOT to need
   a collapsible nav on top -- see the conversation this was built in. */
@media (max-width: 640px) {
  .rg-layout {
    flex-direction: column;
    gap: 0;
  }

  .rg-nav {
    flex: none;
    width: 100%;
    padding-right: 0;
    padding-bottom: 1rem;
    margin-bottom: 1rem;
    border-right: none;
    border-bottom: 1px solid #e3ddcf;
  }

  .rg-nav-link {
    padding: 0.5rem;
  }

  .rg-content table {
    display: block;
    overflow-x: auto;
  }
}

/* ============================================================
   Full Search (Search section) -- ported from studia_hebraica's
   docs/syntax_searcher.html (the "mega-search" dev tool). Manual
   copy, not auto-synced; re-copy by hand if the dev tool's own CSS
   changes (same discipline as syntax_search.py/word_search.py/
   token_search.py). Everything below (including the .mword-scope
   component) is scoped under .sf-page, the page root wrapper in
   search_full.html -- two real class-name collisions with existing
   site-wide CSS were found and fixed during the port: `.breadcrumb`
   (renamed `.sf-breadcrumb` -- collides with the page-level Study/
   Search breadcrumb `<p class="breadcrumb">` every template uses)
   and `.groups-container` (renamed `.sf-groups-container` -- collides
   with Syntax Analyzer's own Phrase-card Groups panel). The dev
   tool's own Tailwind-CDN utility classes (flex/gap-3/text-sm/etc.,
   used throughout the body markup and JS-generated DOM) are NOT
   loaded here -- sh-web has never used Tailwind -- so a small,
   explicitly .sf-page-scoped utility shim below reproduces the exact
   handful actually used, letting the ported JS's class-name strings
   work unchanged. Every other class name in this section (.clause-
   editor, .mword-scope, etc.) was checked against the rest of this
   file for collisions before porting verbatim.
   ============================================================ */

/* Tailwind ships a Preflight reset that strips native <button> chrome
   (border/background/padding/font) site-wide -- the dev tool relies on
   this for every "+ Add X"/"Clear all"-style <button> that's meant to
   read as plain colored text, not an actual button. sh-web never loads
   Tailwind, so without this those elements fell back to the browser's
   own bordered/padded button appearance. Scoped to .sf-page rather than
   a real site-wide Preflight (which would affect every real button
   elsewhere on the site, e.g. .btn-primary). Lower specificity than any
   of the utility classes below (a single class beats a class+element
   selector), so #sf-btn-search's own bg-blue-600/px-4/py-2/rounded
   still render as a real button -- only the reset properties those
   rules don't touch (border, in particular) apply everywhere else. */
.sf-page button {
  appearance: none;
  -webkit-appearance: none;
  border: none;
  background: none;
  padding: 0;
  font: inherit;
  cursor: pointer;
}

/* Canvas-root "+ Add" buttons are the one deliberate exception to the
   reset above: real, bordered buttons (not plain text like their
   nested "+ Add Phrase"/"+ Add Group"/"+ Add Word" counterparts inside
   a Clause/Phrase/Group/Token card, which stay <span>-based labels,
   unaffected by the button reset either way) -- these five are the
   entry point to the whole canvas, so they read more clearly as
   actionable buttons than as another line of colored text. Each one's
   border/hover-background reuses the exact same hue already used for
   the CARD it creates (.clause-editor/.phrase-editor/.group-editor/
   .word-editor/.token-editor further down), so the button previews the
   color of the card it's about to add -- full `border` shorthand (not
   just border-color) so it fully overrides the `border: none` reset
   above despite the lower specificity of a bare .sf-page button rule. */
#sf-btn-add-clause, #sf-btn-add-phrase, #sf-btn-add-group,
#sf-btn-add-word, #sf-btn-add-token {
  display: inline-flex;
  align-items: center;
  font-size: .8rem;
  font-weight: 600;
  background: #fff;
  border-radius: .375rem;
  padding: .35rem .85rem;
  transition: background-color .12s, border-color .12s;
}
#sf-btn-add-clause { color: #4338ca; border: 1px solid #a5b4fc; }
#sf-btn-add-clause:hover { background: #eef2ff; border-color: #6366f1; }
#sf-btn-add-phrase { color: #15803d; border: 1px solid #86efac; }
#sf-btn-add-phrase:hover { background: #f0fdf4; border-color: #22c55e; }
#sf-btn-add-group { color: #0f766e; border: 1px solid #5eead4; }
#sf-btn-add-group:hover { background: #f0fdfa; border-color: #14b8a6; }
/* Word and Token share one warm stone/taupe identity, matching their
   card's own left-stripe/--accent above -- see that rule's own comment
   for why (both are lexical/leaf-level units, not part of the Clause/
   Phrase/Group containment hierarchy, and a vivid button paired with
   the card's own plain paper body read as a mismatch). */
#sf-btn-add-word, #sf-btn-add-token { color: #57534e; border: 1px solid #d6d3d1; }
#sf-btn-add-word:hover, #sf-btn-add-token:hover { background: #fafaf9; border-color: #a8a29e; }

/* Tailwind-utility-shim -- only the ~30 classes actually used by the
   ported page, scoped so they never leak site-wide. Values match
   Tailwind's own default spacing/color scale exactly (the dev tool's
   own hex choices already match Tailwind v3 defaults, confirmed
   against the custom hex colors used elsewhere in this same ported
   section, e.g. #15803d/#a21caf for green-700/fuchsia-700). */
.sf-page .flex { display: flex; }
.sf-page .items-center { align-items: center; }
.sf-page .gap-3 { gap: .75rem; }
.sf-page .mb-1 { margin-bottom: .25rem; }
.sf-page .mb-2 { margin-bottom: .5rem; }
.sf-page .mb-3 { margin-bottom: .75rem; }
.sf-page .mb-6 { margin-bottom: 1.5rem; }
.sf-page .mt-6 { margin-top: 1.5rem; }
.sf-page .mx-auto { margin-left: auto; margin-right: auto; }
.sf-page .sf-ml-auto { margin-left: auto; }
.sf-page .max-w-3xl { max-width: 48rem; }
.sf-page .p-6 { padding: 1.5rem; }
.sf-page .px-4 { padding-left: 1rem; padding-right: 1rem; }
.sf-page .py-2 { padding-top: .5rem; padding-bottom: .5rem; }
.sf-page .rounded { border-radius: .25rem; }
.sf-page .text-xl { font-size: 1.25rem; line-height: 1.75rem; }
.sf-page .text-xs { font-size: .75rem; line-height: 1rem; }
.sf-page .text-sm { font-size: .875rem; line-height: 1.25rem; }
.sf-page .font-bold { font-weight: 700; }
.sf-page .font-medium { font-weight: 500; }
.sf-page .text-white { color: #fff; }
.sf-page .bg-blue-600 { background-color: #2563eb; }
.sf-page .bg-blue-600.hover\:bg-blue-700:hover { background-color: #1d4ed8; }
.sf-page .bg-gray-50 { background-color: #f9fafb; }
.sf-page .text-gray-400 { color: #9ca3af; }
.sf-page .text-gray-500 { color: #6b7280; }
.sf-page .text-gray-800 { color: #1f2937; }
.sf-page .text-red-600 { color: #dc2626; }
.sf-page .text-amber-600 { color: #d97706; }
.sf-page .text-indigo-700 { color: #4338ca; }
.sf-page .text-green-700 { color: #15803d; }
.sf-page .text-teal-700 { color: #0f766e; }
.sf-page .text-stone-600 { color: #57534e; }
.sf-page .hover\:underline:hover { text-decoration: underline; }
  .sf-page select {
    max-width: 100%;
    min-width: 0;
    text-overflow: ellipsis;
  }
  /* "Show in: Syntax Analyzer / Word Analyzer" toggle (Full Search only
     -- Lexical/Syntactic Search each have exactly one fixed destination,
     since their own canvas is already restricted to one unit family).
     Full Search combines syntax units (Clause/Phrase/Group) and Word/
     Token, so a single result can carry both a matched_slots word AND
     a clause/phrase/group match -- the user picks which tool's own
     highlighting they want to see, see search_full.js's own runSearch().
     Plain buttons, not .mword-scope's own .pill (that class is scoped
     to the Word/Token card component, not meant for page-level chrome
     like this). */
  .sf-show-in {
    display: flex;
    align-items: center;
    gap: 0.4rem;
  }
  .sf-show-in-label {
    font-size: 0.8rem;
    color: var(--color-muted);
  }
  .sf-show-in-pill {
    appearance: none;
    border: 1px solid #d1d5db;
    background: #fff;
    color: var(--color-text);
    border-radius: 999px;
    padding: 0.3rem 0.75rem;
    font-size: 0.8rem;
    cursor: pointer;
  }
  .sf-show-in-pill:hover {
    border-color: var(--color-accent);
  }
  .sf-show-in-pill.is-active {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #fff;
    font-weight: 600;
  }

  #sf-results a {
    color: #1d4ed8;
    text-decoration: none;
  }
  #sf-results a:hover {
    text-decoration: underline;
  }
  #sf-results-list {
    column-width: 9rem;
    column-gap: 1.5rem;
  }
  #sf-results-list div {
    break-inside: avoid;
  }

  /* Same color scheme as Syntax Analyzer's Clause/Phrase/Group cards, made
     more assertive here since these are the top-level editable objects
     on the canvas (a plain left-border read too weak on its own). */
  .clause-editor {
    border: 1px solid #a5b4fc;
    border-left: 5px solid #6366f1;
    background: #f8fafc;
    border-radius: .5rem;
    padding: 1rem;
    box-shadow: 0 1px 2px rgba(0,0,0,.04);
  }
  .phrase-editor {
    background: #f0fdf4;
    border: 1px solid #86efac;
    border-radius: .375rem;
    padding: .4rem .75rem .75rem;
  }
  .group-editor {
    background: #ffffff;
    border: 1px solid #d1fae5;
    border-radius: .375rem;
    padding: .6rem .75rem;
  }
  /* Word/Token get the SAME "bordered box, colored left accent stripe"
     identity as Clause/Phrase/Group above (2026-09-03) -- previously
     these two just used the mword-scope component's own generic navy
     --accent with no outer frame at all, reading as visually unrelated
     to the rest of the canvas's own syntactic-unit palette. Word's own
     hue was first shipped amber, then corrected the SAME session: amber
     read as red/maroon in practice, and separately, every syntactic
     unit's own palette (indigo/green/teal/violet/sky) is COOL while
     amber is WARM -- Word needed a cool hue too, to actually belong to
     that family rather than stand apart from it. Settled on blue
     (`#1d4ed8`-family) -- distinct from indigo (more violet-leaning) and
     sky (more cyan-leaning), and unambiguously neither red nor brown.
     No `padding`
     here, deliberately -- unlike Clause/Phrase/Group, the mword-scope
     card's OWN inner elements (.card-top, .token-fields, .summary)
     already carry their own padding; adding padding here too would
     double it up. `overflow: hidden` clips .card-top's own background
     gradient to this rule's `border-radius` (the reason `.mword-scope
     .card` itself -- unused on this page, see its own comment -- has the
     identical pairing). Overriding the `--accent`/`--accent-ink`/
     `--accent-soft`/`--accent-line` custom properties HERE, rather than
     re-coloring every individual .mword-scope rule that references them
     (pills, tabs, the badge dot, matchtype, the chain-strip's own wc-op/
     wc-paren...), is what makes the recolor total with a handful of
     lines: CSS custom properties cascade to every descendant, and
     .mword-scope's own rules already read them via var(...) throughout
     -- the same mechanism this file's own dark-mode block already uses
     to redefine --accent globally. */
  /* Word and Token share ONE identity, deliberately -- both are
     lexical/leaf-level search units (not part of the Clause/Phrase/
     Group syntactic-containment hierarchy, which keeps its own
     distinct indigo/green/teal), so they read as one family together
     rather than competing for their own hue. A vivid accent (blue,
     then fuchsia) was tried on the border/button first, but the
     mword-scope card's own body is always its native paper/off-white
     (`--paper`/`--surface-2`, further down this file) regardless of
     what the outer wrapper sets -- a bright stripe+button next to a
     plain paper card read as a mismatch, not a real per-unit color.
     Settled on a warm stone/taupe accent instead: muted enough to sit
     comfortably against the paper card, still a real, visible color
     (not gray) on the left stripe/button/chain-strip. */
  /* No `background` here -- would never render (same cascade-tie bug
     documented below for --accent: .word-editor/.token-editor share
     the SAME element as .mword-scope, whose own `background:var(
     --paper)` rule comes later in the stylesheet and wins the tie).
     The real, visible card body color is set via --paper/--surface-2
     below instead. */
  .word-editor, .token-editor {
    border: 1px solid #d6d3d1;
    border-radius: .5rem;
    overflow: hidden;
  }
  /* Token-only: the colored left stripe marks a canvas-root-only unit
     (Token is never nested inside a Clause/Phrase, unlike Word, which
     already renders without any outer border/stripe at all when nested
     -- see .word-accent below). Dropped from .word-editor so a
     top-level Word matches its own nested self -- nestable units carry
     no left stripe, non-nestable ones do. */
  .token-editor {
    border-left: 5px solid #78716c;
  }
  /* `--accent` only (no outer border/background) -- for bare mword-
     scope Word cards that already sit INSIDE another bordered card
     (Group's own Anchor/Dependent, Token's own nested words): a second
     full border there would read as over-nested. Applied via JS at
     each of those two creation sites (addGroupWord, addTokenNestedWord)
     -- NOT applied to Mother's own word card, which keeps the page's
     default navy accent (Mother already has its own established
     violet identity for the PANEL around it; recoloring just the card
     inside would split one control's identity across two colors, not
     requested here).

     Compound selector (.word-editor.mword-scope, not two separate
     rules), not just specificity hygiene here -- also documents that
     Word and Token now intentionally share one set of values. Real bug
     fixed in the same pass (also present, unfixed, in studia_
     hebraica's own dev tool source): .word-editor/.token-editor/
     .word-accent share the SAME element as .mword-scope (JS sets e.g.
     className = 'word-editor mword-scope'), not a separate ancestor --
     so a plain, single-class .word-editor{--accent:...} rule was never
     actually "cascading to every descendant" as originally assumed. It
     and .mword-scope's own base TOKENS rule (--accent:#2B3F6B, further
     down this file) have EQUAL specificity (one class each) applied to
     the SAME element, and .mword-scope's own rule happens to come
     LATER in the stylesheet -- so it silently won every tie, leaving
     every Word/Token card's badge dot, tabs, pills, and internal
     filter-chain strip (all of which read var(--accent)/var(--accent-
     soft)/var(--accent-line) throughout, see the TOKEN CARD BODY
     section further down) stuck on the generic navy default regardless
     of which editor wrapped it. Fixed with a compound selector (two
     classes = higher specificity than one), order-independent. */
  .word-editor.mword-scope, .token-editor.mword-scope, .word-accent.mword-scope {
    --accent: #78716c;
    --accent-ink: #ffffff;
    --accent-soft: #f5f5f4;
    --accent-line: #44403c;
  }
  /* Token's own card body, one step warmer/darker than Word's default
     --paper (#FDFDFB, unchanged, set once up in the TOKENS section)
     -- same reasoning as the left-stripe distinction: Word and Token
     share one accent family, but still need to read as two different
     cards sitting side by side, not one indistinguishable from the
     other. --surface-2 (the .card-top header strip's own gradient
     start) shifts correspondingly so the header doesn't end up lighter
     than the body beneath it. */
  .token-editor.mword-scope {
    --paper: #f5f5f4;
    --surface-2: #e7e5e4;
  }
  .editor-header {
    margin-bottom: .6rem;
  }
  /* The actual "Type N ... Remove" line -- split out from .editor-header
     itself (2026-09-03) so a .sf-breadcrumb can sit above it, inside the
     SAME wrapper, without becoming a separate direct child of the card
     (which would throw off the existing "gap between sibling cards"
     rules above -- those measure from a card's own first/non-first
     DIRECT children, and .editor-header must stay that first child). */
  .editor-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: .7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: #6b7280;
  }
  .remove-btn {
    font-size: .7rem;
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
    color: #dc2626;
    cursor: pointer;
  }
  .remove-btn:hover {
    text-decoration: underline;
  }
  /* Compact sf-breadcrumb (2026-09-03) -- one small line above every
     object's own header/badge, showing the path of its ANCESTOR objects
     (never including the object's own label, which is already the big
     text right below it) -- e.g. "Clause 1 → Phrase 2 → Group 1" for a
     Group nested that deep, "Clause 1 → Mother" for a word card living
     in a Mother panel. Computed by renderAllBreadcrumbs()/
     computeBreadcrumbParts() (see below), never inline in any
     build*Block()/renderCard() function -- those only emit the empty
     slot, since a freshly built object usually isn't attached to the
     document yet at build time (the caller appends it afterward), so
     computing the path there would see no ancestors at all on first
     paint. Empty (top-level objects, or before the first refresh) ->
     hidden via :empty, no visual placeholder. */
  .sf-breadcrumb {
    font-size: .62rem;
    color: #9ca3af;
    margin-bottom: .3rem;
    white-space: nowrap;
    overflow-x: auto;
  }
  .sf-breadcrumb:empty {
    display: none;
    margin-bottom: 0;
  }
  /* Same sf-breadcrumb, scoped for use inside the shared mword-scope Word-
     card component (renderCard/renderTokenState) -- a plain re-scope,
     not a redesign, since .mword-scope's own CSS is fully prefixed and
     the bare `.sf-breadcrumb` rule above lives outside that scope.
     `margin-bottom: 0` here is load-bearing, not decorative -- the bare
     `.sf-breadcrumb` rule above sets `margin-bottom: .3rem` and this rule
     doesn't touch every property that one does, so without an explicit
     reset the unrelated `.3rem` leaks through (higher specificity only
     wins property-by-property, not rule-by-rule) and stacks on top of
     this rule's own `padding-top`, giving a nested Word/Token card
     visibly MORE top gap than a top-level one -- the exact "top padding
     problem" reported after item 19 shipped. */
  .mword-scope .sf-breadcrumb {
    font-size: .62rem;
    color: var(--ink-faint);
    padding: 8px 20px 0;
    margin-bottom: 0;
    white-space: nowrap;
    overflow-x: auto;
  }
  .mword-scope .sf-breadcrumb:empty {
    display: none;
    padding: 0;
  }
  /* Units label (2026-09-03) -- "Units in Clause 2" / "Units in Phrase
     3" / "Units in the request" (canvas root) / "Words in Token 1" --
     sits directly above a container's own chain-strip, filled in by
     renderAllUnitsLabels() ONLY when that container actually holds
     something (updateUnitsLabel checks childElementCount), never a
     permanent static caption -- "+ Add Word"/"+ Add Phrase"/etc. alone
     is the resting state, matching every other add-button on this page;
     the label appears once there's real content to label, telling the
     reader exactly which Clause/Phrase/Token/request-level canvas a
     given set of nested cards belongs to. Text color, not weight/case,
     carries the "secondary" signal -- red is reserved for Remove
     buttons only. */
  .units-label {
    font-size: .65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: #9ca3af;
    margin-bottom: .3rem;
  }
  .units-label:empty {
    display: none;
    margin-bottom: 0;
  }
  .mword-scope .units-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--ink-faint);
    margin-bottom: 8px;
  }
  .mword-scope .units-label:empty {
    display: none;
    margin-bottom: 0;
  }
  /* Uniform top gap between every direct child of a Clause/Phrase card
     (2026-08-29 fix) -- .children-container/.sf-groups-container/.mother-slot
     have no border/padding of their own, so the gap around them used to
     depend entirely on margin-collapsing through whatever they happened
     to contain: zero gap when empty, an inconsistent one once populated
     (e.g. no space at all between the filter-row and the +Add buttons
     row when a Clause had no children yet). This makes the gap fixed and
     predictable regardless of content. This rule is about spacing INSIDE
     one card (between its own filter-row/children-container/etc.) --
     spacing BETWEEN sibling cards is a separate rule, right below. */
  .clause-editor > * + *,
  .phrase-editor > * + * {
    margin-top: .75rem;
  }
  /* Spacing BETWEEN sibling cards (2026-08-29 fix, second bug in the same
     area). Originally each card type carried its OWN margin for this --
     .clause-editor had margin-bottom, .phrase-editor/.group-editor had
     margin-top -- which only worked by accident under the old fixed
     hierarchy (Clause always first, Phrase/Group always after). Under
     free placement any of the three can precede or follow any other at
     the SAME level (canvas root, a Clause's own children, or a Phrase's
     own groups), so a Clause-only margin-bottom and a Phrase/Group-only
     margin-top left real gaps: after a Phrase, followed by a Clause,
     NEITHER side contributed any margin at all (zero gap); after a
     Group, the gap varied depending on what followed (inconsistent).
     Fixed by moving the gap to the CONTAINER that actually holds the
     siblings -- one rule per container, applied only to non-first
     children, so it's identical regardless of which types are adjacent
     or in what order. */
  #sf-canvas-objects > * + * {
    margin-top: 1rem;
  }
  .children-container > * + * {
    margin-top: .75rem;
  }
  .sf-groups-container > * + * {
    margin-top: .6rem;
  }
  /* Word/Token cards need a higher-specificity repeat of the two rules
     above (2026-09-03 bug fix). A Word/Token card's root carries BOTH
     its own type class (.word-editor/.token-editor) AND .mword-scope,
     and .mword-scope's own base rule (further down this file, "BASE"
     section) sets `margin:0`. That rule has the SAME specificity as
     `.children-container > * + *`/`.sf-groups-container > * + *` (one
     class each) and comes LATER in the stylesheet, so it wins and
     silently zeroes the gap -- but only for Word/Token, since Phrase/
     Clause/Group don't carry .mword-scope. (#sf-canvas-objects > * + *
     above is unaffected -- an ID selector always outranks a single
     class regardless of order.) Fixed with an explicitly higher-
     specificity selector so it wins independent of source order. */
  .children-container > .mword-scope:not(:first-child) {
    margin-top: .75rem;
  }
  .sf-groups-container > .mword-scope:not(:first-child) {
    margin-top: .6rem;
  }
  /* Nesting indent (2026-09-03, guide LINE dropped 2026-09-03 same
     session -- tried alongside the indent, judged to add nothing once
     breadcrumbs and "Units in ..." labels already spell the path out in
     text) -- every container that holds CHILD objects (as opposed to
     #sf-canvas-objects itself, the root, which stays flush) gets a left
     indent, so containment reads as nesting depth at a glance instead of
     relying on the reader to notice which box's own border happens to
     enclose which other box. Depth is never computed explicitly --
     ordinary CSS containment nesting already compounds the indent
     automatically wherever a container sits inside another container's
     own content (e.g. a Word inside a Group's Anchor section inside a
     Phrase inside a Clause naturally accumulates one increment per
     level). `:empty` removes the indent entirely rather than leaving a
     bare gap with nothing to its right. */
  .children-container,
  .sf-groups-container,
  .group-word-section [data-role="cards"],
  .nested-words [data-role="nested-cards"] {
    padding-left: 1rem;
  }
  /* `display: none` here too (2026-09-03), not just the indent removal
     above -- previously these stayed in normal flow even empty (zero
     height, no padding, no border), which is exactly the shape CSS
     margin-collapsing-through-an-empty-box applies to: the container's
     OWN top margin (from `> * + *` in its parent) and the FOLLOWING
     sibling's own top margin collapse together in a way that isn't
     wrong, but is fragile and inconsistent with how every OTHER
     conditionally-shown element on this page (.chain-strip, .sf-breadcrumb,
     .units-label) already handles emptiness -- reported as "gaps
     between buttons and containers sometimes disappear." Matching those
     three's own convention (fully removed from flow when empty, no
     ambiguous collapsing) makes the visible gap between whichever
     elements ARE actually showing predictable and uniform everywhere,
     rather than depending on collapse behavior that varies with how
     many empty boxes happen to sit in a row. */
  .children-container:empty,
  .sf-groups-container:empty,
  .group-word-section [data-role="cards"]:empty {
    display: none;
    padding-left: 0;
  }
  .nested-words [data-role="nested-cards"]:empty {
    padding-left: 0;
    /* display:none itself lives with the rest of Token's own
       [data-role="nested-cards"] styling, in the "TOKEN CARD BODY"
       section further down -- that rule sets an unconditional
       `display:flex; margin-bottom:10px` this one doesn't touch, so the
       :empty override needs to sit next to it, not here, to stay
       readable as "the same property, immediately re-overridden". */
  }
  .mother-slot:empty {
    display: none;
  }
  /* minmax(0, 1fr), not plain 1fr -- a <select> with long option text
     otherwise forces its grid track past its fair share (grid items
     default to min-width:auto = their content's min-content size),
     pushing the whole row wider than its container. minmax(0, ...)
     lets the track shrink below that and the select's own
     max-width/overflow rules take over from there. */
  .filter-row {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: .6rem;
  }
  /* Both Clause and Phrase's own filter-rows dropped to 3 fields
     (Phrase 2026-08-29, Clause 2026-09-01): relation moved into the
     Mother panel (see below) in both -- type/domain/kind for Clause,
     function/type/determination for Phrase. Every real .filter-row on
     this page carries .cols-3 now; the plain 4-column .filter-row rule
     above is kept as the base (display/gap), not currently rendered at
     4 columns by anything. */
  .filter-row.cols-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .filter-row label {
    display: block;
    font-size: .65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: #9ca3af;
    margin-bottom: .25rem;
  }
  .group-editor label {
    display: block;
    font-size: .65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: #9ca3af;
    margin-bottom: .25rem;
  }
  /* Anchor/Dependent word-content containers (see GroupFilter's own
     docstring in api/main.py) -- each a labeled sub-section inside the
     Group card, holding any number of Word cards (the same mword-scope
     component the Mother panel's own mother-word-card uses). Kept in
     Group's own teal identity (not Mother's violet) since these are
     part of the Group itself, not a dependency pointer to another
     object. */
  .group-word-section {
    margin-top: .6rem;
    padding-top: .5rem;
    border-top: 1px dashed #99f6e4;
  }
  .group-word-section-title {
    font-size: .65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: #0f766e;
    margin-bottom: .4rem;
  }
  .group-word-section [data-role="cards"] > * + * {
    margin-top: .6rem;
  }
  /* Stone/taupe (sh-web production, matches Word's own new shared-
     with-Token identity -- see .word-editor's own comment above) --
     a "+ Add Word" link is colored by what it ADDS, same convention
     .add-phrase-btn/.add-group-btn already use, regardless of which
     container it lives in (Group's own Anchor/Dependent sections, or
     Clause/Phrase's own children). */
  .add-word-btn {
    display: inline-block;
    font-size: .8rem;
    color: #57534e;
    margin-top: .4rem;
    cursor: pointer;
  }
  .add-word-btn:hover {
    text-decoration: underline;
  }
  .add-phrase-btn, .add-group-btn {
    font-size: .8rem;
    color: #15803d;
    margin-top: .75rem;
    cursor: pointer;
  }
  .add-group-btn {
    color: #0f766e;
    margin-top: .5rem;
  }
  .add-phrase-btn:hover, .add-group-btn:hover {
    text-decoration: underline;
  }
  /* .filter-row's grid already gives each label+select div the full
     width of its 1fr track (grid's default justify-items:stretch) -- but
     a bare <select> sizes to its own content (shrink-to-fit) unless told
     otherwise, so the dropdowns weren't actually filling that width.
     Scoped to .filter-row rather than every <select> on the page, so
     Mother's own rows and the embedded Word-card component are
     unaffected. */
  .filter-row select {
    width: 100%;
  }
  /* Exclude checkbox (2026-09-02) -- per-field "=" -> "!=" flip, offered
     only on Clause's type/domain/kind and Phrase's function/type (not
     det -- only 2 values, != is already fully expressible by picking
     the other one directly; not rela -- lives in the Mother panel).
     Disabled whenever the field's own dropdown is unset (— Any —): a
     dropdown-side change handler (see buildClauseBlock/buildPhraseBlock)
     keeps `.disabled` in sync, since "exclude nothing" has no meaning --
     the backend already ignores the flag in that case too, this is
     belt-and-suspenders UI clarity, not the only guard. */
  .exclude-label {
    display: flex;
    align-items: center;
    gap: .3rem;
    margin-top: .3rem;
    font-size: .65rem;
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
    color: #6b7280;
    cursor: pointer;
  }
  .exclude-label input[type="checkbox"] {
    margin: 0;
    cursor: pointer;
  }
  .exclude-label:has(input:disabled) {
    color: #d1d5db;
    cursor: default;
  }
  .exclude-label input:disabled {
    cursor: default;
  }
  /* Clause's own children (2026-08-29 free-placement redesign; Word
     added 2026-09-03) can be a mix of Phrase, Group, and Word blocks, in
     whatever order they were added -- .children-container just stacks
     them; .add-buttons-row offers "+ Add Phrase" / "+ Add Group" / "+
     Add Word" side by side, since any of the three can be added
     directly under a Clause now. */
  .add-buttons-row {
    display: flex;
    gap: 1rem;
  }
  .add-buttons-row .add-phrase-btn, .add-buttons-row .add-group-btn, .add-buttons-row .add-word-btn {
    margin-top: 0;
  }

  /* Mother (dependency, not containment) -- deliberately NOT the green/teal
     containment palette used by .phrase-editor/.group-editor: a violet
     identity signals "this is a pointer to a different object, not a
     child of this one" (chosen 2026-08-29 to read clearly as neither red
     nor any hue already in use: indigo=Clause, green=Phrase, teal=Group,
     red=Remove). One consistent violet identity runs through the button
     AND the panel it opens -- not just the button -- so there's no jarring
     color switch the moment "Mother" is clicked. */
  .add-mother-btn {
    display: inline-block;
    font-size: .75rem;
    font-weight: 600;
    color: #6d28d9;
    background: #fff;
    border: 1px solid #c4b5fd;
    border-radius: .25rem;
    padding: .35rem 1rem;
    margin-top: .75rem;
    cursor: pointer;
  }
  .add-mother-btn:hover {
    background: #f5f3ff;
    border-color: #8b5cf6;
  }
  .mother-editor {
    border: 1px dashed #8b5cf6;
    background: #f5f3ff;
    border-radius: .375rem;
    padding: .6rem .75rem;
    margin-top: .75rem;
  }
  .mother-editor label {
    display: block;
    font-size: .65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: #5b21b6;
    margin-bottom: .25rem;
  }
  /* Clause/Phrase/Word type buttons + the relation dropdown all sit as
     siblings in one row (2026-08-29) -- relation is independent of type,
     not nested inside any type's own sub-panel (see mother-filter-row
     below): both can be set, either alone, or neither (in which case the
     Mother filter contributes nothing and the user is asked to pick at
     least one -- see motherIncompleteError()). */
  .mother-type-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .5rem;
    margin-bottom: .6rem;
  }
  .mother-type-row button {
    font-size: .75rem;
    padding: .3rem .7rem;
    border: 1px solid #8b5cf6;
    border-radius: .25rem;
    background: #fff;
    color: #5b21b6;
    cursor: pointer;
  }
  .mother-type-row button.active {
    background: #7c3aed;
    border-color: #7c3aed;
    color: #fff;
  }
  .mother-relation-field {
    display: flex;
    align-items: center;
    gap: .4rem;
    margin-left: .5rem;
  }
  .mother-relation-field label {
    margin-bottom: 0;
    white-space: nowrap;
  }
  .mother-relation-field select {
    font-size: .75rem;
    padding: .3rem .4rem;
    border: 1px solid #8b5cf6;
    border-radius: .25rem;
    background: #fff;
    color: #5b21b6;
  }
  .mother-filter-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .6rem;
  }
  .mother-filter-row select {
    width: 100%;
  }
  .mother-word-card {
    margin-top: .25rem;
    /* Nesting indent (2026-09-03), no guide line -- same treatment every
       other child-holding container gets, see that rule's own comment
       above -- Mother's own word card is content nested one level below
       the object carrying the Mother panel. */
    padding-left: 1rem;
  }

  /* AND/OR/NOT + single-level-parens chain strip (2026-09-02) --
     same mechanism and the same "wc-op"/"wc-paren" component already
     shipped in docs/word_searcher.html/docs/token_searcher.html
     (chainOpEl/chainParenEl below are copied from there, unchanged),
     restyled here with this page's own neutral slate identity --
     deliberately NOT indigo/green/teal/violet (Clause/Phrase/Group/
     Mother's own hues), since the chain connects objects of ANY type
     and isn't owned by one. One strip per sibling level: the canvas
     root (#sf-canvas-chain) and, inside each Clause/Phrase editor, one
     more for its own `children` list. */
  .chain-strip {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
    font-size: .8rem;
    margin-bottom: .75rem;
  }
  .chain-strip:empty {
    display: none;
    margin-bottom: 0;
  }
  /* Base = the canvas-root strip's own treatment: a neutral gray
     visibly darker than the page background (--color-bg: #fdfdfb) it
     sits directly on. Clause/Phrase override this below with a shade
     darker than THEIR OWN card background instead -- a chain-strip
     living inside a colored card needs its own contrast baseline, not
     the page's, or it reads as the same flat slate everywhere
     regardless of context (the bug being fixed here: all three used to
     share this one rule verbatim). */
  .chain-strip:has(.wc-word) {
    border: 1px solid #9ca3af;
    border-radius: .375rem;
    background: #e5e7eb;
    padding: .5rem .75rem;
  }
  .chain-strip .wc-word {
    font-weight: 600;
    color: #334155;
    padding: 0 .1rem;
  }
  /* Clause's own children-chain -- tinted indigo (not plain neutral
     slate, which read as indistinguishable from the canvas-root strip
     right above it) to correlate with .clause-editor's own left-side
     accent stripe (#6366f1). */
  .clause-editor > .chain-strip:has(.wc-word) {
    border-color: #818cf8;
    background: #e0e7ff;
  }
  .clause-editor > .chain-strip .wc-word {
    color: #3730a3;
  }
  /* Phrase's own children-chain -- pale salad/lime green (toned down
     from an earlier, too-saturated dark-green fill), matching Phrase's
     own green identity. Light enough that the shared .wc-word/.wc-paren
     dark-slate defaults would look muddy rather than legible, so text/
     paren colors are still overridden here, just to dark lime instead
     of white-on-dark. */
  .phrase-editor > .chain-strip:has(.wc-word) {
    border-color: #84cc16;
    background: #ecfccb;
  }
  .phrase-editor > .chain-strip .wc-word {
    color: #365314;
  }
  .phrase-editor > .chain-strip .wc-paren {
    color: #65a30d;
  }
  .phrase-editor > .chain-strip .wc-paren:hover,
  .phrase-editor > .chain-strip .wc-paren.is-active {
    color: #365314;
  }
  .wc-op {
    appearance: auto;
    border: 1px solid #cbd5e1;
    border-radius: .3rem;
    background: #fff;
    color: #475569;
    font-family: inherit;
    font-size: .65rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    padding: .15rem .3rem;
    margin: 0 .1rem;
    cursor: pointer;
  }
  .wc-op:hover {
    border-color: #94a3b8;
  }
  .wc-paren {
    appearance: none;
    border: none;
    background: none;
    cursor: pointer;
    font-family: Georgia, serif;
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1;
    padding: 0 .05rem;
    color: #cbd5e1;
    transition: color .12s;
  }
  .wc-paren:hover {
    color: #94a3b8;
  }
  .wc-paren.is-active {
    color: #475569;
  }
  /* Nested children-chain strips (inside a Clause/Phrase editor) get a
     small top margin so they don't visually collide with the object's
     own filter-row/Mother panel right above -- the shared `> * + *`
     spacing rule already used for other direct children handles this,
     no extra rule needed; empty strips collapse via .chain-strip:empty
     above, same as the canvas-root one. */

  /* ============================================================
     Mother=Word filter -- the full Word Searcher card component,
     copied verbatim from docs/token_searcher.html (2026-08-29),
     scoped under .mword-scope so its own design system (CSS custom
     properties, dark-mode handling, fonts) never leaks onto the rest
     of this page, which uses Tailwind + a separate ad hoc palette.
     ============================================================ */

/* Same color/font tokens as docs/word_searcher.html (2026-08-22 sh-web
   realignment) -- copied, not reinvented, so the two tools read as one
   family. --token-accent/--token-accent-soft are new here: the only
   visual difference between a Word object and a Token object on this
   tool's canvas is the badge dot/name color, everything else (pills,
   tabs, inputs, switches) is shared, unscoped CSS. */

/* ============================= TOKENS ============================= */
.mword-scope {
  --paper:#FDFDFB;
  --surface:#FFFFFF;
  --surface-2:#F3F2EE;
  --ink:#1A1A1A;
  --ink-soft:#666666;
  --ink-faint:#9A9690;
  --line:#DDD9D0;
  --line-soft:#EAE8E3;

  --accent:#2B3F6B;
  --accent-ink:#FFFFFF;
  --accent-soft:#E6E9F1;
  --accent-line:#17223E;

  --token-accent:#0D9488;
  --token-accent-soft:#CCFBF1;

  --shadow: 0 1px 2px rgba(26,26,26,.06), 0 8px 24px -12px rgba(26,26,26,.18);
  --radius: 10px;
}

@media (prefers-color-scheme: dark){
  .mword-scope:not([data-theme="light"]) {
    --paper:#14161C;
    --surface:#1B1E26;
    --surface-2:#22262F;
    --ink:#E8E9EC;
    --ink-soft:#A8ACB8;
    --ink-faint:#6B7080;
    --line:#2C3140;
    --line-soft:#262A37;

    --accent:#8EA3D6;
    --accent-ink:#0F1420;
    --accent-soft:#232C42;
    --accent-line:#4A5D8F;

    --token-accent:#5EEAD4;
    --token-accent-soft:#134E4A;

    --shadow: 0 1px 2px rgba(0,0,0,.3), 0 12px 32px -16px rgba(0,0,0,.6);
  }
}
.mword-scope[data-theme="dark"] {
  --paper:#14161C;
  --surface:#1B1E26;
  --surface-2:#22262F;
  --ink:#E8E9EC;
  --ink-soft:#A8ACB8;
  --ink-faint:#6B7080;
  --line:#2C3140;
  --line-soft:#262A37;

  --accent:#8EA3D6;
  --accent-ink:#0F1420;
  --accent-soft:#232C42;
  --accent-line:#4A5D8F;

  --token-accent:#5EEAD4;
  --token-accent-soft:#134E4A;

  --shadow: 0 1px 2px rgba(0,0,0,.3), 0 12px 32px -16px rgba(0,0,0,.6);
}

/* ============================= BASE ============================= */
.mword-scope, .mword-scope * {box-sizing:border-box;}
.mword-scope {
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size:14.5px;
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
}
.mword-scope h1, .mword-scope h2, .mword-scope h3 { font-family:Georgia, "Iowan Old Style", "Palatino Linotype", "Book Antiqua", serif; font-weight:600; margin:0; text-wrap:balance; }
.mword-scope .heb { font-family:'SBL Hebrew', 'Ezra SIL', serif; font-weight:400; font-synthesis:none; }
.mword-scope .mono { font-family:"JetBrains Mono", ui-monospace, monospace; font-variant-numeric:tabular-nums; }
.mword-scope a { color:var(--accent); }

.mword-scope button { font-family:inherit; }
.mword-scope :focus-visible { outline:2px solid var(--accent); outline-offset:2px; border-radius:4px; }

.mword-scope .page { max-width:1180px; margin:0 auto; padding:32px 20px 96px; }

.mword-scope .masthead { margin-bottom:22px; }
.mword-scope .masthead h1 { font-size:22px; margin-top:2px; }

/* ============================= CARD ============================= */
.mword-scope #canvas { display:flex; flex-direction:column; gap:20px; margin-bottom:20px; }
.mword-scope .card {
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:14px;
  box-shadow:var(--shadow);
  overflow:hidden;
}

.mword-scope .card-top {
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  padding:14px 20px;
  border-bottom:1px solid var(--line);
  background:linear-gradient(var(--surface-2), var(--surface));
  flex-wrap:wrap;
}
.mword-scope .card-top .unit-badge { display:flex; align-items:center; gap:9px; }
.mword-scope .unit-badge .dot {
  width:9px; height:9px; border-radius:50%;
  background:var(--accent); box-shadow:0 0 0 3px var(--accent-soft);
}
.mword-scope .unit-badge .name { font-family:Georgia, "Iowan Old Style", "Palatino Linotype", "Book Antiqua", serif; font-weight:600; font-size:15.5px; }
.mword-scope .card-remove {
  appearance:none; border:none; background:none; cursor:pointer;
  color:var(--ink-faint); font-size:12px; text-decoration:underline;
}
.mword-scope .card-remove:hover { color:var(--accent); }

/* Token cards keep the same card chrome as Word cards -- only the badge
   dot/name recolor, so the canvas still reads as one visual family.
   (Dormant here, same as before 2026-09-03: would need an extra element
   carrying literally `card-token` as a DESCENDANT of `.mword-scope`,
   which the Token editor built below doesn't introduce -- its own
   canvas-level "+ Add Token" button, in this page's own blue/sky/
   indigo/green/teal/violet palette, already carries the distinguishing
   color at the point of adding one, so this recolor was judged not
   worth an extra wrapper div just for a differently-colored badge dot.) */
.mword-scope .card-token .unit-badge .dot { background:var(--token-accent); box-shadow:0 0 0 3px var(--token-accent-soft); }
.mword-scope .card-token .unit-badge .name { color:var(--token-accent); }

.mword-scope .lang-strip { display:flex; align-items:center; gap:10px; }
.mword-scope .lang-strip .label { font-size:11.5px; text-transform:uppercase; letter-spacing:.08em; color:var(--ink-faint); font-weight:600; }
.mword-scope .lang-pills { display:flex; gap:6px; }

/* ---- pills (single-select, click active again to clear) ---- */
.mword-scope .pill {
  appearance:none; border:1px solid var(--line); background:var(--surface);
  color:var(--ink); border-radius:999px; padding:5px 13px; font-size:13px;
  cursor:pointer; transition:background .12s, border-color .12s, color .12s, transform .06s;
  white-space:nowrap;
}
.mword-scope .pill:hover { border-color:var(--accent-line); }
.mword-scope .pill:active { transform:scale(.97); }
.mword-scope .pill.is-active { background:var(--accent); border-color:var(--accent); color:var(--accent-ink); font-weight:600; }

/* ============================= TABS ============================= */
.mword-scope .tabbar {
  display:flex; gap:2px; padding:0 20px; border-bottom:1px solid var(--line);
  background:var(--surface); overflow-x:auto;
}
.mword-scope .tabbtn {
  appearance:none; background:none; border:none; cursor:pointer;
  padding:12px 16px 11px; font-size:14px; color:var(--ink-soft);
  border-bottom:3px solid transparent; font-weight:600; white-space:nowrap;
}
.mword-scope .tabbtn:hover { color:var(--ink); }
.mword-scope .tabbtn.is-active { color:var(--accent); border-bottom-color:var(--accent); }
.mword-scope .tabbtn .n {
  display:inline-block; min-width:16px; text-align:center;
  font-family:"JetBrains Mono", monospace; font-size:10.5px; font-weight:500;
  background:var(--accent-soft); color:var(--accent); border-radius:8px; padding:1px 5px; margin-left:6px;
}

/* Fixed body height so the card never resizes when switching tabs or
   Grammar side-menu items -- a scrollbar appears on whichever region
   (the plain tab, or Grammar's own detail pane) doesn't fit instead. */
.mword-scope .tabpanel {
  display:none; height:480px; overflow-y:auto; box-sizing:border-box;
  padding:22px 22px 8px;
}
.mword-scope .tabpanel.is-active { display:block; }
.mword-scope .tabpanel.tabpanel-grammar { padding:0; overflow:hidden; }

/* ============================= FORM FIELDS ============================= */
.mword-scope .field { display:grid; grid-template-columns:1fr; gap:8px; padding:14px 0; border-bottom:1px solid var(--line-soft); }
.mword-scope .field:last-child { border-bottom:none; }
.mword-scope .field-head { display:flex; align-items:baseline; justify-content:space-between; gap:10px; flex-wrap:wrap; }
.mword-scope .field-label { font-weight:600; font-size:13.5px; }

.mword-scope .field-row { display:flex; gap:8px; flex-wrap:wrap; align-items:center; }
.mword-scope .input {
  flex:1 1 220px; min-width:160px;
  border:1px solid var(--line); background:var(--surface);
  border-radius:8px; padding:8px 11px; font-size:14px; color:var(--ink);
}
.mword-scope .input.heb { font-size:17px; direction:rtl; }
.mword-scope .input:focus { border-color:var(--accent); }
.mword-scope .input::placeholder { color:var(--ink-faint); }

.mword-scope .matchtype { display:flex; border:1px solid var(--line); border-radius:8px; overflow:hidden; flex:0 0 auto; }
.mword-scope .matchtype button {
  appearance:none; border:none; background:var(--surface); color:var(--ink-soft);
  font-size:11.5px; padding:8px 10px; cursor:pointer; border-right:1px solid var(--line);
}
.mword-scope .matchtype button:last-child { border-right:none; }
.mword-scope .matchtype button.is-active { background:var(--accent-soft); color:var(--accent); font-weight:700; }

.mword-scope .toggle-row { display:flex; align-items:center; gap:10px; margin-top:2px; }
.mword-scope .switch {
  width:36px; height:21px; border-radius:999px; border:1px solid var(--line);
  background:var(--surface-2); position:relative; cursor:pointer; flex:0 0 auto;
}
.mword-scope .switch::after {
  content:""; position:absolute; top:2px; left:2px; width:15px; height:15px; border-radius:50%;
  background:var(--ink-faint); transition:left .15s, background .15s;
}
.mword-scope .switch.is-on { background:var(--accent-soft); border-color:var(--accent-line); }
.mword-scope .switch.is-on::after { left:17px; background:var(--accent); }
.mword-scope .toggle-text { font-size:13px; color:var(--ink-soft); }
.mword-scope .toggle-text b { color:var(--ink); font-weight:600; }

.mword-scope select.input { flex:0 0 auto; min-width:120px; }

/* ============================= GRAMMAR LAYOUT ============================= */
.mword-scope .grammar-shell { display:grid; grid-template-columns:208px 1fr; gap:0; height:100%; }
.mword-scope .gram-nav {
  border-right:1px solid var(--line); padding:16px 10px; background:var(--surface-2);
  height:100%; overflow-y:auto; box-sizing:border-box;
}
.mword-scope .gram-nav .grp-label {
  font-size:10.5px; text-transform:uppercase; letter-spacing:.08em; color:var(--ink-faint);
  font-weight:700; padding:10px 10px 6px;
}
.mword-scope .gram-nav .nav-item {
  display:flex; align-items:center; justify-content:space-between; gap:8px;
  width:100%; text-align:left; appearance:none; border:none; background:none; cursor:pointer;
  padding:8px 10px; border-radius:7px; font-size:13.5px; color:var(--ink-soft); font-weight:500;
}
.mword-scope .gram-nav .nav-item:hover { background:var(--surface); color:var(--ink); }
.mword-scope .gram-nav .nav-item.is-active { background:var(--accent); color:var(--accent-ink); font-weight:700; }
.mword-scope .gram-nav .nav-item .cnt { font-family:"JetBrains Mono", monospace; font-size:10px; opacity:.75; }

.mword-scope .gram-detail { padding:20px 24px; height:100%; overflow-y:auto; box-sizing:border-box; }
.mword-scope .gram-detail h2 { font-size:16.5px; margin-bottom:2px; }

.mword-scope .gsection { padding:14px 0; border-bottom:1px solid var(--line-soft); }
.mword-scope .gsection:last-child { border-bottom:none; }
.mword-scope .gsection-title { display:flex; align-items:center; gap:8px; font-weight:600; font-size:13px; margin-bottom:10px; }
.mword-scope .pillgroup, .mword-scope .checkgroup { display:flex; flex-wrap:wrap; gap:7px; }

.mword-scope .check {
  display:inline-flex; align-items:center; gap:6px;
  border:1px solid var(--line); border-radius:8px; padding:5px 10px 5px 8px;
  font-size:13px; cursor:pointer; background:var(--surface); user-select:none;
}
.mword-scope .check:hover { border-color:var(--accent-line); }
.mword-scope .check.is-active { background:var(--accent-soft); border-color:var(--accent-line); color:var(--accent); font-weight:600; }
.mword-scope .check .box {
  width:14px; height:14px; border-radius:4px; border:1.5px solid var(--ink-faint); flex:0 0 auto;
  display:flex; align-items:center; justify-content:center;
}
.mword-scope .check.is-active .box { background:var(--accent); border-color:var(--accent); }
.mword-scope .check.is-active .box::after { content:"✓"; color:var(--accent-ink); font-size:10px; line-height:1; }

/* ============================= ACCENTS ============================= */
.mword-scope .accent-groups { display:grid; grid-template-columns:1fr 1fr; gap:22px; }
@media (max-width:720px){ .accent-groups{ grid-template-columns:1fr; } }
.mword-scope .rank-group { padding-bottom:10px; margin-bottom:10px; border-bottom:1px solid var(--line-soft); }
.mword-scope .rank-group:last-child { border-bottom:none; padding-bottom:0; margin-bottom:0; }

/* ============================= SUMMARY BAR ============================= */
.mword-scope .summary { border-top:1px solid var(--line); background:var(--surface-2); padding:12px 20px 14px; }
.mword-scope .summary-head { display:flex; align-items:center; justify-content:flex-end; margin-bottom:8px; }
.mword-scope .summary-clear {
  appearance:none; border:none; background:none; color:var(--ink-faint); font-size:11.5px;
  cursor:pointer; text-decoration:underline;
}
.mword-scope .summary-clear:hover { color:var(--accent); }
.mword-scope .summary-body { font-size:13.5px; line-height:1.7; }
.mword-scope .summary-empty { color:var(--ink-faint); font-style:italic; }
.mword-scope .sf {
  display:inline-flex; align-items:center; gap:6px; background:var(--surface); border:1px solid var(--line);
  border-radius:7px; padding:3px 9px; margin:2px 3px 2px 0; font-size:12.5px;
}
.mword-scope .sf .k { color:var(--ink-faint); }
.mword-scope .sf .v { font-weight:600; }
.mword-scope .sf-x {
  appearance:none; border:none; background:none; cursor:pointer; padding:0 0 0 2px;
  color:var(--ink-faint); font-size:13px; line-height:1; font-family:inherit;
}
.mword-scope .sf-x:hover { color:var(--accent); }
.mword-scope .op { color:var(--accent); font-weight:700; font-size:11.5px; margin:0 2px; }
.mword-scope .op.and { color:var(--ink-faint); }

/* ---- chain strip: same "growing AND/OR/NOT-joined chips" component
   used at both the canvas level (Word/Token objects) and, nested,
   inside a Token card (its own Word cards) -- a plain class rather than
   docs/word_searcher.html's #word-chain id, since this page needs more
   than one instance on screen at once. ---- */
.mword-scope .chain-strip {
  display:flex; flex-wrap:wrap; align-items:center; gap:6px;
  font-size:14.5px; margin-bottom:16px;
}
.mword-scope .chain-strip:has(.wc-word) {
  border:1px solid var(--accent-line); border-radius:var(--radius);
  background:var(--accent-soft); padding:10px 16px;
}
.mword-scope .chain-strip .wc-word { font-weight:600; color:var(--ink); padding:0 1px; }

.mword-scope .wc-op {
  appearance:auto; border:1px solid var(--line); border-radius:7px;
  background:var(--surface); color:var(--accent);
  font-family:inherit; font-size:11px; font-weight:700; letter-spacing:.03em;
  text-transform:uppercase; padding:4px 6px; margin:0 2px; cursor:pointer;
}
.mword-scope .wc-op:hover { border-color:var(--accent-line); }

.mword-scope .wc-paren {
  appearance:none; border:none; background:none; cursor:pointer;
  font-family:Georgia, "Iowan Old Style", "Palatino Linotype", "Book Antiqua", serif;
  font-size:17px; font-weight:700; line-height:1; padding:0 1px;
  color:var(--line); transition:color .12s;
}
.mword-scope .wc-paren:hover { color:var(--ink-faint); }
.mword-scope .wc-paren.is-active { color:var(--accent); }

/* ============================= TOKEN CARD BODY =============================
   Actually in use since 2026-09-03 (Token editor) -- was already sitting
   here dormant since the original word_searcher.html-verbatim CSS port
   for the Mother-Word feature (2026-08-29), same as .card-token above.
   `#canvas` in the selector below is a leftover from docs/token_searcher.
   html's own different page-level architecture (a top-level canvas div
   with that literal id) -- this page's Token editor only ever uses
   [data-role="nested-cards"], never a bare #canvas, but the selector is
   left as-is (harmless dead alternative) rather than touched in an
   unrelated pass. */
.mword-scope .token-fields { padding:22px; }
/* De-paneled 2026-09-03: was its own dashed-border/background box,
   reading as a separate floating panel unlike every other "+ Add X"
   section on this page (Clause's own .children-container/Phrase's own
   .sf-groups-container carry no border/background of their own, just an
   indent) -- now just flows like those, indented the same way via
   [data-role="nested-cards"]'s own padding-left (see the shared
   nesting-indent rule). */
.mword-scope .nested-words {
  margin-top:6px;
}
.mword-scope .nested-words #canvas, .mword-scope .nested-words [data-role="nested-cards"] { display:flex; flex-direction:column; gap:14px; margin-bottom:10px; }
/* 2026-09-03: without this, an EMPTY nested-cards still sat in flow as a
   zero-height flex container carrying a real 10px margin-bottom (the
   rule right above has no :empty variant of its own) -- one of the
   "gaps sometimes disappear" reports, specifically the reverse of that:
   a phantom gap appearing/disappearing inconsistently depending on
   whether this element or its neighbors happened to be empty. Matches
   the shared nesting-indent block's own :empty handling for every other
   children-holding container on this page. */
.mword-scope .nested-words [data-role="nested-cards"]:empty { display:none; margin-bottom:0; }
.mword-scope .nested-words .chain-strip { margin-bottom:10px; }
.mword-scope .nested-words .chain-strip:not(:has(.wc-word)) { margin-bottom:0; }

/* ============================= TOOLBAR / RESULTS ============================= */
.mword-scope .toolbar { display:flex; align-items:center; gap:12px; margin-bottom:24px; flex-wrap:wrap; }
.mword-scope .btn {
  appearance:none; border-radius:9px; font-size:13.5px; font-weight:600; cursor:pointer;
  padding:9px 18px; transition:background .12s, border-color .12s, transform .06s;
}
.mword-scope .btn:active { transform:scale(.98); }
.mword-scope .btn-outline { border:1px dashed var(--line); background:var(--surface); color:var(--ink-soft); }
.mword-scope .btn-outline:hover { border-color:var(--accent-line); color:var(--accent); }
.mword-scope .btn-sm { padding:7px 14px; font-size:12.5px; }
.mword-scope .btn-primary { border:1px solid var(--accent); background:var(--accent); color:var(--accent-ink); }
.mword-scope .btn-primary:hover { background:var(--accent-line); border-color:var(--accent-line); }
.mword-scope .status-text { font-size:12.5px; color:var(--ink-faint); }
.mword-scope .status-error { color:var(--accent); font-weight:600; }

.mword-scope #sf-results {
  max-height:420px; overflow-y:auto; box-sizing:border-box;
}
.mword-scope #sf-results:has(.results-book) {
  border:1px solid var(--line); border-radius:10px; background:var(--surface);
  padding:14px 16px;
}
.mword-scope .results-book { font-size:13.5px; line-height:1.7; margin-bottom:4px; }
.mword-scope .results-book .book-name { font-weight:700; margin-right:.4em; }
.mword-scope .results-book a { text-decoration:none; font-weight:600; }
.mword-scope .results-book a:hover { text-decoration:underline; }
.mword-scope .results-empty, .mword-scope .results-error { color:var(--ink-faint); font-style:italic; font-size:13.5px; }
.mword-scope .results-error { color:var(--accent); font-style:normal; }
