:root {

  --palette-black: rgb(1,22,30);
  --palette-teal: rgb(18,69,89);
  --palette-blue: rgb(89,131,146);
  --palette-green: rgb(174,195,176);
  --palette-beige: rgb(239,246,224);
  --palette-white: rgb(246,246,246);
  --palette-breaking: linear-gradient(0deg, rgb(255, 171, 0) 0%, rgb(249, 255, 0) 85%);

  --color-bg: var(--palette-white);
  --color-text: var(--palette-black);
  --color-muted: #6b6b6b;
  --color-border: #e2e2e2;
  --color-accent: rgb(10,93,213);
  --font-headline: 'Merriweather', Georgia, 'Times New Roman', serif;
  --font-body: 'Montserrat', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --max-width: 1000px;
  /* Header/footer inner band - wider than the article/showcase reading
     column (--max-width), roughly matching a large news masthead. */
  --max-width-wide: 1150px;
  /* The 24px left/right inset main > * (below) uses everywhere - named so
     the L/XL templates' full-bleed breakouts can cancel out exactly this
     much padding without a hardcoded number silently drifting out of
     sync with it if this ever changes. */
  --gutter: 24px;
}

/* Self-hosted from this template's own fon/ folder (harvested from Google
   Fonts) rather than linked to fonts.googleapis.com, so rendering this
   page never depends on a third-party request. Both fonts below ship as
   single variable files covering their whole weight range - one
   @font-face with a weight range each, not one file per weight the way a
   static font would need. Google's CSS2 API actually serves a variable
   file only when more than one weight is requested in the same query
   (?family=Name:wght@300;400;700;800;900) - asking for just one weight
   gets back a static, single-instance file instead, which is what an
   earlier version of this rule accidentally fetched for Merriweather. */
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/asset/templates/abbey/fon/montserrat-variable.woff2') format('woff2');
}

@font-face {
  font-family: 'Merriweather';
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url('/asset/templates/abbey/fon/merriweather-variable.woff2') format('woff2');
}

* { box-sizing: border-box; }

body {
  font-family: var(--font-body);
  color: var(--color-text);
  background: var(--color-bg);
  margin: 0;
  line-height: 1.5;
  /* General safety net against any accidental horizontal overflow -
     nothing currently relies on it deliberately, cheap to keep. */
  overflow-x: hidden;
  /* Sticky footer: main is the only flex-growing child, so the footer
     always sits at the bottom of the viewport when content is short,
     and below the fold otherwise. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-size: 1.15em;
}

a { color: inherit; }

/* ── Masthead ─────────────────────────────────────────────────── */
header.site-header {
  flex-shrink: 0;
  background: var(--palette-teal);
  color: rgba(255,255,255,1);
}

.site-nav-toggle-checkbox { display: none; }

/* Logo row only - the nav lives in its own .site-nav-bar strip below,
   not in here (see main.php). */
.site-header-inner {
  max-width: var(--max-width-wide);
  margin: 0 auto;
  padding: 16px 24px;
  display: flex;
  align-items: center;
}

/* inline-flex so the anchor's box hugs the image exactly - a plain
   inline <a> around an <img> leaves a few px of baseline gap underneath. */
.site-logo-link { display: inline-flex; }

.site-logo-link img.site-logo { height: 50px; width: auto; margin: 0.5em 0; }

/* Hidden on desktop (shown again in the small-screens block below) - the
   checkbox it's paired with lives at the body level, not nested in here,
   purely so the CSS below can reach .site-nav-bar as a sibling; this
   label only needs the matching `for` id to still work. */
.site-nav-toggle-btn {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 26px;
  height: 20px;
  margin-left: auto;
  cursor: pointer;
}

.site-nav-toggle-btn span {
  display: block;
  height: 2px;
  width: 100%;
  background: rgba(255, 255, 255, 1);
}

/* Its own strip, separate from the header band above it - a flat solid
   rather than the header's gradient, and much slimmer padding, so it
   reads as a distinct nav bar sitting under the masthead rather than
   more of the masthead itself. */
.site-nav-bar {
  flex-shrink: 0;
  background: var(--palette-blue);
  color: rgba(255, 255, 255, 1);
}

.site-nav-inner {
  max-width: var(--max-width-wide);
  margin: 0 auto;
  padding: 0 24px;
  /* flex + margin-left:auto on .site-search-toggle-btn below is what
     pushes the search icon to the right edge, same technique
     .site-nav-toggle-btn already uses within .site-header-inner. */
  display: flex;
  align-items: center;
}

.site-nav-list {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  padding: 0;
  margin: 0 0 0 -0.6em;
}

/* Anchor point for a dropdown (.site-nav-sublist, absolutely positioned
   against this) - every item gets it, not just ones with children, so
   nesting/un-nesting an item in the admin never needs a matching CSS change. */
.site-nav-item { position: relative; }

.site-nav-link {
  display: inline-block;
  text-decoration: none;
  font-size: 1.15em;
  font-weight: 300;
  letter-spacing: 0;
  padding: 0.1em 0.4em 0.2em 0.4em;
  border-top: 4px solid transparent;
}

/* Plain white, not the red accent - that's reserved for in-page content
   (showcase card hover, pagination links), not this chrome. */
.site-nav-link:hover,
.site-nav-item.is-active > .site-nav-link {
  border-top-color: rgba(255, 255, 255, 0.95);
}

/* Dropdown - hidden until its parent .has-children item is hovered/
   focused, so keyboard nav (Tab into a link inside it) reveals it too,
   not just a mouse hover. */
.site-nav-sublist {
  display: none;
  flex-direction: column;
  list-style: none;
  margin: 0;
  padding: 8px 0;
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 180px;
  background: rgb(24, 24, 24);
  border: 1px solid rgba(255, 255, 255, 0.1);
  z-index: 10;
}

.site-nav-item.has-children:hover > .site-nav-sublist,
.site-nav-item.has-children:focus-within > .site-nav-sublist {
  display: flex;
}

.site-nav-sublist .site-nav-link {
  display: block;
  padding: 8px 16px;
  text-transform: none;
  letter-spacing: normal;
  font-weight: 500;
  border-bottom: none;
}

.site-nav-sublist .site-nav-link:hover,
.site-nav-sublist .site-nav-item.is-active > .site-nav-link { background: rgba(255, 255, 255, 0.08); }

/* A third-level dropdown opens to the side of its parent rather than
   further down, so it doesn't stack underneath and cover its own trigger. */
.site-nav-sublist .site-nav-sublist { top: 0; left: 100%; }

/* ── Search ────────────────────────────────────────────────────── */
.site-search-toggle-checkbox { display: none; }

.site-search-toggle-btn {
  display: inline-flex;
  align-items: center;
  margin-left: auto;
  color: rgba(255, 255, 255, 1);
  cursor: pointer;
}

/* display can't be transitioned, hence max-height + overflow:hidden as
   the open/close mechanism - this codebase's first CSS transition (the
   mobile nav toggle this pattern is otherwise copied from is a hard,
   instant display:none/block swap, no animation). Driven off the same
   :checked ~ sibling-selector idiom as .site-nav-toggle-checkbox:checked
   ~ .site-nav-bar further down - see main.php's own comment on why
   site-search-toggle has to be a body-level sibling of this for that to
   reach it. 80px is enough for one input row plus the bar's own padding;
   generous rather than exact since overflow:hidden clips anything past it
   either way. */
.site-search-bar {
  max-height: 0;
  overflow: hidden;
  background: var(--palette-teal);
  transition: max-height 0.25s ease;
}
.site-search-toggle-checkbox:checked ~ .site-search-bar { max-height: 80px; }

.site-search-bar-inner {
  max-width: var(--max-width-wide);
  margin: 0 auto;
  padding: 14px 24px;
}
.site-search-bar-inner form { display: flex; gap: 8px; }

/* Shared with search.php's own results-page form (component-defaults) -
   one input/button style for both places search appears, rather than two
   near-duplicate rule sets. */
.site-search-input {
  flex: 1;
  padding: 8px 12px;
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.95);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 1em;
}
.site-search-submit {
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  background: var(--color-accent);
  color: #fff;
  font-family: var(--font-body);
  font-weight: 600;
  cursor: pointer;
}

/* ── Footer ────────────────────────────────────────────────────── */
footer.site-footer {
  flex-shrink: 0;
  font-size: 0.8em;
  background: rgb(48,48,54);
  color: rgba(255,255,255,1);
}

.site-footer-inner {
  max-width: var(--max-width-wide);
  margin: 0 auto;
  padding: 20px 24px;
}

/* Four column slots (col 2 is intentionally left empty - no menu is wired
   up behind it). Empty grid cells just collapse to whitespace, same as an
   empty renderMenu() call anywhere else. Columns 2-4 are a fixed 22% each;
   the first column takes whatever that frees up (grid resolves the
   percentage tracks first, gaps are already excluded from that math, then
   hands the 1fr track everything left over) rather than a plain equal
   25% split. Column gap is capped at 1em (resolves against this element's
   own 0.8em, inherited from footer.site-footer) - row-gap only ever
   matters once the 800px breakpoint below stacks this into two rows, and
   stays a plain px value since nothing asked it to track the type scale.
*/
.site-footer-columns {
  display: grid;
  grid-template-columns: 1fr 22% 22% 22%;
  gap: 24px 1em;
  padding-bottom: 20px;
}

/* renderMenu()'s default markup (nav.site-nav > ul.site-nav-list > li >
   a) is the same one the horizontal header nav uses - flipped to a
   vertical list here purely with CSS, same idiom as the mobile nav
   override further down. Hover feedback switches from the header's
   top-border tick (reads oddly to the left of a stacked list) to a plain
   underline. */
.site-footer-col .site-nav-list {
  flex-direction: column;
  gap: 6px;
  margin: 0;
}

.site-footer-col .site-nav-link {
  padding: 0;
  border-top: none;
}

.site-footer-col .site-nav-link:hover { text-decoration: underline; }

/* Horizontal strip, immediately above the copyright line - keeps the
   header nav's row layout and hover tick as-is, just smaller and
   separated from the columns above by a hairline. */
.site-footer-base {
  border-top: 1px solid rgba(255, 255, 255, 0.15);
}

.site-footer-base .site-nav-link { border-top: none; }

@media (max-width: 800px) {
  .site-footer-columns { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 560px) {
  .site-footer-columns { grid-template-columns: 1fr; gap: 16px; }
}

/* ── Main content column ─────────────────────────────────────────*/
/* 1em top padding - breathing room between the nav bar and every page's
   own content, on the wrapper every page kind already shares, rather than
   each new component (category, article, ...) needing its own one-off
   top margin to avoid sitting flush against the nav bar. */
main {
  flex: 1 0 auto;
  padding: 1em 0 60px;
}

/* Every direct child of <main> gets the same width/centering/edge-padding
   recipe .site-header-inner/.site-nav-inner/.site-footer-inner already
   use (var(--max-width-wide) + auto margins + var(--gutter) padding), so
   an article's or category listing's content lines up with the
   masthead's own left/right edges - EXCEPT .showcase, reset back to
   genuinely full-width immediately below (matching main exactly, the
   same "just don't constrain it" approach that already makes header/nav-
   bar/footer reliably full-width - see .showcase-section further down
   for why the constraint moves to a level below .showcase itself instead
   of being dropped here).

   Plain `main > *`, not `main > *:not(.showcase)` - :not()'s argument
   counts toward specificity as if it were written plain (a class
   selector, here), which made the exclusion-selector version of this
   rule MORE specific than a bare element selector like `article {}` or a
   single class like `.category-header {}` - so article's own narrower
   max-width (below) and, before this fix, a category page's title block
   were both silently losing their own overrides back to this rule's
   wider max-width, specificity beating source order regardless of which
   one actually came later in the file. A plain `main > *` has the same
   specificity as those single-selector rules, so normal source-order
   cascade (last one wins) does what was actually intended all along. */
main > * {
  max-width: var(--max-width-wide);
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}

main > .showcase {
  max-width: none;
  margin-left: 0;
  margin-right: 0;
  padding-left: 0;
  padding-right: 0;
}

/* ── Showcase sections ────────────────────────────────────────────*/
/* Each section carries its own width/centering now, not the .showcase
   wrapper (main > .showcase above deliberately resets it back out) -
   because the L/XL templates (below) need to be full-width, and they can
   only actually reach that by having a genuinely unconstrained parent to
   sit inside; nesting them inside a constrained .showcase and then trying
   to calculate a breakout back out past that constraint is exactly what
   caused the last several rounds of trouble (vw vs. the scrollbar,
   container-query units vs. main's own flex sizing, etc.) - all fragile
   workarounds for a self-imposed problem. A normal section keeps the
   masthead-matching width via this rule; L/XL cancel it straight out
   below, the same "just don't constrain it" fix as main itself gets from
   its own parent. */
.showcase-section {
  max-width: var(--max-width-wide);
  margin: 0 auto 40px;
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}

/* Design-authored, admin-supplied markup (see showcase.php) - only
   minimal spacing here, no opinionated typography, since a prefix/suffix
   is expected to bring its own. */
.showcase-section-prefix { margin: 0 0 14px; }
.showcase-section-suffix { margin: 14px 0 0; }

.showcase-breaking {background: var(--palette-breaking);}

/* .showcase-home-trending - one-off full-bleed var(--palette-blue) band
   for the home page's "Trending topics" tags section (set via the
   Builder tab's own CSS class field, not auto-added by a Template the way
   showcase-l/showcase-xl below are). Same break-out as those: cancel
   .showcase-section's own max-width/margin-left/margin-right straight
   back to genuinely full-width, then re-apply that same width+centering
   one level down to the section's actual content (prefix/.showcase-tags/
   suffix - see _renderShowcaseTagsSection(), showcase.php) so it lines up
   with the rest of the page inside the full-bleed band. A bespoke class
   rather than a new Template option since this is one specific section's
   look, not a reusable layout. */
.showcase-home-trending {
  max-width: none;
  margin-left: 0;
  margin-right: 0;
  padding: 32px 0;
  text-align: center;
  background: linear-gradient(132deg, rgba(42, 123, 155, 1) 0%, rgba(87, 199, 133, 1) 50%, rgba(83, 173, 237, 1) 100%);
}

.showcase-home-trending .showcase-section-prefix,
.showcase-home-trending .showcase-tags,
.showcase-home-trending .showcase-section-suffix {
  max-width: var(--max-width-wide);
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}

/* .showcase-tags is itself a flex row (see its own rule further down) -
   text-align: center above only centers inline/block content, so the
   pill row needs its own justify-content to actually center as a group
   rather than just hugging the left edge of its (still full-width, now
   centered-text) container. */
.showcase-home-trending .showcase-tags {
  justify-content: center;
}

/* Overrides the home page's shared title styling (#showcase-home-page
   .showcase-section-prefix, further down) for this one band only - an id
   selector beats any number of classes on specificity alone, so this has
   to repeat that same id rather than just adding a .showcase-home-trending
   rule, or it would silently lose to the shared rule regardless of source
   order. Shared font-size/line-height/font-weight/::after still apply;
   only what actually needs to differ against a coloured band - no
   uppercase, white with a subtle shadow for legibility over the gradient's
   lighter end - is repeated here. */
#showcase-home-page .showcase-home-trending .showcase-section-prefix {
  text-transform: none;
  color: var(--palette-white);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}

/* "L" template (Template dropdown, Builder tab) - its markup lives in
   components/section-templates/l.php, but its styling lives here with
   everything else; showcase.php auto-adds a "showcase-l" class to any
   section using that template, giving these rules a stable selector
   regardless of what's (or isn't) in the section's own css_class field.
   Full-bleed width, edge to edge (no background of its own - combine
   with a utility class like showcase-breaking, below, in the section's
   own CSS class field for one), while its content stays lined up with
   everything else. This section lives inside .showcase, and every
   .showcase-section (above) already gets its own width/centering
   directly (var(--max-width-wide), centered, var(--gutter) padding)
   specifically so .showcase itself is never constrained in the first
   place. That means this template doesn't have to calculate a breakout
   past anything (earlier attempts at exactly that - width:100vw or
   100cqw plus a negative margin - kept hitting new edge cases: vw
   doesn't subtract the scrollbar's width on most desktop browsers; cqw
   needs a query container, and main establishing one fought its own flex
   sizing). It just cancels the same three properties it inherits from
   .showcase-section (max-width/margin-left/margin-right) back to nothing
   - the same "just don't constrain it" fix main itself gets from body. */
.showcase-l {
  max-width: none;
  margin-left: 0;
  margin-right: 0;
  padding: 32px 0;
  color: var(--palette-black);
}

.showcase-l .showcase-section-prefix,
.showcase-l .showcase-grid,
.showcase-l .showcase-section-suffix {
  max-width: var(--max-width-wide);
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}

/* L defaults to the page's dark body colour (var(--palette-black), set
   above) - only illegible when combined with an accent-coloured band
   like .showcase-breaking (via the section's own css_class field), so
   the white override - plus a lighter placeholder tint for images
   without one, matching the masthead's own image-less-card treatment
   logic - only applies then, not unconditionally. */
.showcase-l.showcase-breaking,
.showcase-l.showcase-breaking .showcase-card-link {
  color: rgba(255, 255, 255, 1);
}

.showcase-l.showcase-breaking .showcase-card img { background: rgba(255, 255, 255, 0.12); }

/* var(--color-muted) (the meta line's default colour) is a dark grey -
   low-contrast on the same accent band the two rules above exist to fix,
   so it gets its own translucent-white override alongside them. */
.showcase-l.showcase-breaking .showcase-card-meta { color: rgba(255, 255, 255, 0.75); }

/* Hero layout, not the standard repeating card grid - one column (so a
   second curated item stacks below rather than squeezing in beside the
   first), each card laid out image-beside-headline instead of
   image-above-headline. .showcase-card-link only exists when the item
   actually has a link (see item-card.php) - .showcase-card itself is
   targeted too so an unlinked item (no resolvable URL yet) still gets the
   same side-by-side treatment instead of silently falling back to the
   default stacked layout. */
/* minmax(0, 1fr), not bare 1fr - a bare 1fr track is implicitly
   minmax(auto, 1fr), and "auto" resolves to the item's max-content size.
   The card's image has flex-shrink: 0 (never shrinks below its 50%
   basis), so paired with a long headline its automatic minimum width
   can exceed the track's real width and blow the card out past the
   grid's edge instead of shrinking to fit. */
.showcase-l .showcase-grid { grid-template-columns: minmax(0, 1fr); gap: 28px; }

.showcase-l .showcase-card,
.showcase-l .showcase-card-link {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 28px;
}

/* When the item has a link, .showcase-card-link (not .showcase-card) is
   the one actually holding the image+headline row - but as a flex ITEM
   of .showcase-card's own row, it defaults to hugging its content's
   width rather than filling the card, which left the nested img's
   flex-basis: 50% (below) resolving against that too-small width instead
   of the card's real width - min-width: 0 overrides the default
   flex-item min-width: auto, which otherwise refuses to shrink narrower
   than the image's intrinsic size and would reintroduce the same
   too-wide-image symptom this whole rule exists to fix. */
.showcase-l .showcase-card-link {
  flex: 1;
  min-width: 0;
}

.showcase-l .showcase-card img {
  /* flex-basis (the box's width) and aspect-ratio move together on
     purpose: growing the box wider at the same ratio would grow its
     height too, so the ratio widens by the same proportion (40% @ 4/3 ->
     50% @ 5/3, i.e. x1.25 on both) specifically to cancel that out and
     keep the rendered height where it was. */
  flex: 0 0 50%;
  min-width: 0;
  width: auto;
  /* Overrides the "first section's lead card" hero rule further down
     (21/9, meant for a full-width image above a headline, and explicitly
     excludes .showcase-l/.showcase-xl via :not() so it can't silently
     outrank this on specificity) - at half the row's width beside the
     headline rather than above it, that ratio reads as an odd thin
     strip; object-fit: cover (base .showcase-card img rule) does the
     actual re-cropping to this ratio. */
  aspect-ratio: 5 / 3;
}

/* .showcase-card-body (title + meta) takes over h3's old flex-item role
   here - it, not h3 alone, is what sits beside the image now that a
   .showcase-card-meta line lives underneath the headline (see item-card.php). */
.showcase-l .showcase-card-body {
  flex: 1;
  min-width: 0;
}

.showcase-l .showcase-card h3 {
  margin: 0;
  font-size: 1.9em;
  line-height: 1.25;
}

/* "XL" template - components/section-templates/xl.php, auto-tagged
   "showcase-xl" the same way L is tagged "showcase-l" (see above). The
   straight Major -> XL rename: same full-bleed width breakout and same
   image-beside-headline row layout as L, just bigger (larger headline
   type, wider image column) to match Major's original proportions. */
.showcase-xl {
  max-width: none;
  margin-left: 0;
  margin-right: 0;
  padding: 32px 0;
  color: var(--palette-black);
}

.showcase-xl .showcase-section-prefix,
.showcase-xl .showcase-grid,
.showcase-xl .showcase-section-suffix {
  max-width: var(--max-width-wide);
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}

/* Same conditional white-on-accent-band override as L, above - XL
   defaults to var(--palette-black) (set above) too. */
.showcase-xl.showcase-breaking,
.showcase-xl.showcase-breaking .showcase-card-link {
  color: rgba(255, 255, 255, 1);
}

.showcase-xl.showcase-breaking .showcase-card img { background: rgba(255, 255, 255, 0.12); }
.showcase-xl.showcase-breaking .showcase-card-meta { color: rgba(255, 255, 255, 0.75); }

/* Hero layout, same shape as L (image-beside-headline, single column so
   a second curated item stacks below) - see .showcase-l above for why
   .showcase-card-link is targeted alongside .showcase-card. XL's own
   proportions run bigger throughout: wider gap, a larger image column,
   and (below) larger headline type. */
/* minmax(0, 1fr), not bare 1fr - same grid-blowout fix as L, above. */
.showcase-xl .showcase-grid { grid-template-columns: minmax(0, 1fr); gap: 32px; }

.showcase-xl .showcase-card,
.showcase-xl .showcase-card-link {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 32px;
}

.showcase-xl .showcase-card-link {
  flex: 1;
  min-width: 0;
}

.showcase-xl .showcase-card img {
  /* Wider than L's 50%/5:3 (see .showcase-l .showcase-card img above for
     why flex-basis and aspect-ratio move together) - same idea, just
     scaled up to match XL's grander proportions. */
  flex: 0 0 55%;
  min-width: 0;
  width: auto;
  aspect-ratio: 3 / 2;
}

/* .showcase-card-body (title + meta) takes over h3's old flex-item role
   here - same reasoning as .showcase-l .showcase-card-body above. */
.showcase-xl .showcase-card-body {
  flex: 1;
  min-width: 0;
}

.showcase-xl .showcase-card h3 {
  margin: 0;
  font-size: 2.4em;
  line-height: 1.2;
}

.showcase-xl .showcase-card-link:hover h3 { text-decoration: underline; }

.showcase-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 24px 20px;
}

.showcase-card { display: flex; flex-direction: column; }

.showcase-card img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  display: block;
  background: #f0f0f0;
}

.showcase-card h3 {
  font-family: var(--font-headline);
  font-size: 1.15em;
  line-height: 1.3;
  font-weight: 800;
  margin: 10px 0 0;
}

/* Same structure-only/no-colour base as .article-flags/.article-flag (see
   that comment) - sits above the headline in the card's own text block,
   ahead of h3's own margin-top so a flagged card doesn't get extra top
   spacing on top of this row's own. Square corners throughout (no
   border-radius anywhere in this file's flag rules) - this title's own
   look, not a default worth assuming for every template. */
.showcase-card-flags { display: flex; flex-wrap: wrap; gap: 4px; }
.showcase-card-flag {
  display: inline-block;
  padding: 2px 7px;
  font-size: 0.7em;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  background: var(--palette-blue);
  color: #fff;
}
.showcase-card-flags + h3 { margin-top: 6px; }

/* "Breaking News" example (flagkey breaking-news) - black badge / yellow
   text, same var(--palette-breaking) gradient .showcase-breaking already
   uses site-wide. The parent modifier targets .showcase-card-body
   specifically, not the whole .showcase-card - that's what keeps the
   image itself from getting tinted too in the standard grid (M/S, image
   above text, no flex gap between the two to worry about). Add a
   --{flagkey} pair like this for every flag that needs its own look;
   anything without one keeps the neutral default above. */
.showcase-card-flag--breaking-news { background: var(--palette-black); color: rgb(249, 255, 0); }
.showcase-card--flag-breaking-news .showcase-card-body {
  background: var(--palette-breaking);
  padding: 14px;
}

/* L/XL hero only, below - the standard grid above needs none of this. */

/* The badge sits directly against the headline otherwise (h3's own
   margin-top is 0 in L/XL - see .showcase-l/.showcase-xl .showcase-card h3
   further down, which wins on specificity over .showcase-card-flags + h3
   above regardless of source order) - this gives it real breathing room
   instead. */
.showcase-l .showcase-card-flags,
.showcase-xl .showcase-card-flags {
  margin-bottom: 1em;
}

/* L/XL's row layout has a gap (28px, see .showcase-l/.showcase-xl
   .showcase-card below) between the image and .showcase-card-body - color
   on .showcase-card-body alone (above) doesn't reach into that gap, so it
   showed as an undesired band of plain page background between the image
   and the coloured text panel. Colouring .showcase-card itself instead
   (its own flex container, gap included) fills that band too - the image
   sits opaquely on top of its own half so it's never actually visible
   through it, only the gap and the text panel are. */
.showcase-l .showcase-card--flag-breaking-news,
.showcase-xl .showcase-card--flag-breaking-news {
  background: var(--palette-breaking);
}
/* L/XL's row layout centers .showcase-card-body on its content height
   (align-items: center, above) rather than stretching it - fine for plain
   text, but it means the coloured panel would only wrap the text snugly
   instead of filling the row edge-to-edge like the image beside it.
   align-items: stretch (scoped to the flagged state only, so an unflagged
   hero keeps its original centered text) plus justify-content: center on
   the now-taller body re-centers the text within it, giving the
   full-height panel the mockup shows. */
.showcase-l .showcase-card--flag-breaking-news,
.showcase-l .showcase-card--flag-breaking-news .showcase-card-link,
.showcase-xl .showcase-card--flag-breaking-news,
.showcase-xl .showcase-card--flag-breaking-news .showcase-card-link {
  align-items: stretch;
}
.showcase-l .showcase-card--flag-breaking-news .showcase-card-body,
.showcase-xl .showcase-card--flag-breaking-news .showcase-card-body {
  justify-content: center;
  padding: 24px;
}

/* Wraps title + meta together (see item-card.php) - lets L/XL's flex row
   put the whole text block beside the image as one flex item instead of
   the image row splitting three ways once a meta line exists alongside
   the headline (see .showcase-l/.showcase-xl .showcase-card-body above).
   In the default column layout (M/S) it's also what makes the meta line
   cling to the bottom of the card: a flex column of its own, so
   .showcase-card-meta's margin-top: auto (below) has somewhere to push
   against - title stays put, meta eats whatever space a shorter title
   (one line vs. two) leaves below it, so every card in a row lines its
   meta up at the same height regardless of title length. flex: 1 is what
   gives it that spare space to push into in the first place - see
   .showcase-card-link below for why it's there to hand down. */
.showcase-card-body { display: flex; flex-direction: column; flex: 1; min-width: 0; }

/* Category · time, same secondary-text treatment as the article byline
   (.byline, below) - one line, small, muted. The category/time separator
   is a ::after on category rather than a static string between two spans,
   so it disappears on its own when time is absent (:not(:last-child)) -
   an item can have a category with no resolvable time, or vice versa.
   margin-top: auto (not a fixed value) is what clings this to the bottom
   of .showcase-card-body's flex column - padding-top is a floor under
   that, so title and meta never actually touch even when there's no
   spare height to push into (a two-line title filling the card). */
.showcase-card-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: auto;
  padding-top: 6px;
  color: var(--color-muted);
  font-size: 0.8em;
}
.showcase-card-category {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.showcase-card-category:not(:last-child)::after { content: '·'; margin-left: 6px; font-weight: 400; }

/* M section tiles (showcase-m, added by showcase.php's own
   "showcase-{template}" class - see renderShowcase()) and category page
   tiles (.category-grid, category.php's own fixed 4-across grid) both
   want the date on its own line below the category, instead of the
   default row's "category · time". S/L/XL are untouched - they keep the
   base row layout (S has no dedicated styling yet; L/XL override
   .showcase-card-meta separately, further up). The category/time
   separator is only meaningful in a row, so it's dropped here too. */
.showcase-m .showcase-card-meta,
.category-grid .showcase-card-meta {
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
}
.showcase-m .showcase-card-category:not(:last-child)::after,
.category-grid .showcase-card-category:not(:last-child)::after {
  content: none;
}

/* flex: 1 (not the block it used to be) is what gives .showcase-card-body
   the row's full spare height to work with in the default column layout -
   .showcase-card itself already stretches to the tallest card in its grid
   row (CSS Grid's own default align-items: stretch), but that extra height
   was previously just dead space after a shorter card's content; now it's
   available for .showcase-card-meta to push into. L/XL override display/
   flex-direction back to their own row layout (see .showcase-l/.showcase-xl
   .showcase-card-link above) - flex: 1 there is the same value they already
   set themselves, so this changes nothing for them. */
.showcase-card-link { display: flex; flex-direction: column; flex: 1; color: inherit; text-decoration: none; }

.showcase-card-link:hover h3 { text-decoration: underline; text-decoration-color: var(--color-accent); }

/* The first section's lead story reads bigger, like a front-page splash -
   plain grid-column span, not a row-span/track-size trick, so it stays
   predictable regardless of how many other items are in that section.
   :not(.showcase-l):not(.showcase-xl) because those are their own, more
   specific templates with their own sizing - without the exclusion these
   three rules outrank L/XL's own image/h3 rules on specificity alone (5
   class-level selectors here vs. 2 there) and silently win regardless of
   source order, overriding their aspect-ratio and font-size with values
   meant for a completely different layout. */
.showcase-section:first-of-type:not(.showcase-l):not(.showcase-xl) .showcase-grid .showcase-card:first-child { grid-column: span 2; }
.showcase-section:first-of-type:not(.showcase-l):not(.showcase-xl) .showcase-grid .showcase-card:first-child img { aspect-ratio: 21 / 9; }
.showcase-section:first-of-type:not(.showcase-l):not(.showcase-xl) .showcase-grid .showcase-card:first-child h3 { font-size: 1.5em; }

/* ── Article / static page ────────────────────────────────────────*/
article { max-width: 720px; margin: 0 auto; }

article h1 {
  font-family: var(--font-headline);
  font-size: 2.1em;
  line-height: 1.2;
  font-weight: 800;
  margin: 0 0 10px;
}

.byline {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin: 0 0 20px;
}

.byline-author {
  display: flex;
  align-items: center;
  gap: 8px;
}

.byline-photo {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
}

.byline-meta {
  display: flex;
  flex-direction: column;
  font-size: 0.88em;
  line-height: 1.3;
}

.byline-name { color: var(--color-text); font-weight: 600; }
.byline-position { color: var(--color-muted); }

article > img { width: 100%; height: auto; margin-bottom: 20px; }

.page-body { font-size: 1.08em; line-height: 1.7; color: var(--palette-black); }
.page-body p { margin: 0 0 1.2em; }
.page-body img { max-width: 100%; height: auto; }

/* ── Live blog (migration 043/044) - replaces .page-body when is_liveblog
   is on (see article.php). Newest (and pinned) posts lead - same ordering
   liveblog_posts.php's own query already returns, maintained incrementally
   client-side as new ones arrive (item-card.php's renderLiveblogStreamScript).
   Two-column layout - a fixed time column on the left (relative label over
   the literal clock time, both computed once at render/insert - see
   _liveblogRelativeTime()), a bordered box on the right holding the
   post itself. Square corners throughout, no border-radius - this title's
   own look (same call already made for flag badges/cards elsewhere in
   this file), not a default worth assuming for every template. ─*/
.liveblog-stream { display: flex; flex-direction: column; gap: 20px; }

/* Summary pane above the stream (migration 046) - tinted the same way a
   pinned post already is (rgba(89,131,146,0.06)) so it reads as "the
   standing context for everything below", not just another entry. */
.liveblog-summary-box {
  background: rgba(89, 131, 146, 0.06);
  border: 1px solid var(--palette-blue);
  padding: 20px 24px;
  margin-bottom: 20px;
  font-size: 1.05em;
  line-height: 1.6;
  color: var(--palette-black);
}
.liveblog-summary-box p { margin: 0 0 1em; }
.liveblog-summary-box p:last-child { margin-bottom: 0; }
.liveblog-summary-box img { max-width: 100%; height: auto; display: block; margin: 10px 0; }

.liveblog-post {
  display: flex;
  align-items: flex-start;
  gap: 16px;
}

/* padding-top roughly levels the clock time with the box's own first line
   of text (its padding-top, below) rather than the box's outer edge. */
.liveblog-post-time {
  flex: 0 0 64px;
  text-align: right;
  padding-top: 20px;
}
.liveblog-post-relative {
  font-size: 0.8em;
  color: var(--color-muted);
  margin-bottom: 2px;
}
.liveblog-post-clock {
  display: block;
  font-weight: 800;
  font-size: 0.95em;
  color: var(--palette-black);
}

.liveblog-post-box {
  flex: 1;
  min-width: 0;
  background: #fff;
  border: 1px solid rgba(1, 22, 30, 0.14);
  padding: 20px 24px;
}
.liveblog-post--pinned .liveblog-post-box {
  border-color: var(--palette-blue);
  background: rgba(89, 131, 146, 0.06);
}

.liveblog-post-pin-badge {
  display: block;
  margin-bottom: 8px;
  text-transform: uppercase;
  font-weight: 700;
  font-size: 0.75em;
  letter-spacing: 0.02em;
  color: var(--palette-blue);
}

.liveblog-post-title {
  font-family: var(--font-headline);
  font-size: 1.3em;
  line-height: 1.3;
  font-weight: 800;
  margin: 0 0 10px;
  color: var(--palette-black);
}

.liveblog-post-body { font-size: 1.05em; line-height: 1.6; color: var(--palette-black); }
.liveblog-post-body p { margin: 0 0 1em; }
.liveblog-post-body p:last-child { margin-bottom: 0; }
.liveblog-post-body img { max-width: 100%; height: auto; display: block; margin: 10px 0; }

.liveblog-post-author {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid rgba(1, 22, 30, 0.1);
  font-size: 0.8em;
  font-weight: 700;
  color: var(--color-muted);
}

/* ── Article view (abbey's own components/article.php override) ────*/
/* .article-content-area itself needs no rule of its own - it's a direct
   child of <main> (not .showcase), so width/centering/gutter-padding
   already comes from main > * above, same masthead-matching content
   width every other page type gets. Everything in the header block below
   (title, cut, time, image) is a plain block/replaced element with no
   width rule of its own, so it just fills this box's already-constrained
   width - 100% of "the content area" is the default, not something to
   declare. */
/* Flag badges (migration 040/042) - structure only here (size, weight,
   spacing); colour is deliberately NOT set on the base class. Each flag
   gets its own modifier class keyed off flagkey
   (article-flag--{flagkey}/showcase-card-flag--{flagkey}, stamped on by
   article.php/item-card.php) - that's what a real flag's actual look
   comes from, same idea as showcase-l/showcase-xl/showcase-breaking
   already being plain classes this stylesheet - not the admin portal -
   decides the look of. A flag with no matching modifier rule yet just
   falls back to this neutral default rather than rendering unstyled. */
.article-flags { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
.article-flag {
  display: inline-block;
  padding: 4px 10px;
  font-size: 0.8em;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  background: var(--palette-blue);
  color: #fff;
}

/* "Breaking News" example (flagkey breaking-news, from the Flags admin's
   own slug-from-label default) - black badge / yellow text, same
   var(--palette-breaking) gradient .showcase-breaking already uses
   site-wide for an urgent-news band, applied to just .article-flag-header
   (the badge + headline, article.php) - not the whole
   .article-content-area (cut/category/time/hero image too), which read as
   a mistake rather than emphasis. Square corners (no border-radius) - this
   title's own look. Add a --{flagkey} rule like this pair for every flag
   that needs its own look; anything without one keeps the neutral default
   above. */
.article-flag--breaking-news { background: var(--palette-black); color: rgb(249, 255, 0); }
.article-flag-header--flag-breaking-news {
  background: var(--palette-breaking);
  padding: 24px;
  margin: 0 0 20px;
}

/* Same treatment as .showcase-card-category (a showcase/category-tile's
   own category label) - uppercase, bold, letter-spaced - so a category
   name reads the same way wherever it shows up on the site. */
.article-category {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--color-accent);
  margin: 0 0 8px;
}

.article-title {
  font-family: var(--font-headline);
  font-size: 2.415em; /* 2.1em + 15% */
  line-height: 1.2;
  font-weight: 800;
  margin: 0 0 12px;
}

/* cut is TinyMCE-authored (see item-edit.js), same trust/escaping model
   as .page-body - raw HTML, its own <p> margins reset since this block
   already carries its own. */
.article-cut {
  font-size: 1.2em;
  line-height: 1.5;
  color: var(--color-muted);
  margin: 0 0 14px;
}
.article-cut p { margin: 0; }

.article-time {
  display: block;
  font-size: 0.85em;
  color: var(--color-muted);
  margin: 0 0 20px;
}

.article-image-wrap { position: relative; margin: 0 0 24px; }
.article-image { width: 100%; height: auto; display: block; }

.article-image-credit {
  position: absolute;
  top: 12px;
  right: 12px;
  color: #fff;
  font-size: 0.7em;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  /* Straight drop shadow (no blur radius) rather than the soft/diffused
     look a blur would give - just an offset copy behind the text so it
     stays legible over a bright patch of the hero image. */
  text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.8);
}

.article-image-caption {
  background: var(--color-border);
  color: var(--palette-black);
  font-size: 0.85em;
  padding: 8px 12px;
}

/* A plain sibling of .article-area (not nested inside it - see
   article.php), so it isn't subject to .article-area's own width
   constraint below and instead just fills .article-content-area at 100%,
   the same as the header block above it. Used in place of the byline's
   old border-bottom so the rule reads as spanning the whole content area,
   not just the narrower reading column. */
.article-rule {
  border: none;
  border-top: 1px solid var(--color-border);
  margin: 0 0 20px;
}

/* Overrides the shared `article { max-width: 720px; margin: 0 auto; }`
   rule above - same element, but nested here (not a direct child of
   <main> the way component-defaults/article.php's own bare <article>
   is), so this class-level rule's higher specificity wins for every
   property it actually redeclares. width: 70% of .article-content-area's
   own already-constrained/padded box (not the viewport) - explicit
   margin-left/right: 0 (not the inherited margin: 0 auto) is what makes
   it sit at the box's LEFT edge instead of centering, leaving the
   remaining 30% clear on the right. Goes to 100% under the small-screens
   breakpoint below - there's no room to spare a 30% gutter once the page
   itself is phone-width. */
.article-area {
  width: 70%;
  max-width: none;
  margin: 0;
}

/* ── Category listing ─────────────────────────────────────────────*/
/* Direct child of <main> (not .showcase) - width/centering/gutter-padding
   comes from main > * above; top spacing from the nav bar comes from
   main's own padding-top now (not a category-specific margin-top - see
   main above), so this is just the gap before the content below. */
.category-header {
  margin-bottom: 20px;
}

/* Normal body font, not the headline serif article/showcase titles use -
   a category listing's own title reads more like a section label than a
   headline. The rule sits directly under the title as its own border,
   not on .category-header as a whole, so the description (a plain sibling
   below - see .category-description) reads BELOW the rule, not inside
   the same bordered block as the title. */
.category-title {
  font-family: var(--font-body);
  font-size: 1.8em;
  font-weight: 800;
  margin: 0;
  padding-bottom: 12px;
  border-bottom: 3px solid var(--palette-blue);
}

.category-description {
  margin: 10px 0 0;
  color: var(--color-muted);
}

/* Abbey's own category.php override (component-defaults/category.php's
   shared fallback has no equivalent - it uses plain .showcase-grid at
   .showcase-grid's own auto-fill sizing, see below) - a fixed 4-across
   grid ("3 rows of 4"), not auto-fill's however-many-fit-at-220px. Narrows
   to 2 then 1 alongside the existing small-screens breakpoint further
   down, since a hard 4-up would otherwise squeeze each tile far narrower
   than any other card on the site gets. */
.category-grid { grid-template-columns: repeat(4, 1fr); }

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

/* The no-JS fallback is a real link (see components/category.php's own
   comment, and category-infinite-scroll.js) - styled like one, centered
   under the grid it's extending, not like a button pretending to be
   something more interactive than a plain page reload actually is. */
.category-load-more {
  text-align: center;
  margin-top: 32px;
}
.category-load-more a {
  display: inline-block;
  padding: 10px 28px;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  text-decoration: none;
  color: var(--color-accent);
  font-weight: 600;
  font-size: 0.9em;
}
.category-load-more a:hover { border-color: var(--color-accent); }

/* ── Pagination ────────────────────────────────────────────────────*/
.pager {
  display: flex;
  justify-content: space-between;
  margin-top: 36px;
  padding-top: 16px;
  border-top: 1px solid var(--color-border);
  font-size: 0.9em;
  font-weight: 600;
}

.pager a { text-decoration: none; color: var(--color-accent); }
.pager a:hover { text-decoration: underline; }

/* ── Small screens ─────────────────────────────────────────────────*/
@media (max-width: 560px) {
  .site-nav-toggle-btn { display: flex; }

  /* Collapsed by default, revealed by the checkbox hack (see main.php) -
     :checked lives on an input that's a sibling of .site-nav-bar (both
     direct children of <body>), not inside it, so this selector can
     reach across to it. */
  .site-nav-bar { display: none; }
  .site-nav-toggle-checkbox:checked ~ .site-nav-bar { display: block; }

  .site-nav-inner { padding: 10px 24px; }
  .site-nav-list { flex-direction: column; gap: 4px; }

  /* Stacked input/button, taller allowance for the extra row - same
     "collapsed by default, :checked reveals it" mechanism as .site-nav-bar
     above, just via max-height instead of display (see the transition on
     .site-search-bar itself). */
  .site-search-bar-inner { padding: 10px 24px; }
  .site-search-bar-inner form { flex-direction: column; gap: 8px; }
  .site-search-toggle-checkbox:checked ~ .site-search-bar { max-height: 130px; }

  /* No hover on touch - a submenu just renders open, stacked and
     indented under its parent, instead of relying on :hover/:focus-within. */
  .site-nav-sublist {
    display: flex;
    position: static;
    background: none;
    border: none;
    padding: 4px 0 4px 16px;
  }

  .showcase-section:first-of-type .showcase-grid .showcase-card:first-child { grid-column: span 1; }

  /* The fixed-width time column has no room to sit beside the box once
     the reading column itself is this narrow - stacked instead, time row
     laid out horizontally (relative + clock side by side, not one above
     the other) directly above its own post. */
  .liveblog-post { flex-direction: column; gap: 4px; }
  .liveblog-post-time {
    flex: 0 0 auto;
    display: flex;
    gap: 8px;
    align-items: baseline;
    text-align: left;
    padding-top: 0;
  }
  .liveblog-post-relative { margin-bottom: 0; }
  .liveblog-post-box { padding: 16px; width: 100%; }

  /* Side-by-side is too cramped at phone widths - stack image above
     headline instead, same shape as an ordinary card, just bigger. XL
     gets the same treatment as L now that it has an image row too. */
  .showcase-l .showcase-card,
  .showcase-l .showcase-card-link,
  .showcase-xl .showcase-card,
  .showcase-xl .showcase-card-link {
    flex-direction: column;
    align-items: stretch;
  }
  .showcase-l .showcase-card img,
  .showcase-xl .showcase-card img {
    flex-basis: auto;
    width: 100%;
  }

  .category-grid { grid-template-columns: 1fr; }

  .showcase-xl .showcase-card h3 { font-size: 1.8em; }

  /* No 30% gutter to spare at phone widths - the reading column fills
     the same full content area the header block above it already does. */
  .article-area { width: 100%; }
}

/* ── Weather page (migration 052/053, component=7) ──────────────────*/
/* .weather-page is a plain <div>, not <article> - see weatherPage.php's
   own comment for why (the `article { max-width: 720px }` rule above
   would otherwise silently narrow it to the reading-column width). Being
   a bare child of <main>, it already gets the same width/centering
   main > * gives every other page kind - nothing here needs to set that
   itself. */
.weather-page h1 {
  font-family: var(--font-headline);
  font-size: 2.1em;
  line-height: 1.2;
  font-weight: 800;
  margin: 0 0 16px;
}

.weather-page-current {
  display: flex;
  gap: 16px;
}

.weather-page-current-box {
  width: 120px;
  height: 120px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Black text, not white - same reasoning as the reference CasparCG
   script's own temp badge (weather.php's own getTempColour() port,
   see weather.php): the temp-bg-* backgrounds span dark blues through
   bright yellows to dark reds, and black-with-a-light-stroke is what the
   original relied on for legibility across all of them rather than
   choosing a text colour per band. */
.weather-page-current-temp-box {
  font-family: var(--font-headline);
  font-size: 2.4em;
  font-weight: 800;
  color: black;
  -webkit-text-stroke: 0.5px rgba(255, 255, 255, 0.5);
}

.weather-page-current-icon-box {
  background: var(--palette-beige);
}

.weather-page-current-icon-box img {
  width: 76px;
  height: 76px;
}

.weather-page-current--unavailable {
  color: var(--color-muted);
  margin-bottom: 16px;
}

.weather-page-current-observed {
  color: var(--color-muted);
  font-size: 0.85em;
  margin: 8px 0 0;
}

/* "Divs styled like a table" - display:table/table-row/table-cell on the
   same <dl>/<div>/<dt>/<dd> markup (a <dl> is the semantically correct
   element for a set of label/value pairs), not a real <table> element. */
.weather-page-details {
  display: table;
  width: 100%;
  max-width: 420px;
  margin: 24px 0 32px;
  border-collapse: collapse;
}

.weather-page-detail {
  display: table-row;
}

.weather-page-detail dt,
.weather-page-detail dd {
  display: table-cell;
  padding: 8px 12px;
  border-bottom: 1px solid var(--color-border);
  font-size: 0.92em;
}

.weather-page-detail dt {
  color: var(--color-muted);
  font-weight: 600;
  width: 40%;
}

.weather-page-detail dd {
  margin: 0;
  color: var(--color-text);
}

/* Two rows per day: the full weekday name, then Hi/Lo/Precip%/icon/UV in
   a row underneath - not the table-cell treatment .weather-page-details
   above uses, since each day's second row has a fixed, known set of
   items rather than an arbitrary label/value list, so a plain flex row
   is simpler and doesn't need column-alignment across rows the way the
   details table does. */
.weather-page-forecast-day {
  padding: 12px 0;
  border-bottom: 1px solid var(--color-border);
}

.weather-page-forecast-day:last-child {
  border-bottom: none;
}

.weather-page-forecast-day-name {
  font-weight: 700;
  margin-bottom: 8px;
}

.weather-page-forecast-day-row {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

/* Same "black text, band-coloured background" treatment as the current-
   conditions temp box (see above) - just a smaller pill instead of a
   120px square, since two of these now sit inline in a row of five. */
.weather-page-forecast-hi,
.weather-page-forecast-lo {
  padding: 4px 10px;
  font-weight: 700;
  font-size: 0.9em;
  color: black;
  -webkit-text-stroke: 0.5px rgba(255, 255, 255, 0.5);
}

.weather-page-forecast-precip,
.weather-page-forecast-uv {
  color: var(--color-muted);
  font-size: 0.9em;
}

.weather-page-forecast-icon img {
  width: 32px;
  height: 32px;
  display: block;
}

#showcase-home-page .showcase-section-prefix { font-size: 1.5em; line-height: 80%; text-transform: uppercase; font-weight: 600; }
#showcase-home-page .showcase-section-prefix::after {height: 0.3em; display: block;content:'';}

/* ── Tags (migration 049/056) ─────────────────────────────────────*/
/* White background, --color-accent text+border - the same blue an
   article's own .article-category already uses, just as a genuine pill
   (background/border/radius) rather than plain text, since no pill style
   existed anywhere to copy before this. Shared by the article-bottom tag
   list, the showcase "Tags" section (.showcase-tags), and anywhere else
   a tag renders, so a tag always looks the same wherever it shows up. */
.tag-pill {
  display: inline-block;
  padding: 4px 12px;
  border: 1px solid var(--color-accent);
  border-radius: 999px;
  background: var(--palette-white);
  color: var(--color-accent);
  font-size: 0.85em;
  font-weight: 600;
  text-decoration: none;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

a.tag-pill:hover {
  background: var(--color-accent);
  color: var(--palette-white);
}

.article-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--color-border);
}

.showcase-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}