/*
 * Nashville Grotto — components. Reads tokens.css exclusively (never a raw hex).
 * Small, named component set; templates compose these classes. No build step.
 */

/* --- Self-hosted fonts (woff2, latin subset) --- */
@font-face { font-family: 'Oswald'; font-style: normal; font-weight: 500; font-display: swap; src: url("../fonts/Oswald-500.5f49329007f1.woff2") format('woff2'); }
@font-face { font-family: 'Oswald'; font-style: normal; font-weight: 600; font-display: swap; src: url("../fonts/Oswald-600.5f49329007f1.woff2") format('woff2'); }
@font-face { font-family: 'Raleway'; font-style: normal; font-weight: 400; font-display: swap; src: url("../fonts/Raleway-400.e401cae7a129.woff2") format('woff2'); }
@font-face { font-family: 'Raleway'; font-style: normal; font-weight: 600; font-display: swap; src: url("../fonts/Raleway-600.e401cae7a129.woff2") format('woff2'); }
@font-face { font-family: 'Raleway'; font-style: normal; font-weight: 700; font-display: swap; src: url("../fonts/Raleway-700.e401cae7a129.woff2") format('woff2'); }

/* --- Base --- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  min-height: 100vh;
  /* Clip horizontal overflow here (not on individual sections) so full-bleed
     decorative graphics — e.g. the centred CTA-band spikes — can't produce a
     page-wide horizontal scrollbar on narrow screens. */
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
main { flex: 1 0 auto; }
img { max-width: 100%; height: auto; }

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.2;
  color: var(--text);
  text-wrap: balance;
  margin: 0 0 .625rem;
}
p { margin: 0 0 1rem; }
a { color: var(--text); text-decoration: underline; text-underline-offset: .15em; text-decoration-color: var(--ng-red); }
a:hover { color: var(--ng-red); }
:focus-visible { outline: .1875rem solid var(--ng-blue); outline-offset: .125rem; }

/* --- Layout helpers --- */
.wrap { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 1.5rem; }
.stack > * + * { margin-top: 1rem; }
.measure { max-width: 65ch; }
/* Content-page body/intro (story 0016 amendment): unlike `.measure`'s
   65ch reading-column cap, a page's editorial body should fill the
   centred `.wrap` it sits in — capping it at 65ch left it hugging the
   left edge with dead space on the right. No max-width here (fills the
   wrap), just a comfortable reading line-height. */
/* flow-root establishes a block-formatting context so a floated
   .richtext-image.left/.right inside the body is contained here rather
   than bleeding past this section into whatever follows (story 0016
   amendment). */
.content-body { line-height: 1.7; display: flow-root; }

/* --- Type utilities --- */
.display { font-family: var(--font-display); font-weight: 600; font-size: clamp(2.1rem, 5vw, 3rem); line-height: 1.04; letter-spacing: -.01em; }
.lead { font-size: 1.125rem; color: var(--muted); }
.eyebrow { font-family: var(--font-display); font-weight: 600; font-size: .75rem; text-transform: uppercase; letter-spacing: .12em; color: var(--ng-red); margin: 0 0 .5rem; }
.small { font-size: .875rem; }
.muted { color: var(--muted); }

/* --- Masthead ---
 * Single row: badge left, primary nav pushed right, account control at the far
 * right. The badge is oversized and absolutely placed so it overhangs the bar
 * onto the content/hero below (the club's front-page signature); the bar keeps
 * a compact height regardless. z-index keeps the overhang above the hero.
 */
.masthead { background: var(--surface); position: relative; z-index: 3; }
/* The masthead is full-bleed (edge to edge), unlike page content: badge pins to
   the far left, nav to the far right. So it opts out of .wrap's centered column. */
.masthead > .wrap { max-width: none; padding: 0; }
.masthead__top { position: relative; display: flex; align-items: center; gap: 1.5rem; min-height: 84px; padding: 0 2rem 0 172px; }
.masthead__brand { position: absolute; left: 1.25rem; top: .4rem; display: inline-flex; }
.masthead__badge { width: 140px; height: 140px; border-radius: 50%; box-shadow: var(--shadow); }
.masthead__account { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; width: 42px; height: 42px; border-radius: 50%; background: var(--ng-ink); border: .0625rem solid var(--ng-ink); color: var(--ng-white); text-decoration: none; transition: background-color .15s, border-color .15s; }
.masthead__account:hover { background: var(--ng-brown); border-color: var(--ng-brown); color: var(--ng-white); }

/* --- Nav (masthead primary) --- */
.nav { margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: .25rem; padding: 0; }
.nav a { font-family: var(--font-body); font-weight: 600; font-size: .98rem; color: var(--ng-slate); text-decoration: none; padding: .4rem .6rem; border-radius: var(--radius); }
/* Hover/active reveal a dark underline (a gap below the text), matching the
   live site — the text itself stays dark rather than switching to red. */
.nav a:hover, .nav a[aria-current="page"], .nav a.is-active {
  color: var(--ng-ink); text-decoration: underline; text-decoration-color: var(--ng-ink);
  text-decoration-thickness: .14em; text-underline-offset: .34em;
}

/* Visually-hidden but screen-reader-available (accessible icon labels). */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* --- Buttons --- */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: .5rem; font-family: var(--font-display); font-weight: 500; font-size: .95rem; letter-spacing: .02em; padding: .6rem 1.15rem; border-radius: var(--radius); border: .0625rem solid transparent; cursor: pointer; text-decoration: none; transition: background-color .15s, border-color .15s, color .15s; }
.btn:focus-visible { outline: .1875rem solid var(--ng-blue); outline-offset: .125rem; }
.btn-primary { background: var(--ng-blue); color: #fff; border-color: var(--ng-blue); }
.btn-primary:hover { background: var(--ng-blue-ink); border-color: var(--ng-blue-ink); color: #fff; }
.btn-brand { background: var(--ng-red); color: #fff; border-color: var(--ng-red); }
.btn-brand:hover { background: var(--ng-red-ink); border-color: var(--ng-red-ink); color: #fff; }
.btn-outline { background: transparent; color: var(--text); border-color: var(--border); }
.btn-outline:hover { background: var(--surface-2); border-color: var(--muted); color: var(--text); }
.btn-ghost { background: transparent; color: var(--ng-blue); border-color: transparent; text-decoration: underline; text-underline-offset: .2em; }
.btn-ghost:hover { color: var(--ng-blue-ink); }
.btn-row { display: flex; flex-wrap: wrap; gap: .75rem; align-items: center; }

/* --- Forms --- */
.field { display: flex; flex-direction: column; gap: .4rem; max-width: 26rem; }
.field label { font-family: var(--font-display); font-weight: 500; font-size: .85rem; letter-spacing: .02em; }
.field input, .field select, .field textarea { font-family: var(--font-body); font-size: 1rem; padding: .6rem .75rem; border: .0625rem solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); }
.field input:focus, .field select:focus, .field textarea:focus { outline: 0; border-color: var(--ng-blue); box-shadow: 0 0 0 .1875rem rgba(31, 177, 234, .25); }
.field .help { font-size: .8rem; color: var(--muted); }

/* --- Cards --- */
.grid { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.card { background: var(--surface); border: .0625rem solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; display: flex; flex-direction: column; }
.card__cover { height: 7px; }
.card__body { padding: 1.1rem 1.2rem 1.25rem; display: flex; flex-direction: column; gap: .55rem; }
.card__kicker { font-family: var(--font-display); font-weight: 600; font-size: .72rem; text-transform: uppercase; letter-spacing: .1em; color: var(--ng-brown); }
.card h3, .card h4 { margin: 0; }
.card p { font-size: .9rem; color: var(--muted); margin: 0; }

/* --- Pills / tags --- */
.tags { display: flex; flex-wrap: wrap; gap: .4rem; }
.pill { font-family: var(--font-display); font-weight: 500; font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; padding: .2rem .55rem; border-radius: 2rem; color: #fff; background: var(--ng-slate); }
.pill--success { background: var(--ng-success); }
.pill--danger { background: var(--ng-danger); }
.pill--green { background: var(--ng-green); }
.pill--blue { background: var(--ng-blue); }
.pill--brand { background: var(--ng-red); }

/* --- Alerts --- */
.alert { display: flex; gap: .7rem; padding: .85rem 1rem; border-radius: var(--radius); border-left: .25rem solid var(--border); font-size: .92rem; background: var(--surface-2); margin: 0 0 .75rem; }
.alert b { font-family: var(--font-display); font-weight: 600; }
.alert--success { border-left-color: var(--ng-success); }
.alert--danger { border-left-color: var(--ng-danger); }
.alert--info { border-left-color: var(--ng-blue); }

/*
 * --- CTA band: "become a member" (site-wide, above the footer) ---
 * A full-bleed red band with organic "stalactite"/"stalagmite" edges matching
 * the reference site (nashvillegrotto.org), added per the maintainer's AC6
 * interactive-review follow-up (2026-08-19). Each edge is the reference site's
 * spike polygon as an inline SVG (core/_cta_edge.svg), filled via currentColor
 * so the colour comes from a token per edge — --bg (page white, top) and
 * --ng-ink (footer dark, bottom). Like the reference, the SVG is natural-size
 * and centred, and each edge is pulled a few px past the band boundary with a
 * negative margin so its solid base BLEEDS over the seam into the white page
 * (top) / dark footer (bottom): the white/dark above the spikes simply is the
 * page/footer, so there is no seam line — only the spikes cut into the red. The
 * centred graphic can overflow a phone-width band, so horizontal overflow is
 * clipped on <body> (below) rather than the band, keeping the vertical bleed
 * visible (AC4). No new colour value, no <style> block.
 */
.cta-band { background: var(--ng-red); position: relative; }
.cta-band__inner { padding: 3rem 0 3.5rem; text-align: center; }
.cta-band__inner h2 { color: var(--ng-white); margin: 0 0 1.25rem; }
.cta-band__edge { line-height: 0; text-align: center; }
.cta-band__spikes { display: inline-block; width: auto; height: 6rem; }
/* Top: the shape is rotated 180° (both axes, like the reference) so white
   stalactites hang DOWN and the pattern is the horizontal mirror of the bottom;
   the -4px margin bleeds its solid base up into the white page, flush. */
.cta-band__edge--top { color: var(--bg); margin-top: -4px; }
.cta-band__edge--top .cta-band__spikes { transform: scale(-1, -1); }
/* Bottom: dark stalagmites rise UP; the -7px margin bleeds the solid base down
   into the dark footer far enough to leave no seam line. */
.cta-band__edge--bottom { color: var(--ng-ink); margin-bottom: -7px; }

/*
 * --- Footer ---
 * A dark, full-bleed footer with a red top rule — the same brand-red
 * signature used on the masthead (docs/style-guide.md) — approximating the
 * live site's dark-footer-under-a-red-accent look (story 0008 AC6) with
 * existing tokens only, no new colour/spacing values (AC4). Three flexible
 * groups (about+social, link columns, bottom bar) wrap with no fixed
 * widths, so a narrow viewport reflows rather than overflowing (AC4).
 */
.footer { flex-shrink: 0; background: var(--ng-ink); color: var(--ng-mist); font-size: .9rem; }
.footer a { color: var(--ng-mist); text-decoration: none; }
.footer a:hover { color: var(--ng-white); text-decoration: underline; text-decoration-color: var(--ng-red); }
/* Centred four-column layout — three link groups plus the social/logos column —
   in the shared centred content column (.wrap), matching the reference. */
.footer__top { display: flex; flex-wrap: wrap; gap: 2.5rem 3rem; justify-content: space-between; align-items: flex-start; padding-top: 3.25rem; padding-bottom: 3.25rem; }
.footer__col h4 { font-family: var(--font-display); font-weight: 600; font-size: 1.1rem; color: var(--ng-white); margin: 0 0 1rem; }
.footer__col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .6rem; }
.footer__col a { text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--ng-mist) 45%, transparent); text-underline-offset: .18em; }
.footer__right { display: flex; flex-direction: column; align-items: flex-end; gap: 1.25rem; }
/* Coloured circular social icons, matching the reference site's footer. */
.footer__social { display: flex; gap: .6rem; }
.footer__social a { display: inline-flex; align-items: center; justify-content: center; width: 2.25rem; height: 2.25rem; border-radius: 50%; background: var(--ng-brown); color: var(--ng-white); flex: 0 0 auto; transition: background-color .15s; }
.footer__social a:hover { background: var(--ng-red); color: var(--ng-white); }
/* Grotto + NSS logos, right of the columns. Desaturated to a light tone so the
   colourful badge reads cleanly on the dark footer (as the reference does). */
.footer__logos { display: flex; flex-direction: column; gap: .9rem; align-items: flex-end; }
.footer__logos img { display: block; height: auto; filter: grayscale(1) brightness(1.6) contrast(.9); opacity: .8; }
.footer__logos img:hover { filter: none; opacity: 1; }
/* Copyright bar: a separate, slightly-lighter full-bleed strip with centred
   text, matching the reference. */
.footer__bar { background: var(--ng-slate); }
.footer__bar-inner { display: flex; flex-direction: column; align-items: center; gap: .15rem; padding: .9rem 0; text-align: center; color: var(--ng-grey); font-size: .8rem; }
.footer__note { color: var(--ng-grey); font-size: .74rem; }

/* --- Section rhythm (used by content pages & the style guide) --- */
.section { padding: 3rem 0; border-top: .0625rem solid var(--border); }
.section:first-child { border-top: 0; }

/*
 * --- Bands (full-bleed marketing sections) ---
 * A "band" is an edge-to-edge <section> with an inner .wrap for content
 * width, used for full-width marketing rhythm (home page, landing sections).
 * Variants set background/border only; vertical padding lives on .band
 * itself so every band shares one spacing scale instead of repeating it
 * inline per page.
 */
.band { padding: 3.5rem 0; text-align: center; }
.band .measure { margin: 0 auto; }
.band--light { background: var(--surface-2); border-top: .0625rem solid var(--border); }
.band--brand { background: var(--ng-red); }
.band--brand .display,
.band--brand p { color: var(--ng-white); }
.band--brand .measure { opacity: .95; }
.band__actions { justify-content: center; margin-top: 1.5rem; }
.btn-on-brand { background: var(--ng-ink); color: var(--ng-white); border-color: var(--ng-ink); }

/*
 * --- Hero (full-bleed overlaid-image band) ---
 * Fixed viewport-relative min-height so the hero reads as a "top of page"
 * statement regardless of copy length; the image is absolutely positioned
 * behind the content, with a scrim derived from --ng-ink via color-mix so
 * no new colour value is introduced.
 */
.hero { position: relative; min-height: 72vh; display: flex; align-items: center; overflow: hidden; background: var(--ng-ink); }
.hero__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; }
.hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, color-mix(in srgb, var(--ng-ink) 80%, transparent), color-mix(in srgb, var(--ng-ink) 20%, transparent));
}
/* Hero copy sits at the left edge (its own gutter, not the centered content
   column) and vertically centred, per the reference. */
.hero__content { position: relative; max-width: none; margin: 0; padding: 2.5rem; }
.hero__heading { color: var(--ng-white); max-width: 22ch; font-size: clamp(2.5rem, 5.5vw, 3.6rem); text-shadow: 0 .125rem .375rem color-mix(in srgb, var(--ng-ink) 55%, transparent); }
.hero__tagline { color: var(--ng-white); opacity: .92; margin-top: .75rem; }

/*
 * --- Hero banner modifier (story 0014 amendment) ---
 * Content-page header banners (About/Resources/FAQ) are not "top of page"
 * statements like the home hero — they match the reference site's fixed
 * band instead of a viewport-relative one. 25rem/18.75rem are the reference
 * site's own values, not part of the spacing-token scale; 52.5rem is the
 * app's first content breakpoint. Placed after .hero so it overrides the
 * 72vh min-height on elements carrying both classes; the home hero (.hero
 * alone) is unaffected.
 */
.hero--banner { min-height: 25rem; }
@media (max-width: 52.5rem) {
  .hero--banner { min-height: 18.75rem; }
}

/*
 * --- Hero carousel (story 0015, amended to use Splide) ---
 * Only present when `HomePage` has more than one `HeroPanel` (AC4: a single
 * panel renders as the plain `.hero` above, with no Splide markup at all).
 * `.hero-carousel` is the Splide root (see cms/templates/cms/home_page.html
 * and core/static/js/hero-carousel-init.js); Splide's own structural CSS
 * (core/static/css/vendor/splide-core-4.1.4.min.css, loaded in base.html)
 * handles the track/slide layout and transitions. Here we only size each
 * slide to the hero's own 72vh (so a slide looks exactly like today's
 * single hero, AC5) and restyle Splide's pagination dots with the existing
 * `--ng-white` token — no new colour value (AC6).
 */
.hero-carousel { position: relative; }
.hero-carousel .splide__slide { min-height: 72vh; }
/* Overlay the dots centred at the bottom INSIDE the image (like the reference)
   rather than leaving Splide's default pagination in flow below the carousel —
   which also removes the white strip/line that in-flow row produced. */
.hero-carousel .splide__pagination {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 1.5rem;
  z-index: 2;
  padding: 0;
}
.hero-carousel .splide__pagination__page {
  width: .625rem;
  height: .625rem;
  margin: 0 .3rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: color-mix(in srgb, var(--ng-white) 50%, transparent);
  opacity: 1;
  cursor: pointer;
  transition: background-color .2s ease, transform .2s ease;
}
.hero-carousel .splide__pagination__page:hover { background: color-mix(in srgb, var(--ng-white) 75%, transparent); }
.hero-carousel .splide__pagination__page.is-active { background: var(--ng-white); transform: scale(1.25); }

/*
 * --- Gallery carousel (story 0019; interactive-design pass) ---
 * Only present when HomePage's chosen featured_gallery has photos (AC1).
 * `.gallery-carousel` is the Splide root (cms/templates/cms/home_page.html
 * + core/static/js/gallery-carousel-init.js); Splide's own structural CSS
 * (core/static/css/vendor/splide-core-4.1.4.min.css) handles the
 * track/slide layout. The wrapping `.gallery-features` is a plain `.section`
 * with no `.wrap` around the carousel div, so the track spans the full
 * viewport width like `.hero`/`.hero-carousel` above (AC6).
 *
 * Maintainer-directed look: a flush, fixed-700px-tall 3-up strip. Photos crop
 * (object-fit:cover) rather than shrink, so a narrower viewport trims the
 * image instead of letterboxing it. The centre photo (Splide's is-active) is
 * bright; the left/right neighbours are dimmed until hovered.
 */
/* Flush to the band above: drop .section's top padding and its top divider so
   the images meet the About section with no white gap and no grey line. The
   bottom keeps .section's white padding as the backdrop for the dots (matching
   the white section below), and the following section's top divider is removed
   so there is no grey line under the gallery either. */
.gallery-features { padding-top: 0; border-top: 0; }
.gallery-features + .section { border-top: 0; }

.gallery-carousel { position: relative; }

/* Fixed height + crop (photos flush via gap:0 in the init JS). */
.gallery-carousel .splide__slide { position: relative; height: 700px; overflow: hidden; }
.gallery-carousel .splide__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover; /* crop to fill the fixed box; never distort */
  display: block;
}

/* 3-up centre-focus (AC2): dim every slide except Splide's own is-active
   (centre) marker; un-dim on hover of either the slide or its caption (AC10). */
.gallery-carousel .splide__slide:not(.is-active) {
  opacity: .5;
  transition: opacity .2s ease;
}
.gallery-carousel .splide__slide:hover { opacity: 1; }

/* Caption (AC5): present in the DOM for every slide, shown on hover — an
   overlay at the foot of the image using the same ink/white tokens the hero
   scrim and brand bands already use, no new color value. */
.gallery-carousel__caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: .75rem 1rem;
  background: color-mix(in srgb, var(--ng-ink) 65%, transparent);
  color: var(--ng-white);
  opacity: 0;
  transition: opacity .2s ease;
}
.gallery-carousel .splide__slide:hover .gallery-carousel__caption { opacity: 1; }

/* Dot controls (AC3), centred just below the track (not overlaid): circular,
   using the existing ink token; the active dot is filled and slightly larger. */
.gallery-carousel .splide__pagination {
  position: static;
  display: flex;
  justify-content: center;
  gap: .5rem;
  margin: 1rem 0 0;
  padding: 0;
}
.gallery-carousel .splide__pagination__page {
  width: .625rem;
  height: .625rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: color-mix(in srgb, var(--ng-ink) 25%, transparent);
  transition: background .2s ease, transform .2s ease;
}
.gallery-carousel .splide__pagination__page:hover {
  background: color-mix(in srgb, var(--ng-ink) 50%, transparent);
}
.gallery-carousel .splide__pagination__page.is-active {
  background: var(--ng-ink);
  transform: scale(1.25);
}

/* Responsive (AC7): fewer photos per view on the site's existing 52.5rem/30rem
   breakpoints (matching gallery-carousel-init.js's Splide `breakpoints`); the
   fixed 700px height holds and the crop absorbs the narrowing. At 2-up/1-up
   there is no centre neighbour to dim against, so nobody is dimmed. */
@media (max-width: 52.5rem) {
  .gallery-carousel .splide__slide:not(.is-active) { opacity: 1; }
}

/*
 * --- Content-page body: pull-quote + image alignment (story 0016) ---
 * `body|richtext` on ContentPage (and the reused .measure wrapper it renders
 * in — cms/templates/cms/content_page.html) previously had no render rules
 * for the two new features the editor now offers (0016 AC1/AC2): a
 * `<blockquote>` and Wagtail's own `richtext-image left|right|full-width`
 * classes on inline images. A page that uses neither continues to render
 * exactly as before (AC3) — these rules only ever match markup a maintainer
 * has deliberately inserted. Colors/type below reuse existing tokens only,
 * no new values (AC4).
 */
.measure blockquote,
.content-body blockquote,
.pull-quote {
  margin: 1.5rem 0;
  padding: .25rem 0 .25rem 1.25rem;
  border-left: .25rem solid var(--ng-red);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.25rem;
  line-height: 1.35;
  color: var(--text);
}
.measure blockquote p:last-child,
.content-body blockquote p:last-child,
.pull-quote p:last-child { margin-bottom: 0; }

/*
 * --- Structured content blocks (story 0017) ---
 * `content_blocks` on ContentPage — a constrained StreamField, see
 * architecture.md § "Content-page composition — constrained StreamField" —
 * rendered via cms/templates/cms/content_page.html and the block templates
 * in core/templates/blocks/. `.pull-quote` above is the `quote` block
 * (AC4), extending story 0016's existing blockquote rule rather than
 * duplicating its values (AC5: no new color value). The two rules below are
 * the `image_text` (AC2) and `table` (AC3) blocks.
 */

/* `<cite>` attribution under a pull-quote (AC4's "optional attribution");
   layout-only addition, reuses --muted/--font-body, no new color. */
.pull-quote cite {
  display: block;
  margin-top: .5rem;
  font-family: var(--font-body);
  font-weight: 600;
  font-style: normal;
  font-size: .85rem;
  color: var(--muted);
}

/* image_text: single column (image above text) until the same 48rem
   content breakpoint core/static/css/app.css already uses elsewhere (see
   .hero--banner's 52.5rem breakpoint above), then a two-column row. `order`
   places the image on the maintainer-chosen side (AC2); it also applies
   below the breakpoint, which is harmless in a single-column grid — both
   items still span the full row, just in the maintainer's chosen visual
   order. */
.content-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
  align-items: center;
}
.content-row--left .content-row__media { order: 1; }
.content-row--left .content-row__text { order: 2; }
.content-row--right .content-row__media { order: 2; }
.content-row--right .content-row__text { order: 1; }
.content-row__img { display: block; width: 100%; height: auto; border-radius: var(--radius); }
/* image_shape "circle" (story 0018): a bare border-radius:50% on the
   default landscape rendition would clip to an ellipse, not a circle — the
   template pairs this class with a square (fill-700x700) rendition, and
   aspect-ratio/object-fit here is belt-and-suspenders in case a rendition
   ever comes back slightly off-square. No new color/token, layout only. */
.content-row__img--round { border-radius: 50%; aspect-ratio: 1; object-fit: cover; }
@media (min-width: 48rem) {
  .content-row { grid-template-columns: 1fr 1fr; gap: 2rem; }
}

/* table: TableBlock's own default template (wagtail.contrib.table_block)
   already renders a semantic, accessible <table> (AC3) — scope/thead/th are
   its own markup, not ours. This wrapper (applied in
   cms/templates/cms/content_page.html) only adds the horizontal scroll a
   wide table needs on narrow screens (AC5's "responsively"), plus borders
   and header shading from existing tokens. */
.content-table-wrap { overflow-x: auto; }
.content-table-wrap table { width: 100%; border-collapse: collapse; }
.content-table-wrap th,
.content-table-wrap td {
  border: .0625rem solid var(--border);
  padding: .5rem .75rem;
  text-align: left;
  vertical-align: top;
}
.content-table-wrap thead th {
  background: var(--surface-2);
  font-family: var(--font-display);
  font-weight: 600;
}

/* Left/right float the image so body text wraps beside it; the margin gives
   the wrapped text breathing room, and the containing .content-body (a
   `display: flow-root` block-formatting context, above) contains the float
   so it can't bleed past this section into whatever follows. Capped width
   keeps a large source image from swallowing the whole content column on
   narrow viewports. */
.richtext-image.left,
.richtext-image.right {
  max-width: 45%;
  height: auto;
}
.richtext-image.left { float: left; margin: .25rem 1.5rem 1rem 0; }
.richtext-image.right { float: right; margin: .25rem 0 1rem 1.5rem; }
/* Below the .measure column's narrowest practical width, a floated image
   leaves too little room for wrapped text to read — drop the float instead
   of fighting it. */
@media (max-width: 30rem) {
  .richtext-image.left,
  .richtext-image.right {
    float: none;
    max-width: 100%;
    margin: .25rem 0 1rem;
  }
}

/* Full-width spans the content column as its own block, clearing any float
   left running above it. */
.richtext-image.full-width {
  display: block;
  clear: both;
  width: 100%;
  height: auto;
  margin: 1rem 0;
}

/*
 * --- News feed: detail byline/back-link + index card restyle
 *     (story 0006 maintainer design amendment 2026-08-24). Existing tokens
 *     only, no new colour or spacing value. ---
 */
/* Article foot: "Published: <date>" left, "by <author>" right. */
.news-article__byline {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .5rem;
  margin: 0;
}
.news-article__back { justify-content: flex-end; margin-top: 1.5rem; }

/* Dark "Back to News" button — a plain ink variant of the existing .btn family. */
.btn-ink { background: var(--ng-ink); color: var(--ng-white); border-color: var(--ng-ink); }
.btn-ink:hover { background: var(--ng-brown); border-color: var(--ng-brown); color: var(--ng-white); }

/* Index cards: the whole card is the link, so drop the global red
   link-underline from the card text — only the "Keep Reading" affordance
   below stays underlined. */
.news-card, .news-card:hover { text-decoration: none; color: var(--text); }
.news-card__byline { font-style: italic; }
.news-card__more {
  align-self: flex-start;
  margin-top: .35rem;
  font-family: var(--font-display);
  font-weight: 500;
  color: var(--ng-blue);
  text-decoration: underline;
  text-underline-offset: .2em;
}
.news-card:hover .news-card__more { color: var(--ng-blue-ink); }

/* Category badge (maintainer amendment 2026-08-25): a colour-coded tag per
 * news category. `.news-tag--<category value>` matches NewsArticle.Category's
 * DB values (underscored, CSS-safe). Colours reuse brand tokens where they
 * match the reference site; two categories need hues the palette doesn't have
 * (teal / purple), added here as literal values scoped to this component. */
.news-tag {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: .8rem;
  line-height: 1.2;
  padding: .35rem .7rem;
  border-radius: var(--radius);
  color: var(--ng-white);
  background: var(--ng-slate);
  text-decoration: none;
}
.news-tag--grotto_news        { background: var(--ng-green); }
.news-tag--special_news       { background: #0f6f6a; }            /* teal */
.news-tag--other_news         { background: var(--ng-ink); }
.news-tag--trip_announcement  { background: var(--ng-blue); }
.news-tag--event_announcement { background: var(--ng-red-ink); }  /* maroon */
.news-tag--gear_news          { background: var(--ng-brown); }
.news-tag--accidents          { background: var(--ng-red); }
.news-tag--classes_workshops  { background: #5b2a86; }            /* purple */

/* On a card the badge sits at the foot, separated from the excerpt by a rule. */
.news-card__tag { margin-top: .5rem; padding-top: .9rem; border-top: .0625rem solid var(--border); }
.news-tag-row { margin: 0 0 1.25rem; }

@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }
