/* Design tokens — clean white/gray editorial palette. White and gray
   dominate the chrome (header, nav, backgrounds), with a single accent —
   the vivid orange used for links throughout sebastianpress.org (#fc4f00,
   owner request 2026-07-06, replacing an earlier muted brick red) — applied
   to every plain hyperlink by default (not just on hover) plus hover/active
   states and small badges, matching how that reference site itself treats
   its own prose links as orange while chrome elements (nav, breadcrumbs)
   stay neutral and only pick up the accent on hover/active. */
:root {
  /* Grayscale ramp */
  --white: #ffffff;
  --gray-50: #fafafa;
  --gray-100: #f2f2f2;
  --gray-200: #e4e4e4;
  --gray-300: #cfcfcf;
  --gray-400: #a8a8a8;
  --gray-500: #7a7a7a;
  --gray-700: #4a4a4a;
  --gray-800: #2b2b2b;
  --gray-900: #1a1a1a;

  /* Single accent — sebastianpress.org's own link/button orange */
  --accent: #fc4f00;
  --accent-hover: #d64300;

  /* Semantic */
  --bg: var(--white);
  --bg-sunken: var(--gray-50);
  --text: var(--gray-900);
  --text-muted: var(--gray-500);
  --text-faint: var(--gray-400);
  --link: var(--accent);
  --link-hover: var(--accent-hover);
  --border: var(--gray-200);
  --border-strong: var(--gray-300);

  /* Layout */
  --container: clamp(320px, 90vw, 1200px);
  --container-wide: clamp(320px, 95vw, 1400px);
  --pad: clamp(1rem, 4vw, 3rem);

  /* Typography */
  --font-display: 'Playfair Display', serif;
  --font-sans: 'Inter', sans-serif;

  /* Spacing & transitions */
  --transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  --radius: 2px;
}

/* Reset & base styles */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  overscroll-behavior-y: none;
}

/* Disables the elastic rubber-band bounce at the top/bottom of the page —
   a fast trackpad/touch overscroll gesture briefly drags the whole viewport
   (sticky header included) beyond its normal position, which is what let a
   sliver of page-banner content flash into view above the header for an
   instant (owner-reported, 2026-07-06). Removing the bounce itself is more
   reliable than trying to patch whatever specific element happened to be
   visible during it — this is the standard, well-supported way to prevent
   that whole class of overscroll glitch. */
body {
  overscroll-behavior-y: none;
  font-family: var(--font-sans);
  color: var(--text);
  background: var(--bg);
  line-height: 1.6;
  font-size: 16px;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.2;
  color: var(--gray-900);
  margin-bottom: 1rem;
}

h1 {
  font-size: clamp(2rem, 5vw, 3.5rem);
}

h2 {
  font-size: clamp(1.5rem, 3.5vw, 2.5rem);
}

h3 {
  font-size: clamp(1.25rem, 3vw, 1.75rem);
}

p {
  margin-bottom: 1rem;
}

a {
  color: var(--link);
  text-decoration: none;
  transition: color var(--transition);
}

a:hover {
  color: var(--link-hover);
}

/* Layout utilities */
.container {
  width: var(--container);
  max-width: 100%;
  margin: 0 auto;
  padding: 0 1rem;
}

.container-wide {
  width: var(--container-wide);
  max-width: 100%;
  margin: 0 auto;
  padding: 0 1rem;
}

.container--narrow {
  width: min(760px, 90vw);
  max-width: 100%;
  margin: 0 auto;
  padding: 0 1rem;
}

.article-hero {
  padding-top: 1.75rem;
  padding-bottom: 0.5rem;
}

.article-date {
  font-size: 0.85rem;
  color: var(--text-muted);
  margin-top: 0.5rem;
}

/* ============ Header & mega-menu navigation ============ */
.site-header {
  background: var(--white);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 100;
}

.header-container {
  display: flex;
  justify-content: space-between;
  align-items: center;
  max-width: 1400px;
  margin: 0 auto;
  padding: 0.9rem 1.5rem;
}

/* Wordmark: "Serbica" upright/bold, "Americana" italic/lighter — a
   typographic contrast rather than a color one, keeping the logo inside
   the same white/gray-dominant system as the rest of the site (no accent
   color here; --accent is reserved for hover/active states and badges). */
.site-logo a {
  font-family: var(--font-display);
  font-size: 1.5rem;
  color: var(--gray-900);
  text-decoration: none;
  letter-spacing: 0.01em;
  display: inline-flex;
  gap: 0.35rem;
  align-items: baseline;
}

.site-logo a:hover {
  color: var(--gray-900);
}

.logo-serbica {
  font-weight: 700;
}

.logo-americana {
  font-style: italic;
  font-weight: 400;
  color: var(--text-muted);
}

.main-nav {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1;
  justify-content: flex-end;
}

.nav-toggle,
.search-icon-btn {
  background: none;
  border: none;
  font-size: 1.3rem;
  cursor: pointer;
  padding: 0.5rem;
  color: var(--gray-900);
  line-height: 1;
}

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

.nav-toggle {
  display: none;
}

.nav-list {
  display: flex;
  align-items: center;
  list-style: none;
  gap: 0.25rem;
}

.nav-link {
  display: inline-block;
  padding: 0.6rem 0.9rem;
  color: var(--gray-900);
  text-decoration: none;
  font-weight: 500;
  font-size: 0.95rem;
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--font-sans);
  transition: color var(--transition);
}

.nav-link:hover {
  color: var(--accent);
}

/* Chevron indicator for a mobile accordion toggle — hidden on desktop (the
   hover-revealed dropdown there needs no open/closed indicator); rotates
   from pointing right to pointing down once its section is expanded. The
   chevron is a separate button from the section's own label link (which
   navigates to that section's own listing page — e.g. Church has 18
   articles filed directly under it, not in any child category — clicking
   the label must not be swallowed by an expand/collapse toggle). */
.nav-chevron-btn {
  display: none;
}

.nav-chevron {
  display: none;
}

/* Dropdown (desktop mega-menu): CSS-only hover/focus reveal */
.nav-item {
  position: relative;
}

.dropdown {
  display: none;
  position: absolute;
  top: 100%;
  /* Left-anchored (not centered) so a wide dropdown from a nav item near the
     left edge never gets pushed off-screen — every mega-menu item in this
     nav sits left-of-center or mid-nav, so this never overflows the right
     edge in practice either, and max-width/column-width below keep any
     individual dropdown's natural size reasonable regardless. */
  left: 0;
  background: var(--white);
  border: 1px solid var(--border);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.1);
  padding: 1.5rem;
  max-width: 90vw;
  z-index: 200;
}

/* Scoped to desktop widths only (min-width matching the 900px mobile
   breakpoint used everywhere else in this file) — owner-reported bug,
   2026-07-07: with no media-query gate at all, this rule used to apply at
   ANY viewport width, which is exactly why narrowing a desktop browser
   window down to phone size still opened dropdowns on hover instead of via
   the chevron tap. Worse, on a real phone the same unscoped rule outranked
   the mobile .nav-item.is-open .dropdown rule on CSS specificity (three
   selectors vs. two) whenever WebKit's own touch/tap "simulate :hover"
   behavior applied it to the tapped .nav-item ancestor, which is what made
   the chevron arrows look non-functional — the JS toggle class was firing
   correctly, but this higher-specificity hover rule kept overriding it
   regardless of .is-open's own state. Gating on viewport width (not a
   touch/mouse capability check) fixes both reports with one change: below
   900px this rule can no longer match at all, on a resized desktop window
   or a real phone alike, leaving the chevron's is-open toggle as the only
   thing controlling the dropdown there. */
@media (min-width: 901px) {
  .nav-item:hover .dropdown,
  .nav-item:focus-within .dropdown {
    display: block;
  }
}

/* Fixed 2-column grid, filling row-major (item 1, 2 on row 1; 3, 4 on row 2;
   etc., in the same order the items already appear in) rather than a CSS
   multi-column layout's column-major fill (all of column 1 top-to-bottom,
   then column 2) — owner request, 2026-07-06, after the scroll-arrow
   approach tried first didn't work out: "podeli na dve kolone koje se
   broje po redovima 1 i 2, pa 3, 4". No height cap and no scroll/arrow —
   removed entirely along with initDropdownScrollArrows() in nav.js; a
   longer section just runs to as many rows as it needs. */
.dropdown-columns {
  display: grid;
  grid-template-columns: repeat(2, minmax(180px, 1fr));
  gap: 0.1rem 2rem;
  width: max-content;
  max-width: min(70vw, 460px);
}

/* 1-3 total items (children + grandchildren) — a 2-column grid would either
   leave a column empty or stretch a short line across a wide, mostly-empty
   gap (owner-reported with screenshots, 2026-07-06: Church's 2-item
   Dioceses/Video pair, Graveyards' single intro item). See nav.js's
   itemCount calculation for exactly where this threshold is applied. */
.dropdown-columns--single {
  grid-template-columns: 1fr;
  width: auto;
  min-width: 220px;
  max-width: min(70vw, 340px);
}

.dropdown-link,
.dropdown-sublink {
  display: block;
  padding: 0.4rem 0;
  color: var(--gray-800);
  font-size: 0.92rem;
  /* Long category titles ("Sts. Sebastian and Mardarije Orthodox Institute")
     wrap within their own grid column rather than forcing the whole
     dropdown wide enough to fit on one line. */
  white-space: normal;
  line-height: 1.3;
}

.dropdown-link:hover,
.dropdown-sublink:hover {
  color: var(--accent);
}

.dropdown-sublink {
  padding-left: 1rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  position: relative;
}

.dropdown-sublink::before {
  content: "–";
  position: absolute;
  left: 0;
  color: var(--gray-300);
}

/* Mobile nav: hamburger + accordion */
@media (max-width: 900px) {
  .nav-toggle {
    display: block;
  }

  .nav-list {
    /* position:fixed anchored to the JS-measured --header-height custom
       property (breadcrumb.js's updateHeaderHeightVar(), same variable
       .sticky-subheader/.nav-backdrop already use) rather than the earlier
       position:absolute + percentage scheme (top:100%/max-height:calc(100vh
       - 100%) resolved against .site-header, which has no explicit height of
       its own). Switched 2026-07-07 after the percentage version stopped
       reliably opening at all on a real phone once this same fix started
       toggling position:fixed on <body> for the scroll-lock below — with
       body's own positioning scheme changing at the exact moment the menu
       opens, a containing-block-relative percentage is exactly the kind of
       value that can silently resolve differently across browsers/engines.
       Anchoring to a plain, already-measured pixel custom property instead
       removes that ambiguity entirely, matching the same robust pattern
       already used elsewhere in this file.

       This collapses/expands as a true dropdown (max-height + opacity, same
       mechanism as .sticky-subheader elsewhere in this file) rather than
       sliding in from the right via a transform — owner request, 2026-07-07:
       the previous translateX(100%)->translateX(0) side-drawer meant the
       menu's "open" visual state was a horizontal slide from the right, which
       is exactly what a mobile browser's own swipe-left/right history
       navigation gesture would visibly replay (it shows a snapshot of the
       page as it was left, including an open menu, sliding in from the edge)
       if the menu was ever left open at the moment the user tapped a link and
       navigated away. Removing the transform removes that failure mode
       structurally, not just cosmetically — see wireNavLinkClicks() /
       the pageshow listener in nav.js for the JS half of this fix (closing
       the menu before/at navigation and on bfcache restore). */
    position: fixed;
    top: var(--header-height, 64px);
    left: 0;
    right: 0;
    max-height: 0;
    background: var(--white);
    border-top: 0 solid var(--border);
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0;
    overflow-x: hidden;
    overflow-y: auto;
    opacity: 0;
    visibility: hidden;
    transition: max-height var(--transition), opacity var(--transition), padding var(--transition), visibility 0s linear var(--transition);
    z-index: 150;
  }

  .nav-list.mobile-open {
    max-height: calc(100vh - var(--header-height, 64px));
    border-top-width: 1px;
    padding: 0.5rem 0;
    opacity: 1;
    visibility: visible;
    transition: max-height var(--transition), opacity var(--transition), padding var(--transition);
  }

  .nav-link {
    width: 100%;
    text-align: left;
    padding: 0.9rem 1.5rem;
    border-bottom: 1px solid var(--border);
  }

  .dropdown {
    display: none;
    position: static;
    transform: none;
    border: none;
    box-shadow: none;
    padding: 0;
    background: var(--gray-50);
    max-width: 100%;
    min-width: 0;
  }

  .nav-item.is-open .dropdown {
    display: block;
  }

  .nav-item.is-open .nav-dropdown-toggle {
    color: var(--accent);
  }

  /* .nav-item holds three children on mobile: the label link, the chevron
     button, and the dropdown panel. flex-wrap lets the first two share a
     row (chevron sized to content, label filling the rest) while the
     dropdown — via flex-basis: 100% below — is forced onto its own full-
     width row underneath, since flex-wrap has no room left for it on the
     first line. */
  .nav-item {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    border-bottom: 1px solid var(--border);
  }

  .nav-item .nav-dropdown-toggle {
    flex: 1;
    width: auto;
    border-bottom: none;
  }

  .nav-item .dropdown {
    flex-basis: 100%;
  }

  .nav-chevron-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 3rem;
    height: 3rem;
    background: none;
    border: none;
    cursor: pointer;
  }

  .nav-chevron {
    display: inline-block;
    transition: transform var(--transition);
  }

  .nav-chevron::before {
    content: "\276F";
    font-size: 0.75rem;
    color: var(--gray-500);
  }

  .nav-item.is-open .nav-chevron {
    transform: rotate(90deg);
  }

  .dropdown-columns {
    display: block;
    width: 100%;
    max-width: 100%;
  }

  /* Visual nesting: level-1 (.nav-link) sits at 1.5rem; each level indents
     further so a submenu item is unmistakably "inside" its parent, not just
     visually appended after it. */
  .dropdown-link,
  .dropdown-sublink {
    padding: 0.7rem 0;
    padding-left: 2.25rem;
    white-space: normal;
    border-bottom: 1px solid var(--border);
  }

  .dropdown-sublink {
    padding-left: 3.25rem;
  }

  /* The desktop dash marker (::before, absolutely positioned against the
     link's own border-box) would sit at the link's un-indented left edge,
     not near its now-indented text — the padding-left steps above already
     communicate the nesting clearly, so just drop the marker here. */
  .dropdown-sublink::before {
    content: none;
  }

  /* Opaque backdrop, created/toggled by nav.js's setMobileNavOpen() — sits
     directly under the header (top: var(--header-height), the same
     JS-measured value the sticky breadcrumb uses, not a guessed pixel
     offset) and covers everything below it down to the bottom of the
     viewport, so the real page content underneath can never show through
     the gap below a shorter menu, and (combined with that function's own
     scroll-lock) can't be seen moving if someone tries to scroll it while
     the menu is open.

     z-index must stay BELOW .site-header's own (100), not just below
     .nav-list's (150) — found via a real headless-browser check, 2026-07-07,
     after the menu rendered as a totally blank white screen on a real phone.
     .nav-list is a DOM descendant of .site-header (position:sticky,
     z-index:100), so its own z-index:150 is only compared against its
     header-internal siblings — it can never locally out-rank an element
     that isn't also inside that same stacking context. This backdrop is
     appended directly to <body> (a sibling of .site-header, not a child of
     it — see ensureNavBackdrop() in nav.js), so at the document's root
     stacking level it was competing directly against .site-header's own
     z-index:100, not against .nav-list's. At z-index:140 it was winning
     that comparison and painting over .site-header's entire subtree —
     including .nav-list nested inside it — regardless of nav-list's own
     "higher" number. Dropping this below 100 lets .site-header's whole
     stacking context (menu included) render above it again; it only ever
     needed to be above ordinary page content, which has no z-index of its
     own (auto, effectively 0), so any value under 100 works. */
  .nav-backdrop {
    display: none;
    position: fixed;
    top: var(--header-height, 64px);
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--white);
    z-index: 90;
  }

  .nav-backdrop.is-visible {
    display: block;
  }
}

/* Hero section */
.hero {
  background: var(--gray-50);
  padding: clamp(3rem, 10vw, 5rem) 0;
  text-align: center;
}

/* Emblem (circular seal), centered above the text heading — homepage only,
   per owner request 2026-07-06. Has its white background keyed to
   transparent (see images/branding/) so it sits directly on the hero's own
   background with no visible box edge. The wordmark banner that originally
   sat beside it was removed per owner request (2026-07-06 follow-up) — the
   emblem alone, larger, reads more cleanly than the pair side by side. */
.hero-emblem {
  display: block;
  height: clamp(120px, 16vw, 200px);
  width: auto;
  margin: 0 auto 1.75rem;
}

.hero h1 {
  margin-bottom: 1rem;
}

.hero p {
  font-size: clamp(1rem, 2vw, 1.25rem);
  color: var(--text-muted);
}

/* Wide hero photo (2026-07-06 — srb-usa.jpg, Serbian/American flags).
   `width: 100%` with `height: auto` (no aspect-ratio/object-fit forcing a
   box the image then has to be cropped to fit) so the rendered box always
   exactly matches the photo's own ratio — the whole image is always fully
   visible, at any screen width, with nothing ever cropped off. */
.hero-image {
  display: block;
  width: 100%;
  height: auto;
  margin-top: 2.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

/* Three historical quotes from the old site's header (Tesla/Roosevelt/
   Wilson), integrated onto the new homepage per owner request 2026-07-06.
   Stacked (not a multi-column grid) and sized/spaced like the rest of the
   page's running text, so each quote reads as a normal paragraph within the
   container rather than a differently-formatted block that can overflow it
   (an earlier 3-column-grid version broke out of the container because the
   grid was mistakenly applied one level too high, to .quotes itself rather
   than its .container child, leaving only one real grid item to size). */
.quote {
  max-width: 700px;
  margin: 0 auto clamp(1.5rem, 4vw, 2rem);
  padding-left: 1.2rem;
  border-left: 3px solid var(--accent);
}

.quote:last-child {
  margin-bottom: 0;
}

.quote p {
  font-size: 1rem;
  font-style: italic;
  line-height: 1.7;
  color: var(--gray-800);
  margin-bottom: 0.5rem;
}

.quote cite {
  display: block;
  font-style: normal;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-muted);
}

.quote cite::before {
  content: "— ";
}

/* Main content sections */
.site-main > section {
  padding: var(--pad) 0;
}

.site-main > section:nth-child(even) {
  background: var(--gray-50);
}

/* Homepage section headings (Latest US News/Serbia News, Featured Articles) */
.featured-news h2,
.featured-articles h2 {
  color: var(--gray-900);
  margin-bottom: 2rem;
  text-align: center;
}

/* Footer */
.site-footer {
  background: var(--gray-50);
  color: var(--text-muted);
  border-top: 1px solid var(--border);
  padding: 3rem 0 1.5rem;
  margin-top: 4rem;
  text-align: center;
}

.site-footer p {
  margin-bottom: 0;
  font-size: 0.9rem;
}

/* Responsive design */
@media (max-width: 768px) {
  h1 {
    font-size: 2rem;
  }

  h2 {
    font-size: 1.5rem;
  }
}


.breadcrumbs {
  display: block;
  font-size: 0.9rem;
  color: var(--text-muted);
  padding-top: 1.25rem;
  margin-bottom: 1rem;
}

.breadcrumbs a {
  color: var(--gray-700);
}

.breadcrumbs a:hover {
  color: var(--accent);
}

.crumb-sep {
  margin: 0 0.5rem;
  color: var(--gray-300);
}

.breadcrumb-current {
  color: var(--gray-700);
}

/* ============ Lead image — the article's first substantial photo, pulled
   out of the body flow and shown framed directly below the title (owner:
   "pocetnu sliku mozes staviti ispod naslova i uokviriti jednim tankim
   pravougaonikom", 2026-07-05). `width: fit-content` makes the thin frame
   hug the image's own (possibly-capped) width rather than always stretching
   to the full text column — a naturally narrower photo gets a snugly-fitted
   frame; a wider one is simply capped at the column width and scales down,
   per the owner's own instruction to respect the image's real dimensions
   rather than force a fixed treatment. Small decorative portraits never
   reach this component at all — see LEAD_IMAGE_MIN_WIDTH in
   extract-joomla.py, they stay inline/floated in the body as before. */
.article-lead-image {
  width: fit-content;
  max-width: 100%;
  margin: 0 auto 2rem;
  border: 1px solid var(--border);
  padding: 0.6rem;
}

.article-lead-image img {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 0 auto;
  cursor: zoom-in;
}

/* Contact page (owner request, 2026-07-06): the source content was just a
   scanned business-card image (images/banners/card.jpg) with no real text
   at all — transcribed into real, styled text instead, both so it's
   actually readable/selectable and so it isn't just a picture of contact
   info. Reuses the same card visual language as the rest of the site
   (thin border, --gray-50 fill) rather than inventing a new component. */
.contact-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--gray-50);
  padding: 1.75rem 2rem;
}

.contact-card-org {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  color: var(--gray-900);
}

.contact-card-label {
  margin-top: 1.5rem;
  color: var(--accent);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

/* Article content — the single shared template every article renders through */
.article-body {
  max-width: 800px;
  margin: 0 auto;
  line-height: 1.8;
}

.article-body p {
  margin-bottom: 1.5rem;
}

/* In-prose links: orange by default (inherited from the generic `a` rule)
   with a hairline underline that only appears on hover — a quieter, more
   elegant affordance than a permanent underline across a body of running
   text (owner request, 2026-07-06). `currentColor` keeps the rule from
   needing to duplicate the accent color if it's ever tuned again. */
.article-body a {
  border-bottom: 1px solid transparent;
  transition: color var(--transition), border-color var(--transition);
}

.article-body a:hover {
  border-bottom-color: currentColor;
}

/* Large, non-floated body images get the same thin-border frame as the lead
   image, centered — matching how the first image on the page is framed
   (owner request, 2026-07-06). Every image that reaches this plain rule
   (i.e. not .float-left/.float-right) is already known to be >=400px wide —
   the resolver only assigns float-left/float-right to images below that
   threshold (see LEAD_IMAGE_MIN_WIDTH in CLAUDE.md) — so this never
   accidentally frames a small inline photo. */
.article-body img {
  display: block;
  box-sizing: border-box;
  max-width: 100%;
  height: auto;
  margin: 2rem auto;
  border: 1px solid var(--border);
  padding: 0.6rem;
  background: var(--white);
  cursor: zoom-in;
}

/* Phoca Gallery photos carry these from extraction (float=left/right in the
   source) — the original site floats a photo beside the text it illustrates
   rather than stacking it full-width above/below the paragraph. Explicitly
   resets the frame (border/padding/background) the base .article-body img
   rule above adds — that treatment is only meant for the large, centered
   images that rule was written for; a small floated portrait must stay
   borderless, or it would silently inherit a frame it was never meant to
   have (the base rule and this one both match plain `.article-body img`,
   so any box-model property left unset here falls through from there). */
.article-body img.float-left,
.article-body img.float-right {
  max-width: min(45%, 320px);
  margin: 0.25rem 0 1rem;
  border: none;
  padding: 0;
  background: none;
}

.article-body img.float-left {
  float: left;
  margin-right: 1.5rem;
}

.article-body img.float-right {
  float: right;
  margin-left: 1.5rem;
}

/* A run of consecutive floated images (photo essays) needs a clearfix so the
   next heading/paragraph doesn't ride up alongside a short trailing image */
.article-body::after {
  content: "";
  display: table;
  clear: both;
}

@media (max-width: 600px) {
  /* Floating a photo beside text only works with enough line length to wrap
     around it; on narrow screens it just squeezes the text into a sliver —
     revert to full-width stacked images below this breakpoint. */
  .article-body img.float-left,
  .article-body img.float-right {
    float: none;
    max-width: 100%;
    margin: 1.5rem 0;
    display: block;
  }
}

/* Inline photo gallery — for a batch of many generic photos with no
   individual editorial placement (e.g. an event photo dump), unlike Phoca
   Gallery photos, which stay inline next to the specific paragraph they
   illustrate (see CLAUDE.md's Images section for why a grid gallery isn't
   the default treatment site-wide). Mechanical grid/sizing pattern ported
   from zizioulas.org's own .inline-gallery component (read-only reference,
   no colors shared). Selectors are scoped under .article-body specifically
   so they win over the plain .article-body img rule above regardless of
   source order in this file — same reasoning as .float-left/.float-right's
   own reset above. Every img here still matches the generic ".article-body
   img" selector article.js's buildLightboxGroup() queries, so clicking any
   thumbnail opens the same site-wide lightbox with no JS changes needed. */
.article-body .inline-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 0.6rem;
  margin: 2rem 0;
}

.article-body .inline-gallery img {
  display: block;
  width: 100%;
  height: 150px;
  object-fit: contain;
  margin: 0;
  padding: 0.3rem;
  border: 1px solid var(--border);
  background: var(--gray-50);
  cursor: zoom-in;
}

@media (max-width: 480px) {
  .article-body .inline-gallery {
    grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  }
}

.article-body blockquote {
  border-left: 3px solid var(--gray-900);
  padding-left: 1.5rem;
  margin: 2rem 0;
  font-style: italic;
  color: var(--text-muted);
}

.article-body hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 2.5rem 0;
}

.article-body h4 {
  margin-top: 2rem;
}

/* ============ Lightbox — fixed modal, topbar (counter/caption/close/prev/
   next) always above/beside the image area, never overlaid on top of it.
   Structure ported from zizioulas.org's Lightbox.astro/lightbox.ts (dark
   modal chrome is a near-universal convention for image viewers, kept
   independent of the site's white/gray brand palette used everywhere else).

   The .lightbox-body / .lightbox-inner flex chain below is what actually
   fixes a real bug found in testing: a tall portrait image was rendering
   cropped because .lightbox-img's `max-height: 100%` has no definite height
   to resolve against unless an ancestor in the chain has one. `flex: 1;
   min-height: 0;` on .lightbox-body (inside a `flex-direction: column`
   .lightbox with a fixed-size topbar) gives it a real computed height, which
   .lightbox-inner (100% height, centered flex) then hands down to the img. */
.lightbox {
  display: none;
  position: fixed;
  inset: 0;
  min-height: 100dvh;
  z-index: 1000;
  background: #111113;
  flex-direction: column;
  overscroll-behavior: none;
}

.lightbox.active {
  display: flex;
}

.lightbox-topbar {
  display: grid;
  grid-template-columns: minmax(3.5rem, auto) minmax(0, 1fr) minmax(3.5rem, auto);
  grid-template-rows: auto auto;
  align-items: center;
  gap: 0.3rem 0.5rem;
  padding: 0.5rem 1rem;
  flex-shrink: 0;
}

.lightbox-counter {
  grid-column: 1;
  grid-row: 1;
  font-size: 0.82rem;
  color: var(--white);
  opacity: 0.7;
  justify-self: start;
}

.lightbox-close {
  grid-column: 3;
  grid-row: 1;
  justify-self: end;
  background: none;
  border: none;
  color: var(--white);
  font-size: 1.3rem;
  opacity: 0.85;
  cursor: pointer;
  padding: 0.5rem;
}

.lightbox-close:hover {
  opacity: 1;
  color: var(--accent);
}

.lightbox-info {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
  text-align: center;
  color: var(--white);
  min-width: 0;
}

.lightbox-info[hidden] {
  display: none;
}

.lightbox-info p {
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.8);
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lightbox-prev,
.lightbox-next {
  grid-row: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border: none;
  background: transparent;
  cursor: pointer;
}

.lightbox-prev {
  grid-column: 1;
  justify-self: start;
}

.lightbox-next {
  grid-column: 3;
  justify-self: end;
}

/* Solid triangles via the classic border trick, not a font glyph/icon image —
   reads as pure UI chrome, visually unrelated to the photo underneath. */
.lightbox-prev::after,
.lightbox-next::after {
  content: "";
  width: 0;
  height: 0;
  border-top: 0.55rem solid transparent;
  border-bottom: 0.55rem solid transparent;
  transition: border-color var(--transition);
}

.lightbox-prev::after {
  border-right: 0.85rem solid var(--white);
}

.lightbox-next::after {
  border-left: 0.85rem solid var(--white);
}

.lightbox-prev:hover::after {
  border-right-color: var(--accent);
}

.lightbox-next:hover::after {
  border-left-color: var(--accent);
}

.lightbox-body {
  flex: 1;
  min-height: 0;
  overflow: auto;
  overscroll-behavior: contain;
  display: flex;
  justify-content: center;
}

.lightbox-inner {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.lightbox-img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}

/* ============ Page banner + sticky subheader (zizioulas layout pattern,
   ported structurally — no header images per owner's decision, so the
   banner is a flat gray/white block: eyebrow (top-level section) + heading
   (the category's own title). For an article page the heading names the
   ARTICLE's *category*, not the article itself — every article filed under
   one category shares the same banner, giving visual continuity; the
   article's own title is the real <h1> below it. This mirrors zizioulas's
   own page-banner convention exactly (see ArticleLayout.astro/
   ListingLayout.astro), just without a background photograph. ============ */
.page-banner {
  background: var(--gray-50);
  border-bottom: 1px solid var(--border);
  padding: clamp(1.75rem, 5vw, 3rem) 0 2rem;
}

.page-banner-eyebrow {
  margin: 0 0 0.4em;
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.page-banner-heading {
  margin: 0;
}

/* Sticky breadcrumb clone: pinned directly under the site header once the
   real breadcrumb (.breadcrumbs#breadcrumbs, further down the page) scrolls
   out of view — see initStickySubheader() in categories.js/article.js. `top`
   reads a JS-measured custom property (breadcrumb.js's updateHeaderHeightVar)
   rather than a guessed pixel value, so it sits flush under the header at
   any header height (font-stack fallback, zoom, etc.) instead of overlapping
   it — the previous top:0 version was stacking directly underneath/behind
   the header itself once both were "stuck" at the same coordinate. Light
   gray, not the old dark bar, per owner request 2026-07-06. */
/* Closing (this, the base/hidden state) is intentionally near-instant —
   deliberately a *different, much shorter* transition than the opening one
   below, not the same duration in reverse. Owner request 2026-07-06,
   reiterated a second time ("mnooogo brze"): closing still felt sluggish
   after the JS side was already made instant (no debounce — see
   initStickySubheader() in categories.js/article.js), because a CSS
   transition on `.sticky-subheader` was still animating the collapse over
   the same ~0.2-0.25s used for opening. CSS transitions run the duration
   declared on the *destination* state's own rule — removing `is-visible`
   lands back on this rule, so this is genuinely the only duration that
   governs closing, independent of `.is-visible`'s own transition below. */
.sticky-subheader {
  position: sticky;
  top: var(--header-height, 64px);
  z-index: 95;
  max-height: 0;
  overflow: hidden;
  background: var(--gray-50);
  border-bottom: 0 solid var(--border);
  opacity: 0;
  transition: max-height 0.05s linear, opacity 0.05s linear, border-bottom-width 0.05s linear;
}

.sticky-subheader.is-visible {
  max-height: 3.25rem;
  opacity: 1;
  border-bottom-width: 1px;
  transition: max-height 0.25s var(--transition), opacity 0.2s var(--transition), border-bottom-width 0.25s var(--transition);
}

/* Reuses .breadcrumbs' own link/separator/current-page colors (same class);
   only the block spacing changes, since this copy lives in a slim fixed-height
   bar rather than sitting in the normal document flow below a page-banner. */
#stickyBreadcrumbs {
  padding-top: 0;
  margin-bottom: 0;
  padding-block: 0.85rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* On a narrow phone screen a deep breadcrumb chain (Home / Culture /
   Literature / Book Reviews) can be wider than the viewport — the desktop
   single-line nowrap+ellipsis treatment above would silently cut part of it
   off. Owner-reported, 2026-07-07: on mobile this must wrap onto as many
   lines as it needs, in full, never truncated — a fixed cap here (an
   earlier version of this rule used max-height: 5.5rem, sized for two
   lines) is exactly the same kind of clipping bug in miniature: it happens
   to fit today's deepest real chain but silently cuts off anything longer,
   which is precisely what the owner's follow-up ("mora biti celovit, bez
   obzira na to koliko redova treba da zauzme" — must be complete regardless
   of how many lines it needs) explicitly rules out. max-height: none here
   removes the cap entirely, so the bar always grows to fit however many
   lines the real breadcrumb needs; the only cost is that the *opening*
   transition can't animate a height change to/from `none` (not an
   interpolatable value), so it snaps open instantly on mobile instead of
   sliding — opacity still fades in over the same duration as before, and
   closing is unaffected (it always collapses back to the animatable 0). */
@media (max-width: 900px) {
  #stickyBreadcrumbs {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    line-height: 1.4;
  }

  .sticky-subheader.is-visible {
    max-height: none;
  }
}

/* Page-number indicator ("Page 2 of 5") — shown only when a category has
   more than one page, top-right corner of the grid, for orientation when
   pagination isn't reflected in a real per-page URL (owner request,
   2026-07-06 — see getPageFromHash()/setPageHash() in categories.js). */
.listing-meta {
  text-align: right;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-muted);
  margin: 1.5rem 0 -0.5rem;
}

/* ============ Listing grid (categories.html) — a responsive card grid,
   no sidebar. Each card shows a thumbnail when the article has one. ============ */
.listing-rows {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(1.25rem, 3vw, 2rem);
  margin: 2rem 0;
}

@media (max-width: 1100px) {
  .listing-rows {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 640px) {
  .listing-rows {
    grid-template-columns: 1fr;
  }
}

/* Homepage "Latest US News"/"Latest Serbia News" (owner request, 2026-07-06):
   always exactly up to 6 cards, so 3 columns x 2 rows divides evenly instead
   of the usual 4-wide grid leaving an awkward half-empty last row. */
.listing-rows--compact {
  grid-template-columns: repeat(3, 1fr);
}

@media (max-width: 1100px) {
  .listing-rows--compact {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 640px) {
  .listing-rows--compact {
    grid-template-columns: 1fr;
  }
}

.listing-row {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  transition: all var(--transition);
  cursor: pointer;
  background: var(--white);
}

.listing-row:hover {
  border-color: var(--accent);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

.listing-row:hover .listing-row-title {
  color: var(--accent);
}

/* No fixed aspect-ratio box: the owner wants every card's photo shown whole,
   at its own real width:height ratio (matching how the same photo renders
   uncropped on its full article page), even though that means card thumbnail
   heights vary across a row rather than lining up into a uniform grid of
   identical boxes (2026-07-06). */
.listing-row-thumb {
  display: flex;
  overflow: hidden;
  background: var(--gray-100);
}

.listing-row-thumb img {
  width: 100%;
  height: auto;
  display: block;
  transition: transform 0.4s var(--transition);
}

.listing-row:hover .listing-row-thumb img {
  transform: scale(1.04);
}

.listing-row-body {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
  padding: 1.1rem 1.25rem 1.4rem;
  flex: 1;
}

.listing-row-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.15rem;
  color: var(--gray-900);
  transition: color var(--transition);
}

.listing-row-date {
  font-size: 0.8rem;
  color: var(--text-muted);
}

.listing-row-excerpt {
  font-size: 0.9rem;
  color: var(--text-muted);
  /* Preserves the real line breaks make_excerpt() now keeps for list-like
     content (a patent's fields, a subtitle+author pair) — a plain <p> would
     otherwise collapse them back into one run-on line despite the text
     itself containing real "\n" characters (owner-reported, 2026-07-06). */
  white-space: pre-line;
}

.listing-row-link {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--accent);
  text-transform: uppercase;
  margin-top: auto;
  padding-top: 0.5rem;
}

.listing-pager {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin-top: clamp(1.75rem, 4vw, 2.75rem);
}

.pager-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 2.25rem;
  height: 2.25rem;
  padding: 0 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  transition: all var(--transition);
}

.pager-btn:hover {
  background: var(--accent);
  color: var(--white);
  border-color: var(--accent);
}

.pager-btn.is-current {
  background: var(--accent);
  color: var(--white);
  border-color: var(--accent);
}

.pager-btn.is-disabled {
  opacity: 0.4;
  pointer-events: none;
}

/* ============ Article pager (prev/next within the same category) ============ */
.article-pager {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
  margin-top: clamp(1.75rem, 4vw, 2.75rem);
}

.article-pager-link {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding: 1rem 1.2rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: all var(--transition);
  color: var(--text);
}

.article-pager-link:hover {
  border-color: var(--accent);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}

.article-pager-link:hover .article-pager-title {
  color: var(--accent);
}

.article-pager-link.is-empty {
  visibility: hidden;
  pointer-events: none;
}

.article-pager-next {
  text-align: right;
  align-items: flex-end;
}

.article-pager-label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.article-pager-title {
  font-family: var(--font-display);
  font-size: 0.98rem;
  color: var(--gray-900);
}

@media (max-width: 640px) {
  .article-pager {
    grid-template-columns: 1fr;
  }
}

/* ============ YouTube embeds + buttons (see scripts/youtube.js) ============ */
.yt-embed {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  margin: 1.2rem 0 1.5rem;
  background: var(--gray-900);
  border-radius: var(--radius);
  overflow: hidden;
  /* youtube.js often inserts this right after a paragraph that follows a
     float-left/right photo — without clearing the float, "width: 100%"
     still resolves against whatever narrow space is left beside the
     floated image, squeezing the video instead of spanning the real text
     column width. */
  clear: both;
}

.yt-embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* A bare <iframe> hand-embedded directly in the source Joomla content
   (not a plain YouTube link that youtube.js converts into .yt-embed above)
   — 61 articles across the dataset carry one of these, each with the
   source's own hardcoded width="555" height="315" HTML attributes. An
   author-stylesheet width/aspect-ratio here overrides those attribute
   values (attributes are UA-stylesheet-level, lowest priority), making
   every one of them responsive/full-width instead of a fixed 555px box
   that didn't fill the reading column (owner-reported, 2026-07-06, on
   /church/patrijarh-irinej-u-zapadnoamerickoj-eparhiji.html — checked
   site-wide afterward rather than fixing only that one page). Excludes
   .yt-embed's own iframe, which is already sized via its parent's
   ratio-box + position:absolute;inset:0 above. */
.article-body iframe:not(.yt-embed iframe) {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  height: auto;
  border: 0;
  border-radius: var(--radius);
  margin: 1.5rem 0;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.75em 1.5em;
  border-radius: var(--radius);
  border: 1px solid var(--gray-900);
  color: var(--gray-900);
  transition: all var(--transition);
}

.btn:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--white);
}

.btn--solid {
  background: var(--gray-900);
  border-color: var(--gray-900);
  color: var(--white);
}

.btn--solid:hover {
  background: var(--accent);
  border-color: var(--accent);
}

.yt-btn {
  display: inline-flex;
  margin: 1rem 0 1.5rem;
}

/* Search overlay */
.search-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(20, 20, 20, 0.7);
  z-index: 1100;
  padding-top: clamp(2rem, 8vh, 6rem);
}

.search-overlay.active {
  display: block;
}

.search-panel {
  background: var(--white);
  max-width: 700px;
  margin: 0 auto;
  border-radius: var(--radius);
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25);
}

.search-header {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1.5rem;
  border-bottom: 1px solid var(--border);
}

.search-header input {
  flex: 1;
  border: none;
  outline: none;
  font-size: 1.1rem;
  font-family: var(--font-sans);
  padding: 0.5rem 0;
}

.search-close {
  background: none;
  border: none;
  font-size: 1.5rem;
  cursor: pointer;
  color: var(--text-muted);
  padding: 0.25rem 0.5rem;
}

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

.search-results {
  overflow-y: auto;
  padding: 0.5rem;
}

.search-status {
  padding: 2rem 1rem;
  text-align: center;
  color: var(--text-muted);
}

.search-result {
  display: block;
  padding: 1rem 1rem;
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--text);
  border-bottom: 1px solid var(--border);
  transition: background var(--transition);
}

.search-result:hover {
  background: var(--gray-50);
}

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

.search-result h4 {
  margin-bottom: 0.25rem;
  font-size: 1.05rem;
  color: var(--gray-900);
}

.search-result-category {
  font-size: 0.8rem;
  color: var(--accent);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin-bottom: 0.25rem;
}

.search-result-excerpt {
  font-size: 0.9rem;
  color: var(--text-muted);
  margin-bottom: 0;
}

@media (max-width: 600px) {
  .search-panel {
    max-width: 100%;
    margin: 0 0.5rem;
    max-height: 85vh;
  }
}
